当前位置:   article > 正文

将labelme标注的人体姿态Json文件转成训练Yolov8-Pose的txt格式_labelme骨骼点json转yolo

labelme骨骼点json转yolo

最近在训练Yolov8-Pose时遇到一个问题,就是如何将自己使用labelme标注的Json文件转化成可用于Yolov8-Pose训练的txt文件。

具体代码有以下:

1、labelme2coco.py 将自己标注的人体姿态信息json文件格式合并转换成CoCo格式

  1. import os
  2. import json
  3. import numpy as np
  4. import glob
  5. import shutil
  6. np.random.seed(41)
  7. import cv2
  8. #0为背景
  9. classname_to_id = {"person": 1}
  10. class Lableme2CoCo:
  11. def __init__(self, splitDir=''):
  12. self.images = []
  13. self.annotations = []
  14. self.categories = []
  15. self.img_id = 0
  16. self.ann_id = 0
  17. self.splitDir = splitDir
  18. def save_coco_json(self, instance, save_path):
  19. json.dump(instance, open(save_path, 'w', encoding='utf-8'), ensure_ascii=False, indent=1) # indent=2 更加美观显示
  20. # 由json文件构建COCO
  21. def to_coco(self, json_path_list):
  22. self._init_categories()
  23. for json_path in json_path_list:
  24. # print(type(json_path))
  25. obj = self.read_jsonfile(json_path)
  26. self.images.append(self._image(obj, json_path))
  27. shapes = obj['shapes']
  28. groupIds = []
  29. for shape in shapes:
  30. groupId = shape['group_id']
  31. groupIds.append(groupId)
  32. for i in set(groupIds):
  33. keyPoints = [0] * 51
  34. keyPointNum = 0
  35. bbox = []
  36. for shape in shapes:
  37. if i != shape['group_id']:
  38. continue
  39. if shape['shape_type'] == "point":
  40. labelNum = int(shape['label'])
  41. keyPoints[labelNum * 3 + 0] = int(shape['points'][0][0] + 0.5)
  42. keyPoints[labelNum * 3 + 1] = int(shape['points'][0][1] + 0.5)
  43. keyPoints[labelNum * 3 + 2] = 2
  44. keyPointNum += 1
  45. if shape['shape_type'] == 'rectangle':
  46. x0, y0, x1, y1 = shape['points'][0][0], shape['points'][0][1], \
  47. shape['points'][1][0], shape['points'][1][1]
  48. xmin = min(x0, x1)
  49. ymin = min(y0, y1)
  50. xmax = max(x0, x1)
  51. ymax = max(y0, y1)
  52. bbox = [xmin, ymin, xmax - xmin, ymax - ymin]
  53. annotation = self._annotation(bbox, keyPoints, keyPointNum)
  54. self.annotations.append(annotation)
  55. self.ann_id += 1
  56. self.img_id += 1
  57. # for shape in shapes:
  58. # label = shape['label']
  59. # if label != 'person':
  60. # continue
  61. #
  62. # annotation = self._annotation(shape)
  63. # self.annotations.append(annotation)
  64. # self.ann_id += 1
  65. # self.img_id += 1
  66. instance = {}
  67. instance['info'] = 'spytensor created'
  68. instance['license'] = ['license']
  69. instance['images'] = self.images
  70. instance['annotations'] = self.annotations
  71. instance['categories'] = self.categories
  72. return instance
  73. # 构建类别
  74. def _init_categories(self):
  75. for k, v in classname_to_id.items():
  76. category = {}
  77. category['id'] = v
  78. category['name'] = k
  79. self.categories.append(category)
  80. # 构建COCO的image字段
  81. def _image(self, obj, jsonPath):
  82. image = {}
  83. # img_x = utils.img_b64_to_arr(obj['imageData'])
  84. # h, w = img_x.shape[:-1]
  85. jpgPath = jsonPath.replace('.json', '.jpg')
  86. jpgData = cv2.imread(jpgPath)
  87. h, w, _ = jpgData.shape
  88. image['height'] = h
  89. image['width'] = w
  90. image['id'] = self.img_id
  91. # image['file_name'] = os.path.basename(jsonPath).replace(".json", ".jpg")
  92. image['file_name'] = jpgPath.split(self.splitDir)[-1].replace('\\', '/')
  93. return image
  94. # 构建COCO的annotation字段
  95. def _annotation(self, bbox, keyPoints, keyNum):
  96. annotation = {}
  97. annotation['id'] = self.ann_id
  98. annotation['image_id'] = self.img_id
  99. annotation['category_id'] = 1
  100. # annotation['segmentation'] = [np.asarray(points).flatten().tolist()]
  101. annotation['segmentation'] = []
  102. annotation['bbox'] = bbox
  103. annotation['iscrowd'] = 0
  104. annotation['area'] = bbox[2] * bbox[3]
  105. annotation['keypoints'] = keyPoints
  106. annotation['num_keypoints'] = keyNum
  107. return annotation
  108. # 读取json文件,返回一个json对象
  109. def read_jsonfile(self, path):
  110. with open(path, "r", encoding='utf-8') as f:
  111. return json.load(f)
  112. # COCO的格式: [x1,y1,w,h] 对应COCO的bbox格式
  113. def _get_box(self, points):
  114. min_x = min_y = np.inf
  115. max_x = max_y = 0
  116. for x, y in points:
  117. min_x = min(min_x, x)
  118. min_y = min(min_y, y)
  119. max_x = max(max_x, x)
  120. max_y = max(max_y, y)
  121. return [min_x, min_y, max_x - min_x, max_y - min_y]
  122. if __name__ == '__main__':
  123. labelme_path = r"G:\XRW\Data\selfjson"
  124. print(labelme_path)
  125. jsonName = labelme_path.split('\\')[-1]
  126. saved_coco_path = r"G:\XRW\Data\mycoco"
  127. print(saved_coco_path)
  128. #####################################
  129. # 这个一定要注意
  130. # 为了方便合入coco数据, 定义截断文件的文件夹与文件名字
  131. splitDirFlag = 'labelMePoint\\'
  132. ######################################
  133. # 创建文件
  134. if not os.path.exists("%s/annotations/"%saved_coco_path):
  135. os.makedirs("%s/annotations/"%saved_coco_path)
  136. json_list_path = glob.glob(os.path.join(labelme_path, '*.json'))
  137. train_path, val_path = json_list_path, ''
  138. # print(train_path)
  139. print("train_n:", len(train_path), 'val_n:', len(val_path))
  140. # 把训练集转化为COCO的json格式
  141. l2c_train = Lableme2CoCo(splitDirFlag)
  142. # print(train_path)
  143. train_instance = l2c_train.to_coco(train_path)
  144. l2c_train.save_coco_json(train_instance, '%s/annotations/%s.json'%(saved_coco_path, jsonName))

