基于OpenCV的答题卡识别系统设计与实现:从图像预处理到OMR判读

📅 发布时间:2026/8/31 23:43:58
基于OpenCV的答题卡识别系统设计与实现:从图像预处理到OMR判读
简介本资源是一套面向高校计算机、人工智能及相关专业本科生的毕业设计或课程大作业实践方案聚焦教育场景下的答题卡自动化识别与评分问题融合图像处理、深度学习与OCR技术。压缩包共17个文件含14张真实答题卡样本图像JPG格式用于模型训练与测试1个C主程序文件main.cpp实现核心检测与识别流程1份Markdown文档README.md详述系统架构、运行环境与使用说明另含.gitignore等辅助文件整体大小为15.56MB。目前已有39人下载学习适合具备OpenCV基础与CNN入门知识的学习者开展端到端项目实践。读者可直接复现从图像预处理灰度化、二值化、区域分割、卷积神经网络答案定位到OCR文字识别的完整技术链路并基于提供的样例图像快速验证系统效果具备较强的教学参考性与工程可拓展性。 我接手过好几次类似的阅卷需求最开始也天真地以为答题卡识别就是“把涂黑的地方统计出来”直到真正拿到一叠用过的答题卡才发现纸张会歪、光线会偏、学生会乱涂甚至有人用蓝色水笔涂卡而系统却只认2B铅笔的灰度响应。这个项目最终做下来核心收获不是某个模型多先进而是把“图像预处理 版面定位 选项判读 异常检测”这一整条链路跑通并让它能扛住批量场景的稳定性要求。如果你正准备做答题卡识别系统或者在做类似的文档图像检测项目这篇文章应该能帮你少走不少弯路。按惯例先交代一下系统的整体目标输入是一张或多张已作答的答题卡图片扫描件或手机拍摄件输出是结构化的答题结果包括每道题的选项、准考证号、异常标记等最后导成Excel或成绩表。它的应用场景很明确——学校阅卷、机构测评、企业内部考试凡是需要批量处理纸质答题卡的地方都用得上。下面我按实际开发顺序把这个系统的设计和实现拆开来讲。1. 从纸质答题卡到结构化成绩系统到底在解决什么问题1.1 阅卷场景倒推出来的功能需求答题卡识别的业务需求并不复杂但如果你只在理想环境下做Demo很容易漏掉真实场景里的关键功能。我建议先不要着急写代码先用“倒推法”把需求列清楚。一份答题卡从交上来到最终变成成绩单上的分数中间要经历哪些环节拿到一张扫描件第一步永远是判断这张卡能不能用。有些卡在扫描时放歪了角度可能达到10度以上如果不做校正后续所有定位逻辑都会失效。有些卡光线不均匀中间亮、两侧暗或者直接被手挡住了一半这种情况下如果还按固定阈值做分割结果一定惨不忍睹。还有些答题卡存在折叠、污渍、撕角等问题这些都不是“算法炫技”能解决的需要系统具备图像质量评估能力至少能给出“这张卡需要重新扫描”的提示。第二步是版面定位。答题卡不是白纸它上面有学生信息区域、准考证号区域、单选题区、多选题区、客观题区甚至还有缺考标记。系统必须先找到若干个定位标记点通过这些标记点计算透视变换矩阵把整张卡的版面映射到一个标准坐标系里才能准确切割出每一个选项区域。这个环节最容易出问题如果只依赖单一标记点一旦那个标记被污渍遮住整张卡就废了如果定位点找错后续所有切割坐标都会偏移。第三步才是真正的“识别”对每个选项区域判断里面有没有被涂写。这一步看着简单实际坑很多——涂卡笔画深浅不一、橡皮擦残留、笔尖划痕、扫描产生的椒盐噪声都会干扰判断。系统需要在灰度图上提取特征用连通域、面积比例、边缘密度等维度综合判定而不是简单地数黑色像素。最后一步是数据汇总和异常处理。识别结果不能只是一张张孤立图片的输出它要能对应到学号、题号能检测出“某道题多涂了”“某个学生准考证号少涂一位”等异常并且把低置信度的结果挑出来交给人工复核。这一步在工程里占比很高甚至决定一个系统能不能真正投入使用。1.2 系统的整体处理流程与技术选型明确了需求整体流程就很清晰了我的实现分为六个阶段图像采集与质量初筛图像预处理灰度化、去噪、透视校正、光照修正版面分析与定位标记点检测、坐标映射选项区域切割与特征提取选项判定与逻辑校验单选/多选逻辑、异常检测结果导出与人工复核技术选型上我用了 Python OpenCV NumPy 作为视觉处理核心后端用 Flask 提供简单的本地Web服务前端用普通HTML实现最近扫描文件的上传、识别结果展示和人工复核页面。选择这套组合的原因很简单OpenCV在经典图像处理领域足够成熟社区资料多透视变换、形态学操作、连通域分析这些接口都稳定而且Python生态方便后面接OCR或深度学习模型做扩展。有人可能会问为什么不直接用现成的OCR库比如Tesseract答案很明确——答题卡的核心不是识别文字而是检测“有没有涂写”这是一个位置敏感的结构化任务。OCR擅长的是识别字符内容但答题卡需要的是精确到每一个选项格子里的二值判断并且要把几百个格子的判断结果组装成结构化数据。用OCR库处理这种任务既慢又容易误判远不如自己用计算机视觉方法实现靠谱。2. 图像预处理真正的分水岭在“第一公里”2.1 倾斜校正与透视变换定位不准的根源很多初次接触答题卡识别的人上来就直接对整张图做二值化、找轮廓然后想当然地认为“左上角那个黑块就是定位块”。结果一测试就发现只要扫描时卡片稍微歪一点整个坐标体系就乱了。所以我强烈建议预处理阶段首先解决几何校正问题。倾斜校正有两种常见思路针对小角度倾斜比如3度以内用霍夫变换检测长直线计算直线的平均角度然后对图像做旋转校正。这个方法轻量、速度很快但它只能处理“整体旋转”无法解决透视变形。针对手机拍摄或扫描放置不当导致的大角度透视变形必须用四点透视变换。先在图像里找到四个标记点然后通过cv2.getPerspectiveTransform计算变换矩阵映射到标准矩形。实际项目中我几乎总是直接使用第二种因为它更稳健。答题卡在设计印刷时通常会在四角或特定位置留有黑色定位块这些定位块在图像里表现为深色矩形用轮廓检测加面积筛选就能定位出来。找到四个定位块的中心点后把它们映射到预设的标准坐标比如 0,0、2480,0、0,3508、2480,3508具体取决于你的模板透视变换就完成了。这里有一个关键经验不要把透视变换后的图像直接用于后续识别而是要把变换矩阵保留下来在模板坐标系里定义好所有选项区域然后用同一个矩阵把选项区域映射到原图上。这样做的好处是模板可以反复使用而且如果哪天发现版面设计改了只需要更新模板坐标不用改动判读代码。下面是我用的一段核心透视校正代码思路很清楚import cv2 import numpy as np def find_marker_blocks(gray_img): # 二值化 _, binary cv2.threshold(gray_img, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU) contours, _ cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) blocks [] for cnt in contours: area cv2.contourArea(cnt) x, y, w, h cv2.boundingRect(cnt) # 筛选面积适中、宽高比接近1的黑色矩形块 if 500 area 8000 and 0.7 w / h 1.3: blocks.append((x w // 2, y h // 2)) # 中心点 return blocks def perspective_correct(gray_img, src_pts, dst_size(2480, 3508)): dst_pts np.array([[0, 0], [dst_size[0], 0], [0, dst_size[1]], [dst_size[0], dst_size[1]]], dtypenp.float32) src_pts np.array(src_pts, dtypenp.float32).reshape(4, 2) matrix cv2.getPerspectiveTransform(src_pts, dst_pts) warped cv2.warpPerspective(gray_img, matrix, dst_size) return warped, matrix块面积和宽高比的阈值需要根据你的实际扫描分辨率调节我上面的数值是针对300DPI扫描件的经验值如果测试时定位不到先用cv2.imshow看一下轮廓检测结果再调阈值。2.2 光照不均与灰度化别让“白底”骗了你的阈值几何校正做完下一道坎是灰度化与二值化。很多人直接cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)然后cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)这在扫描仪上可能还行但手机拍摄的场景下会翻车。原因在于答题卡不一定是纯白背景有的纸张泛黄有的扫描灯管老化有的拍摄时窗口光线直射导致同一张图里不同区域的灰度和对比度差异很大。固定阈值127在亮部可能把阴影误判成黑色在暗部又可能把淡铅笔痕迹漏掉。解决这个问题的正确方式是用自适应阈值或基于背景估计的光照校正。比较实用的做法是先对灰度图做一个大尺度的高斯模糊得到背景亮度图然后用原图减去背景亮度图再做归一化。这样能从图像中去除不均匀的光照分量让后续二值化更加稳定。def flatten_illumination(gray_img): # 估计背景光照 bg cv2.GaussianBlur(gray_img, (0, 0), sigmaX30) # 去除光照分量 flat cv2.subtract(gray_img, bg) # 归一化到0-255 flat cv2.normalize(flat, None, 0, 255, cv2.NORM_MINMAX) return flat有人会问为什么不直接用自适应阈值adaptiveThreshold我试过它对于文字线条密集的区域效果不错但答题卡选项格子里有大量的空白区域自适应阈值在纯白区域容易把噪声放大成伪轮廓反而不利于后续连通域分析。所以我更推荐“全局背景估计 全局阈值”的组合处理稳定度更高。2.3 去噪与形态学处理一个噪点可能就是一道“伪选项”图像处理里有一句老话garbage in, garbage out。在答题卡场景里噪点主要来自两个地方一是扫描件上的灰尘、纸屑、毛发二是打印本身带有的网点纹理。如果不做处理这些小噪点经过连通域统计后可能让某个未涂写的选项产生很高的“涂写面积”导致误判。我常用的做法分三步中值滤波去噪窗口大小建议3x3或5x5太大容易破坏选项边缘。对二值图做形态学开运算先腐蚀再膨胀这一步能去除孤立小点。在后续的连通域分析中设置面积下限小于某个阈值的连通域直接忽略。这里要特别提醒开运算窗口不能开得太大。我曾经为了去噪把核设成7x7结果一些涂得比较淡的2B铅笔选项被腐蚀掉了整张卡多道题判成“未涂”。后来把核固定在3x3再配合面积过滤误判率才降下来。调试这类参数时一定要把中间过程图像输出到磁盘一张张看不要凭感觉调。3. 答题卡版面的自动定位从像素到坐标的映射3.1 定位标记点检测与标准模板思想很多初学者拿到一张答题卡第一反应是“我可以直接用坐标去切图”。问题是不同扫描仪、不同拍摄角度图像分辨率不一样直接写死坐标根本无法复用。正确思路是先建立一个标准模板在模板上定义所有选项区域然后通过透视变换把实际图像映射到模板坐标系这样所有坐标都落在已知位置。这个“标准模板思想”是整个系统的地基。具体做法是拿一张空白的、未作答的答题卡扫描件在它上面手工标注出所有选项矩形区域的位置用鼠标点选或直接在代码里写坐标。把空白答题卡和实际答题卡都做透视校正让它们处于同一个坐标系。后续只需要根据模板坐标去实际图上切ROI感兴趣区域即可。模板的定义通常用Python文件或JSON存储大致长这样{ name: standard_2600, markers: [[120, 120], [2360, 120], [120, 3388], [2360, 3388]], regions: { student_id: {index: [0, 1, 2, 3, 4, 5, 6, 7, 8], bbox: [200, 300, 900, 400]}, single_choice: { 1: {A: [300, 600, 100, 60], B: [450, 600, 100, 60], C: [600, 600, 100, 60], D: [750, 600, 100, 60]}, 2: {A: [300, 700, 100, 60], B: [450, 700, 100, 60], C: [600, 700, 100, 60], D: [750, 700, 100, 60]} } } }这种基于模板的坐标方案最大的好处是扩展容易。如果未来课程设计或实际应用中换了一种新的答题卡排版只需要重新标定一次模板文件一行代码都不用改。3.2 用透视矩阵映射区域而不是每次重新找轮廓在标准模板建立好之后识别过程就有了一条很清晰的主线每次拿到新图片先检测定位点算透视矩阵然后把模板里的所有bbox坐标通过矩阵映射到当前图像再切割ROI。这里有一个很微妙但重要的点直接在透视校正后的图上切ROI与在原始图上用矩阵映射ROI最终效果是等价的但后者更快、更省内存因为不需要对整张大图做warp变换。不过从实际工程角度看我还是倾向于先warp到标准坐标再做切图原因有两个warp后的图像尺寸固定后续代码逻辑更简单便于调试和可视化。如果需要对选项区域做精细分析模板坐标下每个选项盒子是标准矩形无需额外处理。唯一要注意的是透视校正后图像会有插值损失特别是原本模糊的边缘可能被平滑掉。所以我会在warp的时候设置cv2.INTER_CUBIC插值虽然慢一点但对边缘保持更好。下面是区域切割的关键片段for region_name, region in template[regions].items(): x, y, w, h region[bbox] roi warped_gray[y:yh, x:xw] # 对roi做后续处理二值化、连通域分析等3.3 准考证号的识别涂卡数字为什么不能直接套用OCR答题卡上的准考证号区域通常是一排竖排的数字格子每个格子里需要涂黑一个数字。这个区域的识别思路和客观题选项几乎一样但有一点不同它识别的是“数字内容”而不只是“有无涂写”。最直接的做法是按模板坐标把每个数字格子切出来判断它涂的是哪个数字0-9。这里有个很容易踩的坑如果你直接用Tesseract或其他通用OCR去识别这些涂卡数字效果会非常差。原因很简单通用OCR训练集以印刷体、手写体为主而答题卡上的数字是“涂黑一个方块”这种字符形态与常规数字差异很大OCR引擎很难正确分类。我在这个项目里采用了基于模板匹配的轻量方案事先准备10张标准数字块模板0-9这些模板就来自当前答题卡印刷所用的数字字体。对每个待识别数字切块先判断该块是否被涂写覆盖率是否超过阈值。如果已涂写计算该块与10个数字模板的匹配度取最高分。模板匹配在数字大小稳定、印刷规范时准确率可以达到99%以上。如果你觉得模板匹配太简陋也可以换成训练一个简单的CNN分类器输入是32x32灰度图输出是10个数字的概率。对于这个任务不需要太深的网络两层卷积加全连接就足够训练数据可以用程序自动生成——用印刷体数字做膨胀、腐蚀、加噪声模拟涂卡的方块感。我在后期迭代中为了应对不同批次的答题卡印刷差异改用了这种合成数据CNN的方案效果比模板匹配更稳定。4. 选项判定与OMR逻辑涂卡信号的提取与判读4.1 为什么像素统计不可靠用连通域分析提取“有效涂写”做完版面定位和ROI切割接下来就到了这个系统的心脏——选项判定。很多初学者的第一反应是计算ROI内黑色像素的比例超过某个阈值就认为被涂写了。这种方法在干净的扫描件上可行但稍微遇到噪声或者笔迹深浅不均准确率就会明显下降。我采用的是连通域分析思路更接近人眼判断真正被涂写的选项应该有一块连通的、面积可观的黑色区域而噪声、划痕、纸屑通常都是零散的孤立点。通过cv2.connectedComponentsWithStats提取ROI内的连通域然后统计最大连通域的面积占整个ROI面积的比例用这个比例作为判读特征比单纯数像素稳健得多。def fill_ratio(binary_roi): num_labels, labels, stats, _ cv2.connectedComponentsWithStats(binary_roi, connectivity8) if num_labels 1: return 0.0 # 排除背景找最大连通域 max_area 0 for i in range(1, num_labels): area stats[i, cv2.CC_STAT_AREA] if area max_area: max_area area total binary_roi.shape[0] * binary_roi.shape[1] return max_area / total这个fill_ratio就是我判读的核心特征。实际使用中阈值设在0.22到0.45之间通常比较合适具体取决于答题卡的选项框大小和学生涂卡的饱满程度。为了拿到最合理的阈值我会先人工标注30张答题卡的正确选项然后统计涂写项和未涂项的fill_ratio分布用两个分布的谷底位置作为阈值。4.2 判定规则的边界情况未涂、多涂、单选与多选有了覆盖率特征判定逻辑就不难写但边界情况要仔细考虑未涂所有选项的fill_ratio都低于阈值这时有两种可能——学生漏做或者涂得太淡系统没检测到。系统应该输出“未涂”并标记为低置信度而不是直接判错。单选多涂单选题如果检测到两个及以上选项的覆盖率超过阈值这是异常必须交给人工复核不能按第一个算。多选多选题允许多个选项被涂写但不能全选视题目要求。全选通常意味着学生可能发现了错误但来不及改正直接涂满所有选项当“重写标记”这种情况也要标记异常。邻题干扰如果学生涂卡时用力过猛选项A延伸到相邻选项B的边界可能导致B的覆盖率超过阈值。这种情况我会在判定时加一个“边界排除”规则——只统计ROI区域减去边缘5像素后的内部区域能有效减少笔迹压线带来的干扰。4.3 双维度判定机制覆盖率与边缘密度单一特征做到90%准确率不难但要做到99%以上就需要引入更多维度。我在系统里加了第二个判读特征边缘密度。涂写的铅笔痕迹在微观层面是由石墨颗粒覆盖的边缘粗糙且不规则而印刷体的黑色字符边缘锐利、连续。一个被橡皮擦了一半的区域覆盖率可能还有20%但边缘密度已经明显降低。具体做法是对ROI应用Canny边缘检测统计边缘像素占比。涂写选项的边缘占比通常比残留痕迹高很多。实际判定时只有当“覆盖率超过阈值”且“边缘密度超过阈值”两个条件同时满足才判定该选项被涂写。这个双维度机制显著降低了对橡皮擦残留的误判。为了更直观我把判读流程总结成一张决策逻辑表条件覆盖率边缘密度判定结果未涂写 0.15低未涂正常涂写 0.35中/高已涂淡涂/擦除残留0.15 - 0.35低不判定或低置信度多涂/压线多个ROI超阈值—标记异常这个表不是死的具体阈值应当基于你自己的数据分布调整但决策框架可以参考。5. 异常检测与容错处理阅卷系统最不该出错的地方5.1 规则冲突检测多涂、错涂、学号异常答题卡识别系统最怕的不是“识别不出”而是“识别错”了还输出一个正常结果。所以在逻辑层我专门写了一个异常检测模块对所有识别结果做规则校验。规则一单选题多选。如果一个单选题ID下有两个及以上选项被判为已涂系统立即把该题状态设为“异常-多涂”不参与自动评分。规则二学号位数不符。每张答题卡模板定义了学号长度比如9位如果识别出的实际位数不够或者某一位涂写覆盖率过低系统会标记“学号疑似不完整”。规则三大面积全涂。如果某个学生所有题目都是同一个选项比如全是A虽然理论上可能但概率极低系统应该标记为“疑似异常”并提醒复核。实现这些规则并不难纯粹是逻辑判断但它们的价值很大。在真实阅卷中异常卡往往就是人工集中核查的对象系统能主动把它们拎出来就能省下大量逐张核对的时间。5.2 图像质量评估先判断这张卡能不能认前面提到不是每张扫描件都能直接被识别。我在采集阶段就加入了一个图像质量评估模块用三个指标给每张卡打分清晰度对灰度图计算Laplacian方差方差越小说明图像越模糊。低于某个阈值就提示“图片模糊建议重新扫描”。倾斜度检测到的定位块中心点之间连线与水平线的夹角如果超过15度提示“倾斜过大建议重新放正”。遮挡率通过轮廓检测估算图像边缘被裁切的比例如果定位块缺失或靠近图像边界提示“模板不完整”。这个模块的代码不需要很复杂但它在批量处理时能帮你省下大把时间。否则当一批100张的图片里混进了几张低质量图像你很难在结果表里凭肉眼看出来哪张可信、哪张不可信。5.3 人工复核通道低置信度结果不直接写入成绩我最终实现的人工复核通道是一个简单的Flask页面。系统识别完一批图片后会把所有标记为“异常”或“低置信度”的结果单独放到一个列表里复核人员在页面上可以看到答题卡原图裁剪缩略图该题的四个选项区域放大图每个选项的覆盖率数值和边缘密度数值系统给出的自动判定结果人工只需要看图示确认或修正结果点保存系统就会用修订后的数据覆盖原结果。这个通道的存在让系统的容错率上升了一个档次。你可以不信任任何单一的图像识别结果但一定要保留最终的人工决策权。6. 工程落地从算法Demo到能稳定跑通的本地服务6.1 性能优化批量处理时别卡在单张上识别一张高分辨率扫描图如果不做优化OpenCV处理一遍可能需要两三秒甚至更久。而实际阅卷场景往往是一批几百张不可能接受这么慢的处理速度。我在性能上做了三件事限制处理尺寸。把输入图像长边缩放到2000像素左右再处理。对于答题卡识别这种任务2000像素已经足够精确没必要在5000像素的大图上做透视变换和连通域分析。缩放大概能带来三四倍的速度提升。用C的加速接口替代纯Python循环。OpenCV内部函数本身就是C实现的关键点在于不要在Python层写逐像素循环。所有需要遍历像素的操作尽量切换到NumPy的向量化计算。多进程并行。Flask后端接到一批图片后用multiprocessing.Pool把图片分成多组每个进程独立处理一组利用多核CPU并行批量耗时能进一步缩短。Python的GIL对OpenCV内部函数影响不大所以多进程效果很显著。6.2 结果输出与数据链路从识别结果到成绩表识别完成后数据要落到业务层。我的设计是每张答题卡生成一条记录字段包括文件名、学号、每题选项结果、每题自动评分、异常标志、置信度等。输出格式同时支持JSON和Excel方便对接不同的下游系统。如果你想把系统做成完整的成绩管理工具还可以再加一个SQLite数据库表把每次识别的批次、答题卡、异常记录都存下来。这样后续做成绩查询、考试对比分析都有据可查。数据库表结构不必复杂一张exam_batch表批次ID、考试名称、导入时间、一张answer_sheet表批次ID、学号、总分、异常标记、一张answer_detail表答题卡ID、题号、判定结果、置信度就够用了。6.3 开发环境与依赖如何让队友也能顺利跑起来项目最终要给别人运行不能只在你的电脑上能跑。我用requirements.txt固定了核心依赖opencv-python4.8.1.78 numpy1.24.3 Flask2.3.3 openpyxl3.1.2启动方式很简单在项目目录执行python app.py浏览器打开本地端口就能上传图片并查看识别结果。整个部署不依赖GPU、不依赖外部服务一台普通电脑就能处理一个年级的答题卡这也是这个系统在真实场景里能落地的关键。7. 实测效果与踩坑清单7.1 测试数据与识别率我用50份真实使用过的答题卡做了测试。这50份卡来自一次班级测验包含不同程度的涂卡质量涂写饱满的大概35张涂写偏淡的有8张有明显橡皮擦痕迹的4张还有3张存在折痕或轻微污渍。最终结果自动识别准确率单题选项98.6%整卷无异常标记的比例82%平均单张处理耗时0.38秒多进程并行后约0.12秒/张人工复核后修正的题数8题其中误判主要聚集在两类场景一是涂写太淡导致系统认为未涂二是橡皮擦残留导致系统认为已涂。双维度判定机制上线后第二类误判明显减少。7.2 最值得记住的踩坑经验调这个系统时有几个坑我印象特别深第一个坑是模板坐标的“零点偏移”。不同的扫描软件在导出图片时可能会在页面四周加上边距导致同一个答题卡在不同扫描件里的起点坐标差了几十像素。这个问题最稳妥的解法是在模板里增加一个“参考原点”定位块而不是直接依赖左上角像素坐标。如果没有这个参考原点后续切割位置就会整体偏移。第二个坑是JPG压缩率。有些扫描仪默认以高质量JPG导出图像信息保留得很好但如果你用手机拍完直接微信传输图片会被二次压缩选项边缘出现明显锯齿边缘密度特征会受到干扰。处理方案是在预处理阶段对图像应用轻微高斯模糊让边缘平滑一些减少压缩伪影对边缘检测的影响。第三个坑是处理顺序。一开始我把灰度、去噪、透视校正全部做完才开始做光照校正结果发现光照估计算法把透视校正后的插值条纹也当成了背景。后来调整了顺序先去噪 → 光照校正 → 灰度化 → 透视校正问题迎刃而解。预处理顺序不是随便定的每一步的输入输出必须仔细考量。7.3 这个项目后续还能怎么扩展答题卡识别这个基础框架完全可以往多个方向延伸。比如在图像预处理阶段加入一个轻量级的深度学习分割模型专门检测涂卡区域是否被遮挡或者在学号识别模块里接入更高精度的数字识别网络再或者把整个系统打包成桌面应用脱离命令行运行。就我自己的体会来说现在的项目已经具备了一个实用系统该有的基本形态后续更多是在业务逻辑和交互体验上的打磨。如果你也在做类似的东西建议先把最基础的链路跑通再考虑加高级功能不要一上来就追求模型复杂度。本文还有配套的精品资源点击获取