1. 了解我们要构建的系统
在开始编码之前,我们先了解一下我们要构建的系统:
- 目标:创建一个能够识别零售商品的计算机视觉系统
- 核心技术:深度学习,特别是YOLOv5物体检测算法
- 功能:
- 上传图片并识别其中的商品
- 实时摄像头识别
- 友好的图形用户界面(GUI)
这个系统将能够帮助零售商自动化库存管理,提高结账效率,甚至可以用于自助结账系统。
本文只是相关的开发思路,如需要源码+数据集+相关ui界面可以联系博主。
2. 环境设置
首先,我们需要设置我们的开发环境。我们将使用Python作为主要编程语言,因为它在机器学习和数据科学领域非常流行,并且有大量的库和框架支持。
2.1 安装Python
如果您还没有安装Python,请访问Python官网下载并安装最新版本的Python(推荐Python 3.8或更高版本)。
2.2 创建虚拟环境
虚拟环境允许我们为每个项目创建独立的Python环境,这有助于管理依赖并避免版本冲突。
打开命令行(在Windows上是命令提示符,在Mac或Linux上是终端),然后运行以下命令:
# 创建一个名为retail_env的虚拟环境
python -m venv retail_env
# 激活虚拟环境
# 在Windows上:
retail_env\Scripts\activate
# 在Mac或Linux上:
source retail_env/bin/activate
当您看到命令行前面出现(retail_env)
时,说明虚拟环境已经被激活。
2.3 安装所需的包
现在我们的虚拟环境已经准备好了,让我们安装我们需要的Python包:
# 安装PyTorch(深度学习框架)
pip install torch torchvision
# 安装OpenCV(用于图像处理)
pip install opencv-python
# 安装PyQt5(用于创建图形界面)
pip install PyQt5
# 安装pandas(用于数据处理)
pip install pandas
# 克隆YOLOv5仓库并安装其依赖
git clone https://github.com/ultralytics/yolov5
cd yolov5
pip install -r requirements.txt
这些命令会安装我们项目所需的所有主要依赖。
3. 数据准备
对于任何机器学习项目,数据都是至关重要的。我们需要一个包含各种零售商品图像的数据集来训练我们的模型。
3.1 收集数据
理想情况下,您应该收集或获取一个包含各种零售商品的大型图像数据集。这可能包括:
- 在商店中拍摄的真实照片
- 网上收集的商品图片
- 公开的零售商品数据集
为了本教程的目的,我们假设您已经有了这样一个数据集。如果没有,您可以考虑使用公开的数据集,如Open Images Dataset或COCO Dataset,并从中筛选出与零售商品相关的图像。
3.2 组织数据集
我们需要按照YOLOv5期望的格式组织我们的数据集。创建以下目录结构:
dataset/
├── images/
│ ├── train/
│ └── val/
└── labels/
├── train/
└── val/
images/train/
:存放用于训练的图像images/val/
:存放用于验证的图像labels/train/
:存放训练图像对应的标签文件labels/val/
:存放验证图像对应的标签文件
3.3 标注数据
为了训练模型,我们需要为每张图像创建一个对应的标签文件,指明图像中物体的位置和类别。这个过程称为数据标注。
-
下载并安装LabelImg,这是一个图形化的图像标注工具。
-
使用LabelImg打开您的图像,并为每个商品绘制边界框,指定其类别。
-
确保将保存格式设置为YOLO格式。这将为每张图像生成一个.txt文件,包含物体的类别和位置信息。
-
将图像文件放在
images/train/
或images/val/
中,将对应的标签文件放在labels/train/
或labels/val/
中。
3.4 创建数据配置文件
创建一个名为data.yaml
的文件,定义数据集的路径和类别信息:
train: dataset/images/train
val: dataset/images/val
nc: 20 # 替换为您的类别数量
names: ['apple', 'banana', 'orange', 'milk', 'bread', ...] # 替换为您的类别名称列表
这个文件告诉YOLOv5在哪里找到训练和验证图像,有多少类别,以及每个类别的名称。
4. 模型训练
现在我们的数据已经准备好了,是时候训练我们的模型了。我们将使用YOLOv5,这是一个强大而高效的物体检测算法。
4.1 了解YOLOv5
YOLO(You Only Look Once)是一种单阶段物体检测算法,以其快速和准确而闻名。YOLOv5是YOLO算法的一个实现版本,它在速度和准确性之间取得了很好的平衡。
YOLOv5的工作原理是将输入图像划分为网格,每个网格负责预测落在其中的物体。它直接预测边界框的坐标和类别概率,使得整个过程非常快速。
4.2 开始训练
使用以下命令开始训练过程:
python train.py --img 640 --batch 16 --epochs 100 --data data.yaml --weights yolov5s.pt
让我们解释一下这个命令的各个部分:
--img 640
:设置输入图像的大小为640x640像素。--batch 16
:每次迭代处理16张图像。如果您的GPU内存较小,可能需要减小这个数值。--epochs 100
:训练100个周期。一个周期是遍历整个训练集一次。--data data.yaml
:指定我们之前创建的数据配置文件。--weights yolov5s.pt
:使用预训练的YOLOv5s模型权重开始训练。这叫做迁移学习,可以加快训练过程并提高性能。
训练可能需要几个小时到几天,取决于您的硬件和数据集大小。训练完成后,您将在runs/train/exp/weights/
目录下找到最佳模型权重文件best.pt
。
5. UI界面设计
现在我们有了一个训练好的模型,是时候创建一个用户界面了。我们将使用PyQt5,这是一个强大的Python GUI框架。
5.1 创建主窗口
首先,我们创建一个基本的窗口结构:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QLabel, QFileDialog
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import Qt
class RetailRecognitionUI(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('零售柜商品识别系统')
self.setGeometry(100, 100, 800, 600)
layout = QVBoxLayout()
self.image_label = QLabel(self)
self.image_label.setAlignment(Qt.AlignCenter)
layout.addWidget(self.image_label)
self.upload_btn = QPushButton('上传图片', self)
self.upload_btn.clicked.connect(self.upload_image)
layout.addWidget(self.upload_btn)
self.recognize_btn = QPushButton('识别商品', self)
self.recognize_btn.clicked.connect(self.recognize_products)
layout.addWidget(self.recognize_btn)
self.result_label = QLabel(self)
self.result_label.setAlignment(Qt.AlignCenter)
layout.addWidget(self.result_label)
self.setLayout(layout)
def upload_image(self):
file_name, _ = QFileDialog.getOpenFileName(self, "选择图片", "", "图片文件 (*.png *.jpg *.bmp)")
if file_name:
pixmap = QPixmap(file_name)
self.image_label.setPixmap(pixmap.scaled(640, 480, Qt.KeepAspectRatio))
self.image_path = file_name
def recognize_products(self):
# 这里将调用我们训练好的模型进行识别
# 暂时用占位符表示
self.result_label.setText("识别结果:苹果,香蕉,牛奶")
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = RetailRecognitionUI()
ex.show()
sys.exit(app.exec_())
这段代码创建了一个基本的窗口,包含一个图像显示区域、一个上传按钮、一个识别按钮和一个结果显示标签。
5.2 解释UI代码
让我们详细解释一下这段代码:
QWidget
:这是PyQt中所有用户界面对象的基类。QVBoxLayout
:这创建了一个垂直布局,使得我们可以垂直排列UI元素。QLabel
:用于显示图像和文本。QPushButton
:创建可点击的按钮。QFileDialog
:提供一个文件选择对话框。
upload_image
方法允许用户选择一个图像文件并在界面上显示它。recognize_products
方法目前只是一个占位符,我们稍后会实现实际的识别功能。
6. 模型集成
现在我们有了UI和训练好的模型,是时候将它们结合起来了。
6.1 加载模型
首先,我们需要创建一个类来加载和使用我们训练好的YOLOv5模型:
import torch
from PIL import Image
class ProductDetector:
def __init__(self, weights_path):
self.model = torch.hub.load('ultralytics/yolov5', 'custom', path=weights_path)
def detect(self, image_path):
img = Image.open(image_path)
results = self.model(img)
return results.pandas().xyxy[0]
这个ProductDetector
类加载我们训练好的模型,并提供一个detect
方法来识别图像中的商品。
6.2 在UI中使用模型
现在,让我们更新我们的UI类来使用这个检测器:
# 在RetailRecognitionUI类的__init__方法中添加:
def __init__(self):
super().__init__()
self.detector = ProductDetector('path/to/your/trained/weights.pt')
self.initUI()
# 更新recognize_products方法:
def recognize_products(self):
if hasattr(self, 'image_path'):
results = self.detector.detect(self.image_path)
detected_products = results['name'].unique()
self.result_label.setText(f"识别结果:{', '.join(detected_products)}")
else:
self.result_label.setText("请先上传图片")
这段代码在UI初始化时加载模型,并在用户点击"识别商品"按钮时使用模型进行识别。
非常好,让我们继续完善我们的零售柜商品识别系统,添加实时识别功能并进行一些优化。
7. 实时识别
7.1 添加视频捕获功能
首先,我们需要在UI中添加视频捕获和显示功能。我们将使用OpenCV来捕获视频流,并使用PyQt5的QTimer来定期更新画面。
在RetailRecognitionUI类中添加以下代码:
import cv2
from PyQt5.QtCore import QTimer
from PyQt5.QtGui import QImage, QPixmap
class RetailRecognitionUI(QWidget):
def __init__(self):
# ... 之前的代码 ...
self.video_label = QLabel(self)
layout.addWidget(self.video_label)
self.start_video_btn = QPushButton('开始实时识别', self)
self.start_video_btn.clicked.connect(self.toggle_video)
layout.addWidget(self.start_video_btn)
self.timer = QTimer(self)
self.timer.timeout.connect(self.update_frame)
self.cap = None
def toggle_video(self):
if self.timer.isActive():
self.timer.stop()
if self.cap:
self.cap.release()
self.start_video_btn.setText('开始实时识别')
else:
self.cap = cv2.VideoCapture(0)
self.timer.start(30) # 每30毫秒更新一次,约33 FPS
self.start_video_btn.setText('停止实时识别')
def update_frame(self):
ret, frame = self.cap.read()
if ret:
# 将OpenCV的BGR格式转换为RGB格式
rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
h, w, ch = rgb_image.shape
bytes_per_line = ch * w
qt_image = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)
self.video_label.setPixmap(QPixmap.fromImage(qt_image))
# 在这里添加实时识别代码
results = self.detector.detect(rgb_image)
self.draw_results(frame, results)
def draw_results(self, frame, results):
for _, row in results.iterrows():
x1, y1, x2, y2 = int(row['xmin']), int(row['ymin']), int(row['xmax']), int(row['ymax'])
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.putText(frame, f"{row['name']} {row['confidence']:.2f}",
(x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0,255,0), 2)
这段代码添加了一个新的标签来显示视频流,一个按钮来开始/停止实时识别,以及相应的方法来捕获和处理视频帧。draw_results
方法在视频帧上绘制识别结果。
7.2 优化ProductDetector类
为了支持实时识别,我们需要稍微修改ProductDetector类:
class ProductDetector:
def __init__(self, weights_path):
self.model = torch.hub.load('ultralytics/yolov5', 'custom', path=weights_path)
def detect(self, image):
if isinstance(image, str):
img = Image.open(image)
elif isinstance(image, np.ndarray):
img = image
else:
raise ValueError("Unsupported image type")
results = self.model(img)
return results.pandas().xyxy[0]
这个修改允许检测器接受文件路径或numpy数组(OpenCV图像格式)作为输入。
8. 性能优化
为了提高系统的响应性,特别是在处理大图像或视频流时,我们可以使用多线程来进行识别。
8.1 添加多线程处理
首先,导入必要的模块:
from PyQt5.QtCore import QThread, pyqtSignal
然后,创建一个新的线程类来处理识别任务:
class DetectionThread(QThread):
detection_complete = pyqtSignal(object)
def __init__(self, detector, image):
super().__init__()
self.detector = detector
self.image = image
def run(self):
results = self.detector.detect(self.image)
self.detection_complete.emit(results)
修改RetailRecognitionUI类中的recognize_products方法:
def recognize_products(self):
if hasattr(self, 'image_path'):
self.detection_thread = DetectionThread(self.detector, self.image_path)
self.detection_thread.detection_complete.connect(self.update_results)
self.detection_thread.start()
else:
self.result_label.setText("请先上传图片")
def update_results(self, results):
detected_products = results['name'].unique()
self.result_label.setText(f"识别结果:{', '.join(detected_products)}")
self.draw_results_on_image(results)
def draw_results_on_image(self, results):
pixmap = QPixmap(self.image_path)
painter = QPainter(pixmap)
painter.setPen(QPen(Qt.red, 3))
for _, row in results.iterrows():
x1, y1, x2, y2 = row['xmin'], row['ymin'], row['xmax'], row['ymax']
painter.drawRect(int(x1), int(y1), int(x2-x1), int(y2-y1))
painter.drawText(int(x1), int(y1)-10, f"{row['name']} {row['confidence']:.2f}")
painter.end()
self.image_label.setPixmap(pixmap.scaled(640, 480, Qt.KeepAspectRatio))
这些修改将识别过程移到一个单独的线程中,防止在处理大图像时UI冻结。同时,我们添加了一个方法来在原图上绘制识别结果。
9. 错误处理和用户反馈
为了提高用户体验,我们应该添加适当的错误处理和用户反馈机制。
9.1 添加加载指示器
在进行耗时操作时,比如加载模型或识别图像,我们应该显示一个加载指示器:
from PyQt5.QtWidgets import QProgressDialog
# 在RetailRecognitionUI类中添加:
def show_loading(self, message):
self.progress = QProgressDialog(message, None, 0, 0, self)
self.progress.setWindowModality(Qt.WindowModal)
self.progress.show()
def hide_loading(self):
if hasattr(self, 'progress'):
self.progress.hide()
# 在相应的方法中使用:
def recognize_products(self):
if hasattr(self, 'image_path'):
self.show_loading("正在识别商品...")
self.detection_thread = DetectionThread(self.detector, self.image_path)
self.detection_thread.detection_complete.connect(self.update_results)
self.detection_thread.start()
else:
self.result_label.setText("请先上传图片")
def update_results(self, results):
self.hide_loading()
# ... 其余代码 ...
9.2 错误处理
添加try-except块来捕获可能的错误:
def recognize_products(self):
if hasattr(self, 'image_path'):
try:
self.show_loading("正在识别商品...")
self.detection_thread = DetectionThread(self.detector, self.image_path)
self.detection_thread.detection_complete.connect(self.update_results)
self.detection_thread.start()
except Exception as e:
self.hide_loading()
QMessageBox.critical(self, "错误", f"识别过程中发生错误:{str(e)}")
else:
self.result_label.setText("请先上传图片")
10. 保存和加载设置
为了提高用户体验,我们可以添加保存和加载设置的功能,比如保存最后使用的模型路径:
import json
class RetailRecognitionUI(QWidget):
def __init__(self):
super().__init__()
self.settings = self.load_settings()
self.detector = ProductDetector(self.settings.get('model_path', 'path/to/default/model.pt'))
self.initUI()
def load_settings(self):
try:
with open('settings.json', 'r') as f:
return json.load(f)
except FileNotFoundError:
return {}
def save_settings(self):
with open('settings.json', 'w') as f:
json.dump(self.settings, f)
def closeEvent(self, event):
self.save_settings()
super().closeEvent(event)
这样,程序将在关闭时自动保存设置,并在下次启动时加载。
结论
通过这个详细的教程,我们构建了一个功能完整的零售柜商品识别系统。该系统包括:
- 使用YOLOv5进行物体检测
- 用PyQt5构建的用户友好界面
- 支持图片上传和实时视频识别
- 多线程处理以提高性能
- 错误处理和用户反馈机制
- 设置的保存和加载
这个系统为零售业的自动化提供了一个很好的起点。您可以根据具体需求进一步扩展和优化这个系统,例如添加商品数量统计、与库存系统集成等功能。
本文只是相关的开发思路,如需要源码+数据集+相关ui界面可以联系博主。