labelme_path:自己标注的json文件路径

saved_coco_path:生成的CoCo格式保存位置

运行代码得到

2、将生成的CoCo格式的Json文件转换成Yolov8-Pose格式的txt文件

utils.py

  1. import glob
  2. import os
  3. import shutil
  4. from pathlib import Path
  5. import numpy as np
  6. from PIL import ExifTags
  7. from tqdm import tqdm
  8. # Parameters
  9. img_formats = ['bmp', 'jpg', 'jpeg', 'png', 'tif', 'tiff', 'dng'] # acceptable image suffixes
  10. vid_formats = ['mov', 'avi', 'mp4', 'mpg', 'mpeg', 'm4v', 'wmv', 'mkv'] # acceptable video suffixes
  11. # Get orientation exif tag
  12. for orientation in ExifTags.TAGS.keys():
  13. if ExifTags.TAGS[orientation] == 'Orientation':
  14. break
  15. def exif_size(img):
  16. # Returns exif-corrected PIL size
  17. s = img.size # (width, height)
  18. try:
  19. rotation = dict(img._getexif().items())[orientation]
  20. if rotation in [6, 8]: # rotation 270
  21. s = (s[1], s[0])
  22. except:
  23. pass
  24. return s
  25. def split_rows_simple(file='../data/sm4/out.txt'): # from utils import *; split_rows_simple()
  26. # splits one textfile into 3 smaller ones based upon train, test, val ratios
  27. with open(file) as f:
  28. lines = f.readlines()
  29. s = Path(file).suffix
  30. lines = sorted(list(filter(lambda x: len(x) > 0, lines)))
  31. i, j, k = split_indices(lines, train=0.9, test=0.1, validate=0.0)
  32. for k, v in {'train': i, 'test': j, 'val': k}.items(): # key, value pairs
  33. if v.any():
  34. new_file = file.replace(s, f'_{k}{s}')
  35. with open(new_file, 'w') as f:
  36. f.writelines([lines[i] for i in v])
  37. def split_files(out_path, file_name, prefix_path=''): # split training data
  38. file_name = list(filter(lambda x: len(x) > 0, file_name))
  39. file_name = sorted(file_name)
  40. i, j, k = split_indices(file_name, train=0.9, test=0.1, validate=0.0)
  41. datasets = {'train': i, 'test': j, 'val': k}
  42. for key, item in datasets.items():
  43. if item.any():
  44. with open(f'{out_path}_{key}.txt', 'a') as file:
  45. for i in item:
  46. file.write('%s%s\n' % (prefix_path, file_name[i]))
  47. def split_indices(x, train=0.9, test=0.1, validate=0.0, shuffle=True): # split training data
  48. n = len(x)
  49. v = np.arange(n)
  50. if shuffle:
  51. np.random.shuffle(v)
  52. i = round(n * train) # train
  53. j = round(n * test) + i # test
  54. k = round(n * validate) + j # validate
  55. return v[:i], v[i:j], v[j:k] # return indices
  56. def make_dirs(dir='new_dir/'):
  57. # Create folders
  58. dir = Path(dir)
  59. if dir.exists():
  60. shutil.rmtree(dir) # delete dir
  61. for p in dir, dir / 'labels', dir / 'images':
  62. p.mkdir(parents=True, exist_ok=True) # make dir
  63. return dir
  64. def write_data_data(fname='data.data', nc=80):
  65. # write darknet *.data file
  66. lines = ['classes = %g\n' % nc,
  67. 'train =../out/data_train.txt\n',
  68. 'valid =../out/data_test.txt\n',
  69. 'names =../out/data.names\n',
  70. 'backup = backup/\n',
  71. 'eval = coco\n']
  72. with open(fname, 'a') as f:
  73. f.writelines(lines)
  74. def image_folder2file(folder='images/'): # from utils import *; image_folder2file()
  75. # write a txt file listing all imaged in folder
  76. s = glob.glob(f'{folder}*.*')
  77. with open(f'{folder[:-1]}.txt', 'w') as file:
  78. for l in s:
  79. file.write(l + '\n') # write image list
  80. def add_coco_background(path='../data/sm4/', n=1000): # from utils import *; add_coco_background()
  81. # add coco background to sm4 in outb.txt
  82. p = f'{path}background'
  83. if os.path.exists(p):
  84. shutil.rmtree(p) # delete output folder
  85. os.makedirs(p) # make new output folder
  86. # copy images
  87. for image in glob.glob('../coco/images/train2014/*.*')[:n]:
  88. os.system(f'cp {image} {p}')
  89. # add to outb.txt and make train, test.txt files
  90. f = f'{path}out.txt'
  91. fb = f'{path}outb.txt'
  92. os.system(f'cp {f} {fb}')
  93. with open(fb, 'a') as file:
  94. file.writelines(i + '\n' for i in glob.glob(f'{p}/*.*'))
  95. split_rows_simple(file=fb)
  96. def create_single_class_dataset(path='../data/sm3'): # from utils import *; create_single_class_dataset('../data/sm3/')
  97. # creates a single-class version of an existing dataset
  98. os.system(f'mkdir {path}_1cls')
  99. def flatten_recursive_folders(path='../../Downloads/data/sm4/'): # from utils import *; flatten_recursive_folders()
  100. # flattens nested folders in path/images and path/JSON into single folders
  101. idir, jdir = f'{path}images/', f'{path}json/'
  102. nidir, njdir = Path(f'{path}images_flat/'), Path(f'{path}json_flat/')
  103. n = 0
  104. # Create output folders
  105. for p in [nidir, njdir]:
  106. if os.path.exists(p):
  107. shutil.rmtree(p) # delete output folder
  108. os.makedirs(p) # make new output folder
  109. for parent, dirs, files in os.walk(idir):
  110. for f in tqdm(files, desc=parent):
  111. f = Path(f)
  112. stem, suffix = f.stem, f.suffix
  113. if suffix.lower()[1:] in img_formats:
  114. n += 1
  115. stem_new = '%g_' % n + stem
  116. image_new = nidir / (stem_new + suffix) # converts all formats to *.jpg
  117. json_new = njdir / f'{stem_new}.json'
  118. image = parent / f
  119. json = Path(parent.replace('images', 'json')) / str(f).replace(suffix, '.json')
  120. os.system("cp '%s' '%s'" % (json, json_new))
  121. os.system("cp '%s' '%s'" % (image, image_new))
  122. # cv2.imwrite(str(image_new), cv2.imread(str(image)))
  123. print('Flattening complete: %g jsons and images' % n)
  124. def coco91_to_coco80_class(): # converts 80-index (val2014) to 91-index (paper)
  125. # https://tech.amikelive.com/node-718/what-object-categories-labels-are-in-coco-dataset/
  126. x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, None, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, None, 24, 25, None,
  127. None, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, None, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
  128. 51, 52, 53, 54, 55, 56, 57, 58, 59, None, 60, None, None, 61, None, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
  129. None, 73, 74, 75, 76, 77, 78, 79, None]
  130. return x
