Article Directory
foreword
Recently, I want to change the previously trained YOLOV5 model to rotating target detection (YOLOV5_obb), but I don't want to relabel the data set, and I haven't found a suitable script on the Internet. So I wrote a script to convert the json file of the previously annotated dataset into an xml file, and then adjust it through the corresponding rotation annotation software.
1. Data set preparation
The premise of this conversion script is that you already have a data set for YOLOV5 training before, and the annotation file is a json file, and the annotation frame is a rectangular frame. Here, only the conversion of the rectangular frame to xml is realized. If it is a polygon, you need to change the script yourself. The data set is as follows:
2. Download the labeling tool
The tool we use here is roLabelImg, which adds the function of rotation angle on the basis of LabelImg. The format of the labeled data set is VOC. The reference link is: roLabelImg installation .
1. Source code download
The download link is: roLabelImg
2. Required dependency installation
The commands are as follows, for reference only:
pip install pyqt5-tools
pip install lxml
3. Start command
The command is as follows:
pyrcc5 -o resources.py resources.qrc
python roLabelImg.py(第二次直接执行这个命令应该就可以了)
4. Commonly used shortcut keys
w: Create a horizontal rectangular target frame
e: Create a rotating rectangular target frame
zxcv: Rotate the target frame, keys z and x are used to rotate counterclockwise, keys c and v are used to rotate clockwise
3. File conversion:
The general idea here is to first parse out the original json file, then delete the redundant information in it, such as picture information, and then write the remaining information into the corresponding xml file. Change the path to the code and it should be able to run. The code is as follows:
import os,glob,json
import xml.etree.ElementTree as ET
from pathlib import Path
import numpy as np
#获得中心点的坐标
def get_cx_cy(points):
cx=(points[0][0]+points[1][0])/2.
cy=(points[0][1]+points[1][1])/2.
points=np.array(points)
w=max(points[:,0])-min(points[:,0])
h=max(points[:,1])-min(points[:,1])
return cx,cy,w,h
#美化内容(+换行)
def indent(elem,level=0):
i = "\n" + level * " "
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + " "
if not elem.tail or not elem.tail.strip():
elem.tail = i
for elem in elem:
indent(elem, level + 1)
if not elem.tail or not elem.tail.strip():
elem.tail = i
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
#写入xml文件
def write_xml(data,imgpath,savepath):
root=ET.Element('annotation') #创建节点
tree=ET.ElementTree(root) #创建文档
#图片文件上一级目录
folder=ET.Element("folder")
img_folder=imgpath.split(os.sep)[-2]
folder.text=img_folder
root.append(folder)
#文件名
imgname=Path(imgpath).stem
filename=ET.Element("filename")
filename.text=imgname
root.append(filename)
#路径
path=ET.Element("path")
path.text=imgpath
root.append(path)
#source
source=ET.Element("source")
root.append(source)
database=ET.Element("database")
database.text="Unknown"
source.append(database)
#size
size=ET.Element("size")
root.append(size)
width=ET.Element("width") #宽
width.text=str(data["imageWidth"])
size.append(width)
height=ET.Element("height")#高
height.text=str(data["imageHeight"])
size.append(height)
depth=ET.Element("depth") #深度
depth.text=str(3)
size.append(depth)
#segmented
segmented=ET.Element("segmented")
segmented.text=str(0)
root.append(segmented)
# 目标
for shape in data["shapes"]:
object_=ET.Element("object")
root.append(object_)
#标注框类型
type_=ET.Element("type")
type_.text="robndbox"
object_.append(type_)
#目标类别
name=ET.Element("name")
name.text=shape["label"]
object_.append(name)
#pose
pose=ET.Element("pose")
pose.text="Unspecified"
object_.append(pose)
#截断情况
truncated=ET.Element("truncated")
truncated.text=str(0) #默认为0,表示未截断
object_.append(truncated)
#样本困难度
difficult=ET.Element("difficult")
difficult.text=str(0) #默认为0,表示非困难样本
object_.append(difficult)
#四个端点
robndbox=ET.Element("robndbox")
object_.append(robndbox)
cx,cy,w,h=get_cx_cy(shape["points"])
#cx
cx_=ET.Element("cx")
cx_.text=str(cx)
robndbox.append(cx_)
#cy
cy_=ET.Element("cy")
cy_.text=str(cy)
robndbox.append(cy_)
#w
w_=ET.Element("w")
w_.text=str(w)
robndbox.append(w_)
#h
h_=ET.Element("h")
h_.text=str(h)
robndbox.append(h_)
#angle
angle=ET.Element("angle")
angle.text=str(0.0)
robndbox.append(angle)
indent(root,0)
tree.write(savepath+os.sep+imgname+".xml","UTF-8",xml_declaration=True)
#解析json文件
def load_json(jsonpath):
data=json.load(open(jsonpath,"r"))
del data["version"]
try:
del data["flags"]
except Exception as e:
del data["flag"]
del data["imagePath"]
del data["imageData"]
return data
if __name__ == '__main__':
img_dir=r"" #要转换的原json文件的路径
save_dir=r"" #保存文件夹路径
os.makedirs(save_dir,exist_ok=True)
imglist=glob.glob(img_dir+"*.jpg")
for imgpath in imglist:
# print(imgpath)
jsonpath=imgpath.replace(".jpg",".json")
data=load_json(jsonpath)
write_xml(data,imgpath,save_dir)
If the json file is marked with polygons, it needs to be changed in the get_cx_cy function
Summarize
The above is the whole content of this article. If you have any questions, please leave a message in the comment area, or join the QQ group: 995760755 to communicate.