☰
OpenCV材料表面缺陷检测程序源码解析与实战调参指南
2026/10/8 7:30:57 网站建设 项目流程

简介:基于Python与OpenCV的材料缺陷检测课程设计项目,面向高校图像处理、机器视觉相关课程,解决材料表面缺陷(如裂纹、划痕、污渍)的自动识别问题。项目按“基础处理—功能增强—最新改进”分设三个递进版本,完整演示图像读取、灰度化、阈值分割、轮廓定位、结果标记与数据导出的全流程,每一步均配有结果图和可运行的main.py脚本。压缩包共55个文件,包含jpg样例图、xml工程配置、py核心代码、csv数据记录,以及drawio/png流程图、readme说明文档等,整体约965KB,目录层次清晰,从主程序到实验记录一目了然。该项目无需任何修改即可直接运行,适合作为期末大作业或95分以上课设提交;内置多组实验中间结果与最终对比图,便于对照理解算法原理,快速迁移到其他视觉检测场景。目前已有434人学习下载,是快速完成OpenCV图像处理任务的可靠参考。

1. 材料缺陷检测程序源码(课程设计),到底在检测什么

拿到这份「基于 Python + OpenCV 的材料缺陷检测程序项目源码」课程设计压缩包,第一件事不是急着解压运行,而是先确认课题里要检测的缺陷到底是哪一类。材料表面缺陷的范畴非常宽——金属的划痕和裂纹、纸张的污点和破洞、塑料的麻点和压伤,灰度特征和几何形态完全不同,直接影响后续用阈值还是边缘、形态学核选多大。课程设计答辩里老师问得最多的,不是代码跑没跑通,而是“你的方法对哪类缺陷有效,为什么会漏检”。这篇笔记按检测链路、跑通步骤、参数调整和踩坑清单来组织,适合手头有一份类似源码、想把它跑到能答辩程度的人。

2. 打开这份源码前,先看懂 OpenCV 检测链路里的五步

材料表面缺陷检测的经典流程可以一句话讲完:预处理增强缺陷与背景的对比度,分割出可疑区域,再用面积、形状等特征筛掉假阳性。OpenCV 在这个流程里承担的,是从读图到绘制结果的完整链路。课程设计里最常见也最好答辩的路线,是传统图像处理而不是深度学习——目标缺陷通常是划痕、裂纹、麻点、孔洞,它们的共同点都是与背景存在灰度差异,用灰度图加阈值就能抓出来。下面按代码执行顺序把这五步拆开。

2.1 灰度化、滤波、阈值、形态学、轮廓:最小检测链路

我看过不少课程设计的源码,骨架几乎都是这五步:先读图转灰度,去掉颜色信息干扰;再滤波去噪,但不能把裂纹边缘抹平;然后用阈值分割,把缺陷像素从背景里分离出来;接着做形态学操作,把断裂的缺陷桥接起来、同时去掉孤立噪点;最后一层找轮廓,按面积和形状筛掉假阳性。

这段代码是我常用的骨架,很多课程设计源码都能对号入座:

import cv2 import numpy as np def detect_defects(image_path, area_min=100): img = cv2.imread(image_path) if img is None: raise FileNotFoundError(f"无法读取图像: {image_path}") gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) blur = cv2.GaussianBlur(gray, (5, 5), 0) # 大津法自动求阈值,避免手动猜固定阈值 _, thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) kernel = np.ones((5, 5), np.uint8) closing = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel) opening = cv2.morphologyEx(closing, cv2.MORPH_OPEN, kernel) contours, _ = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) results = [] for cnt in contours: area = cv2.contourArea(cnt) if area < area_min: # 小于阈值的区域直接视为噪声 continue x, y, w, h = cv2.boundingRect(cnt) results.append((x, y, w, h)) return results if __name__ == "__main__": dets = detect_defects("sample.jpg") print("缺陷数量:", len(dets)) for d in dets: print("外接矩形:", d)

逻辑说明:cv2.threshold第二个参数传 0 是配合THRESH_OTSU的固定写法,实际阈值由灰度直方图按类间方差最大化自动计算。MORPH_CLOSE先膨胀后腐蚀,把同一条裂纹中断开的部分接起来;MORPH_OPEN先腐蚀后膨胀,把零散的小白点滤掉。两者共用(5, 5)的结构元,在 800×600 的测试图上表现稳定,分辨率一旦提高就得换更大的结构元。