slefjson2posetxt.py
  1. import json
  2. import cv2
  3. import pandas as pd
  4. from PIL import Image
  5. from collections import defaultdict
  6. from utils import *
  7. def convert_coco_json(cocojsonpath, savepath,use_keypoints=False, cls91to80=True):
  8. """Converts COCO dataset annotations to a format suitable for training YOLOv5 models.
  9. Args:
  10. labels_dir (str, optional): Path to directory containing COCO dataset annotation files.
  11. use_segments (bool, optional): Whether to include segmentation masks in the output.
  12. use_keypoints (bool, optional): Whether to include keypoint annotations in the output.
  13. cls91to80 (bool, optional): Whether to map 91 COCO class IDs to the corresponding 80 COCO class IDs.
  14. Raises:
  15. FileNotFoundError: If the labels_dir path does not exist.
  16. Example Usage:
  17. convert_coco(labels_dir='../coco/annotations/', use_segments=True, use_keypoints=True, cls91to80=True)
  18. Output:
  19. Generates output files in the specified output directory.
  20. """
  21. # save_dir = make_dirs('yolo_labels') # output directory
  22. save_dir = make_dirs(savepath) # output directory
  23. coco80 = coco91_to_coco80_class()
  24. # Import json
  25. for json_file in sorted(Path(cocojsonpath).resolve().glob('*.json')):
  26. fn = Path(save_dir) / 'labels' / json_file.stem.replace('instances_', '') # folder name
  27. fn.mkdir(parents=True, exist_ok=True)
  28. with open(json_file) as f:
  29. data = json.load(f)
  30. # Create image dict
  31. images = {f'{x["id"]:d}': x for x in data['images']}
  32. # Create image-annotations dict
  33. imgToAnns = defaultdict(list)
  34. for ann in data['annotations']:
  35. imgToAnns[ann['image_id']].append(ann)
  36. # Write labels file
  37. for img_id, anns in tqdm(imgToAnns.items(), desc=f'Annotations {json_file}'):
  38. img = images[f'{img_id:d}']
  39. h, w, f = img['height'], img['width'], img['file_name']
  40. bboxes = []
  41. segments = []
  42. keypoints = []
  43. for ann in anns:
  44. if ann['iscrowd']:
  45. continue
  46. # The COCO box format is [top left x, top left y, width, height]
  47. box = np.array(ann['bbox'], dtype=np.float64)
  48. box[:2] += box[2:] / 2 # xy top-left corner to center
  49. box[[0, 2]] /= w # normalize x
  50. box[[1, 3]] /= h # normalize y
  51. if box[2] <= 0 or box[3] <= 0: # if w <= 0 and h <= 0
  52. continue
  53. cls = coco80[ann['category_id'] - 1] if cls91to80 else ann['category_id'] - 1 # class
  54. box = [cls] + box.tolist()
  55. if box not in bboxes:
  56. bboxes.append(box)
  57. if use_keypoints and ann.get('keypoints') is not None:
  58. k = (np.array(ann['keypoints']).reshape(-1, 3) / np.array([w, h, 1])).reshape(-1).tolist()
  59. k = box + k
  60. keypoints.append(k)
  61. # Write
  62. fname = f.split('/')[-1]
  63. # with open((fn / f).with_suffix('.txt'), 'a') as file:
  64. with open((fn / fname).with_suffix('.txt'), 'a') as file:
  65. for i in range(len(bboxes)):
  66. if use_keypoints:
  67. line = *(keypoints[i]), # cls, box, keypoints
  68. file.write(('%g ' * len(line)).rstrip() % line + '\n')
  69. if __name__ == '__main__':
  70. source = 'COCO'
  71. cocojsonpath = r'G:\XRW\Data\mycoco\annotations'
  72. savepath = r'G:\XRW\Data\myposedata'
  73. if source == 'COCO':
  74. convert_coco_json(cocojsonpath, # directory with *.json
  75. savepath,
  76. use_keypoints=True,
  77. cls91to80=True)

