在生鲜零售和食品加工行业,水果品质的快速检测一直是个痛点问题。传统人工分拣不仅效率低下,而且受主观因素影响大。本文将带你从零构建一个基于深度学习的水果品质检测桌面应用,结合YOLOv5的目标检测能力和PyQt的交互界面,实现从图像采集到品质分析的全流程自动化。这个项目特别适合有一定Python基础,想进阶学习AI应用落地的开发者。
开发这类AI视觉应用,合理的硬件配置能大幅提升工作效率。以下是推荐的开发环境:
bash复制# 验证CUDA是否可用
import torch
print(torch.cuda.is_available()) # 应输出True
通过conda创建虚拟环境能有效避免依赖冲突:
bash复制conda create -n fruit_detection python=3.8
conda activate fruit_detection
pip install torch==1.10.0+cu113 torchvision==0.11.1+cu113 -f https://download.pytorch.org/whl/torch_stable.html
pip install pyqt5 opencv-python matplotlib scipy
注意:PyQt5与PyQt6存在API差异,建议统一使用PyQt5以保证代码兼容性
优质的数据集是模型性能的基石。针对水果检测,建议:
典型数据分布示例:
| 水果类型 | 新鲜样本 | 损伤样本 | 腐烂样本 |
|---|---|---|---|
| 苹果 | 1200 | 800 | 500 |
| 香蕉 | 1000 | 600 | 400 |
| 橙子 | 1500 | 700 | 600 |
推荐使用LabelImg进行标注,保存为Pascal VOC格式后转换为YOLO格式:
python复制# VOC转YOLO格式示例代码
def convert(size, box):
dw = 1./size[0]
dh = 1./size[1]
x = (box[0] + box[1])/2.0
y = (box[2] + box[3])/2.0
w = box[1] - box[0]
h = box[3] - box[2]
x = x*dw
w = w*dw
y = y*dh
h = h*dh
return (x,y,w,h)
提示:标注时应确保边界框完全包含特征区域,特别是对于局部腐烂的情况
YOLOv5提供多个预训练模型,根据硬件条件选择:
关键训练参数示例:
yaml复制# data/fruit.yaml
train: ../train/images
val: ../valid/images
nc: 3 # 类别数(新鲜、损伤、腐烂)
names: ['fresh', 'damaged', 'rotten']
使用TensorBoard实时监控训练指标:
bash复制tensorboard --logdir runs/train
重点关注三个损失曲线:
典型训练命令:
bash复制python train.py --img 640 --batch 16 --epochs 100 --data fruit.yaml --weights yolov5s.pt
使用Qt Designer设计主界面应包含:
python复制# 主窗口基础结构
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# 信号槽连接
self.ui.btn_image.clicked.connect(self.load_image)
self.ui.btn_video.clicked.connect(self.load_video)
self.ui.btn_camera.clicked.connect(self.start_camera)
将训练好的YOLOv5模型集成到PyQt中需注意:
python复制def detect_image(self, img_path):
img = cv2.imread(img_path)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 推理
results = self.model(img)
# 结果解析
detections = results.pandas().xyxy[0]
for _, det in detections.iterrows():
label = f"{det['name']} {det['confidence']:.2f}"
cv2.rectangle(img, (int(det['xmin']), int(det['ymin'])),
(int(det['xmax']), int(det['ymax'])), (255,0,0), 2)
cv2.putText(img, label, (int(det['xmin']), int(det['ymin'])-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (36,255,12), 2)
return img
创建spec文件优化打包体积:
python复制# fruit_detector.spec
a = Analysis(['main.py'],
pathex=['/project_path'],
binaries=[],
datas=[('best.pt', '.'), ('ui/*.ui', 'ui')],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
name='FruitDetector',
debug=False,
strip=False,
upx=True,
runtime_tmpdir=None,
console=False)
bash复制# 最终打包命令
pyinstaller --onefile --windowed --add-data "best.pt;." fruit_detector.spec
将FP32模型转换为INT8提升推理速度:
python复制model = torch.quantization.quantize_dynamic(
model, {torch.nn.Linear}, dtype=torch.qint8
)
torch.save(model.state_dict(), 'quantized_model.pt')
使用QThread实现非阻塞式检测:
python复制class DetectionThread(QThread):
finished = pyqtSignal(object)
def __init__(self, model, image):
super().__init__()
self.model = model
self.image = image
def run(self):
results = self.model(self.image)
self.finished.emit(results)
在实际项目中,我发现合理设置检测间隔(如摄像头每3帧处理1次)能平衡性能与实时性。对于批量处理场景,建议实现队列机制避免内存溢出。