参数说明:area_min是最直接的过滤条件,但细长裂纹的面积往往不大,单靠面积容易误杀。所以我在筛选条件里只保留面积下限,真正的形态筛选放到后面的避坑章节讲。这段代码的任务是先把流程跑通,等你看到缺陷框明显偏移或者数量不对时,再回头调阈值和形态学参数。

2.2 预处理为什么先灰度再滤波:三种滤波方式怎么选

彩色图上也能做分割,但材料缺陷检测里绝大多数目标是灰度异常而不是颜色异常,转灰度后通道从 3 变成 1,计算量直接降到三分之一,而且答辩时更好讲:缺陷检测的本质是灰度纹理的异常,不是颜色的异常。OpenCV 里cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)是标准写法,注意通道顺序是 BGR 不是 RGB,读图后先确认这一点,别等到画框时发现颜色对不上。

滤波选型方面,三种最常用:

滤波方式适用噪声边缘影响速度
GaussianBlur高斯噪声,光照平稳轻微柔化边缘快
medianBlur椒盐、表面颗粒噪点保留锐利边缘中等
bilateralFilter综合噪声,需要保边强边缘保留好慢

判断逻辑很简单:工件表面均匀,高斯滤波够了;表面有喷砂或拉丝纹理,直接上中值滤波更省心;想同时压掉背景纹理还保留裂纹边缘,才考虑双边滤波。课程设计里双边滤波用得少,主要是慢,实时性不好讲。

滤波核尺寸必须和图像分辨率挂钩。800×600 的图用(5, 5)合适,放到 4000×3000 的工业相机原图上,同样的核基本起不到平滑效果。我一般按图像宽度的 1/800 取奇数核,宽度 4000 取(7, 7)或(9, 9),宽度 800 就保持(5, 5)。这个比例是经验值,噪声更强时再往上加,但别超过目标缺陷宽度的两倍,否则细裂纹会被抹掉。

2.3 阈值分割:固定阈值、大津法、自适应阈值怎么切换

阈值分割的翻车率在整条链路里排第一。固定阈值写法最简单,但也最容易换张图就失效:

# 127 是拍脑袋的阈值,不建议在真实场景里这样写 _, thresh = cv2.threshold(blur, 127, 255, cv2.THRESH_BINARY)

同一个缺陷在亮区灰度是 150,在暗区可能只有 90,一个 127 的固定阈值很难两头兼顾。课程设计源码里如果只有固定阈值,通常说明作者只用自己的测试图调过参数,没考虑光照变化。更稳的大津法写法是:

# 0 在 OTSU 模式下会被忽略,阈值由算法自动计算 _, thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

大津法按类间方差最大化自动算阈值,适合材料表面这种前景背景灰度分布相对清晰的双峰场景。它的问题是缺陷面积占比极小、比如不到 1% 时,直方图双峰不明显,阈值会偏向背景,缺陷容易被吞掉。这种情况要改用自适应阈值:

# blockSize 必须是奇数,C 是从邻域均值减去的常量 adaptive = cv2.adaptiveThreshold( blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 5 )

blockSize=31 表示每个像素的阈值由其周围 31×31 邻域的加权均值减去 5 得到。邻域太小会把正常纹理边缘也判成前景,太大则退化成了全局阈值。31 和 5 必须先跑通再细调:先固定 31 调 5,看到误检减少后再动 31,一次只调一个变量,这样出了问题你能准确知道是哪个参数引起的。

2.4 形态学参数:结构元形状、尺寸和迭代次数

形态学是很多人眼里的玄学区,其实核心只有两个问题:结构元用什么形状,用多大尺寸。

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) closing = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel, iterations=2)

用np.ones((5,5), np.uint8)和MORPH_RECT在效果上等价,都是矩形结构元。裂纹是线状缺陷,有时椭圆结构元更好用,因为矩形结构元边角处的连接能力强,容易把不该连的噪点也连起来。iterations表示操作重复次数,作用上近似于放大结构元,但对边角形状的累积效应不同,所以调参时先改核尺寸,再考虑迭代次数。