运行代码得到:

<class-index>是对象的类的索引,<x> <y> <width> <height>是边界框的坐标,<px1> <py1> <px2> <py2> ... <pxn> <pyn>是关键点的像素坐标。坐标由空格分隔。

3、检查生成的txt是否准确

PoseVisualization.py:将txt的信息可视化在图片上进行验证。
  1. import cv2
  2. imgpath = r'G:\XRW\Data\selfjson\five_22101205_000930.jpg'
  3. txtpath = r'G:\XRW\Data\myposedata\labels\selfjson\five_22101205_000930.txt'
  4. f = open(txtpath,'r')
  5. lines = f.readlines()
  6. img = cv2.imread(imgpath)
  7. h, w, c = img.shape
  8. colors = [[255, 128, 0], [255, 153, 51], [255, 178, 102], [230, 230, 0], [255, 153, 255],
  9. [153, 204, 255], [255, 102, 255], [255, 51, 255], [102, 178, 255], [51, 153, 255],
  10. [255, 153, 153], [255, 102, 102], [255, 51, 51], [153, 255, 153], [102, 255, 102],
  11. [51, 255, 51], [0, 255, 0], [0, 0, 255], [255, 0, 0], [255, 255, 255]]
  12. for line in lines:
  13. print(line)
  14. l = line.split(' ')
  15. print(len(l))
  16. cx = float(l[1]) * w
  17. cy = float(l[2]) * h
  18. weight = float(l[3]) * w
  19. height = float(l[4]) * h
  20. xmin = cx - weight/2
  21. ymin = cy - height/2
  22. xmax = cx + weight/2
  23. ymax = cy + height/2
  24. print((xmin,ymin),(xmax,ymax))
  25. cv2.rectangle(img,(int(xmin),int(ymin)),(int(xmax),int(ymax)),(0,255,0),2)
  26. kpts = []
  27. for i in range(17):
  28. x = float(l[5:][3*i]) * w
  29. y = float(l[5:][3*i+1]) * h
  30. s = int(l[5:][3*i+2])
  31. print(x,y,s)
  32. if s != 0:
  33. cv2.circle(img,(int(x),int(y)),1,colors[i],2)
  34. kpts.append([int(x),int(y),int(s)])
  35. print(kpts)
  36. kpt_line = [[16, 14], [14, 12], [17, 15], [15, 13], [12, 13], [6, 12], [7, 13], [6, 7], [6, 8], [7, 9],
  37. [8, 10], [9, 11], [2, 3], [1, 2], [1, 3], [2, 4], [3, 5], [4, 6], [5, 7]]
  38. for j in range(len(kpt_line)):
  39. m,n = kpt_line[j][0],kpt_line[j][1]
  40. if kpts[m-1][2] !=0 and kpts[n-1][2] !=0:
  41. cv2.line(img,(kpts[m-1][0],kpts[m-1][1]),(kpts[n-1][0],kpts[n-1][1]),colors[j],2)
  42. img = cv2.resize(img, None, fx=0.5, fy=0.5)
  43. cv2.imshow('1',img)
  44. cv2.waitKey(0)

