Yolov8识别商品生产日期、条形码等目标检测分割
·
背景
零售行业多数规模小,人员少商品过期不易于检查,随着社会对于食品安全问题关注度越高,各个商店也要做好产品的临期处理,及时向厂家更换,作者也遇到了这个问题,于是想通过目标检测分割模型完成该任务
程序演示
识别生产日期和条形码,并进行掩膜分割

启用OCR可以提取日期信息
原理
通过目标检测+实例分割,可以获取到精确的掩膜(图中蓝色部分),再通过ocr识别date,条形码识别barcode数据
训练方式
下载附件(项目内已包含yolov8n-seg以及数据集)
安装
pip install ultralytics
训练
python fgrun.py
评估
本次训练识别率大概为:70%-80%,如果需要更加精确,需要再添加更多的数据集进行训练


YOLOv8 目标检测 + PaddleOCR
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import cv2
import numpy as np
from PIL import Image, ImageTk
from ultralytics import YOLO
import threading
import time
import re
from paddleocr import PaddleOCR
class YOLOv8App:
def __init__(self, root):
self.root = root
self.root.title("YOLOv8 目标检测 + PaddleOCR")
# 加载模型
self.model = YOLO("best.pt") # 替换为你的模型路径
self.ocr_engine = PaddleOCR(use_angle_cls=True, lang="ch") # 初始化PaddleOCR(中文+英文)
# 初始化变量
self.cap = None
self.is_running = False
self.current_mode = None # 'image', 'video', 'camera'
self.fps = 0
self.enable_ocr = tk.BooleanVar(value=False) # OCR开关
self.ocr_results = [] # 存储OCR结果
self.date_patterns = [
r'\d{4}[年.-]\d{1,2}[月.-]\d{1,2}[日]?', # 2023年12月31日 或 2023-12-31
r'\d{1,2}[月.-]\d{1,2}[日]?', # 12月31日 或 12-31
r'\d{4}[年]', # 2023年
r'\d{8}', # 20231231
r'EXP\d{6}', # EXP231231
r'生产日期[::]?\s*\d{4}[年.-]\d{1,2}[月.-]\d{1,2}[日]?' # 生产日期:2023-12-31
]
# 创建界面
self.create_widgets()
def create_widgets(self):
# 控制面板
control_frame = tk.Frame(self.root, padx=10, pady=10)
control_frame.pack(side=tk.TOP, fill=tk.X)
# 按钮区域
btn_frame = tk.Frame(control_frame)
btn_frame.pack(side=tk.LEFT)
tk.Button(btn_frame, text="选择图片", command=self.load_image).pack(side=tk.LEFT, padx=5)
tk.Button(btn_frame, text="选择视频", command=self.load_video).pack(side=tk.LEFT, padx=5)
tk.Button(btn_frame, text="摄像头", command=self.start_camera).pack(side=tk.LEFT, padx=5)
tk.Button(btn_frame, text="停止", command=self.stop).pack(side=tk.LEFT, padx=5)
# 置信度阈值滑块
self.conf_var = tk.DoubleVar(value=0.25)
tk.Label(control_frame, text="置信度阈值:").pack(side=tk.LEFT, padx=5)
tk.Scale(control_frame, variable=self.conf_var, from_=0.1, to=0.9, resolution=0.05,
orient=tk.HORIZONTAL, length=150).pack(side=tk.LEFT, padx=5)
# OCR开关
tk.Checkbutton(control_frame, text="启用OCR", variable=self.enable_ocr).pack(side=tk.LEFT, padx=5)
# 信息显示
self.info_label = tk.Label(control_frame, text="就绪 | FPS: 0 | 模式: 无")
self.info_label.pack(side=tk.RIGHT)
# 图像显示区域
self.image_frame = tk.Frame(self.root, bg='gray')
self.image_frame.pack(fill=tk.BOTH, expand=True)
self.canvas = tk.Canvas(self.image_frame, bg='black')
self.canvas.pack(fill=tk.BOTH, expand=True)
# 底部统计信息
stats_frame = tk.Frame(self.root, padx=10, pady=5)
stats_frame.pack(side=tk.BOTTOM, fill=tk.X)
# 创建标签用于显示检测结果和OCR结果
self.notebook = ttk.Notebook(stats_frame)
self.notebook.pack(fill=tk.BOTH, expand=True)
# 检测结果标签页
detection_tab = tk.Frame(self.notebook)
self.notebook.add(detection_tab, text="检测结果")
self.detection_label = tk.Label(detection_tab, text="检测结果: 无 | 置信度: 无 | 类别: 无")
self.detection_label.pack(side=tk.LEFT)
# OCR结果标签页
ocr_tab = tk.Frame(self.notebook)
self.notebook.add(ocr_tab, text="OCR结果")
self.ocr_label = tk.Label(ocr_tab, text="OCR结果: 无")
self.ocr_label.pack(side=tk.LEFT)
def is_date_like(self, text):
"""检查文本是否符合日期格式"""
text = text.replace(' ', '') # 去除空格
for pattern in self.date_patterns:
if re.search(pattern, text):
return True
return False
def preprocess_for_ocr(self, img):
"""预处理图像以提高数字和日期的识别率"""
# 转换为灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 二值化
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
# 降噪
kernel = np.ones((2, 2), np.uint8)
processed = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
return processed
def extract_numbers_and_dates(self, text):
"""从OCR结果中提取数字和日期信息"""
# 提取纯数字
numbers = re.findall(r'\d+', text)
# 提取日期格式
dates = []
for pattern in self.date_patterns:
dates.extend(re.findall(pattern, text))
return numbers, dates
def load_image(self):
self.stop()
file_path = filedialog.askopenfilename(filetypes=[("Image files", "*.jpg *.jpeg *.png")])
if file_path:
self.current_mode = 'image'
img = cv2.imread(file_path)
self.process_frame(img)
def load_video(self):
self.stop()
file_path = filedialog.askopenfilename(filetypes=[("Video files", "*.mp4 *.avi *.mov")])
if file_path:
self.current_mode = 'video'
self.cap = cv2.VideoCapture(file_path)
self.start_video_thread()
def start_camera(self):
self.stop()
self.current_mode = 'camera'
self.cap = cv2.VideoCapture(0) # 0 表示默认摄像头
if not self.cap.isOpened():
messagebox.showerror("错误", "无法打开摄像头")
return
self.start_video_thread()
def start_video_thread(self):
self.is_running = True
threading.Thread(target=self.process_video, daemon=True).start()
def process_video(self):
prev_time = 0
while self.is_running and self.cap.isOpened():
ret, frame = self.cap.read()
if not ret:
break
# 计算FPS
curr_time = time.time()
self.fps = 1 / (curr_time - prev_time)
prev_time = curr_time
# 处理帧
self.process_frame(frame)
# 控制帧率
time.sleep(0.01)
if self.cap:
self.cap.release()
def process_frame(self, frame):
# 执行预测
conf = self.conf_var.get()
results = self.model(frame, conf=conf)
# 获取原始图像用于OCR
original_frame = frame.copy()
# 绘制结果
annotated_frame = results[0].plot() # 自动绘制检测框和分割掩模
# 更新检测统计信息
boxes = results[0].boxes
if len(boxes) > 0:
confs = boxes.conf.tolist()
classes = boxes.cls.tolist()
stats_text = f"检测结果: {len(boxes)} | 平均置信度: {np.mean(confs):.2f} | 类别: {[self.model.names[int(c)] for c in classes]}"
self.detection_label.config(text=stats_text)
else:
self.detection_label.config(text="检测结果: 0")
# 如果启用了OCR并且检测到了目标
if self.enable_ocr.get() and len(boxes) > 0:
self.ocr_results = [] # 清空之前的OCR结果
# 获取掩膜
if results[0].masks is not None:
masks = results[0].masks.data.cpu().numpy()
for i, mask in enumerate(masks):
# 将掩膜转换为二值图像
mask = (mask * 255).astype(np.uint8)
mask = cv2.resize(mask, (original_frame.shape[1], original_frame.shape[0]))
# 应用掩膜到原始图像
masked_img = cv2.bitwise_and(original_frame, original_frame, mask=mask)
# 预处理图像以提高识别率
processed_img = self.preprocess_for_ocr(masked_img)
# 使用PaddleOCR进行识别
try:
ocr_result = self.ocr_engine.ocr(processed_img, cls=True)
if ocr_result and ocr_result[0]:
# 提取所有识别文本
all_text = " ".join([line[1][0] for line in ocr_result[0]])
# 提取数字和日期
numbers, dates = self.extract_numbers_and_dates(all_text)
if numbers or dates:
# 保存OCR结果
self.ocr_results.append({
'class': self.model.names[int(classes[i])],
'confidence': confs[i],
'numbers': numbers,
'dates': dates,
'raw_text': all_text
})
# 在图像上绘制OCR结果
if dates: # 优先显示日期
text_to_show = " | ".join(dates[:3]) # 最多显示3个日期
else:
text_to_show = " | ".join(numbers[:5]) # 最多显示5个数字
# 获取检测框坐标
box = boxes[i].xyxy[0].cpu().numpy()
x1, y1, x2, y2 = map(int, box)
# 绘制文本背景(提高可读性)
cv2.rectangle(annotated_frame, (x1, y1 - 25), (x2, y1), (0, 0, 0), -1)
# 绘制文本
cv2.putText(annotated_frame, text_to_show, (x1 + 5, y1 - 5),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 255), 2)
except Exception as e:
print(f"OCR错误: {e}")
# 更新OCR结果标签
if self.ocr_results:
ocr_text = "OCR结果:\n"
for res in self.ocr_results:
ocr_text += f"类别: {res['class']} (置信度: {res['confidence']:.2f})\n"
if res['dates']:
ocr_text += f" 日期: {', '.join(res['dates'])}\n"
if res['numbers']:
ocr_text += f" 数字: {', '.join(res['numbers'])}\n"
ocr_text += f" 原始文本: {res['raw_text']}\n"
self.ocr_label.config(text=ocr_text)
else:
self.ocr_label.config(text="OCR结果: 无")
# 显示图像
self.display_image(annotated_frame)
# 更新信息
self.info_label.config(text=f"运行中 | FPS: {self.fps:.1f} | 模式: {self.current_mode}")
def display_image(self, frame):
# 转换颜色空间
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
img = Image.fromarray(frame)
# 调整图像大小以适应窗口
canvas_width = self.canvas.winfo_width()
canvas_height = self.canvas.winfo_height()
if canvas_width > 0 and canvas_height > 0:
img.thumbnail((canvas_width, canvas_height), Image.LANCZOS)
# 显示图像
imgtk = ImageTk.PhotoImage(image=img)
self.canvas.imgtk = imgtk
self.canvas.create_image(canvas_width // 2, canvas_height // 2, anchor=tk.CENTER, image=imgtk)
def stop(self):
self.is_running = False
if self.cap:
self.cap.release()
self.cap = None
self.current_mode = None
self.info_label.config(text="就绪 | FPS: 0 | 模式: 无")
self.detection_label.config(text="检测结果: 无 | 置信度: 无 | 类别: 无")
self.ocr_label.config(text="OCR结果: 无")
self.canvas.delete("all")
def on_closing(self):
self.stop()
self.root.destroy()
if __name__ == "__main__":
root = tk.Tk()
app = YOLOv8App(root)
root.protocol("WM_DELETE_WINDOW", app.on_closing)
root.mainloop()
附件包含
- 商品数据集
- YOLOv8 目标检测 + PaddleOCR应用程序(未打包,使用时需要自行运行)
- 完整训练代码(包含yolov8n-seg.pt、数据集、训练方式、模块安装教程)
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)