拓冰建站拓冰建站
首页 / 资讯中心 / 正文

目标检测数据标注利器:Python小工具自动生成YOLO正样本描述

简介这是一份面向计算机视觉初学者与模型训练者的正样本标注工具objectmarker源码包解决自制物体识别数据集中缺少标注工具的痛点。用户通过鼠标框选图像目标区域程序自动记录边界框坐标并写入info.txt便于后续训练YOLO、SSD等检测模型源码基于OpenCV实现鼠标事件处理与图像显示适合学习交互式标注逻辑和OpenCV基础API。整个压缩包约1.01MB共10个文件主要包含可直接运行的exe程序、OpenCV相关dll动态库、cpp源码、示例bmp图像、说明文档与结果txt文件结构与用途一目了然。资源当前已有289人学习下载比较适合希望摆脱通用标注工具、快速搭建自定义检测数据集的学习者。通过这份资源可以掌握从图像加载、鼠标框选、空格键记录到回车退出的完整标注流程同时理解info.txt中文件名与左上/右下坐标的存储格式为后续数据预处理和数据增强等模型优化工作打下基础。 做目标检测训练的同学应该都知道数据标注有多烦。尤其是准备正样本时既要圈出目标又要确保标注文件里的坐标和类别一一对应稍不留神就把某个框的归一化坐标写错训练时各种报错。我之前在做一个检测小项目的时候就深受其害于是自己用 Python 写了一个小工具取名叫objectmarker专门用来快速生成正样本的描述文件日常项目里已经用得很顺手了。这个东西本质上就是一个轻量级的标注工具核心能力是打开一张图片按住鼠标拖拽画出目标框释放之后自动生成 YOLO 格式的标注描述文本同时支持类别切换、撤销回退、下一张继续标。所有代码我放在了开源仓库里结构很简单依赖很少想改的人可以直接拿去改成自己的标注工具。这篇文章我就从设计思路、源码实现到实际操作完整拆一遍自己做它的过程顺便把踩过的坑也一并倒出来。1. 为什么需要一个生成正样本描述的小工具1.1 正样本描述到底在描述什么很多刚接触目标检测的人会混淆“标注”和“描述”这两个概念。标注通常是人工在图像上画框、给标签这是第一步而“描述”指的是把标注的结果转成模型训练时能消费的文本格式例如 YOLO 的class_id x_center y_center width height其中坐标是相对图片宽高的归一化数值。 objectmarker 做的就是把这套“画框 → 生成描述文本”的流程一体化画完即生成不用再手动算坐标也不用到标注软件里再导出一次。1.2 现成工具很多为什么还要自己写LabelImg、LabelMe 这些工具确实功能完善但在我实际使用中总有几个痛点一是启动慢动不动要配置 Python 环境或 Qt 依赖二是画框之后需要手动确认保存连续标注几百张图的时候效率不高三是坐标格式和自定义类别表需要额外设置如果只是临时做一个简单数据集反而觉得配置成本有点高。 objectmarker 的设计目标就很明确——不搞复杂功能只解决“批量生成正样本描述”这一个核心需求脚本到一个目录就能跑还能随时改源码。1.3 这个项目适合谁参考如果你正在做入门级的目标检测实验或者需要一个极简的标注代码作为学习参考objectmarker 是一个不错的起点。它的源码只有几百行主要用到 OpenCV 读取图像、Tkinter 做界面和文本输出逻辑非常直接。你可以在此基础上加“自动保存”、“多边形标注”、“类别联想”等功能。对于那些只想快速生成几百个正样本描述文件来做训练验证的人来说它比大型标注软件更轻也更容易理解背后的实现原理。2. 源码核心模块与实现思路2.1 界面交互用 OpenCV 窗口还是 Tkinter写这个工具时我最纠结的是界面用哪个方案。OpenCV 的窗口虽然能显示图像但鼠标响应事件不够灵活而且按钮、列表这些组件都得自己画。Tkinter 则是 Python 自带的 GUI 库放按钮、下拉框都很方便但直接在它上面画图像性能又一般。后来我干脆做了个折中用 Tkinter 做主界面左侧放功能按钮和类别列表右侧用 Label 组件绑定 OpenCV 转换后的图像数据然后通过绑定鼠标事件获取画框的起点和终点。这样做的好处是界面逻辑和图像处理的分离非常干净鼠标事件负责记录坐标OpenCV 只负责图像缩放和画框显示描述文本的生成则是独立的函数不依赖界面层。如果你也想改造成 Web 版或者 PyQt 版只需保留画框坐标收集和描述生成的核心部分前端怎么换都行。2.2 描述文件生成的核心逻辑模型训练需要的坐标是归一化的也就是x_center / img_widthbox_width / img_width这样的比例。手动操作最痛苦的就是每个框都要拿计算器除一遍尤其图片分辨率不统一时更是灾难。 objectmarker 里的generate_description函数就专门干这事传入图片宽高和矩形框的左上角、右下角坐标自动计算出中心点、宽高并做归一化最终拼接成一行文本写入.txt文件。这里我贴一下核心代码片段def generate_description(class_id, x1, y1, x2, y2, img_w, img_h): # 保证坐标顺序避免反向框 x1, x2 min(x1, x2), max(x1, x2) y1, y2 min(y1, y2), max(y1, y2) box_w x2 - x1 box_h y2 - y1 # 归一化中心点和宽高 x_center (x1 x2) / 2.0 / img_w y_center (y1 y2) / 2.0 / img_h norm_w box_w / img_w norm_h box_h / img_h # 裁剪到 [0,1] 区间防止越界 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) norm_w min(max(norm_w, 0.0), 1.0) norm_h min(max(norm_h, 0.0), 1.0) return f{class_id} {x_center:.6f} {y_center:.6f} {norm_w:.6f} {norm_h:.6f}这段代码有几个值得注意的细节坐标排序很重要否则会在某些脚本里造成负宽度而归一化后的数值必须裁剪因为鼠标拖拽偶尔会超出图像边界不处理的话描述文件里就出现大于 1 的坐标模型训练时极容易报错。2.3 类别管理的实现方式正样本描述的第一列是类别 ID。 objectmarker 里用一个简单的列表来维护类别字典界面上的下拉框跟字典的 key 对应选择不同类别时实际生成文本的 class_id 也跟着变化。我在源码里预设了一个简单的类别文件格式是单行一个类名例如person car dog启动时自动加载。如果项目类别很多直接在文本里增删即可不需要改代码。同理如果想支持 COCO 格式或者 VOC 格式只要在保存描述时替换输出函数即可核心的坐标计算逻辑完全复用。这也是源码解耦设计的价值所在。3. 从零开始实操快速生成第一批正样本描述3.1 环境准备与启动运行 objectmarker 不需要复杂环境我用的是 Python 3.8 以上版本依赖只有opencv-python和numpy。Tkinter 是标准库装好 Python 后一般自带不需要额外安装。启动过程大概是这样git clone https://github.com/yourname/objectmarker.git cd objectmarker pip install opencv-python numpy python main.py --img_dir ./images --output_dir ./labels--img_dir是你的原始图片目录--output_dir是描述文件输出目录。启动后会自动读取图片列表并在左上角显示当前图片序号和文件名。3.2 标准标注流程打开界面后整个标注流程被刻意设计成“三步走”。第一步在右侧图片上按住鼠标左键拖拽会出现一个实时变化的矩形框框的正上方会显示当前画框的类别第二步拖到合适位置松开鼠标这个框就被记录到当前图片的框列表里同时在图片上画出来第三步如果框画错了按 CtrlZ 回退上一个框确认无误后点击“保存当前图片”就会生成对应的 txt 描述文件然后自动切换到下一张图。我使用下来感觉这个流程最顺畅的顺序是先设定当前要标注的类别然后连续画同类的目标等一批同类目标全部标注完再切换类别。因为界面上切换类别需要点一下下拉框如果每画一个框都切一次效率会低很多。比如标注街景里的车和行人我会先把所有车框完再切到行人类别继续画。3.3 自动输出的描述文件长什么样当你保存一张图片后生成的文件与图片同名但扩展名是.txt。例如image_001.jpg会对应image_001.txt文件里每一行是一个目标描述。一个包含两个行人和一辆车的图片描述文件内容如下0 0.512345 0.678901 0.123455 0.245678 0 0.823456 0.432109 0.098765 0.187654 1 0.643210 0.567890 0.234567 0.312345如果类别表第一个是car第二个是person那第一行就表示一个汽车框第二行和第三行都是行人框。把这样的 txt 文件放到 YOLO 训练目录中与图片一一对应模型就能正确读取正样本信息了。4. 常见问题与排查技巧实录4.1 标注框位置总是偏了一点刚开始用的时候我发现画出来的框总感觉比鼠标位置偏了大概几个像素后来发现不是代码逻辑问题而是 Tkinter 中 Label 组件显示的图像和原始图像的坐标系不一样。图像在界面上被缩放显示鼠标事件拿到的坐标是相对于 Label 控件的像素坐标必须按缩放比例换算回原始图片坐标否则就会出现偏移。解决方法是记录原始图像宽高和显示宽高的比例在鼠标事件里做一次坐标换算。这个问题的典型表现形式就是“框和鼠标对不上”很多人以为是 OpenCV 显示的问题。其实核心就是坐标系变换没有处理。源码中我专门写了一个to_original_coords函数专门负责这件事也给后来者省了不少排查力气。4.2 保存时提示图片不存在或读取失败在批量标注过程中偶尔会遇到个别图片文件损坏或路径中含中文导致 OpenCV 读取失败。OpenCV 的imread不支持中文路径是历史遗留问题这在 Windows 系统上特别常见。我的解决办法是判断读取结果是否为None若是则用PIL读取再转成 OpenCV 格式或者提示用户当前文件无法读取并自动跳到下一张。如果你的图片目录里可能有非 ASCII 字符路径建议做好这个兜底逻辑。另外有些很小的图片在标注时会有显示问题因为 mos 窗口里为了适配界面长度和宽度会被拉伸导致视觉上物体变形。我建议实际训练时保持数据集图片在 640x640 左右过小的图可以先用脚本做统一缩放标注时更不容易看走眼。4.3 生成的描述文件坐标偶尔出现负数或大于 1这种情况绝大多数是因为鼠标拖拽时超出了图像边界或者窗口缩放后没做坐标限制。我在生成描述的函数里已经加了min/max裁剪但最好在鼠标释放事件里也做一次边界限制。如果你是自己写的类似工具建议把坐标裁剪和越界提示放在同一个地方避免多个位置修改后逻辑打架。还有一点要特别注意某些模型训练框架会严格检查所有坐标都在[0,1]范围内一旦出现负数值训练直接中断而且报错信息不会告诉你具体是哪个文件。为了快速定位我建议在保存描述文件时顺便加一个简单的校验函数把所有越界值打印出来这样就能立刻找到问题图片。5. 从工具到生产力的扩展思路5.1 增加自动保存模式手动点击保存按钮总归是个高频交互。我在后来的版本里加了一个--auto_save参数开启后每切换下一张图片时自动保存当前图片的标注结果这样就减少了一次点击。如果你想实现类似功能只需要在“下一张”事件里调用保存函数即可逻辑非常容易集成。自动保存是一把双刃剑它提高了效率但也容易掩盖误操作。比如你标注到一半不小心按了快捷键“下一张”当前图片没有被完整标注就被保存了。因此在自动保存模式下我还额外加了一个“当前图片框数量为零则自动跳过保存”的判断防止生成空描述文件。5.2 描述文件可视化的快速校验写完一批标注后我最怕某张图错乱导致训练半天才发现。后来我写了一个简单的校验脚本读取图片和对应的 txt 描述把每个框重新画回图像上保存成一张check_xxx.jpg。用眼睛扫一遍校验图就能快速发现漏标、错位的问题。这个脚本也被我顺手放到了仓库的utils/目录下非常简单但非常实用。5.3 加入半自动辅助标注如果你的场景比较单一比如始终在检测同一类零件其实可以引入一个简单的目标检测模型做预标注然后用 objectmarker 人工修正。这样做不需要引入复杂的标注后台只需要在代码中加一个--pretrained_model参数加载模型推理得到的候选框自动投放到界面上人工只需检查调整。这套方案我后来在一个小样本项目中用过效率大约提升了 40%而且错误率比完全人工标注还低。半自动标注的前提是你的初始模型在目标场景里已经有一定的可用性否则投出来的框没有参考价值反而会影响人工判断。建议先用工具手动标注一两百张正样本训练一个粗糙的初始模型再回过来辅助标注剩余数据这样收益最大。6. 写在最后的实际体会我在实际项目里用 objectmarker 标注过上千张工业场景图片总体感觉是小工具解决大问题。它没有专业标注软件那么华丽但足够顺手尤其适合“快速验证一个检测 idea”的场景。从代码层面看它最大的价值并不是“功能多”而是结构清楚任何人都能改。我后来在多个项目里都复用了它的坐标计算和保存逻辑相当于把最核心、最易错的部分沉淀下来了。最后再分享一个小技巧如果你需要多人协作标注尽量让每个人的类别字典和图片命名规则保持一致否则合并数据时会出现类别 ID 错乱。别问我怎么知道的第一次合并团队标注数据的时候光对齐类别列表就花了一个下午。工具只是辅助规范流程才是不出错的关键。希望这个小项目能帮你少踩一些标注的坑。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门