这样就将自己的Json格式转成训练Yolov8-Pose姿态的txt格式了。

4、将图片copy到对应路径中

以上步骤完成后只生成了txt,需要再将对应的图片copy到对应路径中。

pickImg.py

  1. import glob
  2. import os
  3. import shutil
  4. imgpath = r'G:\XRW\Data\selfjson'
  5. txtpath = r'G:\XRW\Data\myposedata\labels\selfjson'
  6. savepath = r'G:\XRW\Data\myposedata\images\selfjson'
  7. os.makedirs(savepath,exist_ok=True)
  8. imglist = glob.glob(os.path.join(imgpath ,'*.jpg'))
  9. # print(imglist)
  10. txtlist = glob.glob(os.path.join(txtpath ,'*.txt'))
  11. # print(txtlist)
  12. for img in imglist:
  13. name = txtpath + '\\' +img.split('\\')[-1].split('.')[0 ] +'.txt'
  14. print(name)
  15. if name in txtlist:
  16. shutil.copy(img ,savepath)
  • imgpath CoCo数据集图片路径
  • txtpath 生成的txt路径
  • savepath 保存图片的路径

这样就将自己标注的数据集转换成Yolov8-Pose格式的txt了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/474298
推荐阅读
相关标签
  

闽ICP备14008679号