ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

车牌识别Python源码包实战:OpenCV模板匹配与调优

车牌识别Python源码包实战:OpenCV模板匹配与调优 简介面向初学者与课程设计的车牌识别案例源码包基于Python与OpenCV实现涵盖图像灰度化、边缘检测、形态学处理、车牌定位以及基于SVM和OCR的字符识别等关键环节适合作为计算机视觉入门实践或毕业设计参考。压缩包共24个文件容量约14.94MB包含两个Python识别脚本、SVM模型文件、中文与英文字符集压缩包、测试图片jpg/png以及说明文档与配置脚本目录划分清晰便于按模块阅读。已有429人学习下载。资源不仅提供可运行的识别代码还附带了多组不同角度与场景的车牌照片方便读者验证效果并调试参数同时模型文件与字符集可直接用于字符分类省去自行训练时间。通过学习该项目可以理解从图像预处理、车牌分割到文字识别的完整技术链路掌握OpenCV、SVM及OCR在实际问题中的组合用法为后续优化或拓展识别精度打下基础。1. 车牌识别Python源码包一个小而完整的学习项目能让你跑通识别流程当你看到“简单的车牌识别-python源码.zip”这个压缩包时里面大概率是一个基于OpenCV的传统视觉项目边缘检测加轮廓分析定位车牌投影法切分字符再用模板匹配或OCR输出结果。整个过程不需要GPU、不需要上千张标注图片在一台普通电脑上就能跑通适合停车场出入口、固定摄像头这类背景相对干净的场景。如果你是在学图像处理的学生、刚入门Python视觉方向的开发者或者要快速验证一个识别方案能不能落地的工程师这类源码包值得花一个晚上拆开看一遍。但也要有清醒预期它离复杂路况下的产品化还有距离倾斜、反光和模糊都会让识别率肉眼可见地下降。2. 车牌识别技术选型为什么“简单源码包”大多走OpenCV加模板匹配路线2.1 传统视觉vs深度学习数据集和硬件成本决定的现实取舍车牌识别抽象来看是两个任务的串联定位加识别。现在生产环境里能看到两种主流路线。深度学习路线用YOLO系列做车牌检测再用CRNN或Transformer做字符识别优点是抗干扰能力强但训练数据准备工作量很大几百张带框标注图片加上清洗和增广一周打底。传统视觉路线则用Canny边缘检测提取轮廓用宽高比和面积过滤候选框识别阶段用模板匹配或Tesseract OCR普通CPU就能实时运行。做一个“简单”的源码包为什么选传统视觉因为这类包要解决的问题是“让一个人下载下来就能跑通”深度学习方案的模型权重文件动辄几十MB加上推理框架的安装成本已经不符合“简单”这个前提。传统视觉路线依赖少、单文件能讲清楚、运行快作为教学和POC验证非常合适。我用一张表把两者的边界列出来方便你判断什么时候该换路对比项传统视觉OpenCV模板匹配深度学习YOLOCRNN硬件要求普通CPU即可需要GPU或推理卡数据集需求不需要字符模板可收集几百到几千张标注图光照角度鲁棒性弱靠调参弥补强靠数据泛化开发到跑通时间1到2天1到2周以上适用场景固定机位、可控背景复杂场景、产品化实操里我见过不少团队在监控杆场景试过传统视觉方案白天识别率能到85%以上一到夜间就掉到60%甚至更低最后都转向深度学习。但反过来如果只是做一个小区出入口道闸的验证Demo传统视觉完全够用没必要为了蹭“AI热度”去从头训练一个YOLO模型。深度学习还有个问题是模型像个黑匣子出错了很难说清是检测框偏了还是字符识别错了而传统视觉每一步的中间结果都能可视化对学习和排错都友好得多。2.2 zip压缩包里的常见文件结构先看模板目录再决定跑不跑拿到“简单的车牌识别-python源码.zip”我建议先解压确认结构再决定要不要花时间跑。zip解压到本地后是典型的Python工程布局unzip 简单的车牌识别-python源码.zip -d plate_recognition cd plate_recognition tree解压后常见的文件组成如下plate_recognition/ ├── main.py 主入口读图、调模块、输出结果 ├── plate_locate.py 车牌定位模块 ├── plate_segment.py 字符分割模块 ├── plate_recognize.py 模板匹配识别模块 ├── templates/ 字符模板目录 │ ├── 京.png │ ├── 沪.png │ ├── A.png ... Z.png │ └── 0.png ... 9.png ├── test_images/ 测试图片 └── requirements.txt Python依赖清单看压缩包“成色”有两个关键点。第一模板目录是否完整好的源码包至少覆盖31个省份简称、24个字母、10个数字。注意车牌字符里字母I和O不出现如果你发现模板里有这两个字符说明作者没仔细处理过真实车牌。第二requirements.txt是否锁了版本只写“opencv-python”不写版本号的在OpenCV新版本上往往有接口兼容问题。我踩过一次很典型的坑下载的源码包模板目录只有字母和数字没有汉字模板跑出来车牌号后段全对、省份汉字永远识别成乱码。这类缺文件的zip包在网盘分享里很常见解压后第一步务必检查templates目录完整性。2.3 识别流程拆解定位、分割、识别三步走车牌识别的完整流程可以拆成三步理解清楚这三步对你之后的调参思路非常重要。第一步是定位。灰度化后用高斯滤波去掉噪点用Canny算子提取边缘再用形态学闭运算把车牌的矩形外边框连接成闭合区域最后用findContours查找轮廓并按车牌的宽高比、面积过滤候选。标准小型车蓝牌的比例是440mm比140mm宽高比约3.14所以候选框宽高比通常卡在2.5到4.5之间。第二步是分割。在定位到的车牌区域里做二值化把字符从背景中分离出来。常用做法是先做水平投影把字符区域和上下边框分开再做垂直投影按列方向上的非零像素分布把每个字符切开。这一步最怕车牌倾斜和光照不均倾斜时投影切分的位置会偏光照不均时二值化会把字符细节吞掉。第三步是识别。有两条路线走Tesseract OCR或者走模板匹配。模板匹配的逻辑不复杂把切出的字符图片和templates目录里每张模板逐一算相似度取相似度最高的模板名称作为识别结果。这个方案对字体一致、位置规整的场景表现稳定而且不需要额外装OCR引擎所以我下面讲代码示例时以模板匹配为主线。如果你手里的源码包是OCR路线流程框架完全一样只是第三步换成了pytesseract调用。2.4 环境配置Python版本、依赖安装和两条路线的差别要在本地跑起来环境其实非常轻。Python建议装3.8到3.10版本python安装时记得勾选“Add Python to PATH”这个选项否则后面命令行里敲pip会报command not found。我用3.9比较多3.10以上跑老源码包时findContours返回值解包写法不同会有兼容问题。依赖只需要三个pip install opencv-python numpy matplotlibmatplotlib不是必选项但调试定位结果时画框标注非常方便建议一起装上。如果pip下载速度慢可以临时加国内镜像源pip install opencv-python numpy matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple如果是走Tesseract OCR路线的源码包还要多一步系统级安装。Windows下要先下载tesseract.exe安装后配置环境变量或在代码里写死pytesseract.pytesseract.tesseract_cmd rC:\Program Files\Tesseract-OCR\tesseract.exeLinux下要apt install tesseract-ocr中文识别还要单独装tesseract-ocr-chi-sim语言包。这一步是OCR路线最容易翻车的地方报错里出现TesseractNotFoundError基本就是路径没配好。模板匹配路线没有这些额外依赖这也是我推荐先从模板匹配入手的另一个原因。环境就绪后接下来进入核心代码实现。3. 最小可跑通的代码从图片读取到车牌号输出的完整实现3.1 车牌定位边缘检测加轮廓过滤的定位模块先写定位模块。我习惯单独建一个plate_locate.py文件输入是原始BGR图像输出车牌区域的坐标元组。# plate_locate.py import cv2 import numpy as np def locate_plate(img): 定位车牌区域 img: OpenCV读入的BGR图像 返回: (x, y, w, h) 或 None # 灰度化消除颜色干扰 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 高斯滤波核大小(5,5)去掉传感器噪点 blurred cv2.GaussianBlur(gray, (5, 5), 0) # Canny边缘检测参数为(低阈值, 高阈值) edges cv2.Canny(blurred, 100, 200) # 形态学闭运算先膨胀后腐蚀把车牌的边缘连成闭合矩形 kernel cv2.getStructuringElement(cv2.MORPH_RECT, (17, 5)) closed cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel) # 查找外层轮廓OpenCV 4.x返回两个值 contours, _ cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 按面积从大到小排序 contours sorted(contours, keycv2.contourArea, reverseTrue) for cnt in contours[:15]: x, y, w, h cv2.boundingRect(cnt) aspect w / h area w * h # 标准车牌宽高比约3.14面积阈值按图片分辨率动态调整 if 2.5 aspect 4.5 and area 8000: return x, y, w, h return None这段代码里参数的含义逐个说清楚。高斯滤波核(5,5)是800x600级别图片的经验值图片分辨率更高时可以换到(7,7)。Canny阈值(100,200)的意思是梯度幅值超过200的像素直接判为边缘超过100且与强边缘相邻的也判为边缘低于100的丢弃。形态学核(17,5)是横向长条因为车牌是横向矩形这个核能把四条边的断裂处连起来同时避免把上下两边横向连接到车身其他位置。过滤条件里的宽高比2.5到4.5覆盖了正常拍摄、轻微侧向角度下投影比例的变化。面积8000是以1080p图像为参考的经验值如果测试图只有640x480这个值要下调到2000到3000否则小目标会被过滤掉。调试定位效果时加一行cv2.rectangle(img, (x, y), (xw, yh), (0, 255, 0), 2)画出框选位置比看打印输出直观得多。3.2 字符分割投影法把车牌切成单字符定位到车牌之后第二步是把车牌区域切分成单个字符。这个模块的输入是原始图像加定位框输出字符图像列表。# plate_segment.py import cv2 import numpy as np def segment_characters(img, box, target_size(48, 96)): 从车牌区域切分出字符 img: 原始BGR图像 box: locate_plate返回的(x, y, w, h) target_size: 输出字符的(宽, 高) 返回: 字符图像列表 x, y, w, h box # 裁出车牌区域并统一缩放到标准宽高440x140 plate img[y:y h, x:x w] plate cv2.resize(plate, (440, 140)) # 灰度 Otsu自动二值化 gray cv2.cvtColor(plate, cv2.COLOR_BGR2GRAY) _, binary cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU) # 蓝底白字车牌字符应为白色若白色像素占多数说明反色了 if np.sum(binary 255) np.sum(binary 0): binary cv2.bitwise_not(binary) # 水平投影找非零行范围裁掉上下边框 h_proj np.sum(binary, axis1) // 255 rows np.where(h_proj 0)[0] if len(rows) 0: return [] y_top, y_bottom rows[0], rows[-1] binary binary[y_top:y_bottom, :] # 垂直投影找每列非零像素数按连续区间切分字符 v_proj np.sum(binary, axis0) // 255 char_regions [] in_char False start 0 for i, v in enumerate(v_proj): if v 2 and not in_char: in_char True start i elif v 2 and in_char: in_char False if i - start 15: # 过滤笔画断裂产生的碎片 char_regions.append((start, i)) if in_char: char_regions.append((start, len(v_proj))) # 每个字符统一缩放到目标尺寸 result [] for s, e in char_regions: char_img binary[:, s:e] char_img cv2.resize(char_img, target_size) result.append(char_img) return result这里有一个很关键但容易被新手忽略的处理二值化之后要检查字符颜色在哪一边。蓝底白字的车牌二值化后字符应该是白色但如果输入是临时牌照或者反光导致颜色反转二值化结果就会变成白底黑字此时不反转的话后续投影切分出来的全是背景。判断逻辑很简单白色像素明显多于黑色像素就说明白的是背景取反一次就好。垂直投影切分时用v 2而不是v 0是为了跳过个别噪点列。宽度过滤i - start 15过滤掉笔画断裂或污渍产生的碎片区间。注意“京”“沪”这类汉字内部结构复杂可能出现一个汉字被切分成多块的极端情况遇到这种要回到分辨率和二值化参数上找原因或者改用连通域标记切分。3.3 字符识别模板匹配类的完整实现字符识别我推荐写成模板匹配类。好处是启动时一次性加载所有模板之后每张字符图只需要做几十次matchTemplate计算速度非常快。# plate_recognize.py import os import cv2 import glob class PlateRecognizer: def __init__(self, template_dir, template_size(48, 96)): template_dir: 模板目录文件名即字符名称 template_size: 模板统一尺寸必须与分割输出一致 self.templates {} self.template_size template_size for path in glob.glob(os.path.join(template_dir, *.png)): name os.path.splitext(os.path.basename(path))[0] tpl cv2.imread(path, cv2.IMREAD_GRAYSCALE) if tpl is None: print(f警告: 无法读取模板 {path}) continue tpl cv2.resize(tpl, template_size) _, tpl cv2.threshold(tpl, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU) self.templates[name] tpl def match(self, char_img): 对单个字符图像做模板匹配 返回: (字符, 相似度得分) if len(self.templates) 0: return None, -1 best_name None best_score -1 for name, tpl in self.templates.items(): res cv2.matchTemplate(char_img, tpl, cv2.TM_CCOEFF_NORMED) _, score, _, _ cv2.minMaxLoc(res) if score best_score: best_score score best_name name return best_name, best_score模板匹配的相似度指标用TM_CCOEFF_NORMED这是归一化相关系数取值范围从-1到11代表完全一致。它对比度变化有一定鲁棒性比直接算像素差更适合实际照片。得分超过0.7基本可以认为匹配正确0.5到0.7之间要警惕相似字符比如数字0和字母O在同一个字体下得分可能非常接近。模板目录里的文件名即识别结果所以汉字模板用“京.png”这种格式数字用“0.png”字母用“A.png”不要用小写字母做文件名否则识别结果里会混入非法的小写字符。3.4 主流程串联main.py的调用逻辑最后把三个模块串进main.py# main.py import cv2 from plate_locate import locate_plate from plate_segment import segment_characters from plate_recognize import PlateRecognizer def recognize_image(img_path, recognizer): img cv2.imread(img_path) if img is None: print(f无法读取图片: {img_path}) return None, 0.0 box locate_plate(img) if box is None: print(未定位到车牌区域) return , 0.0 chars segment_characters(img, box) plate_str total_score 0.0 for c in chars: ch, score recognizer.match(c) if ch is None: plate_str ? continue plate_str ch total_score score avg_score total_score / len(chars) if chars else 0.0 return plate_str, avg_score if __name__ __main__: recognizer PlateRecognizer(templates) result, score recognize_image(test_images/京A12345.jpg, recognizer) print(f识别结果: {result}, 平均置信度: {score:.2f})主流程的逻辑顺序和前面拆解的三步完全一致。特别注意recognizer对象在整个程序里只创建一次不要每次识别都重新加载模板否则批量图片时磁盘I/O会吃掉很多时间。这段代码整体就是“下载一个车牌识别-python源码包解压后”最常见的组织方式如果包里没有main.py而是把所有逻辑堆在一个文件里也不影响理解只是可维护性差一些。4. 参数调优的4个关键细节从识别率60%到90%的差距往往就在这些参数里4.1 Canny阈值边缘太少会漏检边缘太多会误检Canny两个阈值直接决定后续轮廓分析的质量。高阈值太大车牌边缘可能因为对比度不够而断成碎段低阈值太小车身线条、树影、灯柱纹理全进入边缘图闭运算后会出现大量噪声轮廓。调Canny参数这件事有一定“玄学”成分差一个数字识别率就完全不同但核心原则是固定的边缘宁可多不可少因为后面还有宽高比和面积过滤能去掉误检漏检就什么都没了。实际调参建议是先固定形态学内核参数在测试图片上跑一遍把edges图用imshow打出来看。发现车牌边缘断裂明显就把高阈值从200下调到150低阈值同步调到80。发现边缘图里噪点密布就把低阈值往上提到120高阈值不动。不同光照的经验值白天强光(100, 200)稳定黄昏或阴影降到(80, 150)夜间补光可以提高到(120, 240)。曝光不稳定的视频流可以用cv2.Canny(blurred, 50, 150)配合前面提到的CLAHE对比度增强对动态范围大的场景更宽容。4.2 形态学内核大小连不上断线和连出伪轮廓之间只有几步形态学闭运算的内核尺寸对定位结果的影响比Canny阈值更直观。内核太小车牌边缘的断口连不上闭运算后矩形不闭合findContours找不到完整外框。内核太大车牌上下边缘会被横向拉长并和车身线条连通候选框变成覆盖半个车头的超大矩形宽高比过滤直接失效。(17, 5)这个尺寸的经验基础是车牌宽高比3.14。宽度17是为了覆盖横向断口高度5是因为车牌字符和边缘之间通常有几像素间隙太高会把字符连入外框。框选结果偏宽、包住多个字符说明内核宽度太大从17改到11或9。定位经常失败返回None先看闭运算后的closed图确认车牌区域是否形成了完整闭合的白块没有就加大内核尺寸。这里可以用一个调试小技巧把不同内核尺寸跑出来的closed图横向拼在一起对比一眼就能找到合适的区间。4.3 车牌宽高比与面积阈值两道候选过滤最容易出错的地方过滤条件直接决定最终返回哪个候选框。宽高比2.5到4.5已经排除掉大部分非车牌矩形但挡不住两种特殊情况一是车身侧面线条或前保险杠形成的长条矩形宽高比也在3附近二是远处的小车车牌面积不够却又真是目标。面积阈值8000在1080p上够用但640x480的图里车牌区域面积可能只有1500到3000阈值不降下来车牌就被误过滤了。我的做法是让面积阈值和图片分辨率挂钩area_threshold int(img_width * img_height * 0.001)车牌至少占全图千分之一面积。低于这个值的基本不可能是车牌。宽高比过滤之后还可以加一道颜色校验统计候选区域在HSV空间里的蓝色像素占比超过30%再认可为蓝牌这招对白车和银灰色车干扰频繁的情况非常有效换到黄牌或新能源绿牌时色相范围也要跟着换。4.4 二值化与模板尺寸统一模板匹配翻车的最常见源头分割模块里选Otsu而不是固定阈值是因为车牌图片亮度变化太大固定阈值在暗场景下会把字符和背景融为一体。Otsu自动找全局最优阈值适合大部分场景但强反光或阴影横穿车牌时也会失效此时改用cv2.adaptiveThreshold局部自适应二值化对光照不均的鲁棒性好很多代价是计算量增加。模板尺寸统一是模板匹配成功的前提。模板不能存多大就用多大必须resize到和分割输出完全一致的(48, 96)。尺寸不一致时matchTemplate会直接报断言错误报错信息还很隐晦。模板预处理时也做一遍Otsu二值化保证模板和待识别字符都是黑底白字否则模板是灰度图、待识别字符是二值图相关系数普遍偏低。注意模板匹配的前提是模板和待识别字符在同一个色彩空间、同一个尺寸下。如果你的匹配得分普遍低于0.5先检查模板预处理逻辑是否和分割模块的二值化方向一致这是最容易被忽略的隐含约定。5. 车牌识别常见问题排查与避坑五个翻车现场和对应解法5.1 定位框一大片或者返回None车牌明明在画面里现象locate_plate返回一个超大矩形框框里露出半个车头或者直接返回None调试输出“未定位到车牌区域”。原因超大矩形框是形态学内核(17, 5)偏大把车牌边缘和保险杠、大灯等亮色轮廓连成一片闭运算后合成一个大外框。返回None则相反是Canny阈值偏高导致车牌边缘断裂形态学闭运算连不起来。解决先看中间结果把edges和closed两张图分别打出来确认车牌区域在edges里是否清晰完整。边缘断裂就降Canny低阈值到80左右边缘完整但框太大就缩小形态学内核宽度到11。可视化中间结果是定位调参的唯一可靠手段不要凭感觉盲改。5.2 字符分割多出来一个竖条或多切出一块现象识别结果末尾多了一个字母或数字或者汉字被切成两半模板匹配输出两个奇怪字符。原因多出来的字符通常来自车牌右边框或螺孔铆钉螺帽在二值化后是圆形亮点位置落在字符区间外时会被垂直投影当成字符列。汉字被切两半则是“京”“沪”这类字中间空隙大垂直投影在汉字内部出现断点。解决分割前先裁剪上下边框然后对二值图做一次形态学开运算核大小(3, 3)去掉螺帽和细小噪点。针对汉字内部断裂在切分后加一个合并约束如果相邻两个字符区间的间隔小于平均字符宽度的三分之一就合并成一个区间。标准车牌是7个字符区间数量超出7个时优先怀疑边框或螺帽未滤干净。5.3 汉字模板匹配得分普遍偏低数字和字母正常现象识别结果数字字母全对汉字部分每次得分只有0.5左右经常匹配成错字或输出问号。原因模板库里的汉字模板太少或风格单一。网上流传的源码包模板大多是官方标准字体但实际照片里的汉字会因光源、污染、轻微角度产生形变另外多个省份简称字形相近低分辨率下得分非常接近。解决把测试图里真实出现的汉字字符裁剪下来存成实际场景模板扩进templates目录让模板集覆盖不同光照下的字形。如果某几个汉字模板互相干扰在识别后加最低得分阈值兜底低于0.6就输出“”并在日志里提示人工复核宁可识别成未知也不要输出一个错误字符。5.4 同一份源码在别人电脑上跑得好好的自己环境一跑就崩现象报ModuleNotFoundError: No module named cv2或者findContours解包时报“ValueError: not enough values to unpack”。原因前者是依赖没装或者装进了错误的虚拟环境后者是OpenCV版本不一致OpenCV 3和OpenCV 4的findContours返回值约定不同老源码包按OpenCV 3语法解包在OpenCV 4上就会崩。解决严格按requirements.txt装依赖如果requirements.txt没有锁版本手动指定稳定版pip install opencv-python4.5.5.64。用python -m venv venv建独立环境再安装避免系统环境里多版本OpenCV互相污染。findContours兼容问题统一改成contours, _ cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)只使用contours返回值。这也是我的血泪经验帮同事排查过三次同样的报错全是版本环境问题。5.5 识别率白天正常晚上掉一半现象白天测试图批量识别率85%晚上或黄昏的照片大量定位失败或者定位成功但结果是乱码。原因光照不足直接削弱边缘信号车牌在暗场景下对比度低蓝底在弱光下变成灰蓝色颜色过滤效果变差夜间补光过强还会造成车牌反光字符和背景的灰度差被压缩。解决为夜间单独准备一组参数Canny低阈值降到60形态学核加大到(21, 5)二值化换adaptiveThreshold。对速度要求不高时在定位前先做CLAHE对比度增强cv2.createCLAHE(clipLimit2.0, tileGridSize(8, 8))能显著提升暗场景边缘的可检测性。工程上拆成白天和夜间两套配置在调用入口按图片亮度均值自动选择改动不大但效果立竿见影。6. 进阶用法用批量识别和置信度阈值把图片集上的表现量化出来模板匹配的识别结果会伴随置信度得分这个得分不只是调试信息它可以成为你判断系统质量、过滤低质量识别的依据。我写一个批量测试脚本把整个test_images目录的图片全跑一遍并输出CSV# batch_test.py import os import cv2 import csv from plate_locate import locate_plate from plate_segment import segment_characters from plate_recognize import PlateRecognizer def batch_recognize(img_dir, recognizer, output_csv): rows [] for fname in sorted(os.listdir(img_dir)): if not fname.lower().endswith((.jpg, .png, .jpeg)): continue path os.path.join(img_dir, fname) img cv2.imread(path) if img is None: continue box locate_plate(img) if box is None: rows.append([fname, , 0.0]) continue chars segment_characters(img, box) plate scores [] for c in chars: ch, score recognizer.match(c) plate ch if ch else ? scores.append(score) avg sum(scores) / len(scores) if scores else 0.0 rows.append([fname, plate, round(avg, 3)]) with open(output_csv, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([图片名, 识别结果, 平均置信度]) writer.writerows(rows) if __name__ __main__: rec PlateRecognizer(templates) batch_recognize(test_images, rec, results.csv)跑完这个脚本你手里就有一张图片名、识别结果、置信度三列的表格。接下来不要急着改参数先按置信度分层看错误分布平均置信度低于0.5的大部分是定位失败或车牌倾斜严重要调形态学和Canny0.5到0.7之间的大概率是字符分割把边框切进来或汉字形变要调分割和二值化0.7以上还识别错才需要怀疑模板质量。按这个顺序排查比对着几十张图挨个肉眼看高效得多。另一个实际部署里很值钱的习惯是设置置信度阈值低于0.6的输出标记为“待人工确认”。停车场道闸系统里低置信度的抓拍图自动转人工复核队列比强行让系统输一个错误车牌好太多。早期我做的版本不加置信度过滤夜间识别把“鲁A”认成“鲁B”用户一天投诉两起后来加了阈值过滤虽然多了一批人工复核单但用户体验反而上来了。希望这些经验和坑位记录能帮到你让你在车牌识别的路上少走几段弯路。本文还有配套的精品资源点击获取
返回列表