自动标注工具 Autolabelimg
原理简介~~
对于数据量较大的数据集,先对其中一部分图片打标签,Autolabelimg利用已标注好的图片进行训练,并利用训练得到的权重对其余数据进行自动标注,然后保存为xml文件。
一、下载yolov5v6.1
https://github.com/ultralytics/yolov5

并对已标注的数据集进行训练,得到权重best.pt
二、将detect_auto.py文件放入yolov5根目录下
import sys
from utils.torch_utils import select_device
from models.common import DetectMultiBackend
from utils.datasets import *
import torch
from utils.general import (check_img_size, non_max_suppression, scale_coords)
from tkinter import *
from tkinter import filedialog
import natsort
# FILE = Path(__file__).resolve()
# ROOT = FILE.parents[0] # YOLOv5 root directory
# if str(ROOT) not in sys.path:
# sys.path.append(str(ROOT)) # add ROOT to PATH
# ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relativeimport os
from os import getcwd
from xml.etree import ElementTree as ETclass Detector():def __init__(self):self.objectList = []self.weights = r"" # 可去官方下载v5 6.0的预训练权重模型self.dnn = Falseself.data = r"" # 选择你的配置文件(一般为.yaml)self.device = select_device(device='0')self.half = self.device.type != 'cpu' # 半精度化self.predefined_classes = []self.imgdir = r"" # 你需要标注的图片文件夹self.outdir = r"" # 你需要保存的xml文件夹self.detect_class = r"" # 你需要自动标注的类型self.root_window = Noneself.flag = False@torch.no_grad()def run(self,frame,model,device,half=False,img_size=None,augment=False,visualize=False,max_det=1000):if img_size is None:img_size = [640, 640]img0 = framestride, names, pt, jit, onnx, engine = model.stride, model.names, model.pt, model.jit, model.onnx, model.engineimg_size = check_img_size(img_size, s=stride)# Halfhalf &= (pt or jit or engine) and device.type != 'cpu' # half precision only supported by PyTorch on CUDAif pt or jit:model.model.half() if half else model.model.float()img = letterbox(img0, auto=True, new_shape=img_size, stride=32)[0]img = img.transpose((2, 0, 1))[::-1] # HWC to CHW, BGR to RGBimg = np.ascontiguousarray(img)# model.warmup(imgsz=(1, 3, *img_size), half=half) # warmupimg = torch.from_numpy(img).to(device)img = img.half() if half else img.float() # uint8 to fp16/32img /= 255 # 0 - 255 to 0.0 - 1.0if len(img.shape) == 3:img = img[None] # expand for batch dimpred = model(img, augment=augment, visualize=visualize)pred = non_max_suppression(pred, max_det=max_det)for i, det in enumerate(pred):if det is not None and len(det):det[:, :4] = scale_coords(img.shape[2:], det[:, :4], img0.shape).round()info_list = []for *xyxy, conf, cls in reversed(det):xyxy = torch.tensor(xyxy).view(-1).tolist()info = [xyxy[0], xyxy[1], xyxy[2], xyxy[3], int(cls)]info_list.append(info)return info_listelse:return Nonedef create_annotation(self, xn):global annotationtree = ET.ElementTree()tree.parse(xn)annotation = tree.getroot()# 遍历xml里面每个object的值如果相同就不插入def traverse_object(self, AnotPath):tree = ET.ElementTree(file=AnotPath)root = tree.getroot()ObjectSet = root.findall('object')for Object in ObjectSet:ObjName = Object.find('name').textBndBox = Object.find('bndbox')x1 = int(BndBox.find('xmin').text)y1 = int(BndBox.find('ymin').text)x2 = int(BndBox.find('xmax').text)y2 = int(BndBox.find('ymax').text)self.objectList.append([x1, y1, x2, y2, ObjName])# 定义一个创建一级分支object的函数def create_object(self, root, objl): # 参数依次,树根,xmin,ymin,xmax,ymax# 创建一级分支object_object = ET.SubElement(root, 'object')# 创建二级分支name = ET.SubElement(_object, 'name')# print(obj_name)name.text = str(objl[4])pose = ET.SubElement(_object, 'pose')pose.text = 'Unspecified'truncated = ET.SubElement(_object, 'truncated')truncated.text = '0'difficult = ET.SubElement(_object, 'difficult')difficult.text = '0'# 创建bndboxbndbox = ET.SubElement(_object, 'bndbox')xmin = ET.SubElement(bndbox, 'xmin')xmin.text = '%s' % objl[0]ymin = ET.SubElement(bndbox, 'ymin')ymin.text = '%s' % objl[1]xmax = ET.SubElement(bndbox, 'xmax')xmax.text = '%s' % objl[2]ymax = ET.SubElement(bndbox, 'ymax')ymax.text = '%s' % objl[3]# 创建xml文件的函数def create_tree(self, image_name, h, w, imgdir):global annotation# 创建树根annotationannotation = ET.Element('annotation')# 创建一级分支folderfolder = ET.SubElement(annotation, 'folder')# 添加folder标签内容folder.text = (imgdir)# 创建一级分支filenamefilename = ET.SubElement(annotation, 'filename')filename.text = image_name# 创建一级分支pathpath = ET.SubElement(annotation, 'path')# path.text = getcwd() + '\{}\{}'.format(imgdir, image_name) # 用于返回当前工作目录path.text ='{}/{}'.format(imgdir, image_name) # 用于返回当前工作目录# 创建一级分支sourcesource = ET.SubElement(annotation, 'source')# 创建source下的二级分支databasedatabase = ET.SubElement(source, 'database')database.text = 'Unknown'# 创建一级分支sizesize = ET.SubElement(annotation, 'size')# 创建size下的二级分支图像的宽、高及depthwidth = ET.SubElement(size, 'width')width.text = str(w)height = ET.SubElement(size, 'height')height.text = str(h)depth = ET.SubElement(size, 'depth')depth.text = '3'# 创建一级分支segmentedsegmented = ET.SubElement(annotation, 'segmented')segmented.text = '0'def pretty_xml(self, element, indent, newline, level=0): # elemnt为传进来的Elment类,参数indent用于缩进,newline用于换行if element: # 判断element是否有子元素if (element.text is None) or element.text.isspace(): # 如果element的text没有内容element.text = newline + indent * (level + 1)else:element.text = newline + indent * (level + 1) + element.text.strip() + newline + indent * (level + 1)# else: # 此处两行如果把注释去掉,Element的text也会另起一行# element.text = newline + indent * (level + 1) + element.text.strip() + newline + indent * leveltemp = list(element) # 将element转成listfor subelement in temp:if temp.index(subelement) < (len(temp) - 1): # 如果不是list的最后一个元素,说明下一个行是同级别元素的起始,缩进应一致subelement.tail = newline + indent * (level + 1)else: # 如果是list的最后一个元素, 说明下一行是母元素的结束,缩进应该少一个subelement.tail = newline + indent * levelself.pretty_xml(subelement, indent, newline, level=level + 1) # 对子元素进行递归操作def work(self):with open(self.detect_class, "r") as f: # 打开文件for line in f.readlines():line = line.strip('\n') # 去掉列表中每一个元素的换行符self.predefined_classes.append(line)model = DetectMultiBackend(self.weights, device=self.device, dnn=self.dnn, data=self.data)model.to(self.device).eval()names = model.module.names if hasattr(model, 'module') else model.namesIMAGES_LIST = os.listdir(self.imgdir)for image_name in natsort.natsorted(IMAGES_LIST):# print(image_name)# 判断后缀只处理图片文件if image_name.endswith(('.jpg', '.png', '.jpeg', '.bmp')):image = cv2.imread(os.path.join(self.imgdir, image_name))if image is None:print(image_name+"图像为空请删除")continuefile_tail = os.path.splitext(image_name)[1]coordinates_list = self.run(image, model, self.device, self.half)(h, w) = image.shape[:2]# xml_name = ('.\{}\{}.xml'.format(outdir, image_name.strip('.jpg')))xml_name = ('{}\{}.xml'.format(self.outdir, image_name.strip('.jpg')))if (os.path.exists(xml_name)):self.create_annotation(xml_name)self.traverse_object(xml_name)else:self.create_tree(image_name, h, w, self.imgdir)if coordinates_list:print(image_name+"已标注完成")for coordinate in coordinates_list:label_id = coordinate[4]if (self.predefined_classes.count(names[label_id]) > 0):object_information = [int(coordinate[0]), int(coordinate[1]), int(coordinate[2]),int(coordinate[3]), names[label_id]]if (self.objectList.count(object_information) == 0):self.create_object(annotation, object_information)self.objectList = []# 将树模型写入xml文件tree = ET.ElementTree(annotation)root = tree.getroot()self.pretty_xml(root, '\t', '\n')# tree.write('.\{}\{}.xml'.format(outdir, image_name.strip('.jpg')), encoding='utf-8')tree.write('{}\{}.xml'.format(self.outdir, image_name.strip(file_tail)), encoding='utf-8')else:print(image_name)# 客户端def client(self):def creatWindow():self.root_window.destroy()window()def judge(str):if (str):text = "你已选择" + strelse:text = "你还未选择文件夹,请选择"return textdef test01():self.imgdir = r""self.imgdir += filedialog.askdirectory()creatWindow()def test02():self.outdir = r""self.outdir += filedialog.askdirectory()creatWindow()def test03():self.data = r""self.data += filedialog.askopenfilename()creatWindow()def test04():self.weights = r""self.weights += filedialog.askopenfilename()creatWindow()def test05():self.detect_class = r""self.detect_class += filedialog.askopenfilename()creatWindow()def tes06():self.work()self.flag=TruecreatWindow()def window():self.root_window = Tk()self.root_window.title("")screenWidth = self.root_window.winfo_screenwidth() # 获取显示区域的宽度screenHeight = self.root_window.winfo_screenheight() # 获取显示区域的高度tk_width = 500 # 设定窗口宽度tk_height = 400 # 设定窗口高度tk_left = int((screenWidth - tk_width) / 2)tk_top = int((screenHeight - tk_width) / 2)self.root_window.geometry('%dx%d+%d+%d' % (tk_width, tk_height, tk_left, tk_top))self.root_window.minsize(tk_width, tk_height) # 最小尺寸self.root_window.maxsize(tk_width, tk_height) # 最大尺寸self.root_window.resizable(width=False, height=False)btn_1 = Button(self.root_window, text='请选择你要标注的图片文件夹', command=test01,height=0)btn_1.place(x=169, y=40, anchor='w')text = judge(self.imgdir)text_label = Label(self.root_window, text=text)text_label.place(x=160, y=70, anchor='w')btn_2 = Button(self.root_window, text='请选择你要保存的xml文件夹(.xml)', command=test02,height=0)btn_2.place(x=169, y=100, anchor='w')text = judge(self.outdir)text_label = Label(self.root_window, text=text)text_label.place(x=160, y=130, anchor='w')btn_3 = Button(self.root_window, text='请选择你的配置文件(.yaml)', command=test03,height=0)btn_3.place(x=169, y=160, anchor='w')text = judge(self.data)text_label = Label(self.root_window, text=text)text_label.place(x=160, y=190, anchor='w')# if(self.outdir and self.imgdir and self.data):btn_4 = Button(self.root_window, text='请选择使用的模型(.pt)', command=test04,height=0)btn_4.place(x=169, y=220, anchor='w')text = judge(self.weights)text_label = Label(self.root_window, text=text)text_label.place(x=160, y=250, anchor='w')btn_5 = Button(self.root_window, text='请选择需要自动标注的类别文件(.txt)', command=test05,height=0)btn_5.place(x=169, y=280, anchor='w')text = judge(self.detect_class)text_label = Label(self.root_window, text=text)text_label.place(x=160, y=310, anchor='w')btn_6 = Button(self.root_window, text='开始自动标注', command=tes06,height=0)btn_6.place(x=169, y=340, anchor='w')if (self.flag):text = "标注完成"else:text = "等待标注"text_label = Label(self.root_window, text=text)text_label.place(x=160, y=370, anchor='w')self.root_window.mainloop()window()if __name__ == '__main__':detector = Detector()detector.client()
三、运行detect_auto.py文件

