执行代码:
绝对路径在Linux上
python detect.py --weights /root/user/yolov5/runs/train/exp40/weights/best.pt --source /root/user/yolov5/data/CowDetection02/test06
注:labelimg自动标注软件只支持YOLOv5s的训练模型和coco数据集中的80个类别。
首先自己标注小部分数据几十张也可以进行模型识别生成自己数据集的权重文件best.pt。然后,使用detect.py推理来完成大量照片的半自动化标注。恩非常完美很棒!如果有24级学弟学妹看到这里我想你应该已经帮我完成了剩下一半的半自动化标注了。
安装labelimg代码:
(base) C:\Users\ASUS>e:
python --version
(base) E:\>conda create -n labelimg python=3.8.19
(base) E:\> conda activate labelimg
(labelimg) E:\>pip install labelimg -i https://pypi.tuna.tsinghua.edu.cn/simple
(labelimg) E:\>labelimg
之前python版本是3.11结果安装完标注图像时会自动闪退,就降低虚拟环境下的python版本了。
step1:
在detect.py文件夹中修改下面两个参数:
--save-txt default=True 表述预测时保存label的 txt文件,会在runs/exp下生成一个labels
--nosave default=False表述对预测的图片进行保存 True则是不保存
--classes 修改为19表示只识别牛,其他目标不识别就是不标框。
step2:
如果你想将生成的txt转化为xml
# 将 txt 标签 文件转换为 xml 标签文件, 修改dict中的类,以及xml txt 和jpg 路径。
from xml.dom.minidom import Document
import os
import cv2
# 'person','head','helmet','lifejacket'
def makexml(txtPath,xmlPath,picPath): #读取txt路径,xml保存路径,数据集图片所在路径
dict = {'0': "person", #字典对类型进行转换,自己的标签的类。
'1': "head",
}
files = os.listdir(txtPath)
for i, name in enumerate(files):
xmlBuilder = Document()
annotation = xmlBuilder.createElement("annotation") # 创建annotation标签
xmlBuilder.appendChild(annotation)
txtFile=open(txtPath+name)
txtList = txtFile.readlines()
img = cv2.imread(picPath+name[0:-4]+".jpg")
Pheight,Pwidth,Pdepth=img.shape
for i in txtList:
oneline = i.strip().split(" ")
folder = xmlBuilder.createElement("folder")#folder标签
folderContent = xmlBuilder.createTextNode("VOC2007")
folder.appendChild(folderContent)
annotation.appendChild(folder)
filename = xmlBuilder.createElement("filename")#filename标签
filenameContent = xmlBuilder.createTextNode(name[0:-4]+".png")
filename.appendChild(filenameContent)
annotation.appendChild(filename)
size = xmlBuilder.createElement("size") # size标签
width = xmlBuilder.createElement("width") # size子标签width
widthContent = xmlBuilder.createTextNode(str(Pwidth))
width.appendChild(widthContent)
size.appendChild(width)
height = xmlBuilder.createElement("height") # size子标签height
heightContent = xmlBuilder.createTextNode(str(Pheight))
height.appendChild(heightContent)
size.appendChild(height)
depth = xmlBuilder.createElement("depth") # size子标签depth
depthContent = xmlBuilder.createTextNode(str(Pdepth))
depth.appendChild(depthContent)
size.appendChild(depth)
annotation.appendChild(size)
object = xmlBuilder.createElement("object")
picname = xmlBuilder.createElement("name")
nameContent = xmlBuilder.createTextNode(dict[oneline[0]])
picname.appendChild(nameContent)
object.appendChild(picname)
pose = xmlBuilder.createElement("pose")
poseContent = xmlBuilder.createTextNode("Unspecified")
pose.appendChild(poseContent)
object.appendChild(pose)
truncated = xmlBuilder.createElement("truncated")
truncatedContent = xmlBuilder.createTextNode("0")
truncated.appendChild(truncatedContent)
object.appendChild(truncated)
difficult = xmlBuilder.createElement("difficult")
difficultContent = xmlBuilder.createTextNode("0")
difficult.appendChild(difficultContent)
object.appendChild(difficult)
bndbox = xmlBuilder.createElement("bndbox")
xmin = xmlBuilder.createElement("xmin")
mathData=int(((float(oneline[1]))*Pwidth+1)-(float(oneline[3]))*0.5*Pwidth)
xminContent = xmlBuilder.createTextNode(str(mathData))
xmin.appendChild(xminContent)
bndbox.appendChild(xmin)
ymin = xmlBuilder.createElement("ymin")
mathData = int(((float(oneline[2]))*Pheight+1)-(float(oneline[4]))*0.5*Pheight)
yminContent = xmlBuilder.createTextNode(str(mathData))
ymin.appendChild(yminContent)
bndbox.appendChild(ymin)
xmax = xmlBuilder.createElement("xmax")
mathData = int(((float(oneline[1]))*Pwidth+1)+(float(oneline[3]))*0.5*Pwidth)
xmaxContent = xmlBuilder.createTextNode(str(mathData))
xmax.appendChild(xmaxContent)
bndbox.appendChild(xmax)
ymax = xmlBuilder.createElement("ymax")
mathData = int(((float(oneline[2]))*Pheight+1)+(float(oneline[4]))*0.5*Pheight)
ymaxContent = xmlBuilder.createTextNode(str(mathData))
ymax.appendChild(ymaxContent)
bndbox.appendChild(ymax)
object.appendChild(bndbox)
annotation.appendChild(object)
f = open(xmlPath+name[0:-4]+".xml", 'w')
xmlBuilder.writexml(f, indent='\t', newl='\n', addindent='\t', encoding='utf-8')
f.close()
makexml("runs/detect/exp/labels/", # txt文件夹
"runs/detect/exp/xmls/", # xml文件夹
"data/images/") # 图片数据文件夹
在YOLO中合并txt文件
import os
def concattxt(path1,path2):
num = 0
pathdir1 = os.listdir(path1)#获取第一部分txt文件夹中的文件列表
for txtname in pathdir1:
#name1 = os.path.splitext(txtname)[0]#获取当前txt文件名字
#txtfile1 = open(txtpath1+name1, "rb")
#txtfile2 = open(txtpath2+name1, "rb")
txtfilepath1 = path1+txtname
txtfilepath2 = path2+txtname
txt1 = open(txtfilepath1,'a+', encoding='utf-8')
if os.path.exists(txtfilepath2):
num = num+1
with open(txtfilepath2, 'r', encoding='utf-8') as txt2:
#txt1.write('\n')
for i in txt2:
txt1.write(i)
print('the concat txt num is:',num)
if __name__ == '__main__':
txtpath1 = '/home/jun/work/2021Z001/test/ann/'
txtpath2 = '/home/jun/work/2021Z001/test/labels/'
concattxt(txtpath1,txtpath2)
划分数据集:
import os
import random
import re
import shutil
def create_folders():
os.makedirs(r'F:\yoloslowfast\train01\images\train', exist_ok=True)
os.makedirs(r'F:\yoloslowfast\train01\images\val', exist_ok=True)
os.makedirs(r'F:\yoloslowfast\train01\labels\train', exist_ok=True)
os.makedirs(r'F:\yoloslowfast\train01\labels\val', exist_ok=True)
def split_data(path_images, path_labels, rate):
images_list = os.listdir(path_images)
for img_file in images_list:
img_id = re.match(r'(.*)\.(jpg|png)', img_file)
if img_id:
img_id = img_id.group(1)
label_file = img_id + '.txt'
prob = random.randint(1, 100)
if prob < rate:
src_img = os.path.join(path_images, img_file)
dst_img = os.path.join(r'F:\yoloslowfast\train01\images\train', img_file)
src_label = os.path.join(path_labels, label_file)
dst_label = os.path.join(r'F:\yoloslowfast\train01\labels\train', label_file)
else:
src_img = os.path.join(path_images, img_file)
dst_img = os.path.join(r'F:\yoloslowfast\train01\images\val', img_file)
src_label = os.path.join(path_labels, label_file)
dst_label = os.path.join(r'F:\yoloslowfast\train01\labels\val', label_file)
shutil.copy(src_img, dst_img)
shutil.copy(src_label, dst_label)
if __name__ == '__main__':
path_images = r'F:\yoloslowfast\train01\images'
path_labels = r'F:\yoloslowfast\train01\labels'
rate = 70 # 可以调整训练集和验证集的比例
create_folders()
split_data(path_images, path_labels, rate)
YOLOv5中牛类别是19:
step3:
将最开始测试图片和得到的labels文件夹放在一起,使用labelImg打开该文件夹。
打开labelImg之前,先在labels文件夹下手动创建一个classes.txt文件,里面写上你的类别名称,防止labelImg的闪退。
数据集划分:
import os
import random
import re
import shutil
def creat_files():
try:
shutil.rmtree('./data//images')
shutil.rmtree('./data/labels')
except:
pass
try:
os.makedirs('./data/images/train')
os.makedirs('./data/images/val')
os.makedirs('./data/labels/train')
os.makedirs('./data/labels/val')
except:
pass
def read_files(path):
dir_list = os.listdir(path)
for i in dir_list:
#利用正则表达,切割出图片ID
image_id = re.match(r'(.*)?.(jpg|png)',i).group(1)#图片后缀为jpg,png,可自行添加后缀格式
labels_name = image_id+'.txt' #由图片ID找到对应标签名
prob = random.randint(1, 100)#随机数prob
if (prob < rate): # train dataset
old_images_path = path_images + '/' + i
new_images_path = './data/images/train' + '/' + i
old_labels_path = path_labels + '/'+ labels_name
new_labels_path = './data/labels/train' + '/' + labels_name
shutil.copy(old_images_path, new_images_path)
shutil.copy(old_labels_path, new_labels_path)
else: # val dataset
old_images_path = path_images + '/' + i
new_images_path = './data/images/val' + '/' + i
old_labels_path = path_labels + '/' + labels_name
new_labels_path = './data/labels/val' + '/' + labels_name
shutil.copy(old_images_path, new_images_path)
shutil.copy(old_labels_path, new_labels_path)
if __name__ == '__main__':
path_images = './images' #图片的路径
path_labels = './labels' #标签路径
rate = 90 # (1-100)之间,90即练集与验证集比例9:1,可以改成自己喜欢的比例
creat_files()
read_files(path_images)
训练集:在训练阶段我们要用训练集来训练模型,用验证集来验证你训练的模型效果
验证集:在训练阶段去使用,辅助模型训练。
测试集:模型训练好之后,最终对模型做出评估。
OK,以上就是官方使用YOLOv5与labelimg实现半自动标注。
ImageNet支持xml格式
YOLO支持txt格式
待总结数据集格式及各个网络模型所支持的
yolo半自动标注实现流程
1、72张照片手动标注,两个动作站立和走动。
2、在yolo中训练模型并获得最好权重
修改配置文件:yolov5s.yaml中nc修改成自己数据集的类别
修改coco.yaml,把类别和数据集文件路径换成自己的
文件路径
必须得是images和labels命名,不然会报错的。
以上即是加入自己的数据集需要修改的地方。
训练结果:训练完后拿到了一个最好权重和最后一次权重。
训练:
验证:
问题很多或者改进地方很多,下用YOLO半自动标注这个事搞完再慢慢说吧!
修改detect.py中的save-txt在最上面和执行detect
python detect.py --weights /root/user/yolov5/runs/train/exp40/weights/best.pt --source /root/user/yolov5/data/CowDetection02/test06
确实生成了txt文件
遇到问题:
1、无法对自动标注的框进行操作
2、行为也是错的,应该是两个walk一个stand,可能和训练集样本有关系。
问题终于解决了,总结一下。
第一个问题:detect.py检测的照片在labelimg中打开会出现置信度和修改不了照片位置问题
第二个问题:detect.py检测的照片在labelimg中打开出现修改照片位置或类别名出线闪退问题
问题已解决
数据集标注注意事项:
1、从视频里抽帧、去重、缩放后对图片重命名。再筛选图片,筛选完对图片进行标注及半自动标注,划分数据集。