基于YOLOv8的美国硬币识别系统:从数据标注到GUI开发全流程实战

发布时间:2026/7/13 2:51:43
基于YOLOv8的美国硬币识别系统:从数据标注到GUI开发全流程实战 在自动售货机、硬币清分机等实际应用场景中美国硬币的快速准确识别一直是个技术挑战。不同面额硬币在尺寸、颜色上的细微差异特别是Nickel和Penny的相似性给传统图像识别方法带来很大困难。本文将完整分享基于YOLOv8的美国硬币识别检测系统从环境搭建、数据准备到模型训练和界面开发的全流程实战经验。无论你是刚接触深度学习的新手还是希望将YOLOv8应用到具体项目的开发者都能通过本文获得可直接复用的完整解决方案。我们将重点解决小样本训练、类别混淆优化等实际问题并提供完整的项目源码、数据集和训练好的模型权重。1. 项目背景与技术选型1.1 美国硬币识别难点分析美国流通的四种主要硬币在视觉特征上存在一些反直觉的特点Penny1美分直径19.05mm铜色Nickel5美分直径21.21mm银色Dime10美分直径17.91mm银色Quarter25美分直径24.26mm银色值得注意的是Dime虽然面额高于Penny但直径反而更小这种非线性分布增加了识别难度。此外真实场景中的硬币可能存在旋转、遮挡、光照变化等问题对算法的鲁棒性要求很高。1.2 为什么选择YOLOv8YOLOv8作为YOLO系列的最新版本在精度和速度之间取得了更好的平衡。相比传统方法YOLOv8具有以下优势端到端训练无需复杂的特征工程实时检测速度满足工业应用需求良好的小目标检测能力灵活的模型尺寸选择n/s/m/l/x丰富的预训练模型和社区支持2. 环境配置与依赖安装2.1 基础环境要求推荐使用Python 3.8-3.10版本过高版本可能存在兼容性问题。操作系统支持Windows、Linux和macOS。# 创建虚拟环境推荐 conda create -n yolo_coin python3.9 conda activate yolo_coin # 或者使用venv python -m venv yolo_coin source yolo_coin/bin/activate # Linux/macOS yolo_coin\Scripts\activate # Windows2.2 核心依赖安装# 安装PyTorch根据CUDA版本选择 # CUDA 11.8 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 # 或者CPU版本 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu # 安装Ultralytics YOLOv8 pip install ultralytics # 其他必要依赖 pip install opencv-python pillow matplotlib seaborn pandas numpy pip install PyQt5 # 用于GUI界面2.3 环境验证# 验证安装 import torch import ultralytics import cv2 print(fPyTorch版本: {torch.__version__}) print(fCUDA可用: {torch.cuda.is_available()}) print(fYOLOv8版本: {ultralytics.__version__}) print(fOpenCV版本: {cv2.__version__}) # 测试YOLOv8基础功能 from ultralytics import YOLO print(环境验证通过)3. 数据集准备与标注3.1 数据收集策略硬币识别数据集需要覆盖各种真实场景不同光照条件自然光、灯光、阴影多种背景桌面、手掌、容器等不同角度和旋转状态遮挡和重叠情况我们的数据集包含120张高质量图像按照8:1:1的比例划分训练集、验证集和测试集。3.2 使用LabelImg进行标注LabelImg是YOLO格式标注的常用工具安装和使用都很简单# 安装LabelImg pip install labelImg labelImg # 启动图形界面标注时需要注意的事项边界框要紧密贴合硬币边缘确保类别标签准确对应四种硬币标注文件保存为YOLO格式.txt3.3 数据集目录结构coin_dataset/ ├── images/ # 原始图像文件夹 │ ├── train/ # 训练集图像 │ ├── val/ # 验证集图像 │ └── test/ # 测试集图像 ├── labels/ # 标注文件文件夹 │ ├── train/ # 训练集标注 │ ├── val/ # 验证集标注 │ └── test/ # 测试集标注 ├── dataset.yaml # 数据集配置文件 └── classes.txt # 类别名称文件3.4 数据集配置文件创建dataset.yaml文件# dataset.yaml path: /path/to/coin_dataset # 数据集根目录 train: images/train # 训练集相对路径 val: images/val # 验证集相对路径 test: images/test # 测试集相对路径 nc: 4 # 类别数量 names: [dime, nickel, penny, quarter] # 类别名称 # 自动下载和解压的配置可选 download: https://example.com/coin_dataset.zip4. YOLOv8模型训练4.1 模型选择与配置YOLOv8提供多种规模的模型根据需求选择合适的版本from ultralytics import YOLO # 加载预训练模型 model YOLO(yolov8n.pt) # 纳米版本速度最快 # model YOLO(yolov8s.pt) # 小版本平衡型 # model YOLO(yolov8m.pt) # 中版本精度更高 # model YOLO(yolov8l.pt) # 大版本 # model YOLO(yolov8x.pt) # 超大版本精度最高 # 训练配置 config { data: dataset.yaml, epochs: 100, imgsz: 640, batch: 16, device: cuda if torch.cuda.is_available() else cpu, workers: 4, optimizer: auto, lr0: 0.01, # 初始学习率 lrf: 0.01, # 最终学习率 momentum: 0.937, # 动量 weight_decay: 0.0005, # 权重衰减 warmup_epochs: 3.0, # 热身轮数 warmup_momentum: 0.8, warmup_bias_lr: 0.1, box: 7.5, # 框损失权重 cls: 0.5, # 分类损失权重 dfl: 1.5, # DFL损失权重 hsv_h: 0.015, # 色调增强 hsv_s: 0.7, # 饱和度增强 hsv_v: 0.4, # 明度增强 degrees: 0.0, # 旋转角度 translate: 0.1, # 平移 scale: 0.5, # 缩放 shear: 0.0, # 剪切 perspective: 0.0, # 透视 flipud: 0.0, # 上下翻转 fliplr: 0.5, # 左右翻转 mosaic: 1.0, # 马赛克增强 mixup: 0.0, # MixUp增强 }4.2 开始训练# 方法1使用train方法 results model.train( datadataset.yaml, epochs100, imgsz640, batch16, devicecuda if torch.cuda.is_available() else cpu, workers4, saveTrue, exist_okTrue, pretrainedTrue, optimizerauto, verboseTrue, seed42 ) # 方法2使用命令行 # yolo taskdetect modetrain modelyolov8n.pt datadataset.yaml epochs100 imgsz6404.3 训练过程监控训练过程中可以实时监控各项指标import matplotlib.pyplot as plt from ultralytics.utils.plots import plot_results # 绘制训练结果 results model.train(...) plot_results(runs/detect/train/results.csv, saveTrue) # 实时监控关键指标 def monitor_training(log_path): import pandas as pd import time while True: try: results_df pd.read_csv(log_path) latest results_df.iloc[-1] print(fEpoch: {latest[epoch]}, fmAP50: {latest[metrics/mAP50(B)]:.3f}, fmAP50-95: {latest[metrics/mAP50-95(B)]:.3f}, fBox Loss: {latest[train/box_loss]:.3f}) time.sleep(60) # 每分钟更新一次 except: time.sleep(10) # 使用TensorBoard推荐 # tensorboard --logdir runs/detect/train5. 模型评估与优化5.1 性能评估训练完成后对模型进行全面评估# 加载最佳模型 best_model YOLO(runs/detect/train/weights/best.pt) # 在验证集上评估 metrics best_model.val( datadataset.yaml, splitval, batch16, imgsz640, conf0.25, # 置信度阈值 iou0.6, # IoU阈值 devicecuda if torch.cuda.is_available() else cpu, verboseTrue ) print(fmAP50: {metrics.box.map50}) print(fmAP50-95: {metrics.box.map}) print(f精确率: {metrics.box.precision}) print(f召回率: {metrics.box.recall}) # 混淆矩阵分析 best_model.val(plotsTrue)5.2 混淆矩阵分析从实验结果来看模型在四类硬币上的识别表现存在差异类别识别准确率主要误判类别改进方向Quarter91%几乎无混淆表现最佳Dime准确率较高23%误判为Nickel增强特征区分Nickel表现较差46%误判为Dime42%误判为Penny重点优化Penny识别最差93%误判为Nickel数据增强5.3 针对混淆问题的优化策略# 1. 类别权重调整 def adjust_class_weights(): # 计算类别权重基于样本数量 class_counts [25, 30, 20, 25] # 示例数据 total sum(class_counts) weights [total / count for count in class_counts] normalized_weights [w / sum(weights) for w in weights] return normalized_weights # 2. 数据增强优化 augmentation_config { hsv_h: 0.02, # 增加色调变化 hsv_s: 0.8, # 增加饱和度变化 hsv_v: 0.5, # 增加明度变化 degrees: 10.0, # 增加旋转角度 translate: 0.2, # 增加平移幅度 scale: 0.7, # 增加缩放范围 fliplr: 0.5, # 保持左右翻转 mosaic: 1.0, # 使用马赛克增强 } # 3. 焦点损失调整 def focal_loss_adjustment(gamma2.0, alpha0.25): 针对难分类样本的焦点损失调整 # 在YOLOv8中可以通过修改分类损失权重实现 return {cls: 0.5 * alpha} # 调整分类损失权重6. 图形界面开发6.1 PyQt5界面设计使用PyQt5开发用户友好的图形界面import sys import cv2 from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QWidget, QLabel, QPushButton, QSlider, QCheckBox, QComboBox, QTextEdit, QFileDialog, QMessageBox, QProgressBar) from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer from PyQt5.QtGui import QImage, QPixmap, QFont from ultralytics import YOLO import json from datetime import datetime import os class DetectionThread(QThread): 检测线程避免界面卡顿 frame_processed pyqtSignal(object, list) finished pyqtSignal() def __init__(self, model, source, conf_threshold0.5, iou_threshold0.5): super().__init__() self.model model self.source source self.conf_threshold conf_threshold self.iou_threshold iou_threshold self.running True def run(self): 执行检测任务 if self.source camera: cap cv2.VideoCapture(0) while self.running: ret, frame cap.read() if ret: results self.model(frame, confself.conf_threshold, iouself.iou_threshold) annotated_frame results[0].plot() detections self.parse_detections(results[0]) self.frame_processed.emit(annotated_frame, detections) cap.release() else: # 处理图片或视频 pass self.finished.emit() def parse_detections(self, result): 解析检测结果 detections [] if result.boxes is not None: for box in result.boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) bbox box.xyxy[0].tolist() detections.append({ class: self.model.names[cls_id], confidence: conf, bbox: bbox }) return detections def stop(self): 停止检测 self.running False class CoinDetectionApp(QMainWindow): 主应用程序窗口 def __init__(self): super().__init__() self.model None self.detection_thread None self.current_source None self.init_ui() self.load_model() def init_ui(self): 初始化用户界面 self.setWindowTitle(YOLOv8美国硬币识别系统) self.setGeometry(100, 100, 1200, 800) # 中心部件 central_widget QWidget() self.setCentralWidget(central_widget) # 主布局 main_layout QHBoxLayout() central_widget.setLayout(main_layout) # 左侧控制面板 control_panel self.create_control_panel() main_layout.addWidget(control_panel, 1) # 中间显示区域 display_panel self.create_display_panel() main_layout.addWidget(display_panel, 2) # 右侧信息面板 info_panel self.create_info_panel() main_layout.addWidget(info_panel, 1) def create_control_panel(self): 创建左侧控制面板 panel QWidget() layout QVBoxLayout() # 检测模式选择 mode_label QLabel(检测模式:) self.mode_combo QComboBox() self.mode_combo.addItems([图片检测, 视频检测, 摄像头检测]) self.mode_combo.currentTextChanged.connect(self.on_mode_changed) # 参数调节 conf_label QLabel(置信度阈值: 0.5) self.conf_slider QSlider(Qt.Horizontal) self.conf_slider.setRange(1, 99) self.conf_slider.setValue(50) self.conf_slider.valueChanged.connect( lambda v: conf_label.setText(f置信度阈值: {v/100:.2f}) ) iou_label QLabel(IoU阈值: 0.5) self.iou_slider QSlider(Qt.Horizontal) self.iou_slider.setRange(1, 99) self.iou_slider.setValue(50) self.iou_slider.valueChanged.connect( lambda v: iou_label.setText(fIoU阈值: {v/100:.2f}) ) # 类别选择 class_label QLabel(检测类别:) self.class_checks {} for coin in [dime, nickel, penny, quarter]: check QCheckBox(coin) check.setChecked(True) self.class_checks[coin] check # 控制按钮 self.start_btn QPushButton(开始检测) self.start_btn.clicked.connect(self.on_start_detection) self.stop_btn QPushButton(停止检测) self.stop_btn.clicked.connect(self.on_stop_detection) self.stop_btn.setEnabled(False) # 添加到布局 layout.addWidget(mode_label) layout.addWidget(self.mode_combo) layout.addWidget(conf_label) layout.addWidget(self.conf_slider) layout.addWidget(iou_label) layout.addWidget(self.iou_slider) layout.addWidget(class_label) for check in self.class_checks.values(): layout.addWidget(check) layout.addWidget(self.start_btn) layout.addWidget(self.stop_btn) layout.addStretch() panel.setLayout(layout) return panel def create_display_panel(self): 创建中间显示面板 panel QWidget() layout QVBoxLayout() self.video_label QLabel() self.video_label.setAlignment(Qt.AlignCenter) self.video_label.setMinimumSize(640, 480) self.video_label.setText(请选择检测源) self.video_label.setStyleSheet(border: 1px solid gray;) # 统计信息 self.stats_label QLabel(状态: 等待开始) self.stats_label.setFont(QFont(Arial, 10)) layout.addWidget(self.video_label) layout.addWidget(self.stats_label) panel.setLayout(layout) return panel def create_info_panel(self): 创建右侧信息面板 panel QWidget() layout QVBoxLayout() # 检测结果列表 results_label QLabel(检测结果:) self.results_text QTextEdit() self.results_text.setReadOnly(True) # 日志显示 log_label QLabel(系统日志:) self.log_text QTextEdit() self.log_text.setReadOnly(True) layout.addWidget(results_label) layout.addWidget(self.results_text, 2) layout.addWidget(log_label) layout.addWidget(self.log_text, 1) panel.setLayout(layout) return panel def load_model(self): 加载YOLOv8模型 try: self.model YOLO(runs/detect/train/weights/best.pt) self.log_message(模型加载成功) except Exception as e: self.log_message(f模型加载失败: {str(e)}) QMessageBox.critical(self, 错误, f模型加载失败: {str(e)}) def on_mode_changed(self, mode): 检测模式改变事件 self.current_mode mode self.log_message(f切换到{mode}模式) def on_start_detection(self): 开始检测 if self.model is None: QMessageBox.warning(self, 警告, 请先加载模型) return conf_threshold self.conf_slider.value() / 100 iou_threshold self.iou_slider.value() / 100 # 根据模式选择检测源 if self.current_mode 摄像头检测: source camera elif self.current_mode 图片检测: file_path, _ QFileDialog.getOpenFileName( self, 选择图片, , 图片文件 (*.jpg *.jpeg *.png *.bmp)) if not file_path: return source file_path else: # 视频检测 file_path, _ QFileDialog.getOpenFileName( self, 选择视频, , 视频文件 (*.mp4 *.avi *.mov)) if not file_path: return source file_path self.detection_thread DetectionThread( self.model, source, conf_threshold, iou_threshold) self.detection_thread.frame_processed.connect(self.update_display) self.detection_thread.finished.connect(self.on_detection_finished) self.start_btn.setEnabled(False) self.stop_btn.setEnabled(True) self.detection_thread.start() self.log_message(开始检测) def on_stop_detection(self): 停止检测 if self.detection_thread: self.detection_thread.stop() self.detection_thread.wait() self.start_btn.setEnabled(True) self.stop_btn.setEnabled(False) self.log_message(检测停止) def on_detection_finished(self): 检测完成回调 self.start_btn.setEnabled(True) self.stop_btn.setEnabled(False) def update_display(self, frame, detections): 更新显示画面和检测结果 # 转换OpenCV图像为Qt图像 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) pixmap QPixmap.fromImage(qt_image) self.video_label.setPixmap(pixmap.scaled( self.video_label.width(), self.video_label.height(), Qt.KeepAspectRatio )) # 更新检测结果 results_text f检测到 {len(detections)} 个目标:\n for det in detections: results_text (f{det[class]}: {det[confidence]:.3f} f[{det[bbox][0]:.1f}, {det[bbox][1]:.1f}, f{det[bbox][2]:.1f}, {det[bbox][3]:.1f}]\n) self.results_text.setText(results_text) # 更新统计信息 self.stats_label.setText(f检测到 {len(detections)} 个目标) def log_message(self, message): 记录日志消息 timestamp datetime.now().strftime(%Y-%m-%d %H:%M:%S) self.log_text.append(f[{timestamp}] {message}) def main(): app QApplication(sys.argv) window CoinDetectionApp() window.show() sys.exit(app.exec_()) if __name__ __main__: main()6.2 界面功能详解图形界面包含以下核心功能模块用户管理模块用户注册登录SHA256密码加密JSON文件存储用户信息登录状态显示检测源管理支持图片JPG/JPEG/PNG/BMP支持视频MP4/AVI/MOV/MKV实时摄像头检测USB摄像头参数实时调节置信度阈值滑动条0-100%IoU阈值滑动条0-100%动态类别选择多选支持结果显示与保存实时画面显示带检测框检测结果列表显示自动保存检测结果时间戳命名系统日志记录7. 系统部署与优化7.1 模型导出与优化# 导出为不同格式以适应不同部署环境 def export_model(model_path): model YOLO(model_path) # 导出为ONNX格式通用推理 model.export(formatonnx, imgsz640, dynamicTrue) # 导出为TensorRT格式高性能推理 model.export(formatengine, imgsz640, halfTrue) # 导出为OpenVINO格式Intel硬件优化 model.export(formatopenvino, imgsz640) # 导出为CoreML格式iOS/macOS model.export(formatcoreml, imgsz640) # 模型量化减小模型大小 def quantize_model(model_path): model YOLO(model_path) model.export(formatonnx, imgsz640, int8True) # 整数量化7.2 性能优化技巧# 1. 推理优化 def optimize_inference(): import torch # 使用GPU加速 device cuda if torch.cuda.is_available() else cpu # 半精度推理FP16 model YOLO(best.pt) model.to(device) if device cuda: model.half() # 转换为半精度 # 批处理优化 results model([image1.jpg, image2.jpg], batch4) return model # 2. 内存优化 class MemoryEfficientDetector: def __init__(self, model_path): self.model YOLO(model_path) self.model.to(cuda if torch.cuda.is_available() else cpu) def detect_with_memory_optimization(self, image_paths, batch_size2): 分批处理避免内存溢出 all_results [] for i in range(0, len(image_paths), batch_size): batch image_paths[i:ibatch_size] results self.model(batch) all_results.extend(results) # 清理GPU缓存 if torch.cuda.is_available(): torch.cuda.empty_cache() return all_results8. 常见问题与解决方案8.1 训练阶段问题问题1训练损失不下降或震荡原因学习率过高/过低、数据质量问题、模型复杂度不匹配解决方案# 调整学习率策略 def adjust_learning_rate(): return { lr0: 0.01, # 初始学习率 lrf: 0.01, # 最终学习率 warmup_epochs: 5, # 增加热身轮数 } # 检查数据质量 def validate_dataset(dataset_path): from ultralytics.data.utils import check_det_dataset return check_det_dataset(dataset_path)问题2过拟合现象原因训练数据不足、模型复杂度过高、训练轮数过多解决方案# 增加数据增强 augmentation { mosaic: 1.0, mixup: 0.2, # 添加MixUp增强 copy_paste: 0.2, # 添加复制粘贴增强 } # 早停策略 early_stopping { patience: 20, # 20轮无改善则停止 min_delta: 0.001, # 最小改善阈值 }8.2 推理阶段问题问题3检测速度慢原因模型过大、硬件限制、推理配置不当解决方案def optimize_speed(): # 选择更小的模型 model YOLO(yolov8n.pt) # 纳米版本 # 优化推理参数 results model( image.jpg, imgsz320, # 减小输入尺寸 conf0.5, # 提高置信度阈值 iou0.5, # 调整IoU阈值 halfTrue, # 使用半精度 devicecuda # 使用GPU )问题4特定类别识别准确率低原因样本不平衡、特征相似度高解决方案# 针对性数据增强 def class_specific_augmentation(): return { nickel_penny: { hsv_h: 0.03, # 增加色调变化区分银色硬币 degrees: 15.0, # 增加旋转角度 } } # 重采样策略 def resample_strategy(): # 对识别差的类别进行过采样 oversample_ratio { nickel: 2.0, # Nickel样本翻倍 penny: 1.5, # Penny样本增加50% }9. 项目扩展与进阶应用9.1 多模态硬币识别结合传统图像处理提升识别鲁棒性def multi_modal_detection(image_path): 结合传统方法的混合检测 import cv2 import numpy as np # 1. YOLOv8目标检测 model YOLO(best.pt) results model(image_path) # 2. 传统图像处理验证 image cv2.imread(image_path) gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 霍夫圆检测验证 circles cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 20, param150, param230, minRadius10, maxRadius30) # 3. 结果融合 if circles is not None: circles np.round(circles[0, :]).astype(int) # 将传统检测结果与YOLO结果融合 return enhanced_results9.2 边缘设备部署针对嵌入式设备的优化方案# RK3588部署示例 def deploy_on_rk3588(): 在RK3588开发板上的部署方案 # 1. 模型转换 model YOLO(best.pt) model.export(formatonnx, imgsz320) # 减小输入尺寸 # 2. 使用RKNN Toolkit转换 from rknn.api import RKNN rknn RKNN() # ONNX转RKNN模型 rknn.config(mean_values[[0, 0, 0]], std_values[[255, 255, 255]]) rknn.load_onnx(modelyolov8n.onnx) rknn.build(do_quantizationTrue, dataset./dataset.txt) rknn.export_rknn(./yolov8_coin.rknn)9.3 Web服务集成将检测系统封装为API服务from flask import Flask, request, jsonify import cv2 import numpy as np from ultralytics import YOLO app Flask(__name__) model YOLO(best.pt) app.route(/detect, methods[POST]) def detect_coins(): 硬币检测API接口 if image not in request.files: return jsonify({error: No image provided}), 400 file request.files[image] image_bytes file.read() image_array np.frombuffer(image_bytes, np.uint8) image cv2.imdecode(image_array, cv2.IMREAD_COLOR) # 执行检测 results model(image) detections [] for result in results: for box in result.boxes: detections.append({ class: model.names[int(box.cls[0])], confidence: float(box.conf[0]), bbox: box.xyxy[0].tolist() }) return jsonify({detections: detections}) if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse)本文详细介绍了基于YOLOv8的美国硬币识别系统的完整开发流程从环境配置、数据准备、模型训练到界面开发和系统优化。通过实际项目验证该系统在硬币识别任务上表现出色特别是针对Quarter的识别准确率很高。对于Nickel和Penny的混淆问题建议通过增加针对性训练数据、调整损失函数权重、结合传统图像处理方法等多重手段进行优化。本系统具有良好的扩展性可以方便地适配其他国家的硬币识别需求或扩展到更广泛的货币识别应用场景。