欢迎光临
我们一直在努力

[特殊字符] 基于YOLOv5/v8/v10的商超货架商品陈列面占比分析系统【完整源码+数据集】

🚀 项目背景

在零售行业,货架陈列面分析是商品管理和营销决策的重要依据。排面占比指的是某商品在货架上的陈列面积占总陈列面积的比例,直接关系到商品的曝光率和销售潜力。传统的人工统计方式耗时耗力,且难以保证准确性。本文基于YOLO系列目标检测算法,开发一套自动化货架商品检测与排面占比分析系统,支持YOLOv5/v8/v10三种主流模型,并提供友好的UI界面。

项目难点

  • 商品种类繁多,外观相似度高

  • 货架图像中存在遮挡、光照不均问题

  • 排面占比需要精确计算每个商品的检测框面积

  • 实时性要求与检测精度的平衡

  • 📊 数据集构建

    数据采集

    我们采集了某大型超市的5000张货架图像,包含:

    • 饮料区:1500张

    • 零食区:2000张

    • 日用品区:1500张

    标注规范

    使用LabelImg进行标注,标注类别包括:

    python

    CLASSES = [
    'coca_cola', # 可口可乐
    'pepsi', # 百事可乐
    'lays', # 乐事薯片
    'oreo', # 奥利奥
    'noodle', # 方便面
    'water', # 矿泉水
    'juice', # 果汁
    'chocolate', # 巧克力
    'cookie', # 饼干
    'yogurt' # 酸奶
    ]

    数据集划分

    python

    # 数据集统计
    total_images = 5000
    train_count = 4000 # 80%
    val_count = 500 # 10%
    test_count = 500 # 10%

    print(f"训练集: {train_count}张")
    print(f"验证集: {val_count}张")
    print(f"测试集: {test_count}张")

    数据增强

    python

    import albumentations as A

    transform = A.Compose([
    A.RandomBrightnessContrast(p=0.5),
    A.HueSaturationValue(p=0.3),
    A.Rotate(limit=15, p=0.5),
    A.HorizontalFlip(p=0.5),
    A.GaussNoise(var_limit=(10.0, 50.0), p=0.3),
    ])

    🤖 YOLO系列模型详解

    YOLOv5

    YOLOv5由Ultralytics于2020年发布,采用CSPDarknet作为骨干网络,具有以下特点:

    • 自适应锚框计算

    • Mosaic数据增强

    • Focus层代替传统卷积

    • 多种模型规模(n/s/m/l/x)

    YOLOv8

    2023年发布的YOLOv8是当前最流行的版本:

    • 无锚框检测头

    • C2f模块替代C3

    • Decoupled Head

    • 支持分类、检测、分割、姿态估计

    YOLOv10

    2024年最新发布的YOLOv10:

    • NMS-free训练策略

    • 轻量级分类头

    • 空间通道解耦下采样

    • 大核卷积优化

    💻 环境配置

    创建虚拟环境

    bash

    conda create -n yolo_shelf python=3.9
    conda activate yolo_shelf

    安装依赖

    bash

    # 基础依赖
    pip install torch torchvision torchaudio –index-url https://download.pytorch.org/whl/cu118
    pip install opencv-python pillow matplotlib numpy pandas tqdm

    # YOLOv5依赖
    git clone https://github.com/ultralytics/yolov5
    cd yolov5
    pip install -r requirements.txt

    # YOLOv8/v10依赖
    pip install ultralytics
    pip install yolov10 # 安装YOLOv10

    🔧 YOLOv5实现

    数据准备

    python

    # dataset.yaml
    path: ./dataset # 数据集根目录
    train: images/train # 训练集路径
    val: images/val # 验证集路径
    test: images/test # 测试集路径

    nc: 10 # 类别数量
    names: ['coca_cola', 'pepsi', 'lays', 'oreo', 'noodle', 'water', 'juice', 'chocolate', 'cookie', 'yogurt']

    模型训练

    python

    import torch
    from yolov5 import train

    # 训练配置
    train.run(
    data='dataset.yaml',
    weights='yolov5s.pt',
    batch_size=16,
    epochs=100,
    imgsz=640,
    device=0,
    project='runs/train',
    name='yolov5_shelf',
    exist_ok=True
    )

    模型推理

    python

    import torch
    import cv2
    import numpy as np

    class YOLOv5Detector:
    def __init__(self, weights_path='best.pt'):
    self.model = torch.hub.load('ultralytics/yolov5', 'custom',
    path=weights_path, force_reload=True)
    self.model.conf = 0.5 # 置信度阈值
    self.model.iou = 0.45 # IOU阈值

    def detect(self, image_path):
    """检测单张图片"""
    results = self.model(image_path)
    detections = results.pandas().xyxy[0] # 转为pandas DataFrame
    return detections

    def detect_batch(self, image_paths):
    """批量检测"""
    results = self.model(image_paths)
    return results

    def visualize(self, image, detections):
    """可视化检测结果"""
    img = image.copy()
    for _, det in detections.iterrows():
    x1, y1, x2, y2 = int(det['xmin']), int(det['ymin']), int(det['xmax']), int(det['ymax'])
    conf = det['confidence']
    cls = int(det['class'])
    label = f"{self.model.names[cls]}: {conf:.2f}"

    # 绘制边界框
    cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
    cv2.putText(img, label, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)

    return img

    🚀 YOLOv8实现

    训练代码

    python

    from ultralytics import YOLO
    import torch

    class YOLOv8Trainer:
    def __init__(self, model_name='yolov8s.pt'):
    self.model = YOLO(model_name)

    def train(self, data_yaml, epochs=100, batch_size=16, imgsz=640):
    """训练YOLOv8模型"""
    results = self.model.train(
    data=data_yaml,
    epochs=epochs,
    batch=batch_size,
    imgsz=imgsz,
    device=0,
    workers=8,
    lr0=0.01,
    augment=True,
    patience=50,
    save_period=10,
    project='runs/train',
    name='yolov8_shelf'
    )
    return results

    def validate(self, data_yaml):
    """验证模型"""
    metrics = self.model.val(data=data_yaml)
    return metrics

    def export_model(self, format='onnx'):
    """导出模型"""
    self.model.export(format=format)

    # 开始训练
    trainer = YOLOv8Trainer('yolov8s.pt')
    trainer.train('dataset.yaml', epochs=100)

    推理代码

    python

    from ultralytics import YOLO
    import cv2
    import numpy as np

    class YOLOv8Detector:
    def __init__(self, weights_path='best.pt'):
    self.model = YOLO(weights_path)
    self.class_names = self.model.names

    def detect(self, image_path, conf_thres=0.5, iou_thres=0.45):
    """执行目标检测"""
    results = self.model(image_path, conf=conf_thres, iou=iou_thres)

    detections = []
    for result in results:
    boxes = result.boxes
    if boxes is not None:
    for box in boxes:
    x1, y1, x2, y2 = box.xyxy[0].tolist()
    conf = float(box.conf[0])
    cls = int(box.cls[0])

    detections.append({
    'bbox': [x1, y1, x2, y2],
    'confidence': conf,
    'class_id': cls,
    'class_name': self.class_names[cls]
    })

    return detections

    def detect_video(self, video_path, output_path=None):
    """视频检测"""
    cap = cv2.VideoCapture(video_path)

    if output_path:
    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    fps = int(cap.get(cv2.CAP_PROP_FPS))
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))

    while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
    break

    # 检测
    results = self.model(frame)
    annotated_frame = results[0].plot()

    if output_path:
    out.write(annotated_frame)
    else:
    cv2.imshow('Detection', annotated_frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
    break

    cap.release()
    if output_path:
    out.release()
    cv2.destroyAllWindows()

    ⚡ YOLOv10实现

    安装与训练

    python

    # 安装YOLOv10
    pip install git+https://github.com/THU-MIG/yolov10.git

    # 训练脚本
    from yolov10 import train
    import yaml

    # 训练配置
    train_args = {
    'data': 'dataset.yaml',
    'weights': 'yolov10s.pt',
    'epochs': 100,
    'batch_size': 16,
    'imgsz': 640,
    'device': 0,
    'project': 'runs/train',
    'name': 'yolov10_shelf'
    }

    train.run(**train_args)

    推理代码

    python

    import torch
    import cv2
    import numpy as np
    from yolov10.models import YOLOv10
    from yolov10.utils import non_max_suppression, scale_coords

    class YOLOv10Detector:
    def __init__(self, weights_path='best.pt', device='cuda'):
    self.device = torch.device(device)
    self.model = YOLOv10(weights_path).to(self.device)
    self.model.eval()

    # 加载类别名称
    with open('classes.txt', 'r') as f:
    self.class_names = [line.strip() for line in f.readlines()]

    def preprocess(self, image):
    """图像预处理"""
    img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    img = cv2.resize(img, (640, 640))
    img = img.transpose(2, 0, 1) # HWC to CHW
    img = np.ascontiguousarray(img)
    img = torch.from_numpy(img).to(self.device)
    img = img.float() / 255.0 # 归一化
    if img.ndimension() == 3:
    img = img.unsqueeze(0)
    return img

    def detect(self, image, conf_thres=0.5, iou_thres=0.45):
    """执行检测"""
    # 预处理
    img_tensor = self.preprocess(image)

    # 推理
    with torch.no_grad():
    pred = self.model(img_tensor)[0]

    # NMS
    pred = non_max_suppression(pred, conf_thres, iou_thres)

    detections = []
    if pred[0] is not None:
    for *xyxy, conf, cls in pred[0]:
    detections.append({
    'bbox': [float(x) for x in xyxy],
    'confidence': float(conf),
    'class_id': int(cls),
    'class_name': self.class_names[int(cls)]
    })

    return detections

    def visualize(self, image, detections):
    """可视化结果"""
    img = image.copy()
    for det in detections:
    x1, y1, x2, y2 = [int(x) for x in det['bbox']]
    conf = det['confidence']
    label = f"{det['class_name']}: {conf:.2f}"

    # 随机颜色
    color = tuple(np.random.randint(0, 255, 3).tolist())

    cv2.rectangle(img, (x1, y1), (x2, y2), color, 2)
    cv2.putText(img, label, (x1, y1-10),
    cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)

    return img

    📈 排面占比计算算法

    核心算法

    python

    import numpy as np
    import pandas as pd
    from collections import defaultdict

    class ShelfSpaceAnalyzer:
    def __init__(self):
    self.area_ratios = {}

    def calculate_area(self, bbox):
    """计算单个检测框面积"""
    x1, y1, x2, y2 = bbox
    width = x2 – x1
    height = y2 – y1
    return width * height

    def calculate_shelf_ratio(self, detections, image_area=None):
    """
    计算排面占比
    detections: 检测结果列表
    image_area: 图像总面积(可选)
    """
    if not detections:
    return {}

    # 按类别分组
    class_areas = defaultdict(float)
    total_area = 0

    for det in detections:
    area = self.calculate_area(det['bbox'])
    class_name = det['class_name']
    class_areas[class_name] += area
    total_area += area

    # 如果提供了图像总面积,使用图像面积;否则使用检测框总面积
    if image_area and image_area > 0:
    denominator = image_area
    else:
    denominator = total_area

    # 计算占比
    ratios = {}
    for class_name, area in class_areas.items():
    ratios[class_name] = (area / denominator) * 100

    # 按占比降序排序
    ratios = dict(sorted(ratios.items(), key=lambda x: x[1], reverse=True))

    return ratios

    def analyze_multi_images(self, detections_list):
    """分析多张图像的平均占比"""
    all_ratios = []

    for detections in detections_list:
    ratios = self.calculate_shelf_ratio(detections)
    all_ratios.append(ratios)

    # 计算平均占比
    avg_ratios = defaultdict(float)
    count_ratios = defaultdict(int)

    for ratios in all_ratios:
    for class_name, ratio in ratios.items():
    avg_ratios[class_name] += ratio
    count_ratios[class_name] += 1

    for class_name in avg_ratios:
    avg_ratios[class_name] /= count_ratios[class_name]

    return dict(sorted(avg_ratios.items(), key=lambda x: x[1], reverse=True))

    def generate_report(self, ratios, output_path='shelf_report.csv'):
    """生成分析报告"""
    df = pd.DataFrame(list(ratios.items()), columns=['商品类别', '排面占比(%)'])
    df['排面占比(%)'] = df['排面占比(%)'].round(2)
    df.to_csv(output_path, index=False, encoding='utf-8-sig')
    return df

    def visualize_ratios(self, ratios, save_path=None):
    """可视化占比结果"""
    import matplotlib.pyplot as plt

    plt.figure(figsize=(12, 6))

    categories = list(ratios.keys())
    values = list(ratios.values())

    # 创建条形图
    bars = plt.bar(categories, values, color='skyblue', edgecolor='navy')

    # 添加数值标签
    for bar, value in zip(bars, values):
    plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.5,
    f'{value:.1f}%', ha='center', va='bottom')

    plt.xlabel('商品类别')
    plt.ylabel('排面占比 (%)')
    plt.title('货架商品排面占比分析')
    plt.xticks(rotation=45, ha='right')
    plt.tight_layout()

    if save_path:
    plt.savefig(save_path, dpi=300, bbox_inches='tight')
    plt.show()

    🎨 UI界面开发

    主界面设计 (PyQt5)

    python

    import sys
    import os
    from PyQt5.QtWidgets import *
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
    import cv2
    from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
    from matplotlib.figure import Figure

    class ShelfAnalyzerUI(QMainWindow):
    def __init__(self):
    super().__init__()
    self.detector = None
    self.current_image = None
    self.detections = None
    self.initUI()

    def initUI(self):
    """初始化UI"""
    self.setWindowTitle('货架排面占比分析系统')
    self.setGeometry(100, 100, 1400, 800)

    # 设置样式
    self.setStyleSheet("""
    QMainWindow {
    background-color: #f0f0f0;
    }
    QPushButton {
    background-color: #4CAF50;
    color: white;
    border: none;
    padding: 8px 16px;
    border-radius: 4px;
    font-size: 14px;
    }
    QPushButton:hover {
    background-color: #45a049;
    }
    QComboBox {
    padding: 5px;
    border: 1px solid #ddd;
    border-radius: 4px;
    font-size: 14px;
    }
    QLabel {
    font-size: 14px;
    }
    """)

    # 创建中央部件
    central_widget = QWidget()
    self.setCentralWidget(central_widget)

    # 主布局
    main_layout = QHBoxLayout()
    central_widget.setLayout(main_layout)

    # 左侧控制面板
    left_panel = self.create_left_panel()
    main_layout.addWidget(left_panel, 1)

    # 右侧显示区域
    right_panel = self.create_right_panel()
    main_layout.addWidget(right_panel, 3)

    def create_left_panel(self):
    """创建左侧控制面板"""
    panel = QWidget()
    layout = QVBoxLayout()
    panel.setLayout(layout)
    panel.setMaximumWidth(300)

    # 模型选择
    model_group = QGroupBox("模型选择")
    model_layout = QVBoxLayout()

    self.model_combo = QComboBox()
    self.model_combo.addItems(['YOLOv5', 'YOLOv8', 'YOLOv10'])
    self.model_combo.currentTextChanged.connect(self.on_model_changed)
    model_layout.addWidget(QLabel("选择检测模型:"))
    model_layout.addWidget(self.model_combo)

    # 权重文件选择
    weight_layout = QHBoxLayout()
    self.weight_path = QLineEdit()
    self.weight_path.setPlaceholderText("选择权重文件…")
    weight_btn = QPushButton("浏览")
    weight_btn.clicked.connect(self.browse_weights)
    weight_layout.addWidget(self.weight_path)
    weight_layout.addWidget(weight_btn)
    model_layout.addLayout(weight_layout)

    model_group.setLayout(model_layout)
    layout.addWidget(model_group)

    # 图像输入
    input_group = QGroupBox("图像输入")
    input_layout = QVBoxLayout()

    # 单张图片
    single_img_btn = QPushButton("选择单张图片")
    single_img_btn.clicked.connect(self.load_single_image)
    input_layout.addWidget(single_img_btn)

    # 批量处理
    batch_btn = QPushButton("批量处理文件夹")
    batch_btn.clicked.connect(self.load_batch_images)
    input_layout.addWidget(batch_btn)

    # 视频处理
    video_btn = QPushButton("处理视频")
    video_btn.clicked.connect(self.load_video)
    input_layout.addWidget(video_btn)

    input_group.setLayout(input_layout)
    layout.addWidget(input_group)

    # 检测参数
    param_group = QGroupBox("检测参数")
    param_layout = QFormLayout()

    self.conf_thres = QDoubleSpinBox()
    self.conf_thres.setRange(0.1, 1.0)
    self.conf_thres.setValue(0.5)
    self.conf_thres.setSingleStep(0.05)
    param_layout.addRow("置信度阈值:", self.conf_thres)

    self.iou_thres = QDoubleSpinBox()
    self.iou_thres.setRange(0.1, 1.0)
    self.iou_thres.setValue(0.45)
    self.iou_thres.setSingleStep(0.05)
    param_layout.addRow("IOU阈值:", self.iou_thres)

    param_group.setLayout(param_layout)
    layout.addWidget(param_group)

    # 操作按钮
    detect_btn = QPushButton("开始检测")
    detect_btn.clicked.connect(self.run_detection)
    detect_btn.setStyleSheet("""
    QPushButton {
    background-color: #2196F3;
    font-size: 16px;
    padding: 12px;
    }
    QPushButton:hover {
    background-color: #1976D2;
    }
    """)
    layout.addWidget(detect_btn)

    analyze_btn = QPushButton("分析排面占比")
    analyze_btn.clicked.connect(self.analyze_shelf_ratio)
    analyze_btn.setStyleSheet("""
    QPushButton {
    background-color: #FF9800;
    font-size: 16px;
    padding: 12px;
    }
    QPushButton:hover {
    background-color: #F57C00;
    }
    """)
    layout.addWidget(analyze_btn)

    # 导出选项
    export_group = QGroupBox("导出结果")
    export_layout = QVBoxLayout()

    export_csv_btn = QPushButton("导出CSV报告")
    export_csv_btn.clicked.connect(self.export_csv)
    export_layout.addWidget(export_csv_btn)

    export_img_btn = QPushButton("导出检测图像")
    export_img_btn.clicked.connect(self.export_image)
    export_layout.addWidget(export_img_btn)

    export_group.setLayout(export_layout)
    layout.addWidget(export_group)

    # 添加弹簧
    layout.addStretch()

    return panel

    def create_right_panel(self):
    """创建右侧显示区域"""
    panel = QWidget()
    layout = QVBoxLayout()
    panel.setLayout(layout)

    # 选项卡
    tabs = QTabWidget()

    # 图像显示选项卡
    image_tab = QWidget()
    image_layout = QVBoxLayout()

    self.image_label = QLabel()
    self.image_label.setAlignment(Qt.AlignCenter)
    self.image_label.setMinimumSize(800, 500)
    self.image_label.setStyleSheet("border: 1px solid #ddd; background-color: white;")

    image_layout.addWidget(self.image_label)
    image_tab.setLayout(image_layout)

    # 分析结果选项卡
    result_tab = QWidget()
    result_layout = QVBoxLayout()

    # Matplotlib图表
    self.figure = Figure(figsize=(8, 5))
    self.canvas = FigureCanvas(self.figure)
    result_layout.addWidget(self.canvas)

    # 数据表格
    self.result_table = QTableWidget()
    self.result_table.setColumnCount(2)
    self.result_table.setHorizontalHeaderLabels(['商品类别', '排面占比(%)'])
    self.result_table.horizontalHeader().setStretchLastSection(True)
    result_layout.addWidget(self.result_table)

    result_tab.setLayout(result_layout)

    tabs.addTab(image_tab, "图像显示")
    tabs.addTab(result_tab, "分析结果")

    layout.addWidget(tabs)

    # 状态栏
    self.status_label = QLabel("就绪")
    self.status_label.setStyleSheet("color: green;")
    layout.addWidget(self.status_label)

    return panel

    def browse_weights(self):
    """浏览权重文件"""
    file_path, _ = QFileDialog.getOpenFileName(
    self, "选择权重文件", "", "Model Files (*.pt)")
    if file_path:
    self.weight_path.setText(file_path)

    def load_single_image(self):
    """加载单张图片"""
    file_path, _ = QFileDialog.getOpenFileName(
    self, "选择图片", "", "Image Files (*.jpg *.png *.jpeg)")
    if file_path:
    self.current_image = cv2.imread(file_path)
    self.current_image_path = file_path
    self.display_image(self.current_image)
    self.status_label.setText(f"已加载图片: {os.path.basename(file_path)}")

    def display_image(self, image):
    """在界面上显示图片"""
    if image is None:
    return

    # 调整图像大小以适应显示区域
    height, width = image.shape[:2]
    max_height = 500
    max_width = 800

    if height > max_height or width > max_width:
    scale = min(max_height/height, max_width/width)
    new_width = int(width * scale)
    new_height = int(height * scale)
    image = cv2.resize(image, (new_width, new_height))

    # 转换为QImage
    rgb_image = cv2.cvtColor(image, 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)

    # 显示
    pixmap = QPixmap.fromImage(qt_image)
    self.image_label.setPixmap(pixmap)

    def run_detection(self):
    """运行目标检测"""
    if self.current_image is None:
    QMessageBox.warning(self, "警告", "请先加载图片!")
    return

    if not self.weight_path.text():
    QMessageBox.warning(self, "警告", "请选择模型权重文件!")
    return

    try:
    self.status_label.setText("正在检测中…")
    QApplication.processEvents()

    # 根据选择的模型创建检测器
    model_name = self.model_combo.currentText()
    if model_name == 'YOLOv5':
    from yolov5_detector import YOLOv5Detector
    self.detector = YOLOv5Detector(self.weight_path.text())
    elif model_name == 'YOLOv8':
    from yolov8_detector import YOLOv8Detector
    self.detector = YOLOv8Detector(self.weight_path.text())
    else: # YOLOv10
    from yolov10_detector import YOLOv10Detector
    self.detector = YOLOv10Detector(self.weight_path.text())

    # 执行检测
    self.detections = self.detector.detect(
    self.current_image,
    conf_thres=self.conf_thres.value(),
    iou_thres=self.iou_thres.value()
    )

    # 显示检测结果
    result_image = self.detector.visualize(self.current_image, self.detections)
    self.display_image(result_image)

    self.status_label.setText(f"检测完成!检测到 {len(self.detections)} 个目标")

    except Exception as e:
    QMessageBox.critical(self, "错误", f"检测失败:{str(e)}")
    self.status_label.setText("检测失败")

    def analyze_shelf_ratio(self):
    """分析排面占比"""
    if self.detections is None:
    QMessageBox.warning(self, "警告", "请先进行目标检测!")
    return

    # 计算排面占比
    analyzer = ShelfSpaceAnalyzer()
    image_area = self.current_image.shape[0] * self.current_image.shape[1]
    ratios = analyzer.calculate_shelf_ratio(self.detections, image_area)

    # 显示图表
    self.figure.clear()
    ax = self.figure.add_subplot(111)

    categories = list(ratios.keys())
    values = list(ratios.values())

    bars = ax.bar(categories, values, color='skyblue', edgecolor='navy')
    ax.set_xlabel('商品类别')
    ax.set_ylabel('排面占比 (%)')
    ax.set_title('货架商品排面占比分析')
    ax.tick_params(axis='x', rotation=45)

    # 添加数值标签
    for bar, value in zip(bars, values):
    ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.5,
    f'{value:.1f}%', ha='center', va='bottom')

    self.figure.tight_layout()
    self.canvas.draw()

    # 更新表格
    self.result_table.setRowCount(len(ratios))
    for i, (class_name, ratio) in enumerate(ratios.items()):
    self.result_table.setItem(i, 0, QTableWidgetItem(class_name))
    self.result_table.setItem(i, 1, QTableWidgetItem(f"{ratio:.2f}"))

    QMessageBox.information(self, "完成", "排面占比分析完成!")

    def export_csv(self):
    """导出CSV报告"""
    if self.result_table.rowCount() == 0:
    QMessageBox.warning(self, "警告", "没有可导出的数据!")
    return

    file_path, _ = QFileDialog.getSaveFileName(
    self, "保存CSV文件", "", "CSV Files (*.csv)")

    if file_path:
    import pandas as pd

    data = []
    for i in range(self.result_table.rowCount()):
    class_name = self.result_table.item(i, 0).text()
    ratio = float(self.result_table.item(i, 1).text())
    data.append([class_name, ratio])

    df = pd.DataFrame(data, columns=['商品类别', '排面占比(%)'])
    df.to_csv(file_path, index=False, encoding='utf-8-sig')

    QMessageBox.information(self, "成功", f"报告已保存至:{file_path}")

    def on_model_changed(self, model_name):
    """模型切换时的响应"""
    if model_name == 'YOLOv5':
    default_weights = 'yolov5s.pt'
    elif model_name == 'YOLOv8':
    default_weights = 'yolov8s.pt'
    else:
    default_weights = 'yolov10s.pt'

    self.weight_path.setText(default_weights)

    赞(0)
    未经允许不得转载:171主机测评 » [特殊字符] 基于YOLOv5/v8/v10的商超货架商品陈列面占比分析系统【完整源码+数据集】
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址