南京西之羽文化科技有限公司
文物数字化 · 古文字智能识别
REST · JSON · SSE

羽鉴识别系统 API 文档

羽鉴(Yujian)是西之羽自主研发的碑文垂类大模型体系,由分割模型(字符定位与分栏)与识别模型(古文字单字识别)两阶段协同工作。 本文档面向集成开发者,说明接口契约、鉴权方式与安全要求。

Base URL https://xizhiyu.tech
协议 HTTPS(生产环境强制)
字符编码 UTF-8

概述

羽鉴系统采用先分割、后识别的两阶段架构:

分割模型(Segmentation)

基于深度学习框架,对碑刻拓片、墓志、楹联等复杂版面进行单字级定位,输出 bounding box、置信度及图像尺寸,并支持墓志盖 3×3 大框过滤策略。

  • 输入:JPG / PNG / TIF(TIF 服务端自动转 JPG)
  • 输出:检测框坐标、置信度、图像宽高
  • 典型耗时:视图像分辨率与 GPU 负载,数秒至数十秒

识别模型(Recognition)

基于自研古文字识别网络,对单字小图进行 Top-K 候选预测,并附带书体(script)与领域(domain)辅助头信息,适用于篆书、隶书、楷书等碑刻文字。

  • 输入:单字裁剪图(PNG/JPG,建议 ≥ 32×32 px)
  • 输出:Top-5 候选字、置信度、书体/领域标签
  • 支持单张与批量(batch_size ≤ 64)推理
接口分组说明

/detector/ 面向小程序/第三方直连;/upload/ 面向网页端(需登录 Session);识别类接口均需有效登录态并按字扣减额度。

安全与鉴权

网页 Session 鉴权

分割(/upload/)与全部识别接口需要用户先通过西羽 ID 登录,服务端将 xyid 写入 Django Session Cookie。

# 1. 登录(获取 Session Cookie)
curl -X POST "https://xizhiyu.tech/api/login-by-xyid/" \
  -H "Content-Type: application/json" \
  -c cookies.txt \
  -d '{"xyid":"YOUR_XYID_HERE"}'

# 2. 携带 Cookie 调用需鉴权接口
curl -X POST "https://xizhiyu.tech/upload/" \
  -b cookies.txt \
  -F "file_name=sample.jpg" \
  -F "detection_mode=full"
import requests

BASE = "https://xizhiyu.tech"
session = requests.Session()

# 登录
r = session.post(f"{BASE}/api/login-by-xyid/", json={"xyid": "YOUR_XYID_HERE"})
r.raise_for_status()

# 分割
r = session.post(f"{BASE}/upload/", data={
    "file_name": "sample.jpg",
    "detection_mode": "full",
})
print(r.json())
const BASE = "https://xizhiyu.tech";

// 浏览器端须同域携带 credentials
await fetch(`${BASE}/api/login-by-xyid/`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  credentials: "include",
  body: JSON.stringify({ xyid: "YOUR_XYID_HERE" }),
});

const fd = new FormData();
fd.append("file_name", "sample.jpg");
fd.append("detection_mode", "full");
const seg = await fetch(`${BASE}/upload/`, {
  method: "POST",
  credentials: "include",
  body: fd,
});
console.log(await seg.json());

额度机制

碑文整幅识别属于平台托管的付费能力。用户在官方网页登录并完成识别后,由羽鉴平台服务端自动扣减额度,无需、也不应由第三方集成方自行调用扣减接口。

