• 如何把利用paddlepaddle导出的json文件转化为yolo或者voc文件


    目录

    1. 修改源码,让模型能够生成出对于单个图像的标注。

    2. 把数据转为yolo格式

    3.把yolo格式转化为xml格式


    这两天想偷懒,想让模型先在数据上标一遍,然后我再做修正,主要是图个省事。由于我们主要是利用paddle,模型也是基于paddle推理的,因此即便我对paddle有一万个吐槽但也不得不用它。但在利用paddle保存推理结果文件时,遇到了一个大问题:就是paddle推理出来的所有数据都在同一个json文件,并且导入labelimg中也不能正常的显示到标注的框,不能对数据进行矫正。因此我就想着在代码中间能不能修改某些内容。

    如果你是真想把json文件转化为yolo或者xml的话,那哥们儿,你的思路走窄了,从json里面分离出那么多垃圾消息出来,很难的 !!!

    接下来介绍一下我的做法:

    1. 修改源码,让模型能够生成出对于单个图像的标注。

    首先就是修改源码,对应的文件为 PaddleDetection/ppdet/engine/trainer.py 。

    添加下述代码:

    1. class_label=['背景',
    2. '添加你的检测物品标签'
    3. ]
    4. def save_result_txt(save_path,boxs,threshold=0.5):#,tszie=640,osize=608
    5. with open(save_path,'w') as f:
    6. for msg in boxs:
    7. if msg['score']>threshold:
    8. bbox=msg['bbox']
    9. x1,y1,w,h=bbox
    10. img_m = Image.open('dataset/yz_new_0815/data/0.5data/'+save_path.split('/')[-1].replace('txt','jpg'))
    11. # dw = 1./img_m.width # 图片的宽
    12. # dh = 1./img_m.height # 图片的高
    13. print(save_path)
    14. # return
    15. bbox=np.array([x1,y1,x1+w,y1+h])
    16. #bbox=bbox*(tszie/osize)
    17. bbox=bbox.astype(np.int32)
    18. x1,y1,x2,y2=bbox
    19. # strs='%s %s %s %s %s %s\n'%(class_label[msg['category_id']],msg['score'],x1,y1,x2,y2)
    20. strs='%s %s %s %s %s\n'%(msg['category_id'],x1,y1,x2,y2)
    21. f.write(strs)

    之后在命令行中,令save_result为True,就能保存推理的结果了。从代码中可以看出,得到的数据就是四个点的坐标,非常真诚,不想yolo那种还得归一化或者相对长宽啥的。讲真的,我就听喜欢四个点坐标这种格式的,真诚永远是必杀技。但是没办法,我目前好像没见有这种格式的。

    2. 把数据转为yolo格式

    书接上回,上回说到我们已经把数据变成yolo的形式,而非格式,因为我们没有对数据进行一个归一化的处理。因此在这一回我们把数据归一化,得到yolo格式的数据。代码如下:

    1. # -*- coding:utf-8 -*-
    2. # 作用:
    3. # 将图片标注文件转化成yolo格式的txt标注文件
    4. #
    5. #
    6. import sys
    7. import os
    8. import cv2
    9. import random
    10. data_base_dir = "./20221210_result/" # 这里就是推理出来的yolo形式的数据(姑且叫position数据)文件所在的文件夹
    11. file_list = []
    12. for file in os.listdir(data_base_dir):
    13. if file == 'classes.txt':
    14. continue
    15. if file.endswith(".txt"):
    16. # print(file)
    17. img_name = file[:-4]
    18. print(file)
    19. # print(file[:-4]) #得到图片名,不带后缀
    20. imginfo = cv2.imread('图像所在位置文件夹' + img_name + '.jpg').shape
    21. # h = shape[0] w = shape[1]
    22. raw_file = open(data_base_dir + file) # 返回一个文件对象
    23. print('raw_file is ' + data_base_dir + file)
    24. new_file = open('yolo格式标注文件位置文件夹' + file, 'a+')
    25. line = raw_file.readline() # 调用文件的 readline()方法
    26. while line:
    27. print(line)
    28. line = line.split(" ")
    29. print(line[1])
    30. # line[0] = float(line[0])
    31. x1 = float(line[1])
    32. print(x1)
    33. y1 = float(line[2])
    34. x2 = float(line[3])
    35. y2 = float(line[4])
    36. h = imginfo[0]
    37. w = imginfo[1]
    38. print('h== ' + str(h))
    39. print('w== ' + str(w))
    40. new_x = "%.6s" % ((x1 + x2) / (2 * w))
    41. new_y = "%.6s" % ((y1 + y2) / (2 * h))
    42. new_w = "%.6s" % ((x2 - x1) / w)
    43. new_h = "%.6s" % ((y2 - y1) / h)
    44. new_file.write(line[0] + ' ' + new_x + ' ' + new_y + ' ' + new_w + ' ' + new_h + '\n')
    45. print(line[0] + ' ' + new_x + ' ' + new_y + ' ' + new_w + ' ' + new_h + '\n')
    46. # print line # 后面跟 ',' 将忽略换行符
    47. # print(line, end = '')  # 在 Python 3 中使用
    48. line = raw_file.readline()
    49. new_file.close()
    50. raw_file.close()

    到这一步如果不出意外的话,我们就已经把position数据转化为了yolo数据。但是如果你打开labelimg,你会发现报错了,报错的原因是没有classes.txt文件。因此在yolo格式中,一定要加上一个classes.txt文件,要不然就会报错。

    一定要加上一个classes.txt文件,要不然就会报错。

    一定要加上一个classes.txt文件,要不然就会报错。

    一定要加上一个classes.txt文件,要不然就会报错。

    重要的事情说三遍哈,已经四遍了哈哈哈。

    3.把yolo格式转化为xml格式

    上回说到,我们已经把position数据转化成了yolo格式,但是paddle这个挨千刀的,不支持yolo格式训练,至少在现在还没有对应的yaml文件。因此要是真的把数据调好了,用yolo格式还是没用,因为根本训练不了。这就提到了把yolo格式转化为xml格式的必要性了。代码如下:

    1. import os
    2. import xml.etree.ElementTree as ET
    3. from xml.dom.minidom import Document
    4. import cv2
    5. '''
    6. import xml
    7. xml.dom.minidom.Document().writexml()
    8. def writexml(self,
    9. writer: Any,
    10. indent: str = "",
    11. addindent: str = "",
    12. newl: str = "",
    13. encoding: Any = None) -> None
    14. '''
    15. class YOLO2VOCConvert:
    16. def __init__(self, txts_path, xmls_path, imgs_path):
    17. self.txts_path = txts_path # 标注的yolo格式标签文件路径
    18. self.xmls_path = xmls_path # 转化为voc格式标签之后保存路径
    19. self.imgs_path = imgs_path # 读取读片的路径各图片名字,存储到xml标签文件中
    20. self.classes = [
    21. '添加你的检测物品标签'
    22. ]
    23. # 从所有的txt文件中提取出所有的类别, yolo格式的标签格式类别为数字 0,1,...
    24. # writer为True时,把提取的类别保存到'./Annotations/classes.txt'文件中
    25. def search_all_classes(self, writer=False):
    26. # 读取每一个txt标签文件,取出每个目标的标注信息
    27. all_names = set()
    28. txts = os.listdir(self.txts_path)
    29. # 使用列表生成式过滤出只有后缀名为txt的标签文件
    30. txts = [txt for txt in txts if txt.split('.')[-1] == 'txt' and txt is not 'classes.txt']
    31. print(len(txts), txts)
    32. # 11 ['0002030.txt', '0002031.txt', ... '0002039.txt', '0002040.txt']
    33. for txt in txts:
    34. txt_file = os.path.join(self.txts_path, txt)
    35. with open(txt_file, 'r') as f:
    36. print(txt_file)
    37. objects = f.readlines()
    38. for object in objects:
    39. object = object.strip().split(' ')
    40. print(object) # ['2', '0.506667', '0.553333', '0.490667', '0.658667']
    41. all_names.add(int(object[0]))
    42. # print(objects) # ['2 0.506667 0.553333 0.490667 0.658667\n', '0 0.496000 0.285333 0.133333 0.096000\n', '8 0.501333 0.412000 0.074667 0.237333\n']
    43. print("所有的类别标签:", all_names, "共标注数据集:%d张" % len(txts))
    44. return list(all_names)
    45. def yolo2voc(self):
    46. # 创建一个保存xml标签文件的文件夹
    47. if not os.path.exists(self.xmls_path):
    48. os.mkdir(self.xmls_path)
    49. # 把上面的两个循环改写成为一个循环:
    50. imgs = os.listdir(self.imgs_path)
    51. txts = os.listdir(self.txts_path)
    52. txts = [txt for txt in txts if not txt.split('.')[0] == "classes"] # 过滤掉classes.txt文件
    53. print(txts)
    54. # 注意,这里保持图片的数量和标签txt文件数量相等,且要保证名字是一一对应的 (后面改进,通过判断txt文件名是否在imgs中即可)
    55. if len(imgs) == len(txts): # 注意:./Annotation_txt 不要把classes.txt文件放进去
    56. map_imgs_txts = [(img, txt) for img, txt in zip(imgs, txts)]
    57. txts = [txt for txt in txts if txt.split('.')[-1] == 'txt']
    58. print(len(txts), txts)
    59. for img_name, txt_name in map_imgs_txts:
    60. # 读取图片的尺度信息
    61. print("读取图片:", img_name)
    62. img = cv2.imread(os.path.join(self.imgs_path, img_name))
    63. height_img, width_img, depth_img = img.shape
    64. print(height_img, width_img, depth_img) # h 就是多少行(对应图片的高度), w就是多少列(对应图片的宽度)
    65. # 获取标注文件txt中的标注信息
    66. all_objects = []
    67. txt_file = os.path.join(self.txts_path, txt_name)
    68. with open(txt_file, 'r') as f:
    69. objects = f.readlines()
    70. for object in objects:
    71. object = object.strip().split(' ')
    72. all_objects.append(object)
    73. print(object) # ['2', '0.506667', '0.553333', '0.490667', '0.658667']
    74. # 创建xml标签文件中的标签
    75. xmlBuilder = Document()
    76. # 创建annotation标签,也是根标签
    77. annotation = xmlBuilder.createElement("annotation")
    78. # 给标签annotation添加一个子标签
    79. xmlBuilder.appendChild(annotation)
    80. # 创建子标签folder
    81. folder = xmlBuilder.createElement("folder")
    82. # 给子标签folder中存入内容,folder标签中的内容是存放图片的文件夹,例如:JPEGImages
    83. folderContent = xmlBuilder.createTextNode(self.imgs_path.split('/')[-1]) # 标签内存
    84. folder.appendChild(folderContent) # 把内容存入标签
    85. annotation.appendChild(folder) # 把存好内容的folder标签放到 annotation根标签下
    86. # 创建子标签filename
    87. filename = xmlBuilder.createElement("filename")
    88. # 给子标签filename中存入内容,filename标签中的内容是图片的名字,例如:000250.jpg
    89. filenameContent = xmlBuilder.createTextNode(txt_name.split('.')[0] + '.jpg') # 标签内容
    90. filename.appendChild(filenameContent)
    91. annotation.appendChild(filename)
    92. # 把图片的shape存入xml标签中
    93. size = xmlBuilder.createElement("size")
    94. # 给size标签创建子标签width
    95. width = xmlBuilder.createElement("width") # size子标签width
    96. widthContent = xmlBuilder.createTextNode(str(width_img))
    97. width.appendChild(widthContent)
    98. size.appendChild(width) # 把width添加为size的子标签
    99. # 给size标签创建子标签height
    100. height = xmlBuilder.createElement("height") # size子标签height
    101. heightContent = xmlBuilder.createTextNode(str(height_img)) # xml标签中存入的内容都是字符串
    102. height.appendChild(heightContent)
    103. size.appendChild(height) # 把width添加为size的子标签
    104. # 给size标签创建子标签depth
    105. depth = xmlBuilder.createElement("depth") # size子标签width
    106. depthContent = xmlBuilder.createTextNode(str(depth_img))
    107. depth.appendChild(depthContent)
    108. size.appendChild(depth) # 把width添加为size的子标签
    109. annotation.appendChild(size) # 把size添加为annotation的子标签
    110. # 每一个object中存储的都是['2', '0.506667', '0.553333', '0.490667', '0.658667']一个标注目标
    111. for object_info in all_objects:
    112. # 开始创建标注目标的label信息的标签
    113. object = xmlBuilder.createElement("object") # 创建object标签
    114. # 创建label类别标签
    115. # 创建name标签
    116. imgName = xmlBuilder.createElement("name") # 创建name标签
    117. # print(len(self.classes))
    118. imgNameContent = xmlBuilder.createTextNode(self.classes[int(object_info[0])])
    119. imgName.appendChild(imgNameContent)
    120. object.appendChild(imgName) # 把name添加为object的子标签
    121. # 创建pose标签
    122. pose = xmlBuilder.createElement("pose")
    123. poseContent = xmlBuilder.createTextNode("Unspecified")
    124. pose.appendChild(poseContent)
    125. object.appendChild(pose) # 把pose添加为object的标签
    126. # 创建truncated标签
    127. truncated = xmlBuilder.createElement("truncated")
    128. truncatedContent = xmlBuilder.createTextNode("0")
    129. truncated.appendChild(truncatedContent)
    130. object.appendChild(truncated)
    131. # 创建difficult标签
    132. difficult = xmlBuilder.createElement("difficult")
    133. difficultContent = xmlBuilder.createTextNode("0")
    134. difficult.appendChild(difficultContent)
    135. object.appendChild(difficult)
    136. # 先转换一下坐标
    137. # (objx_center, objy_center, obj_width, obj_height)->(xmin,ymin, xmax,ymax)
    138. x_center = float(object_info[1]) * width_img + 1
    139. y_center = float(object_info[2]) * height_img + 1
    140. xminVal = int(x_center - 0.5 * float(object_info[3]) * width_img) # object_info列表中的元素都是字符串类型
    141. yminVal = int(y_center - 0.5 * float(object_info[4]) * height_img)
    142. xmaxVal = int(x_center + 0.5 * float(object_info[3]) * width_img)
    143. ymaxVal = int(y_center + 0.5 * float(object_info[4]) * height_img)
    144. # 创建bndbox标签(三级标签)
    145. bndbox = xmlBuilder.createElement("bndbox")
    146. # 在bndbox标签下再创建四个子标签(xmin,ymin, xmax,ymax) 即标注物体的坐标和宽高信息
    147. # 在voc格式中,标注信息:左上角坐标(xmin, ymin) (xmax, ymax)右下角坐标
    148. # 1、创建xmin标签
    149. xmin = xmlBuilder.createElement("xmin") # 创建xmin标签(四级标签)
    150. xminContent = xmlBuilder.createTextNode(str(xminVal))
    151. xmin.appendChild(xminContent)
    152. bndbox.appendChild(xmin)
    153. # 2、创建ymin标签
    154. ymin = xmlBuilder.createElement("ymin") # 创建ymin标签(四级标签)
    155. yminContent = xmlBuilder.createTextNode(str(yminVal))
    156. ymin.appendChild(yminContent)
    157. bndbox.appendChild(ymin)
    158. # 3、创建xmax标签
    159. xmax = xmlBuilder.createElement("xmax") # 创建xmax标签(四级标签)
    160. xmaxContent = xmlBuilder.createTextNode(str(xmaxVal))
    161. xmax.appendChild(xmaxContent)
    162. bndbox.appendChild(xmax)
    163. # 4、创建ymax标签
    164. ymax = xmlBuilder.createElement("ymax") # 创建ymax标签(四级标签)
    165. ymaxContent = xmlBuilder.createTextNode(str(ymaxVal))
    166. ymax.appendChild(ymaxContent)
    167. bndbox.appendChild(ymax)
    168. object.appendChild(bndbox)
    169. annotation.appendChild(object) # 把object添加为annotation的子标签
    170. f = open(os.path.join(self.xmls_path, txt_name.split('.')[0] + '.xml'), 'w')
    171. xmlBuilder.writexml(f, indent='\t', newl='\n', addindent='\t', encoding='utf-8')
    172. f.close()
    173. if __name__ == '__main__':
    174. # 把yolo的txt标签文件转化为voc格式的xml标签文件
    175. # yolo格式txt标签文件相对路径
    176. txts_path1 = ''
    177. # 转化为voc格式xml标签文件存储的相对路径
    178. xmls_path1 = ''
    179. # 存放图片的相对路径
    180. imgs_path1 = ''
    181. yolo2voc_obj1 = YOLO2VOCConvert(txts_path1, xmls_path1, imgs_path1)
    182. labels = yolo2voc_obj1.search_all_classes()
    183. print('labels: ', labels)
    184. yolo2voc_obj1.yolo2voc()

    如果你嫌列表要一点一点写类别太麻烦了,可以用这种方式:(classes.txt就是前面提到的类别文本)

    1. cls = []
    2. cnt = 0
    3. for i in open(txtPath + 'classes.txt', 'r', encoding='utf-8').readlines():
    4. cls.append(i)

    至此,就可以把paddle里面图里的数据转化为xml格式了。

  • 相关阅读:
    Spring @Autowired 注解静态变量
    【axios】-- axios 二次封装
    Fedora CoreOS 安装部署详解
    Elasticsearch:什么是 DevOps?
    Qt之实现QQ天气预报窗体翻转效果
    配电室数据中心巡检3d可视化搭建的详细步骤
    Linux多线程服务端编程:使用muduo C++网络库 学习笔记 第七章 muduo编程示例(下)
    使用OpenCVSharp利用PictrueBox对接摄像头获取视频图像
    java“贪吃蛇”小游戏
    Grid布局
  • 原文地址:https://blog.csdn.net/qq_54932411/article/details/133027652