经验值是结构元尺寸取目标缺陷宽度的 1.5 到 2 倍。细划痕宽度约 5 像素,核取 7 或 9;缺陷是直径 30 像素的大麻点时,核取 5 会把麻点内部高光误判成噪声区,这时要加大核,或者先用MORPH_OPEN清掉内部小区域。顺序上,闭运算永远在开运算前面做。先闭运算把断裂缺陷接起来,再开运算清孤立噪点;顺序反了,断裂的小段会被当成噪点直接抹掉,缺陷就彻底丢了。

注意:大津法和自适应阈值不是二选一的关系。光照均匀优先大津法,光照有明显渐变优先自适应阈值,千万不要一上来就用自适应,它慢且参数更敏感。

3. 把源码跑起来:Python 环境、项目结构和最小复现命令

拿到 zip 包,第一件事是装好环境再找入口,而不是直接双击main.py,否则课设现场大概率卡在ModuleNotFoundError或者cv2.error上。这一章按环境准备、目录定位、运行命令三步走,每一步都给能直接照做的操作。

3.1 Python 版本和 OpenCV 安装:别被 ModuleNotFoundError 卡住

课程设计阶段建议用 Python 3.8 到 3.10,配 OpenCV 4.5 到 4.8 的官方 wheel。Python 3.11 以上有些扩展包的 wheel 下载慢、兼容性问题多,没必要在这个阶段给自己加难度。安装命令:

# 先确认当前终端里 python 和 pip 属于同一套环境 python --version pip --version # 安装 OpenCV 和 numpy pip install opencv-python pip install numpy

装完立刻验证:

import cv2 import numpy as np print(cv2.__version__)

能打印版本号说明环境通了。最常见的失败是报ModuleNotFoundError: No module named 'cv2'。排查顺序:先看是不是装到了别的环境,执行pip list | findstr opencv(Windows 下)或者pip3 list | grep opencv(Linux 下);再看机器上是否多个 Python 并存,用python -m pip install opencv-python把包装到当前解释器下;还不行就检查是否同时装了opencv-contrib-python和opencv-python,先卸载其中一个再验证。

比ModuleNotFoundError更隐蔽的是 import 成功但运行报错,比如cv2.error: OpenCV(4.4.0) ...这种。这类问题大多数不是安装问题,而是路径或图像数据问题——imread读不到文件返回 None,代码里没做空值检查就继续处理。对应解法我会在后面的避坑清单里展开。

建议锁定版本而不是永远追最新。用pip install opencv-python==4.8.1.78固定版本,换机器、换环境都能复现,不会因为 API 变动突然冒出新错误。

3.2 zip 解压后的项目结构:入口在哪,哪些文件别动

一份典型课程设计源码包解压后,目录通常长这样:

project/ ├── main.py # 主入口,从这里运行 ├── detect.py # 检测核心函数 ├── utils.py # 画框、保存结果的辅助函数 ├── config.py # 参数配置(也可能叫 config.yaml) ├── images/ # 输入的测试图片 ├── results/ # 输出结果目录 └── requirements.txt # 依赖清单

命名可能变成run.py、defect_detection.py,但找入口的方法不变:打开文件找if __name__ == "__main__":所在的文件,那就是主程序。requirements.txt是依赖清单,直接执行:

pip install -r requirements.txt

找入口时还要快速摸清依赖关系。打开主文件,看它 import 了哪些同目录模块,比如from detect import detect_defects,这就是核心算法所在;from utils import save_results是结果保存逻辑。如果主文件 import 了一个不存在的模块,先看是不是缺文件,再看是不是压缩包解压时丢了层级目录。Python 导入机制对目录层级敏感,保持解压后的目录结构原样不动是最省事的做法。

如果源码里同时有requirements.txt和写死的绝对路径,别急着删文件。先看config.py或config.yaml里的input_path、output_path、threshold_method、kernel_size、area_min这几个字段,改配置永远比改代码安全。源码里最容易翻车的就是类似C:/Users/xxx/Desktop/...的绝对路径,换台机器必然报找不到文件,这是全文最该先排查的地方。

3.3 最小运行命令和结果目录:跑通后先别急着调参