规则说明
新用户注册首次 xyID 登录官方网页端,赠送默认识别额度
扣减方式官方碑文识别页在识别成功后,由平台内部按识别字数自动扣减
单字识别单字识别页免费开放,无需登录、不扣额度(接口:/recognize-char-free/
第三方集成批量/商用 API 需商务授权,额度由平台统一结算,请联系官方

推荐调用流程

  1. 上传原图POST /upload-only/(multipart,字段 image
  2. 字符分割POST /upload/POST /detector/,获取全部检测框
  3. (可选)SSE 进度 — 分割时携带 session_id,订阅 GET /progress-stream/
  4. 裁剪小图POST /save-char-images/ 批量服务端裁剪,或客户端自行裁剪
  5. 批量识别POST /recognize-chars-batch/ 传入小图 URL 列表(需登录,额度由平台托管扣减)
额度说明

上述碑文流程中的额度扣减由羽鉴官方平台自动完成,集成开发者无需自行调用扣减接口。若仅需单字体验,可直接使用免费开放的单字识别页

注意

TIF/TIFF 格式在分割阶段由服务端自动转换为 JPG;JSON 导入场景可先调用 POST /convert-tif/ 预转换。

分割模型 API

以下接口均调用同一分割模型。detection_mode 默认 full(双通道整图检测)。

POST

/detector/

公开 · 无需 Session

小程序/第三方直连分割接口。上传整图文件,返回全部检测框。

请求

字段类型必填说明
filefilemultipart 图片文件(JPG/PNG/TIF)
session_idstring进度追踪 ID,配合 SSE 使用
detection_modestring检测模式,默认 full

成功响应 200

{
  "success": true,
  "count": 128,
  "detections": [
    {
      "bbox": [120.5, 88.0, 156.2, 124.8],
      "confidence": 0.923,
      "class": 0
    }
  ],
  "image_size": { "width": 2080, "height": 3120 }
}

代码示例

import requests

url = "https://xizhiyu.tech/detector/"
with open("stele.jpg", "rb") as f:
    resp = requests.post(
        url,
        files={"file": ("stele.jpg", f, "image/jpeg")},
        data={"detection_mode": "full", "session_id": "my-session-001"},
        timeout=300,
    )
result = resp.json()
print(f"检测到 {result['count']} 个字")
curl -X POST "https://xizhiyu.tech/detector/" \
  -F "file=@stele.jpg" \
  -F "detection_mode=full" \
  -F "session_id=my-session-001"
POST

/upload/

需 Session 登录

网页端分割接口。对已上传至 media/stele_images/ 的图片执行检测,返回前端渲染所需的完整 detections 结构(含 box_points、颜色等)。

请求(application/x-www-form-urlencoded 或 multipart)

字段类型必填说明
file_namestring已上传文件名,如 墓志.jpg
detection_modestring默认 full
session_idstringUUID,用于 SSE 进度推送

成功响应 200

{
  "success": true,
  "file_name": "墓志.jpg",
  "image_url": "/media/stele_images/墓志.jpg",
  "image_width": 2080,
  "image_height": 3120,
  "total_detections": 128,
  "detections": [
    {
      "center_x": 138.35,
      "center_y": 106.4,
      "box_points": [[120.5,88.0],[156.2,88.0],[156.2,124.8],[120.5,124.8]],
      "orig_box_points": [[120.5,88.0],[156.2,88.0],[156.2,124.8],[120.5,124.8]],
      "confidence": 0.923,
      "color": "#6BA15B",
      "segmentation_color": "#6BA15B",
      "source_index": 1
    }
  ],
  "boxes": [["单列", 1, [120.5, 88.0, 156.2, 124.8], 0.923]],
  "session_id": "uuid-string",
  "is_converted_from_tif": false
}
POST

/upload-only/

公开 · 无需 Session

仅上传图片至服务端,不执行分割。保持原格式存储,TIF 转换在后续分割步骤进行。

请求

字段类型必填说明
imagefilemultipart 图片文件

成功响应

{
  "success": true,
  "file_name": "sample.jpg",
  "image_url": "/media/stele_images/sample.jpg",
  "image_width": 2080,
  "image_height": 3120,
  "is_converted_from_tif": false
}
GET

/progress-stream/

SSE 事件流

Server-Sent Events 实时进度。分割请求携带 session_id 后,客户端订阅此端点获取排队、推理进度消息。

请求参数

参数说明
session_id与分割请求一致的 UUID

事件示例

data: {"type": "connected"}

data: {"step": 1, "message": "开始检测...", "progress": 8}

data: {"type": "heartbeat"}

data: {"type": "close"}
const sid = crypto.randomUUID();
const es = new EventSource(
  `https://xizhiyu.tech/progress-stream/?session_id=${encodeURIComponent(sid)}`
);
es.onmessage = (ev) => {
  const data = JSON.parse(ev.data);
  console.log("进度:", data);
  if (data.type === "close") es.close();
};

// 分割时传入相同 session_id
const fd = new FormData();
fd.append("file_name", "sample.jpg");
fd.append("session_id", sid);
await fetch("https://xizhiyu.tech/upload/", { method: "POST", credentials: "include", body: fd });

识别模型 API

识别接口均需有效 Session。模型对单字图像输出 Top-5 候选,并附带书体与领域分类头。

POST

/recognize-char/

需 Session 登录

单字识别。直接上传字符小图文件,适用于交互式修正或低频调用。

请求(multipart/form-data)

字段类型必填说明
imageFilefile单字裁剪图(PNG/JPG)

成功响应 200

{
  "status": 0,
  "result": {
    "list": [
      { "name": "墓志", "ft": "墓志", "score": 0.9821 },
      { "name": "墓", "ft": "墓", "score": 0.0124 }
    ],
    "script": { "name": "隶书", "score": 0.91 },
    "domain": { "name": "碑刻", "score": 0.88 }
  }
}
import requests

session = requests.Session()
session.post("https://xizhiyu.tech/api/login-by-xyid/", json={"xyid": "YOUR_XYID"})

with open("char.png", "rb") as f:
    r = session.post(
        "https://xizhiyu.tech/recognize-char/",
        files={"imageFile": ("char.png", f, "image/png")},
    )
top1 = r.json()["result"]["list"][0]
print(f"识别结果: {top1['name']} ({top1['score']:.2%})")
curl -X POST "https://xizhiyu.tech/recognize-char/" \
  -b cookies.txt \
  -F "imageFile=@char.png"
POST

/recognize-char-free/

公开 · 免费开放

官方单字识别页使用的接口。无需登录、不扣额度,请求/响应格式与 /recognize-char/ 相同。适用于单字拓片、字帖单字等场景。

请求(multipart/form-data)

字段类型必填说明
imageFilefile单字裁剪图(PNG/JPG)

在线体验

打开单字识别页 →

curl -X POST "https://xizhiyu.tech/recognize-char-free/" \
  -F "imageFile=@char.png"
import requests

with open("char.png", "rb") as f:
    r = requests.post(
        "https://xizhiyu.tech/recognize-char-free/",
        files={"imageFile": ("char.png", f, "image/png")},
        timeout=60,
    )
print(r.json()["result"]["list"][0])
POST

/recognize-chars-batch/

需 Session 登录 · 推荐

批量识别。传入服务端已裁切的小图 URL 列表,按顺序返回识别结果,显著优于逐张调用。

请求(application/json)

字段类型必填说明
image_urlsstring[]字符小图 URL,顺序与结果一一对应
batch_sizeint模型批大小,默认 32,范围 1–64

成功响应

{
  "status": 0,
  "results": [
    {
      "list": [{ "name": "魏", "ft": "魏", "score": 0.97 }],
      "script": { "name": "隶书", "score": 0.89 },
      "domain": { "name": "碑刻", "score": 0.85 }
    },
    null
  ]
}

null 表示对应 URL 无法读取或裁剪失败,请检查 image_urls 是否为服务端 /media/ 路径。

import requests

session = requests.Session()
session.post("https://xizhiyu.tech/api/login-by-xyid/", json={"xyid": "YOUR_XYID"})

payload = {
    "image_urls": [
        "https://xizhiyu.tech/media/char_images/sample/1234567890/col_0-1.png",
        "https://xizhiyu.tech/media/char_images/sample/1234567890/col_0-2.png",
    ],
    "batch_size": 32,
}
r = session.post("https://xizhiyu.tech/recognize-chars-batch/", json=payload)
for i, res in enumerate(r.json()["results"]):
    if res:
        print(i, res["list"][0]["name"])
const resp = await fetch("https://xizhiyu.tech/recognize-chars-batch/", {
  method: "POST",
  credentials: "include",
  headers: {
    "Content-Type": "application/json",
    "X-CSRFToken": getCookie("csrftoken"),
  },
  body: JSON.stringify({
    image_urls: charImageUrls,
    batch_size: 32,
  }),
});
const { results } = await resp.json();
POST

/save-char-images/

公开 · 无需 Session

服务端批量裁剪字符小图,返回 URL 列表,供批量识别接口使用。

请求(application/json)

字段类型必填说明
image_urlstring原图 URL(/media/stele_images/...)
orig_box_pointsnumber[][][]四角坐标 [[x,y],...] × N
column_numbersint[]列号,与框一一对应
row_indicesint[]行序号
crop_paddingint裁剪外扩像素
use_resizedbool是否使用 2096 缩放缓存图(提速)

成功响应

{
  "success": true,
  "char_image_urls": [
    "https://host/media/char_images/sample/1700000000/col_0-1.png",
    "https://host/media/char_images/sample/1700000000/col_0-2.png"
  ]
}

用户与额度 API

以下接口服务于羽鉴官方网页客户端(登录、额度查询等)。第三方集成请勿直接调用额度扣减类接口。

方法路径鉴权说明
POST/api/login-by-xyid/Body: {"xyid":"..."},写入 Session
GET/api/user-info/Session返回用户信息、剩余额度、VIP 状态
GET/api/quota-history/Session额度变动明细(最近 100 条)
POST/api/logout/Session清除 Session,退出登录

登录响应示例

{
  "success": true,
  "message": "欢迎回来,用户名!",
  "user": {
    "xyid": "XYLxxxxxx",
    "username": "用户昵称",
    "web_quota": 1000,
    "is_admin": false,
    "is_vip": false
  }
}

错误码与异常处理

HTTP场景响应字段处理建议
400缺少必填参数、格式错误error检查请求体/字段名
401未登录error: "请先登录后再操作"先调用 login-by-xyid
403额度不足error: "额度不足"引导用户充值
404xyID 不存在、文件不存在error确认 xyID 或 file_name
405HTTP 方法不允许error使用文档指定的方法
500模型推理/服务器内部错误errorsuccess:false指数退避重试,勿无限循环

最佳实践与安全清单

  • 生产环境启用 HTTPS,并为 Cookie 设置 SecureHttpOnly(Django 生产配置)。
  • 不要在客户端存储或传输其他用户的 xyID;每个终端独立 Session。
  • 大图分割前先通过 /upload-only/ 上传,避免重复传输。
  • 识别阶段优先使用 /recognize-chars-batch/,batch_size 32 为推荐值。
  • 分割/识别均为计算密集型,请设置合理 timeout(建议 ≥ 300s)并实现 UI 进度反馈。
  • /detector/ 虽无需 Session,建议经 API 网关限流或仅内网开放。
  • 单字体验请使用官方免费页 /single-char-core//recognize-char-free/
  • 集成测试可参考项目根目录 test_segmentation_api.py
  • 商业合作与专属 API Key 方案请联系:nanjingxizhiyu@163.com