- 建议将要标注的图片文件和标注完的.xml文件放在同一文件夹下,这样方便使用labelimg查看标注效果;
- 配置文件即为训练时data文件夹下对应的.yaml文件;
- 使用的模型为训练好的权重文件(best.pt);
- 在根目录下创建class.txt文件,里面为数据集所包含的类别;

- 点击开始自动标注;


tips:自动标注的质量不是特别高,如果对标注质量有要求的话还需手动进行调整
报错处理:改动utils/torch_utils.py文件中第55行如下


reference
【解放双手YOLOv5 6.0自动标注(已开源)】 https://www.bilibili.com/video/BV1TZ4y1S76W/?p=2&share_source=copy_web&vd_source=95705b32f23f70b32dfa1721628d5874
相关文章:
自动标注工具 Autolabelimg
原理简介~~ 对于数据量较大的数据集,先对其中一部分图片打标签,Autolabelimg利用已标注好的图片进行训练,并利用训练得到的权重对其余数据进行自动标注,然后保存为xml文件。 一、下载yolov5v6.1 https://github.com/ultralytic…...
2023-03-20干活
transformer复现 from torch.utils.data import Dataset,DataLoader import numpy as np import torch import torch.nn as nn import os import time import math from tqdm import tqdmdef get_data(path,numNone):all_text []all_label []with open(path,"r",e…...
Java 注解(详细学习笔记)
注解 注解英文为Annotation Annotation是JDK5引入的新的技术 Annotation的作用: 不是程序本身,可以对程序做出解释可以被其他程序(比如编译器)读取。 Annotation的格式: 注解是以注解名在代码中存在的,还…...
LeetCode:35. 搜索插入位置
🍎道阻且长,行则将至。🍓 🌻算法,不如说它是一种思考方式🍀算法专栏: 👉🏻123 一、🌱35. 搜索插入位置 题目描述:给定一个排序数组和一个目标值&…...
菜鸟刷题Day2
菜鸟刷题Day2 一.判定是否为字符重排:字符重排 描述 给定两个由小写字母组成的字符串 s1 和 s2,请编写一个程序,确定其中一个字符串的字符重新排列后,能否变成另一个字符串。 解题思路: 这题思路与昨天最后两道类似&…...
Selenium基础篇之不打开浏览器运行
文章目录前言一、场景二、设计1.引入库2.引入浏览器配置3.设置无头模式4.启动浏览器实例,添加配置信息5.访问质量分地址6.隐式等待5秒7.定位到输入框8.输入博文地址9.定位到查询按钮10.点击查询按钮11.定位到查询结果模块div12.打印结果13.结束webdriver进程三、结果…...
【数据结构初阶】栈与队列笔试题
前言在我们学习了栈和队列之后,今天来通过几道练习题来巩固一下我们的知识。题目一 用栈实现队列题目链接:232. 用栈实现队列 - 力扣(Leetcode)这道题难度不是很大,重要的是我们对结构认识的考察,由于这篇文…...
【Linux入门篇】操作系统安装、网络配置
目录 🍁Linux详解 🍂1.操作系统 🍂2.操作系统组成 🍂3.操作系统历史 🍂4.常见的Linux系统 🍂5.centos7下载 🍂6.安装centos7 🍁linux初始化配置 🍃1.虚拟机系统安装后操作…...
Selenium:找不到对应的网页元素?常见的一些坑
目录 1. 用Xpath查找数据时无法直接获取节点属性 2. 使用了WebDriverWait以后仍然无法找到元素 2.1. 分辨率原因 2.2. 需要滚动页面 2.3. 由于其他元素的遮挡 1. 用Xpath查找数据时无法直接获取节点属性 通常在我们使用xpath时,可以使用class的方式直接获取节…...
flex布局优化(两端对齐,从左至右)
文章目录前言方式一 nth-child方式二 gap属性方式三 设置margin左右两边为负值总结前言 flex布局是前端常用的布局方式之一,但在使用过程中,我们总是感觉不太方便,因为日常开发中,大多数时候,我们想要的效果是这样的 …...
【Django 网页Web开发】03. 初识Django(保姆级图文)
目录1. 命令行创建与pycharm创建的区别2. 项目结构信息2.1 项目结构2.2 项目app结构2.3 快速查看项目结构树3. 创建并注册app3.1 创建app3.2 注册app4. 编写URL与视图的对应关系5. 编写视图文件6. 启动项目7. 写多个页面8. templates模板的使用8.1 编写html文件8.3 导入html文件…...
KubeSphere All in one安装配置手册
KubeSphere All in one安装配置手册 1. 初始化 1.1 配置apt源 # vi /etc/apt/sources.list deb https://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse deb-src https://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiversedeb…...
Spring Boot 核心配置文件
Spring Boot 核心配置文件1、application.properties2、application.yml使用建议3、常用配置项服务器配置数据库配置日志配置其他配置4、配置文件的加载顺序5、配置文件的占位符6、配置文件的动态刷新7、配置文件的属性分组定义属性分组绑定属性分组使用属性分组总结Spring Boo…...
个人小站折腾后记
个人小站折腾后记 🏠个人主页:shark-Gao 🧑个人简介:大家好,我是shark-Gao,一个想要与大家共同进步的男人😉😉 🎉目前状况:23届毕业生,目前在某…...
WebService简单入门
1. JAX-WS发布WebService 创建web工程 创建simple包,和server、client两个子包。正常情况下server和client应该是两个项目,这里我们只是演示效果,所以简化写到一个项目中: 1.1 创建服务类Server package simple.server;import ja…...
「Vue面试题」vue要做权限管理该怎么做?如果控制到按钮级别的权限怎么做?
文章目录一、是什么二、如何做接口权限路由权限控制菜单权限方案一方案二按钮权限方案一方案二小结参考文章一、是什么 权限是对特定资源的访问许可,所谓权限控制,也就是确保用户只能访问到被分配的资源 而前端权限归根结底是请求的发起权,…...
Docker部署springcloud项目(清晰明了)
概述 最近在想做个cloud项目,gitee上找了个模板项目,后端使用到 Nacos、Gateway、Security等技术,需要到 Docker 容器部署,在此总结一下,若有不足之处,望大佬们可以指出。 什么是 Docker Docker 使用 Google 公司推…...
搭建SFTP服务安全共享文件,实现在外远程访问「内网穿透」
文章目录1.前言2.本地SFTP服务器搭建2.1.SFTP软件的下载和安装2.2.配置SFTP站点2.3.Cpolar下载和安装3.SFTP服务器的发布3.1.Cpolar云端设置3.2.Cpolar本地设置4.公网访问测试5.结语1.前言 现在的网络发达,个人电脑容量快速上升,想要保存的数据资料也越…...
ChatGPT优化Python代码的小技巧
使用 chatGPT 优化代码并降低运行时的云成本 许多开发人员说“过早的优化是万恶之源”。 这句话的来源归功于Donald Knuth。在他的书《计算机编程的艺术》中,他写道: “真正的问题是,程序员在错误的时间和错误的地方花费了太多时间来担心效率…...
Stm32-使用TB6612驱动电机及编码器测速
这里写目录标题起因一、电机及编码器的参数二、硬件三、接线四、驱动电机1、TB6612电机驱动2、定时器的PWM模式驱动电机五、编码器测速1、定时器的编码器接口模式2、定时器编码器模式测速的原理3、编码器模式的配置4、编码器模式相关代码5、测速方法六、相关问题以及解答1、编码…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问(基础概念问题) 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...
游戏开发中常见的战斗数值英文缩写对照表
游戏开发中常见的战斗数值英文缩写对照表 基础属性(Basic Attributes) 缩写英文全称中文释义常见使用场景HPHit Points / Health Points生命值角色生存状态MPMana Points / Magic Points魔法值技能释放资源SPStamina Points体力值动作消耗资源APAction…...
linux设备重启后时间与网络时间不同步怎么解决?
linux设备重启后时间与网络时间不同步怎么解决? 设备只要一重启,时间又错了/偏了,明明刚刚对时还是对的! 这在物联网、嵌入式开发环境特别常见,尤其是开发板、树莓派、rk3588 这类设备。 解决方法: 加硬件…...
【Axure高保真原型】图片列表添加和删除图片
今天和大家分享图片列表添加和删除图片的原型模板,效果包括: 点击图片列表的加号可以显示图片选择器,选择里面的图片; 选择图片后点击添加按钮,可以将该图片添加到图片列表; 鼠标移入图片列表的图片&…...
JVM——对象模型:JVM对象的内部机制和存在方式是怎样的?
引入 在Java的编程宇宙中,“Everything is object”是最核心的哲学纲领。当我们写下new Book()这样简单的代码时,JVM正在幕后构建一个复杂而精妙的“数据实体”——对象。这个看似普通的对象,实则是JVM内存管理、类型系统和多态机制的基石。…...
IDEA中微服务指定端口启动
在使用IDEA开发SpringBoot微服务时,经常需要开启多个服务实例以测试负载均衡,以下几种方法开启不同端口。 直接在配置文件中指定 # application.propertiesserver.port8001指定VM参数 点击Modify options,选择Add VM options,值…...
