尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
深度学习车牌识别:OpenCV定位与CNN字符识别
简介《车牌识别系统详细讲解》PPT面向做毕业设计、课程项目或准备答辩的学生围绕Python与OpenCV构建车牌识别系统梳理从图像预处理、车牌定位过滤到字符分割识别的完整技术路线并说明双CNN结构在后台图片识别中的应用思路。压缩包仅含1个pptx文件约8.57MB以讲稿式页面串联研究背景与意义、车牌识别技术特点与难点、实验方案与原理、网络层参数、结果界面展示及课题总结可直接用于汇报或二次整理。资源已有4789人学习下载内容覆盖HSV色彩分割、Sobel边缘检测、Otsu二值化、闭运算、轮廓筛选与漫水填充等定位步骤也列出输入层36×128、卷积池化与全连接层的配置便于对照复现模型。对需要理解车牌定位与字符识别流程、准备答辩展示或拆解卷积网络参数的学习者这份PPT能提供较完整的项目讲解框架与调试思路参考。1. 车牌识别系统这套方案到底解决什么问题去年帮人看一个车牌识别的课程设计图片拖进去能出结果换一张夜间抓拍就全黑摄像头一开窗口直接卡成白板。这类问题多半不是模型不行而是流水线上某个阈值没兜住。这套基于深度学习的车牌识别系统用 Python 加 OpenCV 做图像处理用两个结构相同的卷积神经网络分别承担车牌定位过滤和字符识别输入统一压到 36x128输出 2 类还是 67 类取决于加载哪个权重。它适合三类人要交课程设计或毕业设计的同学、想搞明白传统图像处理和 CNN 怎么接起来的初级开发、以及需要在离线环境跑一个小规模识别 Demo 的工程人员。下面按能照着复现的顺序拆。2. 整体架构拆解两段式流水线为什么比端到端更稳先给结论这套系统没有走「一张图进、一串字符出」的端到端路线而是拆成定位和识别两段中间用字符分割硬切开。端到端模型在公开数据集上刷分好看但一旦换城市、换光照、换车牌底色标注成本会迅速失控拆成两段之后每一段的输入输出都能单独可视化出错时知道该修哪一段这对毕设和小规模落地来说比多两个点准确率重要得多。第二段还有一个容易被忽略的设计定位环节并不是纯传统图像处理它最后还挂了一个二分类 CNN 做兜底。传统方法负责「快」把候选框从全图缩到十几个CNN 负责「准」从候选框里挑出真正是车牌的那个。这种粗细结合的结构在算力有限的机器上比单一大模型更实用。2.1 从一张抓拍图到六位字符整条链路上的六个工序把整条链路摊平可以拆成六个工序每个工序的输入输出都很清楚这也是后面调参时唯一需要盯住的东西。工序输入输出主要手段图像输入本地图片或相机帧BGR 三通道矩阵cv2.imread / VideoCapture预处理原始 BGR 图二值化的边缘掩模灰度化、均值滤波、HSV 取色、Sobel、Otsu、闭运算候选定位边缘掩模若干候选矩形findContours、boundingRect、长宽比过滤区域过滤候选矩形唯一车牌框漫水填充补全 二分类 CNN字符分割校正后的车牌图单字符小图列表水平投影去边框、垂直投影切分字符识别单字符小图字符 置信度67 类 CNN softmax这个表不是拿来背的是拿来打日志的。每个工序结束后把中间结果 dump 成图片出问题的时候直接翻目录比在代码里打断点快十倍。我习惯在debug/下按01_gray.png、02_edge.png这种前缀编号存事后一眼就能看出是哪一步塌了。2.2 两个 CNN 的分工一个只判是不是车牌一个判是什么字符两个网络结构完全一致差别只在最后的分类层神经元个数。定位过滤网络输出 2 维代表「车牌」和「非车牌」字符识别网络输出 67 维覆盖省份汉字、字母和数字。from tensorflow.keras import layers, models def build_net(num_classes): 两个网络共用这套主干只换最后一层的类别数 model models.Sequential([ layers.Input(shape(36, 128, 3)), # 高36 宽128贴合车牌约1:3.1的长宽比 layers.Conv2D(32, (3, 3), paddingsame, activationrelu), layers.MaxPooling2D((2, 2)), # 36x128 - 18x64 layers.Conv2D(64, (3, 3), paddingsame, activationrelu), layers.MaxPooling2D((2, 2)), # 18x64 - 9x32 layers.Conv2D(128, (3, 3), paddingsame, activationrelu), layers.MaxPooling2D((2, 2)), # 9x32 - 4x16 layers.Flatten(), layers.Dense(1024, activationrelu), layers.Dense(1024, activationrelu), layers.Dense(1024, activationrelu), layers.Dense(num_classes, activationsoftmax), # 2 或 67 ]) return model plate_net build_net(2) # 定位过滤 char_net build_net(67) # 字符识别这段代码里有三个点值得说。第一paddingsame保证每层卷积前后特征图尺寸不变尺寸只被池化层砍半这样输入宽高比才不会被拉伸破坏。第二三次池化后特征图是 4x16x128展平后 8192 维接 1024 全连接层时单层就有八百多万参数整个模型绝大部分体积都在这里如果只是想跑通把 1024 降到 256 能砍掉九成体积识别率掉得不多。第三两个网络结构一致意味着主干代码可以抽出来复用训练脚本只传不同的num_classes和数据集路径维护成本直接减半。原文里提到池化时「不想在 batch 和 channels 维度做池化所以设置为 1」这句话落地到 Keras 就是默认行为MaxPooling2D((2, 2))只在空间维度滑动batch 和通道维度不会被动。如果用别的框架注意别把 stride 写成四元组。2.3 目录分层和数据流权重、样本、调试图各放一处工程目录如果一开始不规划半个月后自己都不认识。这套方案我建议按下面这个结构铺plate_recognition/ ├── config.py # HSV阈值、长宽比范围、模型路径等常量 ├── main_gui.py # GUI 入口600x600 窗口 ├── pipeline.py # 串起定位和识别的主流程 ├── locate/ │ ├── preprocess.py # 灰度、滤波、Sobel、Otsu、闭运算 │ ├── contour.py # 轮廓提取与长宽比过滤 │ ├── flood.py # 漫水填充补全与仿射校正 │ └── filter_net.py # 二分类 CNN 推理封装 ├── recognize/ │ ├── split.py # 水平/垂直投影字符分割 │ ├── model.py # build_net 主干定义 │ └── infer.py # 67 类推理与结果拼接 ├── weights/ │ ├── plate_filter.h5 │ └── char_recog.h5 ├── dataset/ │ ├── filter/ # plate / noplate 两个子目录 │ └── chars/ # 按 67 个类别分子目录 ├── debug/ # 中间结果图片按工序编号命名 └── requirements.txtconfig.py单独拎出来的好处是调阈值不用翻代码。蓝色车牌的 HSV 下限、长宽比允许范围、闭运算核尺寸这些参数在不同城市、不同摄像头下都要微调集中在一处出问题时改一个文件重跑就行不用满项目搜数字。3. 车牌定位链路从灰度图到候选框的四个步骤定位这一段的成败决定了后面所有工序。字符识别再准框错了地方也是白搭。这一段全是传统图像处理没有训练成本但也最吃阈值属于典型的「参数玄学区」。3.1 预处理四连灰度化、HSV 取色、Sobel 取边、Otsu 二值化四步的顺序不能随便换。先灰度化是为了把三通道压成一通道Sobel 只在单通道上算速度快三倍HSV 取色必须留在彩色图上做因为灰度图里蓝牌和黄牌的灰度值可能撞在一起最后把颜色掩模和边缘图相乘等于给边缘加了个颜色过滤器路面标线、护栏这些非车牌底色的边缘会被直接抹掉。import cv2 import numpy as np def preprocess(bgr): # 1. 灰度化数据量降到三分之一 gray cv2.cvtColor(bgr, cv2.COLOR_BGR2GRAY) # 2. 均值滤波 3x3压掉路面颗粒和压缩噪点核再大字符边缘会糊 blur cv2.blur(gray, (3, 3)) # 3. Sobel 只取 x 方向车牌字符的垂直边缘响应远强于水平方向 sobel cv2.Sobel(blur, cv2.CV_8U, 1, 0, ksize3) # 4. HSV 域按阈值抠蓝色和黄色两种底色 hsv cv2.cvtColor(bgr, cv2.COLOR_BGR2HSV) blue cv2.inRange(hsv, np.array([100, 43, 46]), np.array([124, 255, 255])) yellow cv2.inRange(hsv, np.array([15, 80, 80]), np.array([35, 255, 255])) color cv2.bitwise_or(blue, yellow) # 5. 颜色掩模与边缘图相乘只保留车牌底色区域的边缘 edge cv2.bitwise_and(sobel, sobel, maskcolor) # 6. Otsu 自动阈值二值化再闭运算把断裂的垂直边缘连成整块 _, binary cv2.threshold(edge, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU) kernel cv2.getStructuringElement(cv2.MORPH_RECT, (17, 5)) closed cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel) return gray, closed参数逐个说。cv2.blur的 3x3 是经验值抓拍图噪点重可以上 5x5但超过 5 字符笔画就开始糊。Sobel的dx1, dy0表示只算水平方向梯度垂直边缘就是车牌字符的竖笔画这是整段预处理的物理依据。HSV 阈值里 OpenCV 的 H 通道范围是 0 到 180不是 360蓝色落在 100 到 124 之间黄色落在 15 到 35 之间这两组数字在不同白平衡下要微调。THRESH_OTSU会自己算一个最佳阈值原理是把灰度直方图分成两类让类间方差最大不用手填 127 这种拍脑袋的数。闭运算核(17, 5)是横向长条因为车牌在图像里是横向拉伸的核的长宽比要跟着走。3.2 轮廓提取与长宽比粗筛先把十几个候选框缩出来二值图上的白色块就是候选区域用findContours全部捞出来再用长宽比和面积砍掉明显不对的。这一步的目标不是精确是尽量不漏宁可由后面的 CNN 误杀也不能在这里就把真车牌筛掉。def locate_candidates(closed, min_ratio2.2, max_ratio4.2): contours, _ cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) boxes [] for c in contours: x, y, w, h cv2.boundingRect(c) if h 10 or w 60: continue # 太小的直接丢多半是噪点 ratio w / float(h) area w * h # 标准车牌 440x140长宽比约 3.14透视变形留出上下余量 if min_ratio ratio max_ratio and area 1200: boxes.append((x, y, w, h)) return boxesmin_ratio2.2和max_ratio4.2这个区间的来历是中国小型汽车车牌 440x140 毫米比值 3.14斜拍时横向会被压缩比值可能掉到 2.4 左右正面略俯拍时可能拉到 3.8。区间开到 2.2 到 4.2 能覆盖绝大多数角度但也会放进一些护栏和广告牌所以后面必须有一道 CNN 过滤。area 1200是像素面积下限对应图像里车牌至少占 60x20 左右再小的话字符分割出来宽度不足 5 像素识别网络根本认不出。3.3 漫水填充补全与仿射校正把歪的车牌掰正候选框里有一部分是残的比如左半边被前车挡住或者边缘被闭运算吃掉一块。漫水填充的作用是从框中心取一个种子点把颜色相近的连通区域填满能补回残缺的部分补完后再算一次外接矩形尺寸不对的候选就被二次排掉了。最后一步是把矩形框做透视变换拉成标准 128x36后面的网络只认这个尺寸。def flood_complete(bgr, box): x, y, w, h box roi bgr[y:y h, x:x w].copy() mask np.zeros((h 2, w 2), np.uint8) # 掩模要比原图宽高各多2 seed (w // 2, h // 2) # 固定范围模式容差 20容忍光照不均但不至于漫到车身 cv2.floodFill(roi, mask, seed, (0, 255, 0), (20, 20, 20), (20, 20, 20), cv2.FLOODFILL_FIXED_RANGE) return roi, mask def warp_plate(bgr, box): x, y, w, h box src np.float32([[x, y], [x w, y], [x w, y h], [x, y h]]) dst np.float32([[0, 0], [128, 0], [128, 36], [0, 36]]) M cv2.getPerspectiveTransform(src, dst) return cv2.warpPerspective(bgr, M, (128, 36))掩模尺寸必须是(h 2, w 2)这是floodFill的硬性约定少一个像素就会抛异常第一次写的人基本都在这翻过车。容差(20, 20, 20)对应 BGR 三通道各自的允许偏差晚上光线暗的时候可以放宽到 35但放到 60 以上就会从车牌漫到保险杠。透视变换的四个目标点固定成 128x36和网络输入对齐后面就不用再 resize避免二次插值损失。3.4 定位 CNN 兜底二分类网络把误检压下去候选框做完几何过滤后剩下的十几个里面可能只有一个是真车牌。把每个候选裁出来统一缩放到 36x128送进二分类网络取 softmax 概率最大且超过阈值的那个。训练数据就是正负样本两个文件夹正样本是人工裁出来的车牌负样本从候选框里随机抠比例大概 1:3负样本多一些模型才不会见谁都说是车牌。推理时不要只取最大概率要加一个置信度下限比如 0.85。低于这个值的候选全部丢掉返回空结果。宁可漏检也别误检因为误检会把后面的字符分割带到一个完全不相关的区域输出一串莫名其妙的字符比直接说「没找到车牌」难排查得多。4. 字符分割与 67 类识别投影切分加三卷积网络定位拿到一张 128x36 的车牌图之后还有两件事把七个字符切成七张小图再逐个送网络分类。这一步的难点不在网络在切分切歪一个字符后面全错位。4.1 水平投影去白边、垂直投影切字符车牌四周有一圈白边和铆钉直接切会把边框当成字符。水平投影统计每一行的白点数量白点少的行就是上下边框砍掉垂直投影统计每一列的白点数量字符之间必然有一段空白列白点数接近零按这个断点切。def split_chars(binary_plate): h, w binary_plate.shape # 水平投影行方向白点数少于总宽 20% 的算边框 row_sum np.sum(binary_plate 0, axis1) rows np.where(row_sum w * 0.2)[0] if len(rows) 0: return [] top, bottom rows[0], rows[-1] cropped binary_plate[top:bottom 1, :] # 垂直投影列方向白点数阈值按字符高度动态算 col_sum np.sum(cropped 0, axis0) cols np.where(col_sum (bottom - top) * 0.15)[0] chars, start [], cols[0] for i in range(1, len(cols)): if cols[i] - cols[i - 1] 2: # 间隔超过2列认为是字符缝隙 chars.append(cropped[:, start:cols[i - 1] 1]) start cols[i] chars.append(cropped[:, start:cols[-1] 1]) # 宽度小于5像素的碎片丢掉正好排掉第2、3字符之间的圆点 return [c for c in chars if c.shape[1] 5]阈值0.2和0.15都是相对值不能写成固定像素。车牌在画面里可大可小如果按固定像素判小图切不出东西大图切出十几段。最后那个shape[1] 5的过滤很关键新能源车牌第二位和第三位之间有个圆点分隔符投影上会形成一个很窄的连通块宽度过滤能自然把它排掉比专门写规则识别圆点省事。4.2 三卷积三池化三层全连接参数都花在哪了网络结构就是 2.2 节build_net里那套这里把每一层的输出尺寸和参数量摊开方便判断该砍哪里。层输出尺寸参数量Conv1 3x3x3x3236x128x32896Pool1 2x218x64x320Conv2 3x3x32x6418x64x6418496Pool2 2x29x32x640Conv3 3x3x64x1289x32x12873856Pool3 2x24x16x1280FC1 102410248389632FC2 102410241049600FC3 102410241049600输出层2 或 672050 / 68675卷积部分加起来不到十万参数全连接部分占了九百多万。这个分布意味着两件事模型体积几乎全在 FC 层想压缩就砍 FC卷积层参数量少但计算量大推理慢的瓶颈在卷积不在全连接。如果目标机器是低配笔记本把三层 1024 改成 256、把第一次池化改成 3x3速度能提一大截字符识别率掉两三个点对课程演示完全够用。4.3 训练配置学习率 0.001、交叉熵、批量 64训练这块没什么花活优化器用 Adam学习率 0.001损失函数交叉熵标签做 one-hot。原文提到迭代 1000 次实际跑的时候 1000 个 epoch 大概率会过拟合正确做法是加早停和验证集。import tensorflow as tf model build_net(num_classes67) model.compile( optimizertf.keras.optimizers.Adam(learning_rate1e-3), losscategorical_crossentropy, metrics[accuracy] ) callbacks [ tf.keras.callbacks.EarlyStopping(monitorval_loss, patience15, restore_best_weightsTrue), tf.keras.callbacks.ModelCheckpoint(weights/char_recog.h5, save_best_onlyTrue) ] model.fit(x_train, y_train, batch_size64, epochs1000, validation_split0.1, callbackscallbacks)learning_rate1e-3是 Adam 的常用起点损失曲线在前 20 个 epoch 没明显下降就说明数据有问题不是学习率的事。patience15表示验证损失连续 15 轮不降就停restore_best_weightsTrue会自动回滚到最好的那一版权重不用自己记轮次。数据进网络前必须除以 255 归一化到 0 到 1这一步漏了的话损失会卡在 4.1 附近不动是最常见的翻车原因之一。4.4 67 维向量拼回字符串类别表和置信度都要留67 类的构成是省份简称加字母加数字。推理时对每张小图取概率最大的下标查表转成字符同时保留置信度用于后续校验。PROVINCES 京津沪渝冀晋蒙辽吉黑苏浙皖闽赣鲁豫鄂湘粤桂琼川贵云藏陕甘青宁新 CHARS PROVINCES 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ def recognize_char(char_img, model): img cv2.resize(char_img, (128, 36)).astype(float32) / 255.0 img np.stack([img] * 3, axis-1) # 单通道复制成三通道对齐输入 probs model.predict(img[None, ...], verbose0)[0] idx int(np.argmax(probs)) return CHARS[idx], float(probs[idx])注意np.stack([img] * 3, axis-1)这一步投影切出来的是单通道二值图网络输入是三通道不补通道会直接报维度不匹配。另外CHARS的顺序必须和训练时生成标签的顺序完全一致训练脚本和推理脚本共用一个常量文件别各写一份。5. 跑通与排查环境、GUI 事件和五个必踩的坑代码能跑和项目能用之间隔着一段距离这段距离基本都由环境、界面事件和阈值组成。5.1 依赖安装与最小运行环境这套系统依赖很轻OpenCV 加 TensorFlow 加 Pillow 就够GUI 用 tkinterPython 自带。python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate pip install opencv-python4.8.1.78 pip install tensorflow2.13.0 pip install pillow numpy版本别乱跳。OpenCV 4.5 之后findContours的返回值从三个变成两个网上抄的旧代码直接报解包错误。TensorFlow 2.13 之后tf.keras独立成包from tensorflow.keras import layers在某些版本要改成from keras import layers。虚拟环境一定要建多个项目共用全局环境时 TensorFlow 版本冲突能折腾一下午。5.2 GUI 三个按钮的事件绑定界面按原文设计是 600x600 的窗口右下角三个按钮打开图片、打开相机、展示图片。按钮不用铺满用place按相对坐标摆右下角就行。import tkinter as tk from tkinter import filedialog import cv2 from PIL import Image, ImageTk root tk.Tk() root.geometry(600x600) panel tk.Label(root) panel.place(x20, y20, width560, height420) def show_frame(bgr): rgb cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB) img ImageTk.PhotoImage(Image.fromarray(rgb).resize((560, 420))) panel.configure(imageimg) panel.image img # 必须持有引用否则图片被回收显示空白 def open_file(): path filedialog.askopenfilename( filetypes[(图片, *.jpg *.png *.bmp)]) if not path: return bgr cv2.imread(path) show_frame(bgr) # 这里接 pipeline(bgr)把识别结果写到下方标签 tk.Button(root, text打开图片, width20, height5, commandopen_file).place(x120, y480) tk.Button(root, text打开相机, width20, height5, commandopen_camera).place(x340, y480) tk.Button(root, text展示图片, width20, height5, commandshow_result).place(x470, y480) root.mainloop()panel.image img这行是 tkinter 的经典坑不给 Label 挂一个引用图片对象会被垃圾回收界面显示一片空白但程序不报错排查起来毫无头绪。相机那条线不要在主线程里写while True: ret, frame cap.read()会把mainloop堵死窗口拖都拖不动。正确做法是用root.after(30, update)递归调度每 30 毫秒读一帧。5.3 五条血泪踩坑记录现象、原因和解决现象一蓝色车牌在隧道或地库里完全检不出debug 图里边缘掩模全黑。原因HSV 阈值是按白天正常白平衡定的暗光下饱和度 S 掉得很低inRange直接把整片区域判成非蓝。 解决把 S 通道下限从 43 降到 20 左右或者干脆在低照度场景关掉颜色掩模只走 Sobel 加形态学。更好的做法是先用cv2.createCLAHE做一次直方图均衡再进 HSV。现象二点开相机后窗口卡死不响应任务管理器显示 CPU 占满。原因相机读取循环写在了按钮回调里同步阻塞了 tkinter 的事件循环。 解决改成root.after定时拉帧或者起一个独立线程读帧写进队列主线程只负责从队列取最新一帧渲染。现象三训练时损失一直在 4.1 附近不动准确率跟随机猜一样。原因图片像素值 0 到 255 直接喂进网络没有归一化梯度爆炸或消失。 解决在ImageDataGenerator里加rescale1./255或者手动除以 255。顺便检查标签是不是 one-hotcategorical_crossentropy配整数标签也会出这个问题要么换sparse_categorical_crossentropy。现象四字符切出来 5 段或 8 段识别结果整体错位。原因垂直投影用了固定像素阈值车牌在画面里偏小或偏大时阈值失效粘连的字符切不开或者一个字符被切成两半。 解决阈值改成相对值按字符高度乘以 0.15 动态计算。切完之后再按平均宽度过滤一遍宽度不到平均值一半的碎片合并回相邻块。现象五数字和字母识别率九成以上省份汉字只有六七成。原因训练集里汉字样本集中在少数几个省份且不同车牌的汉字字体差异大长尾问题明显。 解决按类别做均衡采样每个省份至少凑够同等数量对汉字小图单独做数据增强加轻微旋转、缩放、亮度扰动。字符集的 67 类里如果混进了字母 I 和 O还要确认训练时有没有把它们和数字 1、0 混淆标注这类标注噪声对汉字类别的影响最直接。6. 二次开发换底色、换主干、加视频流的三条改造路径拿到这套代码之后最常被问的三个改造方向是支持更多车牌底色、把模型换小、从单图识别扩到视频流。三条路的改动量差别很大先看下面这张表。改造方向主要改动文件改动量风险点增加绿色新能源车牌config.py加 HSV 区间preprocess.py加一路掩模半天绿牌 8 位字符分割和 67 类表都要动换成 MobileNet 主干recognize/model.py换 backbone一天输入尺寸要重新对齐权重得重训单图扩到视频流main_gui.py加定时器pipeline.py加跳帧半天每帧都跑 CNN 会掉帧要做间隔推理绿色车牌这条最容易上手也最容易翻车。绿牌的底色 HSV 大概在 H 35 到 85 之间加一路inRange和蓝黄做bitwise_or就行。但新能源车牌是 8 位字符第一位省份第二位字母后面六位字母数字混排split_chars切出来的段数会变成 8字符类别表也要加长。如果只是演示可以先把 8 位图缩放成 7 位硬凑正式用就得老老实实改分割逻辑。换主干这条思路是把build_net里的三层卷积替成 MobileNetV2 的前置层include_topFalse后面接自己的全连接。输入尺寸建议保持 36x128 不变因为预处理和透视变换全是按这个尺寸写死的改尺寸要连带改warp_plate的目标点和分割后的 resize牵一发动全身。换完主干必须重训别指望加载旧权重形状对不上会直接抛错。视频流改造的关键是别每帧都推理。抓拍场景里车牌在画面中停留至少半秒按 15 帧每秒算有七八帧可用每隔三帧跑一次完整流水线中间帧只做显示帧率能从个位数拉回二十以上。识别结果做一次简单的多数投票连续三帧输出相同才认为有效能压掉不少闪烁。我自己的习惯是每改一个模块先把该模块的中间结果单独 dump 成图片看一眼确认这一段的输入输出没问题再接下一段。之前图省事跳过这步直接改了三处阈值一起跑结果输出全是乱码回头一个个二分排查花了大半天。从那以后我每次动这类多阶段流水线都强制先把每一阶段的中间图落到本地目录再调参不再凭感觉改数字。这份讲解用的 PPT 把选题背景、技术特点与难题、实验方案与原理、结果与界面展示这几块串成了一条线配合上面这套代码一起看定位和识别两段各自在做什么会比较清楚需要拿去改的地方也都标出来了。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

深入了解Python的继承

深入了解Python的继承

前言 继承(inheritance)是 Python 里最常用的代码复用手段之一:把一组类共有的属性和方法抽到一个基类(base class,也叫父类)里,其余类通过 class Child(Base) 声明自己是它的子类(s…

📅 2026/10/30 2:54:50
Python+OpenCV车牌识别系统:两段式定位、分割与CNN识别

Python+OpenCV车牌识别系统:两段式定位、分割与CNN识别

简介:围绕Python与OpenCV实现车牌识别的毕业设计答辩PPT,面向计算机视觉入门者、本科毕业设计学生及希望梳理车牌识别流程的开发者,重点讲清车牌定位与字符识别两段式方案。内容从选题背景、我国车牌样式特点与技术难点切入,依次展…

📅 2026/10/30 2:54:50
C# 无需 IIS 搭建 Web 服务:.NET 8 自托管与 Kestrel 实战指南

C# 无需 IIS 搭建 Web 服务:.NET 8 自托管与 Kestrel 实战指南

简介:这份资源面向希望摆脱IIS依赖、快速搭建轻量级HTTP服务的C#开发者与学习者,聚焦用内置HttpListener实现自托管Web服务的完整示例。压缩包共17个文件、约18KB,以8个cs源码文件为核心,配合csproj项目配置、resx资源与xml配置&a…

📅 2026/10/30 2:49:50
MORE NEWS

更多资讯

📰

EMC整改实战:辐射发射、传导发射与静电抗扰度排查思路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

嵌入式开发从裸机到Linux的四阶段工程路线图

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

一周就业信息速递:从采集到发布的完整实操指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

20+款ESP32在线开发工具盘点:浏览器仿真、烧录与免安装实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

STM32实战进阶:用嵌入式C++补齐外设、总线与数据上云的关键技术解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

ESP-IDF驱动DHT11实战:从时序原理到稳定采集的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