OpenCV实战项目全解析:从信用卡识别到目标追踪的四个经典案例

OpenCV实战项目全解析:从信用卡识别到目标追踪的四个经典案例 在图像处理与计算机视觉领域OpenCV 几乎是所有中国开发者的第一站。但一个很普遍的现象是很多人学完基础后拿着cv2.cvtColor、cv2.GaussianBlur、cv2.Canny这些接口仍然做不出一个完整的项目。看教程时觉得自己会了打开 IDE 后却发现不知道从哪一行写起。这才是 OpenCV 学习者真正的分水岭不是 API 背得不够熟而是缺少把“图像处理套路”组合成“业务解决方案”的实战训练。这篇文章基于一套完整的 OpenCV 实战课程体系展开覆盖信用卡识别、文档扫描、疲劳检测、目标追踪四个经典企业级项目。我会带你拆解每个项目背后的核心技术链路它们到底解决了什么问题、用了哪些 OpenCV 核心算子、代码怎么组织、有哪些容易踩的坑。读完这篇文章你不仅能掌握这四个项目的完整技术方案更重要的是建立一种能力拿到一个图像或视频任务你能快速判断该用什么 OpenCV 流程去解决。这正是岗位招聘里写的“有项目经验”的真正含义。1. OpenCV 学习为什么容易“学了就废”很多初学者会陷入一个循环刷网课、看文档、抄代码然后等到面试或做实际项目时发现自己能讲出“什么是腐蚀膨胀”却讲不出“为什么要用闭运算去连接数字区域”。问题出在学习粒度。OpenCV 的基础教程通常按算子讲今天学滤波明天学阈值后天学边缘检测。这就像学写作时只背字典从来不练篇章结构结果自然是用不出来。真正的项目经验本质上是一个“组合算子解决业务问题”的思维链。以信用卡识别为例业务问题是“识别卡面上的凸版卡号”技术拆解是卡面背景复杂怎么把数字区域找出来数字是凸版印刷有立体阴影怎么稳定二值化识别不同发卡行的 16 位卡号模板怎么匹配数字顺序怎么保证从左到右、从外到内任何一个环节缺一个算子结果都不可用。这种深度只有在完整项目中才能体会。所以我个人非常认同这套课程的做法不要停留在“学 API”要直接奔着“做出一个能交付的项目”去学。项目会倒逼你理解原理也会自然形成简历上的作品集。2. 四个企业级项目的技术价值分析在系统写代码之前我先用一张表把四个项目的核心技术和业务价值说清楚。这能帮你建立全局认知知道每个项目“到底在练什么”。项目核心技术点实际业务场景OpenCV 核心函数信用卡识别形态学操作、轮廓提取、模板匹配金融卡面识别、绑定卡号自动填写morphologyEx、findContours、matchTemplate文档扫描边缘检测、轮廓近似、透视变换办公扫描、拍照文档矫正、OCR 预处理Canny、approxPolyDP、getPerspectiveTransform、warpPerspective疲劳检测人脸关键点、眼睛纵横比 EAR驾驶行为分析、在线课堂专注度监测dlib.get_frontal_face_detector、关键点索引目标追踪检测器与追踪器结合、实时视频流处理安防监控、自动驾驶感知、无人机跟随TrackerKCF、TrackerCSRT、selectROI从技术难度上看信用卡识别和文档扫描属于图像处理阶段解决的是“一张静态图怎么变成结构化数据”疲劳检测和目标追踪属于视频与实时分析阶段解决的是“连续帧里怎么提取并跟踪语义信息”。两者对开发者的要求差异很大。前者需要扎实的算子组合能力后者需要懂视频流的时间维度、实时性优化和目标状态管理。3. 环境准备与 OpenCV 安装无论你选择哪个项目第一步都是搭建 OpenCV 开发环境。这里我给出最稳妥的安装方案。3.1 Python 环境建议推荐使用 Python 3.7 到 3.10 版本。版本过低缺少类型标注支持版本过高会碰到个别第三方库没有预编译 wheel 的问题。建议使用 Anaconda 管理虚拟环境避免不同项目之间的依赖冲突。# 创建独立虚拟环境 conda create -n opencv_project python3.8 # 激活环境 conda activate opencv_project3.2 安装 OpenCV 与依赖库OpenCV 官方提供了两个 Python 包opencv-python和opencv-contrib-python。目标追踪项目需要用到 contrib 包中的追踪器模块所以直接安装 contrib 版是最省事的。# 安装 OpenCV 完整版包含 contrib 模块 pip install opencv-contrib-python # 安装常用科学计算与图像处理库 pip install numpy matplotlib # 疲劳检测项目需要 pip install dlib imutils这里提醒一个高频问题opencv-python和opencv-contrib-python不能同时安装否则会出现命名空间冲突import 时报各种奇怪错误。3.3 验证安装安装完成后在 Python 交互环境或 IDE 里运行以下代码能打印出版本号就说明安装成功。import cv2 import numpy as np print(OpenCV 版本, cv2.__version__) print(NumPy 版本, np.__version__)预期输出类似OpenCV 版本 4.8.0 NumPy 版本 1.24.3如果你的环境里输出ModuleNotFoundError: No module named cv2优先排查是不是没有激活正确的虚拟环境。4. 信用卡识别项目轮廓与模板匹配的经典组合信用卡识别是四个项目中最适合练“图像预处理”的题目。它不涉及深度学习全靠传统图像处理完成非常适合建立对 OpenCV 算子链路的整体感觉。4.1 业务问题拆解一张信用卡图片包含背景、卡面、卡号、持卡人姓名、有效期、银行 Logo。项目目标是准确识别出 16 位卡号数字。难点在哪里卡号是凸版印刷光线打上去会有阴影卡号可能带有品牌 Logo也可能有背景图案干扰。所以不能直接做 OCR需要先定位卡号区域再做字符分割最后逐个识别。4.2 技术方案设计与核心代码整个流程分三步第一步卡号区域定位。用顶帽运算突出亮背景下的暗字符用 Sobel 检测水平边缘再用闭运算连接相邻数字区域。import cv2 import numpy as np def locate_card_digit_area(gray_img): # 顶帽运算原图 - 开运算结果突出亮背景下的暗细节 rect_kernel cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3)) tophat cv2.morphologyEx(gray_img, cv2.MORPH_TOPHAT, rect_kernel) # Sobel 检测水平边缘卡号区域往往有明显水平纹理 sobelx cv2.Sobel(tophat, ddepthcv2.CV_64F, dx1, dy0, ksize3) sobelx np.absolute(sobelx) sobel_8u np.uint8(sobelx) # 闭运算连接相邻字符形成完整卡号区块 close_kernel cv2.getStructuringElement(cv2.MORPH_RECT, (17, 5)) closed cv2.morphologyEx(sobel_8u, cv2.MORPH_CLOSE, close_kernel) # 二值化并再次闭运算得到清晰轮廓 _, thresh cv2.threshold(closed, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) close_kernel2 cv2.getStructuringElement(cv2.MORPH_RECT, (21, 5)) closed2 cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, close_kernel2) contours, _ cv2.findContours(closed2.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) return contours这段代码里MORPH_TOPHAT的选择是有讲究的。卡号是暗色凸版字符顶帽运算恰好能过滤背景、保留字符Sobel取水平方向是为了强化卡号数字串的横向分布特征闭运算的核大小决定了字符连接的粒度核太小连不起来核太大会把不同区域的文字也连进来。第二步轮廓过滤与分组。遍历轮廓按宽高比和面积筛选出卡号区域。def filter_digit_groups(contours, min_w40, max_w200, min_h10, max_h60): digit_groups [] for cnt in contours: x, y, w, h cv2.boundingRect(cnt) # 卡号区域有固定的宽高比范围这个范围需要根据图片分辨率调整 if min_w w max_w and min_h h max_h: digit_groups.append((x, y, w, h)) # 按 x 坐标排序保证从左到右读取 digit_groups.sort(keylambda rect: rect[0]) return digit_groups轮廓过滤是整个项目中最容易翻车的环节。如果筛选条件太宽松背景纹理会被误判为数字区域如果太严格倾斜或光照不均的卡号会被漏掉。建议先在一张图上可视化所有轮廓的外接矩形观察分布后再调整阈值。第三步模板匹配。准备 0-9 的模板图片对每个分割出的数字做模板匹配取相似度最高的模板作为识别结果。def match_digit_to_template(digit_img, templates): # 统一尺寸避免模板与待识别数字分辨率不一致 digit_img cv2.resize(digit_img, (57, 88)) scores [] for digit, template in templates.items(): # 归一化相关系数匹配值越大越相似 res cv2.matchTemplate(digit_img, template, cv2.TM_CCOEFF_NORMED) _, max_val, _, _ cv2.minMaxLoc(res) scores.append((digit, max_val)) scores.sort(keylambda x: x[1], reverseTrue) return scores[0][0]这里有个关键细节模板和待识别数字必须做同样的尺寸归一化。很多初学matchTemplate的人忽略这一点导致匹配结果完全不可用。4.3 项目关键结论信用卡识别这个项目真正的学习价值不在识别结果本身而在于让你理解传统图像处理项目里预处理质量直接决定最终准确率。形态学核大小、轮廓筛选条件、阈值参数每一项都需要针对实际图片反复调试。这种调参经验没有办法靠看书获得必须实际运行、看中间结果、反思哪里断链了才能真正沉淀下来。5. 文档扫描项目边缘检测与透视变换实战文档扫描是另一个经典项目也是拍照 OCR 系统的关键前置环节。手机上的“扫描全能王”类应用核心就包含这里讲的透视矫正。5.1 项目目标与难点给一张手机拍摄的、放在桌面上的文档照片输出一张正面视角、边界规整的扫描图。难点在于手机拍照角度不垂直文档会产生透视畸变桌面背景复杂文档边界可能不明显。5.2 从 Canny 到四点变换的完整链路核心流程包括四步边缘检测、轮廓查找、四点轮廓筛选、透视变换。import cv2 import numpy as np def scan_document(image): # 1. 预处理缩放、灰度、高斯模糊、边缘检测 ratio image.shape[0] / 500.0 orig image.copy() resized imutils.resize(image, height500) gray cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY) gray cv2.GaussianBlur(gray, (5, 5), 0) edged cv2.Canny(gray, 75, 200) # 2. 查找轮廓文档通常是画面中最大的闭合四边形 contours, _ cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) contours sorted(contours, keycv2.contourArea, reverseTrue)[:5] for c in contours: peri cv2.arcLength(c, True) approx cv2.approxPolyDP(c, 0.02 * peri, True) # 找到近似为四边形的最大轮廓 if len(approx) 4: doc_pts approx break # 3. 透视变换 warped four_point_transform(orig, doc_pts.reshape(4, 2) * ratio) gray_warped cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY) _, thresh cv2.threshold(gray_warped, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) return threshfour_point_transform是这一步的核心工具函数。它基于cv2.getPerspectiveTransform计算变换矩阵再用cv2.warpPerspective完成矫正。变换时要注意点的顺序通常要保证左上、右上、右下、左下的对应关系否则图像会被翻转。def four_point_transform(image, pts): rect order_points(pts) (tl, tr, br, bl) rect # 计算输出图像尺寸 widthA np.sqrt(((br[0] - bl[0]) ** 2) ((br[1] - bl[1]) ** 2)) widthB np.sqrt(((tr[0] - tl[0]) ** 2) ((tr[1] - tl[1]) ** 2)) maxWidth max(int(widthA), int(widthB)) heightA np.sqrt(((tr[0] - br[0]) ** 2) ((tr[1] - br[1]) ** 2)) heightB np.sqrt(((tl[0] - bl[0]) ** 2) ((tl[1] - bl[1]) ** 2)) maxHeight max(int(heightA), int(heightB)) # 构造目标点矩阵 dst np.array([ [0, 0], [maxWidth - 1, 0], [maxWidth - 1, maxHeight - 1], [0, maxHeight - 1]], dtypefloat32) M cv2.getPerspectiveTransform(rect, dst) warped cv2.warpPerspective(image, M, (maxWidth, maxHeight)) return warpedorder_points负责对四个顶点排序。常见做法是把坐标求和与求差左上角求和最小右下角求和最大右上角求差最小左下角求差最大。这个排序是透视变换中最容易被忽略却又最容易出错的细节。5.3 运行与验证运行后保存两张图矫正前的原图和矫正后的扫描图。如果变换结果出现左右翻转或上下颠倒检查order_points的排序逻辑如果文档被裁切检查轮廓点还原时是否乘了缩放比例ratio。6. 疲劳检测项目人脸关键点与实时状态判断疲劳检测比前两个项目更进一步它处理的是视频流需要实时判断驾驶员的睁眼闭眼状态。这个项目在网课专注度监测、驾驶行为分析等领域都有实际应用。6.1 核心原理眼睛纵横比 EAR疲劳检测不直接判断“困不困”而是通过眼睛开合程度来间接判断。这里引出一个非常经典的概念眼睛纵横比EAREye Aspect Ratio。人眼在水平方向和垂直方向各有若干关键点当眼睛闭合时垂直距离会显著缩短水平距离几乎不变。EAR 就是两者的比值。import cv2 import numpy as np from scipy.spatial import distance as dist def eye_aspect_ratio(eye): # 计算垂直方向的两组欧氏距离 A dist.euclidean(eye[1], eye[5]) B dist.euclidean(eye[2], eye[4]) # 计算水平方向的欧氏距离 C dist.euclidean(eye[0], eye[3]) # EAR (A B) / (2 * C) ear (A B) / (2.0 * C) return ear正常睁眼时EAR 大约在 0.2 到 0.35 之间闭眼时EAR 会急速下降趋近于 0。设定一个经验阈值比如 0.25当连续多帧低于该阈值就判定为一次闭眼。6.2 实时视频流处理框架疲劳检测的完整代码结构如下import cv2 import dlib from imutils import face_utils # 初始化 dlib 人脸检测器与关键点预测器 detector dlib.get_frontal_face_detector() predictor dlib.shape_predictor(shape_predictor_68_face_landmarks.dat) # 眼睛关键点索引范围 (lStart, lEnd) face_utils.FACIAL_LANDMARKS_68_IDXS[left_eye] (rStart, rEnd) face_utils.FACIAL_LANDMARKS_68_IDXS[right_eye] def detect_fatigue(frame): gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces detector(gray, 0) for face in faces: shape predictor(gray, face) shape face_utils.shape_to_np(shape) leftEye shape[lStart:lEnd] rightEye shape[rStart:rEnd] leftEAR eye_aspect_ratio(leftEye) rightEAR eye_aspect_ratio(rightEye) ear (leftEAR rightEAR) / 2.0 # 可视化眼部关键点与 EAR 数值 leftEyeHull cv2.convexHull(leftEye) rightEyeHull cv2.convexHull(rightEye) cv2.drawContours(frame, [leftEyeHull], -1, (0, 255, 0), 1) cv2.drawContours(frame, [rightEyeHull], -1, (0, 255, 0), 1) cv2.putText(frame, fEAR: {ear:.2f}, (300, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) if ear 0.25: cv2.putText(frame, FATIGUE DETECTED!, (30, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2) return frame注意这里的目光不只是“算一个 EAR 值”还要维护一个闭眼计数的状态机。比如COUNTER累计连续闭眼帧数只有超过某个阈值比如 20 帧才输出疲劳告警。单纯一次闭眼就报警会产生大量误报。6.3 dlib 的安装与模型文件问题疲劳检测项目最耗耐心的环节不是写识别逻辑而是装 dlib。dlib 依赖 C 编译环境Windows 上经常安装失败。如果你的环境装了 Visual Studio Build Tools可以直接用 pip 装pip install dlib如果是 macOS可能需要先装 cmakebrew install cmake pip install dlib此外dlib 的 68 点关键点模型文件shape_predictor_68_face_landmarks.dat需要单独下载约 99 MB。没有模型文件代码会直接报错。在企业项目中dlib的配置成本较高可以考虑换成 MediaPipe 或 OpenCV 自带的FaceDetectorYN。但用 dlib 做教学是很有价值的因为它把人脸关键点的概念讲得非常直观方便你理解后续所有基于关键点的算法。7. 目标追踪项目从检测到追踪的完整系统目标追踪是四个项目中综合性最强、最接近工业级应用的一个。很多人会把“目标检测”和“目标追踪”混为一谈其实它们是两个不同层面的技术。7.1 检测与追踪的差异目标检测解决“这一帧里目标在哪里”目标追踪解决“连续帧里同一个目标到哪里去了”。检测需要每帧独立运行通常在 GPU 上执行处理速度相对慢追踪只需要在第一帧选定目标后续帧基于特征匹配CPU 就能跑实时。工业级方案几乎都是“检测 追踪”结合检测器周期性地发现新目标追踪器负责帧间连续性。7.2 OpenCV 内置追踪器的基本使用OpenCV contrib 模块提供了多种追踪算法。以 KCF 和 CSRT 为例KCF: 核相关滤波速度快适合计算资源有限的场景。CSRT: 判别相关滤波精度高但速度慢适合对精确度要求高的场景。import cv2 # 读取视频 vs cv2.VideoCapture(test.mp4) ok, frame vs.read() # 手动框选要追踪的目标 bbox cv2.selectROI(Frame, frame, fromCenterFalse, showCrosshairTrue) tracker cv2.TrackerCSRT_create() ok tracker.init(frame, bbox) while True: ok, frame vs.read() if not ok: break # 更新追踪器返回最新位置 ok, bbox tracker.update(frame) if ok: (x, y, w, h) [int(v) for v in bbox] cv2.rectangle(frame, (x, y), (x w, y h), (0, 255, 0), 2) else: cv2.putText(frame, LOST, (30, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2) cv2.imshow(Tracking, frame) if cv2.waitKey(1) 0xFF ord(q): break vs.release() cv2.destroyAllWindows()运行这段代码时窗口会弹出鼠标框选你要追踪的目标然后程序进入追踪循环。这里有一个高频坑位OpenCV 的追踪器 API 在不同版本间有过调整。如果你安装的是 4.5.1 以下版本cv2.TrackerCSRT_create()是正常的如果你用的版本过新可能有些旧追踪器被移除。7.3 追踪丢失与重检测直接使用追踪器会遇到一个痛点目标被遮挡或移出画面后追踪器会“跟丢”。一旦跟丢代码里只能输出LOST无法自己找回来。工业级解决方案是把检测器和追踪器串成一个闭环第一帧使用检测器找到目标。后续帧使用追踪器维持目标位置。当追踪器的置信度下降或丢失时重新调用检测器在整个画面中找目标。找到后重新初始化追踪器。这种架构里的检测器可以是 YOLO、SSD也可以是传统特征检测器。这也是为什么企业招聘要求“熟悉目标追踪”时往往同时要求“熟悉检测模型”。7.4 项目要点总结目标追踪项目真正训练的不只是调用tracker.update()而是状态管理能力如何处理追踪丢失、如何选择重检测时机、如何管理多个目标、如何平衡准确率与速度。这套思路比单一 API 本身值钱得多。8. 从“课程项目”到“简历项目”还差什么很多人学完项目后会有一种感觉跟着课程做能跑但脱离课程就不会了。这里的关键在于课程项目是“从零到一”而简历项目要求你“从一到 N”、能说清楚设计决策和优化空间。要让这些项目成为简历亮点至少需要做三件事第一搞清楚每一个参数的合理性。课程里设置的核大小(9, 3)、threshold0.25你能否解释为什么是这个数值换成别的图片应该怎么动态调整建议用 5 到 8 张不同场景的图片测试记录表现最好的参数区间这本身就是一次完整的实验记录。第二补齐工程化模块。把识别逻辑封装成类加日志、加异常处理、加接口参数校验你会更接近企业代码规范。面试官追问“你这个项目怎么部署的”你也不需要心虚。第三设计一个可以量化的改进点。例如在信用卡识别中对比不同形态学核大小对准确率的影响在疲劳检测中测试不同 EAR 阈值的误报率。能拿出实验数据的项目比只展示“跑通了”高一个层级。9. 常见问题与排查清单以下这些问题覆盖了学习这套 OpenCV 实战课程时最常遇到的卡点。建议先收藏遇到问题直接对照排查。问题现象可能原因排查方式解决方案ModuleNotFoundError: No module named cv2没有安装 opencv或虚拟环境未激活检查pip list是否包含 opencv安装opencv-contrib-python确认当前环境OpenCV 与 contrib 冲突同时安装了opencv-python和opencv-contrib-python执行 pip listfindstr opencvfindContours报ValueErrorOpenCV 4.x 返回两个值旧代码按三个值接收打印cv2.__version__确认版本使用contours, _ cv2.findContours(...)信用卡卡号区域定位失败闭运算核太小字符没有连接成块输出中间结果closed2并检查增大核宽或适当调整 Sobel 的ksize文档扫描结果透视扭曲四个顶点的顺序排列错误打印order_points返回坐标按坐标求和/求差确定顶点顺序dlib 安装失败提示缺少编译器Windows 缺少 C 编译工具查看完整错误日志安装 Visual Studio Build Tools 后重试疲劳检测每秒处理帧数很低dlib 人脸检测器在 CPU 上较慢打印每帧处理耗时降低输入分辨率或用 MediaPipe 替代检测器目标追踪目标跟丢后无法恢复没有设计重检测机制观察ok返回值变化引入检测器定期重新检测目标图片或视频路径含中文读取失败OpenCV 的imread对中文路径支持不佳打印返回的帧是否为None改用cv2.imdecode(np.fromfile(...))方式读取另外单独说一下win7 有一个问题阻止扫描文档这个热搜词很多人在网络上搜索过 Windows 系统自带的扫描程序报错问题。这不是 OpenCV 的问题而是 Windows 扫描程序与驱动或系统服务冲突。如果你在项目中使用的是 USB 扫描仪更推荐直接通过 OpenCV 调用摄像头来完成图像采集不必依赖系统扫描程序。10. 最佳实践与工程建议最后结合四个项目共通的工程要求给出几条建议。10.1 代码组织分级不要把全部代码写在一个文件里。至少分成三层project/ ├── utils/ │ ├── contour_utils.py # 轮廓排序、过滤工具 │ ├── image_utils.py # 图像读取、尺寸统一工具 │ └── transform_utils.py # 透视变换工具 ├── projects/ │ ├── credit_card/ │ ├── document_scan/ │ ├── fatigue_detect/ │ └── object_track/ └── resources/ ├── templates/ # 模板图片 └── models/ # dlib 模型文件这种结构让你后续在多个项目间复用工具函数也是企业项目最基础的组织方式。10.2 中间结果可视化调试图像处理项目时最忌讳“看最终输出猜问题”。建议每个关键阶段都保存中间结果import os def debug_show(img, name, save_dirdebug): os.makedirs(save_dir, exist_okTrue) cv2.imwrite(os.path.join(save_dir, f{name}.png), img)中间结果能帮你快速定位是预处理的问题还是轮廓筛选的问题还是模板匹配的问题。这套方法同样适用于你今后调试任何 CV 项目。10.3 安全与合规提醒信用卡识别示例仅用于技术学习与已授权场景。实际开发中涉及卡号、身份证号、人脸等敏感信息时必须严格遵守数据保护与隐私合规要求测试阶段使用脱敏数据生产环境遵循最小权限原则。11. 总结与后续学习方向到这里四个 OpenCV 实战项目的技术链路已经完整过了一遍信用卡识别让你掌握形态学与模板匹配的组合文档扫描让你理解边缘检测与透视变换疲劳检测带你进入人脸关键点与实时状态判断目标追踪则展示了检测与追踪架构在视频流中的完整应用。这四个项目共同指向一个核心能力把 OpenCV 的基础算子组合成完整解决方案并具备对结果进行调优、验证和工程化的能力。对找工作的人来说这就是课程项目与“简历项目”之间的桥梁。如果你能独立把这四个项目从头实现一遍并且对参数调整和异常情况有自己的记录那么你在图像处理和计算机视觉方向上的实战基础就已经扎实了。下一步建议朝两个方向进阶一是引入深度学习模型把传统的模板匹配替换为轻量级 CNN 分类把 dlib 换成更高效的检测模型二是做项目融合比如把文档扫描接到 OCR 引擎前做一个完整扫描识别工具。后者也是一个很好的小作品能同时体现传统图像处理能力和工程整合能力。