概述
羽鉴系统采用先分割、后识别的两阶段架构:
分割模型(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 需商务授权,额度由平台统一结算,请联系官方 |
推荐调用流程
- 上传原图 —
POST /upload-only/(multipart,字段image) - 字符分割 —
POST /upload/或POST /detector/,获取全部检测框 - (可选)SSE 进度 — 分割时携带
session_id,订阅GET /progress-stream/ - 裁剪小图 —
POST /save-char-images/批量服务端裁剪,或客户端自行裁剪 - 批量识别 —
POST /recognize-chars-batch/传入小图 URL 列表(需登录,额度由平台托管扣减)
上述碑文流程中的额度扣减由羽鉴官方平台自动完成,集成开发者无需自行调用扣减接口。若仅需单字体验,可直接使用免费开放的单字识别页。
TIF/TIFF 格式在分割阶段由服务端自动转换为 JPG;JSON 导入场景可先调用 POST /convert-tif/ 预转换。
分割模型 API
以下接口均调用同一分割模型。detection_mode 默认 full(双通道整图检测)。
/detector/
公开 · 无需 Session小程序/第三方直连分割接口。上传整图文件,返回全部检测框。
请求
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
file | file | 是 | multipart 图片文件(JPG/PNG/TIF) |
session_id | string | 否 | 进度追踪 ID,配合 SSE 使用 |
detection_mode | string | 否 | 检测模式,默认 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"
/upload/
需 Session 登录网页端分割接口。对已上传至 media/stele_images/ 的图片执行检测,返回前端渲染所需的完整 detections 结构(含 box_points、颜色等)。
请求(application/x-www-form-urlencoded 或 multipart)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
file_name | string | 是 | 已上传文件名,如 墓志.jpg |
detection_mode | string | 否 | 默认 full |
session_id | string | 否 | UUID,用于 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
}
/upload-only/
公开 · 无需 Session仅上传图片至服务端,不执行分割。保持原格式存储,TIF 转换在后续分割步骤进行。
请求
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
image | file | 是 | multipart 图片文件 |
成功响应
{
"success": true,
"file_name": "sample.jpg",
"image_url": "/media/stele_images/sample.jpg",
"image_width": 2080,
"image_height": 3120,
"is_converted_from_tif": false
}
/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 候选,并附带书体与领域分类头。
/recognize-char/
需 Session 登录单字识别。直接上传字符小图文件,适用于交互式修正或低频调用。
请求(multipart/form-data)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
imageFile | file | 是 | 单字裁剪图(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"
/recognize-char-free/
公开 · 免费开放官方单字识别页使用的接口。无需登录、不扣额度,请求/响应格式与 /recognize-char/ 相同。适用于单字拓片、字帖单字等场景。
请求(multipart/form-data)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
imageFile | file | 是 | 单字裁剪图(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])
/recognize-chars-batch/
需 Session 登录 · 推荐批量识别。传入服务端已裁切的小图 URL 列表,按顺序返回识别结果,显著优于逐张调用。
请求(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
image_urls | string[] | 是 | 字符小图 URL,顺序与结果一一对应 |
batch_size | int | 否 | 模型批大小,默认 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();
/save-char-images/
公开 · 无需 Session服务端批量裁剪字符小图,返回 URL 列表,供批量识别接口使用。
请求(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
image_url | string | 是 | 原图 URL(/media/stele_images/...) |
orig_box_points | number[][][] | 是 | 四角坐标 [[x,y],...] × N |
column_numbers | int[] | 否 | 列号,与框一一对应 |
row_indices | int[] | 否 | 行序号 |
crop_padding | int | 否 | 裁剪外扩像素 |
use_resized | bool | 否 | 是否使用 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: "额度不足" | 引导用户充值 |
| 404 | xyID 不存在、文件不存在 | error | 确认 xyID 或 file_name |
| 405 | HTTP 方法不允许 | error | 使用文档指定的方法 |
| 500 | 模型推理/服务器内部错误 | error 或 success:false | 指数退避重试,勿无限循环 |
最佳实践与安全清单
- 生产环境启用 HTTPS,并为 Cookie 设置
Secure、HttpOnly(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