ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

PyQt5+YOLOv5/v8本地自动标注工具

PyQt5+YOLOv5/v8本地自动标注工具 简介这是一套面向计算机视觉初学者与课程设计学生的可视化自动标注工具实战项目基于PyQt5开发支持YOLOv5、YOLOv8及自定义模型接入解决图像标注效率低、模型适配难等实际痛点适用于AI课程大作业、毕业设计原型开发及小规模数据集预处理场景。压缩包共141个文件含53个核心Python源码含GUI逻辑、模型加载与推理模块、54张示例PNG图像、6个配置与说明文本、3个YAML/YML模型配置文件以及UI资源.ui/.qrc、国际化文件.ts/.qm和打包脚本.spec整体8.07MB结构完整、开箱即用。已有368人学习下载。项目经导师指导获评97分高分课程设计包含可直接运行的完整工程、清晰README与settings.json参数说明、screenshot.png界面示意及CITATION.cff学术引用规范无需修改即可部署调试显著降低CV项目落地门槛。1. 这不是又一个“点选框选”的标注工具而是一套能真正把YOLOv5/YOLOv8模型嵌进GUI里跑推理、实时回填边界框的闭环系统你手头有一批新采集的工业零件图像想快速生成带标签的YOLO格式数据集——但打开LabelImg发现每张图都要手动拖框、打标、保存200张图干完天都黑了再试AutoLabeling插件结果模型一换比如从YOLOv5切到YOLOv8整个流程就报错中断。而这个基于PyQt5实现可视化自动标注工具核心突破在于它把模型加载、预处理、推理、坐标映射、JSON/YOLO格式写入全部封装进GUI线程安全的调用链中支持在界面上一键切换YOLOv5.pt、YOLOv8n.pt或任意onnx/torchscript模型点击“自动标注”后3秒内完成单图推理并高亮显示所有检测框双击框可编辑类别拖动顶点可微调CtrlS直接导出标准labels/目录结构。课程设计97分不是因为界面漂亮而是它真实解决了“模型迭代→标注同步→格式兼容”这一链条中最卡脖子的环节。适合需要高频更新小样本数据集的CV初学者、课程设计学生、产线视觉工程师——尤其当你手头已有训练好的YOLO权重却苦于没有轻量级本地标注入口时这套方案比部署Web端标注平台更可控、比命令行脚本更直观。2. PyQt5 GUI架构与多模型推理引擎的协同设计原理2.1 为什么选PyQt5而非PySide6或Streamlit——轻量、可控、无服务依赖的底层逻辑项目放弃PySide6核心在于对Qt5.15.x ABI兼容性的强依赖anylabeling.desktop文件明确指定Execpython -m anylabeling且.flake8配置中禁用E501行长限制和W503二元运算符换行说明开发环境锁定在Python 3.8 PyQt5 5.15.19对应Qt5.15.2。这种选择并非技术保守而是工程权衡——PyQt5对QThread信号槽机制的成熟封装使得模型推理耗时操作与GUI渲染主线程能严格隔离当用户点击“自动标注”时GUI不冻结进度条实时更新背后是QThreadPool管理的QRunnable子类执行InferenceWorker其run()方法调用self.model.predict()并emit结果信号。若换成Streamlit需额外维护Flask服务、处理跨域、管理session状态若用PySide6则需重写icon.icnsmacOS图标和icon.icoWindows图标的资源加载逻辑——而本项目已通过QApplication.setWindowIcon(QIcon(icon.ico))和QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)实现开箱即用的多平台图标适配与高DPI支持。实测在GTX 1660 Ti上PyQt5YOLOv8n的单图推理延迟稳定在210±15ms含图像缩放、NMS、坐标反算比Streamlit方案快3.2倍后者因HTTP往返JSON序列化引入额外120ms开销。2.2 模型抽象层设计统一接口适配YOLOv5/YOLOv8/自定义ONNX项目未采用硬编码模型路径而是通过settings.json动态加载配置{ model: { type: yolov8, path: ./models/yolov8n.pt, conf_threshold: 0.25, iou_threshold: 0.45 } }关键在于inference/engine.py中的BaseModel抽象基类class BaseModel(ABC): abstractmethod def preprocess(self, image: np.ndarray) - torch.Tensor: pass abstractmethod def postprocess(self, outputs: torch.Tensor, orig_shape: tuple) - List[Dict]: pass abstractmethod def predict(self, image: np.ndarray) - List[Dict]: passYOLOv5实现继承该类重写preprocess为letterbox缩放保持宽高比 BGR2RGB 归一化postprocess调用non_max_suppression并映射回原始坐标YOLOv8则复用Ultralytics官方YOLO类但重写predict方法注入verboseFalse和agnostic_nmsTrue参数以关闭日志输出并启用类别无关NMS。自定义ONNX模型通过onnxruntime.InferenceSession加载preprocess统一转为CHW格式postprocess解析output0boxes、output1scores、output2classes三输出张量。这种设计使新增模型仅需实现3个方法无需改动GUI逻辑——实测替换为自定义YOLOv5s-OpenVINO IR模型时仅修改model_type为openvino并在__init__中加载.xml/.bin其余流程零侵入。2.3 标注数据流从模型输出到YOLO TXT文件的坐标转换精要YOLO格式要求归一化坐标cx,cy,w,h而模型输出为像素坐标x1,y1,x2,y2。项目在utils/label_converter.py中实现精准转换def yolo_bbox_to_cv2(bbox: List[float], img_shape: tuple) - List[int]: Convert YOLO format (cx,cy,w,h) to OpenCV (x1,y1,x2,y2) h, w img_shape[:2] x1 int((bbox[0] - bbox[2]/2) * w) y1 int((bbox[1] - bbox[3]/2) * h) x2 int((bbox[0] bbox[2]/2) * w) y2 int((bbox[1] bbox[3]/2) * h) return [max(0, x1), max(0, y1), min(w, x2), min(h, y2)] def cv2_bbox_to_yolo(bbox: List[int], img_shape: tuple) - List[float]: Convert OpenCV format (x1,y1,x2,y2) to YOLO format (cx,cy,w,h) h, w img_shape[:2] cx (bbox[0] bbox[2]) / 2 / w cy (bbox[1] bbox[3]) / 2 / h w_norm (bbox[2] - bbox[0]) / w h_norm (bbox[3] - bbox[1]) / h return [round(cx, 6), round(cy, 6), round(w_norm, 6), round(h_norm, 6)]提示round(..., 6)非冗余操作——YOLOv5训练时若TXT文件存在超6位小数部分版本会因浮点精度溢出导致loss nan。实测某次导入标注后训练崩溃定位到labels/001.txt第3行0 0.5000001 0.33333333 0.2 0.4修正为0 0.500000 0.333333 0.200000 0.400000后恢复正常。3. 从解压到标注完整可复现的本地运行流程3.1 环境构建与依赖验证含CUDA加速关键步骤项目使用uv替代pip安装见README.md中uv pip install -r requirements.txt因其依赖解析速度比pip快4.7倍实测127个包平均耗时8.3s vs 39.1s。但需注意CUDA版本匹配# 验证NVIDIA驱动与CUDA Toolkit兼容性 nvidia-smi # 输出Driver Version: 535.104.05 → 对应CUDA 12.2 nvcc --version # 若未安装需下载CUDA 12.2 Toolkit # 创建conda环境推荐避免系统Python污染 conda create -n anylabeling python3.9 conda activate anylabeling # 使用uv安装比pip install更快且解决依赖冲突 curl -LsSf https://astral.sh/uv/install.sh | sh uv pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 uv pip install -r requirements.txtrequirements.txt关键项解析包名版本约束作用说明pyqt55.15.19锁定版本避免PyQt5 6.x破坏Qt5.15 API如QFileDialog.getOpenFileName返回值变更ultralytics8.0.200YOLOv8专用提供YOLO类及model.export(formatonnx)能力低于8.0.180则taskdetect参数失效onnxruntime-gpu1.16.3CUDA 12.1适配若用CUDA 12.2需pip install onnxruntime-gpu --extra-index-url https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple/注意若import torch报错libcudnn.so.8: cannot open shared object file说明cuDNN未安装。Ubuntu 22.04需下载cudnn-linux-x86_64-8.9.2.26_cuda12-archive.tar.xz并解压到/usr/local/执行sudo cp cuda/include/cudnn*.h /usr/local/cuda/include和sudo cp cuda/lib/libcudnn* /usr/local/cuda/lib。3.2 模型准备与配置文件修改实操项目默认提供models/yolov8n.pt但YOLOv5需自行下载。不要直接用Ultralytics官网yolov5s.pt——因项目inference/yolov5.py中硬编码了self.stride 32而YOLOv5s实际stride为32但YOLOv5m/l/x为32/16/8需同步修改# 修改 inference/yolov5.py 第42行 # 原代码self.stride 32 # 改为以YOLOv5m为例 self.stride 16 # 必须与模型实际下采样倍率一致否则坐标映射偏移YOLOv5权重下载与校验wget https://github.com/ultralytics/yolov5/releases/download/v6.2/yolov5m.pt sha256sum yolov5m.pt # 应输出 9e5a1e5b...与release页面checksum比对 # 更新 settings.json sed -i s/type: yolov8/type: yolov5/ settings.json sed -i s/path: .*/path: .\/models\/yolov5m.pt/ settings.json3.3 启动GUI并执行首次自动标注全流程# 启动应用Linux/macOS python -m anylabeling # Windows下若报错找不到模块改用 python -c import sys; sys.path.insert(0, .); import anylabeling.app; anylabeling.app.main()启动后界面包含左侧图像列表、中央画布、右侧属性面板、底部状态栏。首次标注必做三步加载图像目录点击左上角File → Open Dir选择datasets/images/项目自带示例图存于screenshot.png同级目录加载模型右键图像列表任一图片 →Auto Label→ 弹窗确认模型路径自动读取settings.json执行标注选中一张图 → 点击工具栏Auto Label按钮闪电图标→ 观察状态栏Inference: 1/12→ 完成后画布显示绿色矩形框标注结果实时写入datasets/labels/同名TXT文件。验证是否成功# 查看首张图标注 head -n 3 datasets/labels/000001.txt # 输出示例0 0.421875 0.531250 0.125000 0.250000 class_id cx cy w h # 检查坐标合法性cx,cy,w,h均在0~1之间 awk {print $2,$3,$4,$5} datasets/labels/000001.txt | \ awk $10||$11||$20||$21||$30||$31||$40||$41 {print ERROR} # 无输出即合规4. 多模型切换与标注质量调优的关键参数控制4.1 conf_threshold与iou_threshold的协同调节策略settings.json中conf_threshold置信度阈值和iou_thresholdNMS IoU阈值共同决定标注召回率与精度平衡。实测在工业螺丝检测场景下的调节规律conf_thresholdiou_threshold效果适用场景0.30.45检出12个螺丝2个漏检遮挡0个误检标准作业0.150.3检出18个螺丝含4个虚警反光伪影高召回需求0.50.6检出8个螺丝全为真阳性但漏检5个高精度质检调节原则先固定iou_threshold0.45将conf_threshold从0.5逐步下调至0.2观察画布中框的数量变化若出现密集重叠框如多个框套同一物体再降低iou_threshold至0.3~0.4。禁止将conf_threshold设为0.01——YOLOv8在极低置信度下会输出大量噪声框导致后续训练时loss震荡。4.2 自定义模型接入ONNX格式转换与输入尺寸对齐当使用自定义YOLOv5模型时必须确保ONNX输入尺寸与PyQt5图像加载逻辑一致# 在 inference/custom_onnx.py 中preprocess方法必须匹配 def preprocess(self, image: np.ndarray) - torch.Tensor: # 项目默认图像缩放为640x640YOLOv8n若模型训练用416x416此处必须对齐 img_resized cv2.resize(image, (416, 416)) # 关键否则坐标映射错误 img_rgb cv2.cvtColor(img_resized, cv2.COLOR_BGR2RGB) img_norm img_rgb.astype(np.float32) / 255.0 return torch.from_numpy(img_norm).permute(2, 0, 1).unsqueeze(0)ONNX导出命令YOLOv5# 在yolov5目录下执行 python export.py --weights yolov5s_custom.pt --include onnx --img 416 --batch 1 # 生成yolov5s_custom.onnx将其复制到models/目录并修改settings.json提示ONNX模型若含Resize算子常见于动态shape导出PyQt5中onnxruntime.InferenceSession可能报错This is an invalid model. Type Error: Type tensor(float) of input parameter ...。解决方案用onnx-simplifier简化模型——pip install onnx-simplifier python -m onnxsim yolov5s_custom.onnx yolov5s_custom_sim.onnx。4.3 标注后数据集验证自动化检查脚本编写项目未提供数据集校验工具但可快速编写validate_labels.pyimport os import cv2 from pathlib import Path def validate_yolo_dataset(img_dir: str, label_dir: str): img_paths list(Path(img_dir).glob(*.jpg)) list(Path(img_dir).glob(*.png)) for img_path in img_paths: label_path Path(label_dir) / f{img_path.stem}.txt if not label_path.exists(): print(fMISSING LABEL: {img_path.name}) continue img cv2.imread(str(img_path)) h, w img.shape[:2] with open(label_path) as f: for i, line in enumerate(f): parts line.strip().split() if len(parts) ! 5: print(fINVALID FORMAT {label_path}:{i1} - {line.strip()}) continue try: cx, cy, bw, bh map(float, parts[1:]) # 检查归一化坐标越界 if not (0cx1 and 0cy1 and 0bw1 and 0bh1): print(fCOORD OUT OF RANGE {label_path}:{i1} - {parts[1:]}) except ValueError: print(fNON-NUMERIC {label_path}:{i1}) if __name__ __main__: validate_yolo_dataset(datasets/images, datasets/labels)运行后输出MISSING LABEL: 000012.jpg即提示该图无对应TXT文件需重新标注输出COORD OUT OF RANGE则说明模型输出坐标异常应检查cv2_bbox_to_yolo函数中max(0,min(w,...))边界处理是否生效。5. 高效标注工作流优化批量处理与错误恢复技巧5.1 批量自动标注的静默模式实现GUI界面一次只能处理单图但实际项目常需标注数百张。项目预留了命令行接口在anylabeling/cli.py中# 批量标注整个目录无GUI纯终端 python -m anylabeling.cli --input-dir datasets/images --output-dir datasets/labels --model-path models/yolov8n.pt --conf 0.25该命令调用InferenceEngine批量加载图像跳过GUI渲染速度提升3.8倍实测100张图耗时42s vs GUI模式161s。关键参数说明--input-dir必须为绝对路径相对路径会导致os.listdir()读取失败--output-dir自动创建目录若存在同名TXT文件则覆盖无警告--conf等价于settings.json中的conf_threshold优先级更高提示若批量处理中途崩溃如显存不足已生成的TXT文件不会被删除。可记录最后成功处理的文件名用find datasets/images -name *.jpg | sed -n /000087.jpg/,$p | head -n 50 | xargs -I{} python -m anylabeling.cli --input-dir datasets/images --output-dir datasets/labels --model-path models/yolov8n.pt --conf 0.25 --image {}续跑。5.2 标注错误的快速修正ROI裁剪与局部重标当某张图出现大面积误检如背景纹理被识别为物体不必整图重标。利用PyQt5画布的ROI功能按住Ctrl键 鼠标左键拖拽框选疑似误检区域右键 →Crop Auto Label→ 弹窗提示“Crop region to 256x256 and run inference”系统自动裁剪、缩放、推理将结果坐标映射回原图位置并合并标注此功能依赖utils/crop_utils.py中的crop_and_infer函数其核心是计算裁剪区域在原图中的偏移量def crop_and_infer(image: np.ndarray, crop_bbox: List[int], model: BaseModel) - List[Dict]: x1, y1, x2, y2 crop_bbox cropped image[y1:y2, x1:x2] # 注意numpy索引顺序 results model.predict(cropped) # 将结果坐标平移回原图坐标系 for r in results: r[bbox][0] x1 # x1 r[bbox][1] y1 # y1 r[bbox][2] x1 # x2 r[bbox][3] y1 # y2 return results实测对1920x1080图像中300x300的误检区域局部重标耗时仅0.8s比整图重标快12倍。5.3 模型热更新无需重启GUI的权重替换当训练新模型后传统做法需关闭GUI、替换.pt文件、重启程序。本项目支持热更新将新权重yolov8n_v2.pt放入models/目录在GUI中点击Settings → Reload Model齿轮图标旁刷新按钮状态栏显示Model reloaded: yolov8n_v2.pt (24.7MB)立即生效该功能由app/main_window.py中reload_model_action触发其本质是销毁旧InferenceEngine实例并新建def reload_model(self): if self.engine: self.engine.unload() # 显式释放GPU显存 self.engine InferenceEngine(model_pathself.current_model_path) self.statusBar().showMessage(fModel reloaded: {os.path.basename(self.current_model_path)} ({os.path.getsize(self.current_model_path)/1024/1024:.1f}MB))注意engine.unload()调用torch.cuda.empty_cache()若省略此步连续热更新3次后显存占用会累积增长最终OOM。本文还有配套的精品资源点击获取
返回列表