入口确认后,最稳的一步是先把命令行写对。课程设计源码里最常见的接口约定是:

python main.py --input images/sample.jpg --output results/

如果程序没有命令行参数而是读配置文件,最常见的配置长这样:

input: images/sample.jpg output: results/ threshold_method: otsu kernel_size: 5 area_min: 100 save_visualization: true

有的源码用argparse,有的用click,判断方式很简单:运行python main.py --help,有输出说明支持命令行参数;没有输出,就去看代码里有没有input()或硬编码路径。配置文件键名可能不一样,但核心就是输入路径、输出路径、阈值方法、核大小、面积阈值这几个字段。

运行后结果一般出现在results/目录下。常见的输出有三种:带红色矩形框标注的原图、只含缺陷区域的二值图、一个记录缺陷数量与坐标的 csv 或 txt 文件。跑通之后要做的第一件事,是把标注结果图和原图叠在一起看,确认缺陷位置框得准不准。框不准不是代码 bug,而是参数没调好的信号,按第二章的思路逐步排查:先换阈值方法,再动形态学核,最后放宽轮廓筛选条件。不要一开始就想着改算法结构,课程设计里九成效果问题都出在参数上。

4. 避坑清单:材料缺陷检测最容易翻车的 5 个细节

这一章写的是我自己调这类程序时踩过且反复出现的五个问题。每条按现象、原因、解决的顺序写,方便你对照排查。

4.1 光照不均:左侧全检出来,右侧一个不剩

现象:同一张图上,缺陷在亮区能被框出来,换到暗区就全部漏检,检测结果有明显的左右不对称。

原因:不管是固定阈值还是大津法,在整张图上只会算出一个阈值。光照在空间上渐变时,局部灰度基准整体偏移,暗区缺陷像素灰度降到阈值以下,自然就丢了。

解决:两种改法。第一种是把阈值换成自适应阈值,第二章已经给了参数;第二种是先把光照背景估计出来再归一化,处理后再走阈值分割:

# 用大核高斯滤波估计光照背景,核要远大于缺陷尺度 bg = cv2.GaussianBlur(gray, (101, 101), 0) # 原图逐像素除掉背景,得到光照近似恒定的图 normalized = cv2.divide(gray, bg, scale=255)

注意bg的核不能太小,101 是经验起点,取小了会把缺陷本身也当作背景抹掉。cv2.divide之后再做阈值分割,效果通常比直接用自适应阈值更稳,而且对老师解释起来也很顺:先校正光照,再检测缺陷,两步职责分明。

4.2 阈值是玄学吗:每次换图都要改阈值,说明选型错了

现象:固定阈值 127 跑通了,换一张光线稍暗的图就全灭,来回调 120、130、110,调完这张坏那张。

原因:不是参数没调好,而是方案不成立。材料表面检测里光源位置、曝光、反光都会改变灰度分布,固定阈值没有任何自适应能力,就是拿一张图的灰度分布去赌所有图。

解决:优先换大津法,一句话就能改:

_, thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

如果大津法在缺陷面积占比很小的图上阈值漂移,再考虑自适应阈值。调参顺序应该是:先固定阈值跑通流程,再换大津法看稳定性,最后才上自适应阈值。别一上来就用自适应,它慢且参数更敏感,出了问题你很难判断是 blockSize 的问题还是 C 值的问题。

4.3 形态学核大小选错:裂纹断成几段,或者噪点连成整片

现象:一张图上裂纹被断成好几段,另一张图上细划痕却被连成一大块,缺陷数量统计忽多忽少。

原因:结构元尺寸和缺陷尺度不匹配。闭运算想让断裂的裂纹桥接,核至少要大于断口宽度,但核一旦接近缺陷整体长度,就会把相邻的独立缺陷连成一个。

解决:先量缺陷尺度。在二值图上用findContours找几个真实缺陷,统计平均宽度和断口间隙,再按“核 = 最大断口宽度 × 1.5”选尺寸。调试时写一个循环对比,这是最直观的后悔药:

# 快速观察不同核大小下的闭运算效果 for k in [3, 5, 7, 9, 11]: kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (k, k)) closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel) cv2.imshow(f"kernel_{k}", closed) cv2.waitKey(0) cv2.destroyAllWindows()

