ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

基于YOLOv8与OpenCV的柚子缺陷检测项目实战

基于YOLOv8与OpenCV的柚子缺陷检测项目实战 简介这是一套基于Python的柚子缺陷检测项目包含完整源码与项目文档主要面向毕业设计、课程设计与实战开发场景。项目核心思路是利用水果坏损区域呈现黑色、与正常表皮颜色差异明显的特征通过饱和度分析提取柚子表皮黑色斑块并可根据斑块面积占比判断水果是否需剔除当前支持单张图像检测后续接入工业相机SDK即可扩展为在线检测。资源压缩包约663KB共33个文件包括Python源码、字节码文件、23张柚子样本图像、3个XML标注文件及说明文档目录结构清晰便于快速对照学习和二次开发。已有45人学习下载源码经过严格测试适合在此基础上进行算法优化、界面封装或批量检测等延申实践。1. 柚子缺陷检测不只是给柚子画框如果只是把柚子这张图里目标框出来YOLO 很快就能做到但真正的柚子缺陷检测要求的是在果面上找到褐斑、油斑、霉变和机械损伤的位置还要尽量减少把果蒂、反光点误判成缺陷。这类任务放在 Python 里实现技术选型很直接快速验证用 OpenCV 形状加颜色正式落地用 YOLOv8 这类深度学习模型二者配合能覆盖毕业设计、课程设计和后续产品化的大多数诉求。标题里提到的“源码项目文档”说明它不只是一个算法演示而是一个可以直接入库、答辩、交接的完整工程。接下来我会按数据、传统视觉、深度学习、部署和文档编写这条链路把每一个环节的代码和参数讲透。2. 搭数据集柚子缺陷检测项目的第一步是采集和标注2.1 先定缺陷类别和标注尺度很多人在做缺陷检测时习惯把类别写得特别细比如“褐色小斑”“褐色大斑”“条状疤痕”这会让数据集很快就失控。我一般建议先用一组能直接指导处理动作的类别好果、溃疡病斑、油斑、机械损伤、霉变。前两类是分选线最常见需求后两类能让模型学会区分“能轻拿轻放”和“必须剔除”的情况。标签名典型视觉特征标注建议spot_canker褐色/黑褐色边缘不规则多出现在果皮气孔密集区框住病斑不要把整个柚子框住oil_spot浅绿黄色油渍反光明显边缘模糊框到油渍周围颜色跳变的位置damage划痕、压伤几何轮廓比较硬按痕迹主体画框允许一点留白mildew灰白色绒毛或霉层常伴随暗色底斑按霉层覆盖范围画框good果面干净或仅有轻微瑕疵只用于分类实验目标检测可不标标注尺度上外部轮廓比较完整的缺陷按“缺陷本身”来框如果一张果面同时存在三种缺陷就分别标成三个框。只做整果分类的课程设计可以放宽标准但如果你后面要算缺陷占比必须坚持“框缺陷区域”而不是“框果实”。我把这个原则写进项目文档验收时也不容易被挑出标注逻辑不一致的问题。2.2 用 Python 写数据增强脚本柚子在产地拍摄时角度、光照和果皮反光变化很大。一个只有 800 张图的训练集直接训练会在眩光样本上崩掉所以数据增强不是锦上添花而是训练前必须做的步骤。这里给出一个基于 OpenCV 的轻量增强脚本不引入额外依赖适合课程设计阶段快速生成多份训练样本import cv2 import numpy as np import imgaug.augmenters as iaa seq iaa.Sequential([ iaa.Fliplr(0.5), # 水平翻转 iaa.Affine(rotate(-15, 15)), # 小角度旋转 iaa.Multiply((0.7, 1.3)), # 整体亮度变化模拟不同光照 iaa.GaussianBlur(sigma(0.0, 1.0)), # 轻微模糊模拟对焦误差 iaa.AdditiveGaussianNoise(scale(0, 0.02 * 255)), ]) image cv2.imread(yuzu_001.jpg) image_rgb cv2.cvtColor(image, cv2.COLOR_BGR2RGB) augmented seq(imageimage_rgb)Fliplr(0.5)表示一半概率翻转因为柚子没有方向性这样做安全Multiply((0.7, 1.3))控制亮度倍率超过 1.5 会把深色油斑洗没GaussianBlur的 sigma 不要超过 1.5否则缺陷边缘会被模糊掉。增强之后的图和原图要保存在同一份标注目录里标签文件直接复制同名.txt因为翻转和旋转时标注框坐标也需要同步转换。这里用的数据增强方法要记录到项目文档的“训练策略”一节方便答辩时说明模型鲁棒性来源。2.3 标注工具与目录结构标注工具选型上YOLO 生态下用 LabelImg 最顺手。先安装并启动标注工具pip install labelimg labelimg打开软件后选择“PascalVOC”或“YOLO”格式。我建议直接选 YOLO 格式标签文件里每行是class x_center y_center width height坐标值全部归一化到 0~1。第一次统一用这个格式后面训练时不用再做格式转换。一个能直接喂给 YOLO 的数据集目录长这样datasets/yuzu/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ ├── yuzu.yaml └── README.mdyuzu.yaml的内容包括path、train、val、nc和names。目录上不要把训练集和验证集混放因为 YOLO 默认会找同名的images和labels路径一旦混了训练日志里的val_loss就不具参考性。标注完成后用python -c import os; print(len([f for f in os.listdir(datasets/yuzu/labels/train) if f.endswith(.txt)]))快速检查数量避免训练到一半才发现标签目录为空。3. OpenCV 做柚子缺陷检测把“缺陷”翻译成颜色和纹理3.1 把 BGR 转到 HSV缺陷立刻“显色”先做一个最直接的实验把柚子图片从 BGR 转到 HSV 颜色空间用颜色阈值卡住深色斑块。为什么不用 RGB因为 RGB 三个通道对光照变化非常敏感同一块褐斑在曝光正常和过曝时 R 值可能从 140 跳到 185而 HSV 中的 H 分量描述的是色相受光照影响相对小。可以参考下面这段代码import cv2 import numpy as np img cv2.imread(yuzu_sample.jpg) hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 柚子果皮黄绿色范围 fruit_mask cv2.inRange(hsv, (25, 50, 60), (80, 255, 255)) # 深色病斑、霉变、机械损伤的粗略范围 defect_mask cv2.inRange(hsv, (0, 30, 30), (25, 255, 255)) cv2.imwrite(fruit_mask.jpg, fruit_mask) cv2.imwrite(defect_mask_raw.jpg, defect_mask)这里fruit_mask使用的是 H 25~80对应青绿到明黄色defect_mask使用的是 H 0~25会把红褐色和部分黄褐色一起罩进来。单独看defect_mask_raw.jpg你会发现果蒂、阴影和籽附近的区域也会变成白色这说明纯颜色阈值只适合做初筛需要配上形态学操作去掉杂点。3.2 形态学去噪与连通域过滤拿到缺陷掩膜后直接用cv2.findContours提取轮廓并按面积过滤是最常见的手段。给一个带参数说明的版本mask cv2.morphologyEx(defect_mask, cv2.MORPH_OPEN, np.ones((5, 5), np.uint8)) mask cv2.morphologyEx(mask, cv2.MORPH_CLOSE, np.ones((9, 9), np.uint8)) contours, _ cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) min_area 300 for c in contours: area cv2.contourArea(c) if area min_area: continue x, y, w, h cv2.boundingRect(c) cv2.rectangle(img, (x, y), (x w, y h), (0, 0, 255), 2)MORPH_OPEN用 5×5 卷积核去掉孤立噪点MORPH_CLOSE用 9×9 卷积核把破碎的缺陷连通成块。min_area根据图像分辨率调如果图片是 1920×1080300 像素的斑点仍然太小属于可以被忽略的果皮毛孔如果是 640×640min_area可以降到 80。运行后如果发现果蒂被误检我通常会在 HSV 里再把 H 小于 8 的暗红区域剔除因为果蒂表面通常是深棕偏黑和褐斑的颜色重心不完全一样。3.3 Gabor 纹理参数和适用边界颜色分割最怕油斑油斑颜色接近正常果皮只是反光更集中。这种情况往下做常见解法是加一组 Gabor 纹理滤波器。Gabor 滤波器的响应能放大某方向上的纹路变化从而把光滑油斑和粗糙果皮区分开。示例参数如下import cv2 import numpy as np ksize (11, 11) sigma 2.5 theta 0.0 lam np.pi / 4 gamma 0.6 kernel cv2.getGaborKernel(ksize, sigma, theta, lam, gamma, 0) filtered cv2.filter2D(gray, cv2.CV_32F, kernel)theta控制纹理方向缺陷没有统一方向我一般取 0、45、90、135 四个方向分别滤波sigma越大能看到的纹理周期越长取 2.5 适合柚子果皮这种粗纹理gamma越小滤波器对方向的敏感度越低。实际项目中我不会单独用 Gabor 输出判断而是把 filter 响应值作为特征和颜色 mask 叠在一起比如对每个连通域计算 Gabor 响应的均值和方差超过阈值才判成真实缺陷。这个阈值依赖拍摄设备项目文档里要写明设备型号和分辨率否则换一台相机阈值就得全部重调。4. 基于 YOLOv8 的柚子缺陷检测训练脚本与调参记录4.1 为什么选 YOLOv8 做缺陷检测YOLOv8 是目前最省事的目标检测开源方案它把数据加载、模型搭建、训练和验证都封装成了ultralytics包。对柚子缺陷检测这类“缺陷尺寸变化大、正负样本不均衡”的问题YOLOv8 的优势是自带数据增强和 anchor-free 头不用像 YOLOv5 那样反复调 anchor 尺寸训练代码很短项目文档也容易解释。但用 YOLO 前要重新定义一下检测对象你要检测的不是“柚子”而是“柚子表面的缺陷区域”。这意味着很多标注框面积只有整图的 1%~5%模型最容易漏检。缓解办法是把输入分辨率调大比如用imgsz800并开启 YOLOv8 自带的 mosaic 和 mixup 增强。如果你用的 GPU 只有 6GB 显存可以降 batch 到 8但不要直接把图压到 416否则小缺陷框会直接被池化层吞掉。4.2 环境安装与训练命令训练前先完成 Python 环境配置。建议直接用 Python 3.10 版本安装ultralytics时最容易踩坑的是缺少torch或 CUDA 版本不匹配。先执行pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics然后把数据集配置写到datasets/yuzu/yuzu.yamlpath: /home/yourname/datasets/yuzu train: images/train val: images/val nc: 4 names: [spot_canker, oil_spot, damage, mildew]启动训练yolo detect train \ datadatasets/yuzu/yuzu.yaml \ modelyolov8n.pt \ epochs120 \ imgsz800 \ batch16 \ device0 \ patience20modelyolov8n.pt是 nano 版预训练权重毕业设计用它是为了显存门槛低如果机器显存允许第一时间改成yolov8s.pt小缺陷 recall 会明显上升。patience20表示连续 20 轮验证指标不提升就提前停止防止后期过拟合。训练日志会写到runs/detect/train/里面的weights/best.pt是最终启动推理时用的权重文件。4.3 从 results.csv 判断模型是否过拟合训练结束后不要只看last.pt应该先看runs/detect/train/results.csv。用这段代码快速输出最后一轮指标import pandas as pd df pd.read_csv(runs/detect/train/results.csv) last df.iloc[-1] print(last[metrics/mAP50(B)], last[metrics/mAP50-95(B)]) print(last[val/box_loss], last[val/cls_loss])如果val/box_loss一直在降且mAP50-95稳中有升说明训练状态正常如果mAP50(B)超过 0.95 而val/box_loss反弹就要怀疑训练集里同类图片过多模型只在背诵背景。此时我会回看labels_correlogram.jpg检查四个缺陷类别的中心点分布是否集中在图像中上部如果集中在某一块说明拍摄角度单一需要补充不同果面和不同光源位置的样本。4.4 训练脚本和预测脚本的源码结构给课程设计准备的源码建议拆成三个文件不要全部塞在一个 notebook 里。train.py负责读yuzu.yaml并调用 YOLOpredict.py负责加载best.pt对单张图片输出画框结果utils/viz.py负责把缺陷框叠加到原图上并计算缺陷面积占果面的比例。from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict(test_images/yuzu_023.jpg, conf0.3, imgsz800) for r in results: boxes r.boxes for box in boxes: cls int(box.cls[0]) conf float(box.conf[0]) xyxy box.xyxy[0].tolist() print(model.names[cls], conf, xyxy)conf0.3会让模型输出更多候选框适合缺陷检测这类查全率优先的场景如果切换到分选现场想要减少假阳性再把conf提高到 0.55。5. 模型部署和项目文档让柚子缺陷检测源码真正可交付5.1 用 FastAPI 把模型包成检测服务课程设计如果只交一个训练好的 notebook 不够至少要有一个能接收图片并返回 JSON 的接口。FastAPI 是最稳妥的选择。写一个app.pyfrom fastapi import FastAPI, File, UploadFile from ultralytics import YOLO import cv2 import numpy as np app FastAPI() model YOLO(runs/detect/train/weights/best.pt) app.post(/detect) async def detect(file: UploadFile File(...)): data await file.read() img cv2.imdecode(np.frombuffer(data, np.uint8), cv2.IMREAD_COLOR) results model.predict(img, conf0.3, imgsz800) dets [] for r in results: for box in r.boxes: dets.append({ class: model.names[int(box.cls[0])], conf: round(float(box.conf[0]), 4), bbox: [round(v, 2) for v in box.xyxy[0].tolist()] }) return {count: len(dets), detections: dets}启动服务并验证uvicorn app:app --host 0.0.0.0 --port 8000 curl -X POST -F filetest_images/yuzu_023.jpg http://127.0.0.1:8000/detect这个接口返回的bbox是[x1, y1, x2, y2]形式前端画框时直接按这个坐标画。注意UploadFile读入的字节流要先用cv2.imdecode解码不能直接拿去给 YOLO否则模型会拿到一个被压缩为 JPEG 的文件头而不是图像数组。5.2 用 Gradio 做一个可交互检测页面给非技术评审看的时候命令行不够直观。用 Gradio 三行代码就能把模型包成网页import gradio as gr from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) def detect(img): results model.predict(img, conf0.3, imgsz800) return results[0].plot() gr.Interface(fndetect, inputsimage, outputsimage, title柚子缺陷检测).launch()results[0].plot()返回的是已经把框画好的 BGR 图像Gradio 会重新转成 RGB 显示。启动后拿到 7860 端口地址可以直接把 test 目录里的图片一张张拖进去看效果。这个交互页不是正式业务系统但作为课程设计里的功能展示和小批量人工复核工具足够了。5.3 项目文档写什么才能对应源码结构文档是课程设计和毕业设计的评分重灾区。我的建议是只写“能直接从源码验证”的内容文档章节要写的内容对应源码位置需求分析缺陷类别、检测指标、运行环境README.md数据集说明图像数量、标注比例、增强策略datasets/yuzu/README.md系统设计整体流程图、模块划分docs/design.md训练记录训练命令、超参数、最终 mAPdocs/train_log.md接口文档FastAPI 返回字段、curl 示例docs/api.md测试报告不同光照和果面测试结果docs/test_report.md最后给项目根目录准备一份requirements.txt里面最少包含ultralytics、opencv-python、fastapi、uvicorn、gradio、pandas。如果评审换了一台机器只要按pip install -r requirements.txt装好依赖再按文档里的训练命令跑一遍就能得到和答辩时一致的指标当你在浏览器打开http://127.0.0.1:7860就能直接拖一张柚子照片上去看缺陷框。本文还有配套的精品资源点击获取
返回列表