从作业纸到结构化批阅:扫描答题纸与点阵笔的双路识别
本文目录
这篇文章复盘一个早期的智能作业识别项目。它处理的不是单一的“图片转文字”,而是两种输入:扫描版答题纸和点阵笔书写轨迹。系统先把页面几何校正到统一模板坐标,再分别识别学号、选择题、分数与判断题,最后输出带题目结构的结果。
本文核对的是公开默认分支的固定提交 6103d301e88fb98ad2fbaafcd9b9d9be2a1bc280,依据 README、主要流程、预处理、轨迹匹配与识别器源码作静态分析。没有安装依赖、加载模型、处理样本或测量准确率;下面的实现描述不等于可复现的效果报告。
两种输入,共用题目结构
扫描图片 -> QR/方向识别 -> 页面定位点校正 -> 学号与各题型识别 ─┐
├-> 题目结构 + 识别结果
点阵笔 JSON -> 页面/轨迹解码 -> 坐标变换 -> 轨迹归题与分类 ────┘
两条流程会读取页面模板和题目区域,因此模型并不是全部。扫描图、点阵轨迹、页面标识、题目坐标和题型枚举必须遵循同一套页面契约;模板坐标稍有偏差,后面的裁剪和轨迹归题就会一起偏移。
扫描版:先把页面摆正,再看答案
扫描入口按调用者传入的图片顺序把相邻图片组成一组,通常是一组正反面。它会在页面指定角落尝试识别二维码,并结合二维码位置判断页面是否翻转;当前代码通过 180 度翻转处理方向,不是面向任意旋转角度的通用文档矫正器。README 也要求多页输入先按正确顺序整理。函数本身没有排序,因此文件枚举顺序不应被当成页面顺序。
页面校正从四个角附近寻找黑色定位点。预处理先做灰度化和 Otsu 二值化,再用形态学闭运算、开运算压制噪点;检测到三个定位点时使用仿射变换,四个点齐全时使用透视变换,把拍摄页面映射到模板尺寸。前页的二维码位置还会参与定位点恢复。这个流程轻量、容易理解,但依赖纸张布局、定位点对比度与角落区域假设;缺失多个标记、强阴影、折页和大角度拍摄都需要额外样本验证。
页面坐标对齐后,流程从模板数据取得页面尺寸与题目结构,按题型分发到不同识别器:
- 选择题:裁出选项区域,使用平均灰度和填涂比例启发式判断;模糊时再比较候选选项的灰度差。它适合固定模板和清晰填涂,源码没有展示跨纸张、笔色、擦除痕迹的评测结果。
- 分数题:把局部图像转成固定尺寸的输入,再交给 ONNX Runtime 分类;分类结果按字符拼接,特殊类别会被映射为空。模型输出没有在这条流程里附带置信度或人工复核状态。
- 学号:使用独立 ONNX 模型,对序列输出做 CTC 风格的空白与相邻重复折叠,再拼成数字串。仓库包含模型文件路径约定,但没有随代码提供足以验证识别率的固定标注集。
- 判断题:根据题目类型走单独的扫描版识别器;点阵笔路径则可用笔迹区域的图像分类结果。
把识别器按题型拆开是合理的边界:选择题看填涂区域,手写分数看字符图像,学号看序列输出。这样可以单独替换模型或阈值,但也意味着题型枚举、模板坐标、图像预处理和模型输入尺寸必须一起版本化。
点阵笔:把轨迹映射回题目区域
点阵笔流程从 JSON 记录读取页面编号和书写轨迹,取得对应页面图像及题目模板。页面可使用定位点或模板提供的位置进行仿射校正;随后把轨迹点也变换到同一坐标系,再按题目区域筛选笔迹、提取连通区域并交给分数/判断识别。选择题仍会检查页面图像中的填涂区域。
这条路径的关键是坐标,而不只是模型:若图片经过缩放、旋转或透视校正,轨迹必须使用完全一致的变换矩阵。页面内容和轨迹一旦错位,识别器仍可能返回格式正确的结果,却把答案归到相邻题目。
源码中的轨迹匹配实现有一个重要边界:候选笔迹先依据题目外框的矩形交并比阈值与面积门槛筛选;分段后通过垂直方向重叠比,选择最像的子题区域。这里的重叠度只比较纵向范围,不是二维 IoU。每个笔迹区域独立选择最大值,没有做全局一对一分配,因此多个区域有可能同时指向同一道子题。若页面布局紧凑或答案跨框书写,应该增加歧义检测与人工复核,而不能把“找到最大重叠”理解为稳定匹配。
源码审阅发现的复现风险
- 依赖清单有无效版本写法:
opencv-python=4.5.3.56使用单个等号,不符合 pip requirement 语法;若想固定版本应使用==。即使修正语法,也应把 Python、NumPy、OpenCV、ONNX Runtime 和模型文件一并锁定。 - 旧版 NumPy 别名:匹配代码使用
np.float,二维码解码使用np.int。这些旧别名在新版本 NumPy 中已移除,开放式的numpy>=1.18.5可能解析到不兼容版本。 - 输入顺序是隐式契约:扫描流程按相邻项配对,示例使用文件通配符取得列表,但核心函数没有排序。应由调用方明确按学生/页码/正反面排序,并在处理前检查成对关系。
- 远端图片请求缺少可靠失败边界:点阵笔流程的图片读取没有设置超时;异常分支打印错误后仍会继续读取响应对象。网络失败时可能出现二次异常,也可能让整批处理失去明确的失败状态。
- 评估材料不足以证明线上质量:公开树中能看到模型资产和流程代码,但没有一套固定的标注验证集、准确率报告与可重复的端到端回归。模型能加载不等于题型识别正确。
这些是静态源码能够支持的维护风险,不是我已经复现的线上故障。仓库根目录的 LICENSE 与 GitHub 仓库元数据均标注 BSD 3-Clause;这不证明随仓库出现的模型权重、训练数据或第三方组件也按相同条款授权,来源和商业使用条件仍需分别确认。
如果继续维护,先补这些
- 修正并锁定依赖,启动时校验模型文件存在、模型输入输出节点和版本信息。
- 为每类题目准备匿名化、固定版本的验证样本,分别报告准确率、漏识、错归题和拒识率。
- 把页面顺序、正反面配对、模板版本和坐标变换写入显式数据结构;对缺页、重复页、坏图与缺标记给出可恢复错误。
- 给图片下载设置连接/读取超时和大小上限;异常要立即返回可诊断状态,并清理临时文件。
- 轨迹归题同时考虑二维位置、书写时间与歧义;匹配不唯一时进入人工确认,而不是静默选择一个候选。
- 对身份证明、作业图片和笔迹轨迹定义最小留存、访问控制、删除与日志脱敏策略。
可以借鉴的工程方法
这个项目最有价值的不是某个分类模型,而是把“页面几何校正、模板坐标、题型路由、视觉识别、轨迹映射、结构化结果”串成一条可拆解的处理链。遇到识别问题时,可以先定位输入契约和中间坐标,再决定是否需要更换模型;同时把模型版本、模板版本、阈值和样本集作为一个整体记录。这样性能、准确率和失败边界才有机会被分别讨论。
源仓库
完整源码、变更历史与项目许可证请以仓库当前内容为准。本文是原创的工程解读,不是 README 或源代码的镜像,也没有声称在当前环境运行成功:cdyforever/canpoint-work
Live Notes
留言提交后会先进入审核,审核通过后对其他读者公开。