循环里按 1、2、3 的顺序看一遍,哪种核尺寸下裂纹完整且相互独立,就用哪个。

4.4 轮廓筛选只靠面积:麻点和小裂纹的误判

现象:输出框里塞满 5 像素见方的噪点,真实的大裂纹反而漏了。

原因:cv2.contourArea只反映面积。细长裂纹面积小但长度长,很容易被面积下限误杀;圆形噪点面积中等但确实是背景纹理。

解决:在面积基础上加长宽比约束。裂纹长宽比普遍大于 3,圆形麻点接近 1,孤立噪点则比较随机。

x, y, w, h = cv2.boundingRect(cnt) aspect_ratio = w / h if h > 0 else 0 if 30 < area < 8000 and 0.2 < aspect_ratio < 5: results.append((x, y, w, h))

0.2 < aspect_ratio < 5的意思是:既保留水平长条和垂直长条,也排除极端细长的图像边缘和极端扁平的噪声线。具体上下界按你的样本定,课程设计里先用这个区间跑,再对着真实漏检情况收紧。

4.5 cv2.imread 和中文路径:报错不一定算 bug,路径才是

现象:代码在自己电脑跑得好好的,拷到教室电脑上,imread返回 None,或者imwrite不报错但文件没生成。

原因:OpenCV 的imread和imwrite在 Windows 下对中文路径支持比较差,尤其是旧版本。路径里有“测试图片”“结果输出”这类中文名,读取时直接失败。

解决:三种做法按推荐顺序排。第一,路径全部用英文,最省事。第二,用imdecode/imencode绕开路径解析:

# 读取中文路径图片:先按字节流读入再解码 img = cv2.imdecode(np.fromfile(file_path, dtype=np.uint8), cv2.IMREAD_COLOR) # 保存中文路径图片:先编码再写文件 result = cv2.imencode(".jpg", annotated)[1] result.tofile(out_path)

np.fromfile按字节流读入,cv2.imdecode解码成图像,保存同理。这套写法在 Windows 中文路径场景下就是后悔药,不用改任何环境配置。第三,如果程序里大量用了cv2.imread,干脆把所有输入输出目录统一改成英文,一步到位。

注意:imread返回 None 时,OpenCV 通常不会抛异常,而是把错误吞掉,等代码运行到cvtColor(gray, ...)时才爆出cv2.error。所以入口处做if img is None: raise FileNotFoundError(...)是值得养成的习惯。

5. 从课程设计到真实场景:换材料、换分辨率时这些参数必须动

课程设计验收通常是在几张样本图上,真拉到产线,材料、光源、相机分辨率全变了。这一章写清楚不同材料之间的检测差异,以及分辨率变化时哪些参数要跟着缩放。

5.1 不同材料的预处理差异:金属、纸张、塑料

金属表面最大的特点是反光强,容易出现高光斑,高光区域在灰度图里也是一大片白色,极易被误判成缺陷。预处理阶段先做高光抑制,常见做法是用cv2.createCLAHE做限制对比度的自适应直方图均衡化,能明显改善高光和暗部失衡,让后续阈值分割更稳定。

纸张和塑料的特点是纹理密、噪声多,灰度直方图往往不是干净的双峰,大津法阈值不稳定。适合先中值滤波再用自适应阈值。检测目标多为褶皱、污点、破洞:褶皱是灰度渐变,没有强边缘;污点和破洞才是强边缘,筛选时要分两类处理,不能只靠一个面积阈值通吃。

纺织品和拉丝金属有方向性纹理,直接用全局阈值会把纹理本身切成无数小缺陷。先加一步方向性滤波,或者简单一点先用较大的高斯核把纹理抹掉再进阈值。换个材料必须重做三件事:第一,重新选阈值方法;第二,重选结构元形状,拉丝金属用矩形结构元容易被纹理方向带偏,换圆形或椭圆会好很多;第三,改面积和长宽比上下限。

5.2 分辨率变化时,参数缩放公式

