ARTICLE DETAIL

资讯详情

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

基于OpenCV的玻璃瓶口缺陷检测:传统图像处理实战指南

基于OpenCV的玻璃瓶口缺陷检测:传统图像处理实战指南 简介基于OpenCV传统图像处理实现的玻璃瓶口缺陷检测项目专为图像处理与计算机视觉方向的毕业设计、课程设计和期末大作业准备。资源共44个文件打包后仅6MB包含2个Python脚本、41张瓶子与瓶口图像样本和1份Markdown说明文档。两个脚本功能明确bottle_create.py负责对框中的瓶子进行检查bottle_mouth.py用于瓶口缺陷检测图像数据按原始图像与检测结果分目录组织便于直观比对。项目不依赖深度学习框架只需OpenCV环境即可运行有助于理解边缘检测、阈值分割、形态学操作等传统方法在工业质检中的应用说明文档给出了清晰的运行指引适合有Python基础的学习者直接复现或在此基础上扩展其他检测思路。目前已有1245人学习下载作为课程设计或入门实战项目具有较高参考价值。1. 玻璃瓶口缺陷检测传统图像处理为什么还值得用产线视觉检测里瓶口缺陷一直是个“看起来简单、做好很难”的位置。崩口、裂纹、缺口、脏污、变形这些缺陷直接关系到密封性和食品安全漏检一个就是整批召回的风险。很多团队一上来就想着上深度学习但实际落地时发现玻璃瓶口的成像环境高度可控——固定工位、固定光源、固定姿态缺陷类型也就那几类这种情况下传统图像处理反而更快、更稳、更便宜。基于OpenCV的经典方法配合一份靠谱的缺陷数据就能在普通工控机上跑到毫秒级不需要GPU不需要标注几千张图。这篇笔记就围绕“Python OpenCV 传统图像处理实现玻璃瓶口缺陷检测”这个完整链路来写从数据准备、ROI定位、特征提取到参数调优和产线部署。读者对象是已经在做视觉检测、或者准备接手瓶口检测项目的工程师新手可以照步骤复现熟手可以直接抄参数和避坑结论。2. 检测方案选型为什么瓶口场景适合传统视觉而不是深度学习2.1 瓶口缺陷的成像特征与检测难点玻璃瓶口是典型的回转体结构顶部是环形密封面内侧是螺纹或光滑直壁。产线上的瓶口检测关注的是瓶口端面、内壁和外壁三个区域。端面缺陷最常见崩口缺一块、裂纹细线状、缺口边缘缺失、污渍灰黑色块。内壁缺陷主要是螺纹损伤和杂质。外壁缺陷主要是划痕和碰伤。成像上有三个特点决定了这个场景吃传统图像处理这一套。第一玻璃是透明或半透明的在背光照射下轮廓对比度极高瓶口边缘是一条清晰的暗色带适合做边缘提取和圆检测。第二瓶口位置在相机视野里的漂移量很小因为机械定位会把瓶子约束在固定工位上偏差通常只有几个像素。第三缺陷形态相对固定崩口和缺口的尺寸有一定的下限控制不会出现“缺陷长得千奇百怪”的情况。难点也是真难点。玻璃的反光特性导致高光区域过曝缺陷可能被亮斑盖住瓶口端面是环形直接套用整幅图像的阈值分割会把中心背景也卷进来透明材质的灰度分布不均匀同一个缺陷在不同批次瓶子上的灰度表现能差出两倍。这些坑在后面各章都会展开。2.2 传统图像处理与深度学习的选型边界我经常被问一个问题现在YOLO都到v9了为什么还要用传统图像处理答案是看具体约束条件。深度学习方案需要大量标注样本。崩口和裂纹属于低概率缺陷产线上跑一整天可能才积累几十个正样本要凑齐几千张带标注的缺陷图周期是几周到几个月。训练完之后还有部署问题产线工控机往往没有NVIDIA显卡用CPU推理YOLO速度勉强能到20到30毫秒但一旦算上预处理和后处理整个节拍就吃紧了。传统图像处理的推理时间基本在5到10毫秒以内纯CPU就能扛住。另一个维度是可控性。深度模型是个黑匣子漏检了只能加数据重训练很难解释为什么这一张图被判为OK传统方法每个判据都是显式逻辑哪一步筛掉了、哪个特征值超出阈值调试时一目了然。在食品、药品包装这类需要做设备验证的行业可解释性是很重要的审计资产。但传统方法也不是万能的。如果瓶口姿态自由度大、光源不可控、缺陷类型没法归纳成固定的特征模式那传统方法的鲁棒性就会崩掉那种场景老老实实上深度学习。瓶口检测之所以适合传统方案正是因为姿态、光源、缺陷类型这三个变量都被控制在很窄的范围内。2.3 整体检测流程与模块划分一套完整的瓶口缺陷检测程序按模块拆解核心是四个部分。ROI定位模块负责找到瓶口在图像里的位置和半径我们采用Hough梯度圆检测因为瓶口端面在背光下是一个几乎正圆的轮廓这个方法又快又稳。特征提取模块负责在ROI环形区域内计算统计特征包括灰度均值、边缘密度、投影曲线等。缺陷分类模块把特征组合成判据用多条件逻辑或阈值比较来判OK还是NG。最后是可视化与保存模块把检测结果画在图上方便产线人员确认漏检和误检原因。代码工程上一个清晰的流程是这样组织的def detect_bottle_neck(image): # ROI定位找到瓶口圆心和半径 center, radius locate_neck_roi(image) if center is None: return False, ROI定位失败 # 特征提取在环形区域计算多个特征 features extract_neck_features(image, center, radius) # 分类推理按特征判据输出结论 is_ok, reason classify_neck(features, thresholds) return is_ok, reason这种模块化写法的好处是定位、特征、分类三段可以分别调参。实际调试中ROI定位出错和特征阈值不当是两类完全不同的问题混在一起写会导致调一个参数引发连锁反应。模块化之后每个函数可以独立用真实缺陷图做回归测试。这也是我在做视觉项目时坚持的工程习惯宁可多写两个中间函数也不要把检测逻辑写成一个大面条函数。数据处理上上一章已经交代过数据集结构这里补充一下为什么需要先了解数据集再写代码。拿到这批含缺陷的瓶口图片后第一件事是统计正常图和缺陷图的数量、缺陷的大致面积大小、图像分辨率。这些数据决定了后续特征阈值的设计方向例如如果缺陷最小像素面积是200那么阈值下限就会设在这个量级。3. 缺陷数据与标注准备先弄清楚手里有什么3.1 解压并盘点数据集目录结构拿到源码包和缺陷数据之后先动手做数据盘点。从zip解压出来的项目通常包含图像数据、标注文件和Python源码三个部分。我习惯先看一眼目录结构再写任何检测逻辑。# 解压并列出目录结构只显示前3层 unzip glass_neck_defect.zip -d ./glass_project cd ./glass_project tree -L 3 -d一个常见的目录组织方式是data文件夹下面再分ok和ng两个目录ng目录按缺陷类型进一步分崩口、裂纹、缺口。images是原图annotations是标注文件。通过这一步可以快速判断这份数据的完整度——如果有标注文件但缺失缺陷图或者图像分辨率参差不齐后面处理时就会遇到坑。看完目录结构之后下一步做数量统计这是评估数据质量的关键一步决策是否要补充缺陷样本前先看每个类别的样本量是否足够。import os from collections import Counter def count_samples(data_root): counter Counter() for root, dirs, files in os.walk(data_root): for name in files: if name.lower().endswith((.jpg, .png, .bmp)): label os.path.basename(root) counter[label] 1 for label, cnt in counter.items(): print(f{label}: {cnt}张)这一步的落点在于如果你发现某个缺陷类别只有几十张那么后续调参时就要意识到这个类别的参数可能不够稳定。我一般会在调参阶段给每个类别的参数单独做回归而不是用一个大阈值统一处理。计数器脚本的输出还能帮你发现目录归属错误比如ng目录下混入OK图片这种脏数据会直接污染调参过程。3.2 用脚本快速检查图像质量图像质量检查是容易被低估的一步。产线采集的照片如果分辨率不一致或者有运动模糊检测算法的阈值就可能失真。我写了一个快速遍历脚本把所有图像的尺寸、亮度均值、模糊程度一次性打印出来筛出异常样本。import cv2 import numpy as np import os def inspect_images(folder): for name in os.listdir(folder): path os.path.join(folder, name) img cv2.imread(path) if img is None: print(f[FAIL] {name}: 无法读取) continue gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) laplacian_var cv2.Laplacian(gray, cv2.CV_64F).var() print(f{name}: shape{img.shape}, mean_gray{gray.mean():.1f}, blur_var{laplacian_var:.1f})这里重点关注blur_var这个指标它反映的是图像清晰度。Laplacian响应方差越高说明边缘越锐利图像越清晰。如果某张图的blur_var只有几十而正常图的均值在几百以上这张图就是运动模糊或对焦失败的产物建议直接从数据集里剔除不然它会拉偏阈值回归的结果。另一个实用检查是用OpenCV的imread直接判断图片能不能解码。有些数据集里的图片扩展名是.png实际却是JPEG编码OpenCV能解码但某些第三方库可能会报错尽早发现可以省去后面莫名其妙的崩溃。3.3 标注信息怎么组织和转换缺陷数据的标注决定了你能做什么类型的算法验证。如果标注是矩形框坐标你可以用来做ROI切分和面积统计如果标注是分类标签你可以直接用来统计缺陷类型分布如果标注是像素级掩膜就能做模板差分和精确面积计算。传统图像处理最常用的是前两种。标注文件通常是XML、JSON或TXT格式。XML是Pascal VOC格式最典型的结构是每个object节点的bndbox包含xmin、ymin、xmax、ymax。JSON更多见于自定义标注工具的输出。我习惯把标注统一转成CSV因为后续做特征提取和阈值回归时用pandas读取CSV最顺手。import csv import xml.etree.ElementTree as ET import glob def voc_xml_to_csv(annotation_dir, output_csv): rows [] for xml_file in glob.glob(f{annotation_dir}/*.xml): tree ET.parse(xml_file) root tree.getroot() filename root.findtext(filename) for obj in root.iter(object): label obj.findtext(name) bbox obj.find(bndbox) rows.append([filename, label, int(bbox.findtext(xmin)), int(bbox.findtext(ymin)), int(bbox.findtext(xmax)), int(bbox.findtext(ymax))]) with open(output_csv, w, newline) as f: writer csv.writer(f) writer.writerow([filename, label, xmin, ymin, xmax, ymax]) writer.writerows(rows)VOC格式的坐标是整数像素这在传统图像处理里精度够用。但注意如果标注是针对整幅大图而你只检测瓶口ROI区域就涉及到坐标映射问题——从原图坐标换算到ROI局部坐标时偏移量必须统一计算否则后面提取特征时会拿错区域。我通常的做法是在CSV里直接保留原始坐标在特征提取模块内部再换算不在转换阶段硬改坐标。这一步能避免处理不同分辨率输入时产生坐标系错乱问题。4. 基于OpenCV实现瓶口缺陷检测从最小demo到完整流程4.1 环境准备OpenCV安装与版本注意传统图像处理的基本盘是OpenCV的Python绑定。安装本身不难但版本选择有讲究不同版本的API有些差别这往往是新手踩坑的重灾区。# 推荐使用3.4.3以上版本建议4.x稳定版 pip install opencv-python opencv-contrib-python安装完先做一个冒烟测试确认版本和关键接口。我遇到过很多次“代码在别人机器上能跑自己机器上报错”的情况绝大多数是版本号不一致导致的。import cv2 print(cv2.__version__) img cv2.imread(sample.jpg) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) edges cv2.Canny(gray, 50, 150) print(OpenCV ready, edges shape:, edges.shape)关于opencv-contrib-python核心优势是包含xfeatures2d等扩展模块比如SIFT特征。瓶口检测虽然没用到SIFT但后续如果要做瓶身印刷字符比对SIFT可能派上用场。需要注意contrib版本和主版本绑定发布不要混装两个版本导致模块互相找不到。安装完成后验证cv2.findContours的返回值形式。在OpenCV 3.x和4.x里这个函数返回两个值而在旧版2.x返回三个。如果代码是网上抄的旧教程用contours, hierarchy cv2.findContours(...)没问题但如果你看到“ValueError: not enough values to unpack”报错基本可以断定是findContours返回值数量不匹配。这个报错在OpenCV 4里极常见代码里加个断言会更快暴露问题。4.2 第一步ROI提取——用Hough圆检测锁定瓶口区域瓶口区域定位是整个检测流程的基石定位偏了后面的特征全部都是错的。这里采用霍夫梯度圆检测它的原理是利用梯度方向投票找圆心再在候选圆心附近确认半径。在瓶口端面表现为规则圆环的情况下这个算法又快又稳。import cv2 import numpy as np def locate_neck_roi(gray, output_debugFalse): # 先做轻微模糊抑制玻璃纹理噪声 blurred cv2.GaussianBlur(gray, (5, 5), 1.2) # 累积概率霍夫梯度圆检测 circles cv2.HoughCircles( blurred, cv2.HOUGH_GRADIENT, dp1.2, minDistgray.shape[0] / 2, param1180, param280, minRadiusint(gray.shape[0] * 0.18), maxRadiusint(gray.shape[0] * 0.35) ) if circles is None: return None, None circles np.uint16(np.around(circles)) # 取票数最高的一个圆作为瓶口 x, y, r circles[0][0] return (x, y), r这一段的参数含义需要重点说明。dp1.2表示累加器分辨率比原图略低数值越大检测越快但精度略有损失。minDist设为图像高度的一半是因为图像里通常只有一个瓶口避免检测出多个相邻圆。param1是Canny边缘检测的高阈值直接影响边缘提取的严格程度对低对比度的瓶口图像这个值通常需要降到150到180之间。param2是圆心累加器阈值数值越小越容易检出圆但也越容易把非瓶口的圆弧误检成圆。minRadius和maxRadius根据瓶口在画面中的实际占比来设我用图像高度的0.18到0.35作为初始范围具体数值要根据你的相机视野和瓶口直径重新标定。这里踩过一个常见坑HoughCircles对圆形边缘的对比度非常敏感如果瓶口端面反光严重圆会被高光截断导致检测不到。我处理的方法是先做闭运算把断裂边缘连起来再进HoughCircles边缘连续性好了检出率就稳了。ROI定位的调试技巧是可视化。把检测到的圆画在原图上看边缘是否贴合瓶口端面的内外边缘偏差超过5个像素就要调整参数。霍夫圆检测的圆心和半径精度直接影响后面环形掩膜的准确性这里花时间校准是值得的。4.3 第二步缺陷特征提取——灰度投影、边缘统计与模板差分得到瓶口圆心和半径之后缺陷检测的核心就变成了特征提取。三种特征在瓶口场景里最实用环形区域灰度统计、边缘密度统计、模板差分。环形区域灰度统计最简单粗暴核心思路是正常瓶口的环形端面灰度分布均匀崩口和裂纹区域会显著偏离均值。def ring_mean_std(gray, center, radius_in, radius_out): mask np.zeros_like(gray, dtypenp.uint8) cv2.circle(mask, center, radius_out, 255, -1) cv2.circle(mask, center, radius_in, 0, -1) pixels gray[mask 255] return float(pixels.mean()), float(pixels.std())公式上环形区域就是外圆减去内圆。如果缺陷足够大面积超过几十像素均值会明显偏离正常样本标准差也会变大。但小裂纹和细小崩口对均值的影响很小所以这个特征只适合做粗筛不能单独做判据。第二个特征是边缘密度统计。缺陷区域和正常区域的边界都会产生边缘响应但缺陷产生的边缘方向更杂乱、密度更高。Canny边缘在环形区域内的像素占比就是一个有效的缺陷指示器。def ring_edge_density(gray, center, radius_in, radius_out): edges cv2.Canny(gray, 60, 150) mask np.zeros_like(gray, dtypenp.uint8) cv2.circle(mask, center, radius_out, 255, -1) cv2.circle(mask, center, radius_in, 0, -1) edge_pixels cv2.countNonZero(cv2.bitwise_and(edges, mask)) area np.count_nonzero(mask) return edge_pixels / float(area)正常瓶口的环形区域内虽然也有纹理和加工痕迹但Canny边缘密度通常很低。出现崩口或裂纹时局部边缘密度暴增。这里的阈值需要根据实际样本统计我一般取正常样本密度均值的3到5倍作为NG判定线。Canny的低阈值在这个场景里不能设太低否则玻璃表面的细微划痕会全部变成边缘噪声把密度拉高造成误检。第三种是模板差分。如果有标准品图像或者可以从同一批次的图像中统计出平均模板模板差分对崩口和缺口非常敏感。def template_diff(gray, template, center, radius_in, radius_out): aligned cv2.resize(gray, (template.shape[1], template.shape[0])) diff cv2.absdiff(aligned, template) mask np.zeros_like(diff, dtypenp.uint8) cv2.circle(mask, center, radius_out, 255, -1) cv2.circle(mask, center, radius_in, 0, -1) diff_masked cv2.bitwise_and(diff, mask) return float(diff_masked.mean()), float((diff_masked 30).sum())模板差分的核心局限是要求图像严格对齐瓶子的微小旋转都会导致边缘错位形成假差分。所以模板差分通常只做辅助特征不单独作为判据。刚拿到源码包跑通时第一次出图模板差分会把正常瓶口边缘也标出来那其实是没做ROI对齐导致的不是算法错了。这三种特征各有边界实践中最好组合使用。我的默认组合是“灰度标准差 边缘密度 模板差分均值”三个特征各设一个阈值任意两个超阈值就判NG。这种投票式判据比单特征阈值更稳且比AND逻辑更不容易漏检。4.4 第三步多判据融合分类与可视化把特征提取的结果综合成最终结论需要设计一个可解释的判据。我常用的方案是加权投票和阈值比较的组合。class NeckDefectClassifier: def __init__(self, thresholds): self.thresholds thresholds # dict with keys: std, density, diff def predict(self, features): flags [] if features[std] self.thresholds[std]: flags.append(灰度异常) if features[density] self.thresholds[density]: flags.append(边缘密集) if features[diff] self.thresholds[diff]: flags.append(模板偏移) is_ng len(flags) 2 return is_ng, flags两个特征同时超阈值才判NG这个设计的价值是在调节漏检和误检的平衡点。如果产线更怕漏检阈值可以调得更敏感同时保留两票判据如果更怕误检比如后道工序人工复检成本高可以改成三票全中才判NG。这种灵活性是深度学习方案不容易给到的。可视化输出对调试必不可少。我习惯在每次检测后保存一张标记图缺陷区域用红框标出正常图像用绿色框标记。def draw_result(img, center, radius, is_ng, flags): color (0, 0, 255) if is_ng else (0, 255, 0) cv2.circle(img, center, radius, color, 3) if flags: cv2.putText(img, ; .join(flags), (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 2) return img可视化有两个价值一是给产线操作员直观确认二是给自己调参时留底。每次调完参数跑一批样本我都要翻一遍可视化输出看NG图是不是真的缺陷OK图是不是有被误杀。只看准确率数字不看图很容易被偶然的正确掩盖真实问题。5. 参数选型与常见问题避坑从翻车现场总结的经验5.1 HoughCircles参数为什么是玄学现象HoughCircles在A组图上检测稳定换到B组图就偶尔丢失瓶口圆表现为返回None或圆心偏移几个像素。原因HoughCircles的六个参数高度耦合param1和param2直接影响检测数量。param2调低了噪声圆弧也会被当成候选圆导致圆心偏移param1调高了弱边缘的瓶口轮廓直接被滤掉。玻璃反光导致部分瓶口边缘断裂也是检测不稳的重要原因。解决不要单独调一个参数用参数扫描的方式按图像的半径占比、对比度分组观察检测成功率。我在实践中摸索出一个稳定组合dp1.2param1180param280再把闭运算和GaussianBlur串在HoughCircles之前这样检测稳定性显著提高。如果仍然丢失把minRadius和maxRadius范围放宽百分之十再试。还有一个关键习惯批量跑图时打印检测失败样本的编号和图径而不是只打印失败率数字这样才能定位是哪一类图像导致失败。5.2 ContourArea未定义标识符与旧版API混用现象运行代码时报错“contourarea ()未定义标识符”或者“ValueError: not enough values to unpack”。原因这是典型的API版本混用问题。早期OpenCV教程里findContours返回3个值新版返回2个值旧代码里的contourArea全小写写法在新版里其实也变了。网上大量代码是旧教程搬运来的照抄必然踩坑。解决统一使用OpenCV 4.x的APIfindContours用contours, _ cv2.findContours(...)接收返回值面积统计用cv2.contourArea(contour)。如果代码里混了contrib模块确认opencv-python和opencv-contrib-python版本号一致或者直接在纯opencv-python底下跑不要依赖xfeatures2d。每次环境升级后先跑一遍冒烟测试把版本相关的坑挡在批量运行之前。5.3 反光与高光导致二值化失败现象瓶口端面出现大面积白色高光二值化后高光区域变成了一个完整的白色圆斑崩口缺陷被完全掩盖检测直接漏检。原因玻璃表面镜面反射使局部亮度溢出像素值达到255而丢失梯度信息。传统阈值只能捕捉灰度差异而高光区域和缺陷区域的灰度都大于阈值算法无法区分。解决物理上改善打光角度使用低角度环形光源或同轴光减少镜面反射。图像处理上可以把高光区域检测出来并在特征计算时跳过这些像素点或者用多曝光融合——分别采集正常曝光和短曝光的图像短曝光图里高光区域退场缺陷信息反而保留下来。最简单的备用方案是给ROI区域生成掩膜时把高光区域挖掉不参与统计这样至少不会因为高光导致标准差虚高而误判。5.4 边缘密度误检玻璃纹理与划痕的干扰现象好瓶被误判为NG边缘密度指标超阈值但目测瓶口没有缺陷。原因瓶口加工痕迹、瓶身纹路和饶印被Canny算子检测成边缘。正常瓶的环形区域内如果带有强化纹边缘密度天然偏高阈值定得太低就会把纹理当缺陷。解决特征设计上把裂纹和纹理做区分度分析裂纹的边缘细线是连续的、方向杂乱的纹理的边缘方向通常比较一致。改进方法有几种——先用角度直方图统计边缘方向纹理边缘集中在一个主方向裂纹边缘方向分散或者用闭运算把细小纹理边缘合掉再做Canny降低纹理的边缘响应。实践里我常用闭运算加中值滤波的组合对纹理性误检的抑制最明显。6. 进阶让固定阈值变成自适应判据并完成批量验证6.1 动态阈值与自适应判据设计固定阈值最大的软肋是光源衰减、瓶子批次变化会导致灰度整体偏移原来跑得好好的阈值突然开始误检。自适应的思路是从当前图像自身的统计量里推出判据而不是拍一个死数。一种简单有效的自适应策略是把环形区域灰度均值作为基准缺陷判据用“均值偏移倍数”来定义。比如正常样本的灰度均值为m标准差为s如果当前图像的灰度均值和基准差超过3倍s就可能不对劲。这样即使光源整体变暗了也只是m和s一起浮动判据相对稳定。def adaptive_threshold_from_baseline(baseline_mean, baseline_std, current_value, k3.0): upper baseline_mean k * baseline_std lower baseline_mean - k * baseline_std return current_value lower or current_value upper更进一步的自动化方法是做动态基准更新。每100张检测图统计一次灰度均值和标准差实时更新baseline让判据跟着产线实时状态走。但这里有个风险如果产线上废品率突然暴增基准会被污染把缺陷也平均进正常值。所以基准更新一定要设上限NG率超过1%时停止更新等人工干预。6.2 批量验证脚本与误检率统计把阈值改完必须跑一批数据进行回归不能只看一两张图就下结论。我写了一个批量验证脚本对每个类别的数据分别统计表现。import pandas as pd def batch_validate(detector, image_list, labels): results [] for img_path, true_label in zip(image_list, labels): img cv2.imread(img_path) is_ng, _ detector.detect(img) pred_label 0 if is_ng else 1 # 0NG, 1OK results.append({ path: img_path, true: true_label, pred: pred_label, }) df pd.DataFrame(results) ok_true df[df[true] 1] ng_true df[df[true] 0] fp (ok_true[pred] 0).mean() # 误检率 fn (ng_true[pred] 1).mean() # 漏检率 return fp, fn这个脚本的输出是误检率和漏检率两个数字。调参的目标是让这两个数字都低于产线要求比如漏检率0.1%以下、误检率1%以下。如果两个指标打架就需要调整判据的票数和阈值来平衡。我通常把漏检率优先压住因为漏检流到后端就是质量事故误检还有人工复检兜底。验证脚本最好固定下来每一次改完代码重新跑一遍。这个习惯帮我拦住了很多“代码改完没验证就上产线”的翻车风险。视觉检测项目里参数漂移和代码回归是日常最大的敌人没有之一。做瓶口检测这个方向越久越觉得传统图像处理不是过时而是把产线的问题拆成一个个可控的子问题每个子问题都有明确解。拿到一份源码和数据集先动手跑通再逐步调出自己的参数体系这个过程本身就是最大的收获。希望这篇笔记能帮你在瓶口缺陷检测这条路上少踩几个坑。本文还有配套的精品资源点击获取
返回列表