同一张图从 800×600 换到 4000×3000,像素面积放大约 25 倍,所有和像素相关的参数必须一起缩放。三个最关键的参数:

  1. 高斯滤波核:按宽度比例缩放,经验公式是kernel_size = max(3, round(w / 800)),最后取奇数。
  2. 形态学结构元:同样要放大到(9, 9)或(15, 15)。这一步必须实测,因为缺陷的物理宽度不变,像素宽度会随分辨率线性变大。
  3. 面积阈值:按图像面积百分比而不是固定值,比如area_min = 0.0001 * h * w,这样换分辨率不用重新拍脑袋。

代码示例:

h, w = gray.shape[:2] scale = w / 800.0 gauss_ksize = int(round(scale) * 5) if gauss_ksize % 2 == 0: gauss_ksize += 1 area_min = int(h * w * 0.0001)

scale按新图像宽度相对参考宽度 800 计算,gauss_ksize保证是奇数,面积阈值用相对比例。这样同一套检测逻辑在不同分辨率图像上的行为基本一致。如果换了相机分辨率之后缺陷框突然偏大偏小,优先检查这三个值是不是还停留在 800×600 时代。

5.3 用脚本统计检测效果:准确率和漏检率一目了然

答辩时老师大概率问“检测准确率多少”。与其拍脑袋说“还行”,不如花十分钟写一个评估脚本。先准备一个简单的标注格式,每行四个数字image_name,x,y,w,h,表示一个真实缺陷框。然后脚本把预测框和标注框算 IoU,大于 0.5 判定为命中:

def iou(a, b): ax1, ay1, aw, ah = a bx1, by1, bw, bh = b ia_x = max(0, min(ax1 + aw, bx1 + bw) - max(ax1, bx1)) ia_y = max(0, min(ay1 + ah, by1 + bh) - max(ay1, by1)) inter = ia_x * ia_y union = aw * ah + bw * bh - inter return inter / union if union > 0 else 0 def evaluate(pred_file, gt_file): # 按文件名读入预测框和真实框,统计命中数 hits = 0 for name in gt: for p in pred[name]: if any(iou(p, g) > 0.5 for g in gt[name]): hits += 1 break recall = hits / len(gt) precision = hits / len(pred) return precision, recall

缺陷数量少时,IoU 判命中可能过于严格,比如细裂纹的框和标注框偏移几个像素,IoU 就掉到 0.5 以下。可以把阈值放宽到 0.3 再试,但要在答辩备注里写明评估标准,这叫可解释性。有了precision和recall两个数字,你就知道自己调参是有效果还是原地打转。

6. 让检测结果可交付:标注图与 CSV 报告生成技巧

检测代码跑通只算完成三分之一,能输出一份让不看代码的人也能看懂的结果,才算真正交付。课程设计里我的习惯是两类产物:一张带标注的原图加一份 CSV 报告,既方便老师快速看效果,也方便自己分析每张图的缺陷坐标。

画标注框用cv2.rectangle加cv2.putText,按缺陷形态区分颜色,细长缺陷用红色,块状缺陷用黄色,答辩时往屏幕上一放,结论直观可见。CSV 用标准库写就行,字段至少包含文件名、缺陷序号、中心坐标、外接宽高、面积和长宽比:

import csv import cv2 def save_results(image_path, detections, out_csv, out_img): annotated = cv2.imread(image_path) with open(out_csv, "w", newline="", encoding="utf-8") as f: writer = csv.writer(f) writer.writerow(["image", "id", "cx", "cy", "width", "height", "area"]) for i, (x, y, w, h) in enumerate(detections): cx, cy = x + w // 2, y + h // 2 writer.writerow([image_path, i, cx, cy, w, h, w * h]) cv2.rectangle(annotated, (x, y), (x + w, y + h), (0, 0, 255), 2) cv2.putText(annotated, str(i), (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) cv2.imwrite(out_img, annotated)

putText的坐标要放在框的左上角偏上一点,否则文字会压在框线上看不清楚;字体大小 0.5 在 800 宽图上刚好,分辨率翻倍后要按比例加大。

我自己的教训是:课程设计的代码写得再漂亮,最后老师看的还是那张结果图。与其反复调一堆参数让输出框变多,不如先把“一张图对应一份报告”的输出链路做扎实,保证每次运行的中间结果都有存档,调参前后可以对比。这条习惯后来用到实际项目里,排查问题的时间省了一大半,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询