• DETR实现目标检测(一)-训练自己的数据集


    1、DETR架构

    DETR(Detection Transformer)是一种新型的目标检测模型,由Facebook AI Research (FAIR) 在2020年提出。DETR的核心思想是将目标检测任务视为一个直接的集合预测问题,而不是传统的两步或多步预测问题。这种方法的创新之处在于它直接预测目标的类别和边界框,而不是先生成大量的候选区域,然后再对这些区域进行分类和边界框回归。

    DERT的特点主要有二:

    一是Transformer结构在CV网络中的应用;

    二是提出了一种新的或者说不同的损失函数(Loss Function)。

    2、模型下载

    模型代码下载地址:

    GitHub - facebookresearch/detr: End-to-End Object Detection with Transformers

    预训练模型(即权重文件)下载地址:

    GitHub - facebookresearch/detr: End-to-End Object Detection with Transformers

    下载后放到项目下待使用:

    3、labelme标注文件转为coco模式

    首先,labelme标注的文件存放在指定位置,包含json和jpg文件

    然后,利用代码将labelme的标注文件转化为coco。包含annotations(两个json文件)、train2017(训练集图片)、val2017(验证集图片)

    备注:必须严格按照笔者图中的文件命名方式进行命名,训练集清一色命名为instances_train2017.json,验证集清一色命名为instances_val2017.json,这是模型本身的命名要求,用户需要严格遵守。

    实现代码如下:

    1. import json
    2. from labelme import utils
    3. import numpy as np
    4. import glob
    5. import PIL.Image
    6. class MyEncoder(json.JSONEncoder):
    7. def default(self, obj):
    8. if isinstance(obj, np.integer):
    9. return int(obj)
    10. elif isinstance(obj, np.floating):
    11. return float(obj)
    12. elif isinstance(obj, np.ndarray):
    13. return obj.tolist()
    14. else:
    15. return super(MyEncoder, self).default(obj)
    16. class labelme2coco(object):
    17. def __init__(self, labelme_json=[], save_json_path='./tran.json'):
    18. self.labelme_json = labelme_json
    19. self.save_json_path = save_json_path
    20. self.images = []
    21. self.categories = []
    22. self.annotations = []
    23. # self.data_coco = {}
    24. self.label = []
    25. self.annID = 1
    26. self.height = 0
    27. self.width = 0
    28. self.save_json()
    29. def data_transfer(self):
    30. for num, json_file in enumerate(self.labelme_json):
    31. with open(json_file, 'r') as fp:
    32. data = json.load(fp) # 加载json文件
    33. self.images.append(self.image(data, num))
    34. for shapes in data['shapes']:
    35. label = shapes['label']
    36. if label not in self.label:
    37. self.categories.append(self.categorie(label))
    38. self.label.append(label)
    39. points = shapes['points'] # 这里的point是用rectangle标注得到的,只有两个点,需要转成四个点
    40. points.append([points[0][0], points[1][1]])
    41. points.append([points[1][0], points[0][1]])
    42. self.annotations.append(self.annotation(points, label, num))
    43. self.annID += 1
    44. def image(self, data, num):
    45. image = {}
    46. img = utils.img_b64_to_arr(data['imageData']) # 解析原图片数据
    47. height, width = img.shape[:2]
    48. image['height'] = height
    49. image['width'] = width
    50. image['id'] = num + 1
    51. image['file_name'] = data['imagePath'].split('/')[-1]
    52. self.height = height
    53. self.width = width
    54. return image
    55. def categorie(self, label):
    56. categorie = {}
    57. categorie['supercategory'] = 'Cancer'
    58. categorie['id'] = len(self.label) + 1 # 0 默认为背景
    59. categorie['name'] = label
    60. return categorie
    61. def annotation(self, points, label, num):
    62. annotation = {}
    63. annotation['segmentation'] = [list(np.asarray(points).flatten())]
    64. annotation['iscrowd'] = 0
    65. annotation['image_id'] = num + 1
    66. annotation['bbox'] = list(map(float, self.getbbox(points)))
    67. annotation['area'] = annotation['bbox'][2] * annotation['bbox'][3]
    68. annotation['category_id'] = self.getcatid(label) # 注意,源代码默认为1
    69. annotation['id'] = self.annID
    70. return annotation
    71. def getcatid(self, label):
    72. for categorie in self.categories:
    73. if label == categorie['name']:
    74. return categorie['id']
    75. return 1
    76. def getbbox(self, points):
    77. polygons = points
    78. mask = self.polygons_to_mask([self.height, self.width], polygons)
    79. return self.mask2box(mask)
    80. def mask2box(self, mask):
    81. """从mask反算出其边框
    82. mask:[h,w] 0、1组成的图片
    83. 1对应对象,只需计算1对应的行列号(左上角行列号,右下角行列号,就可以算出其边框)
    84. """
    85. # np.where(mask==1)
    86. index = np.argwhere(mask == 1)
    87. rows = index[:, 0]
    88. clos = index[:, 1]
    89. # 解析左上角行列号
    90. left_top_r = np.min(rows) # y
    91. left_top_c = np.min(clos) # x
    92. # 解析右下角行列号
    93. right_bottom_r = np.max(rows)
    94. right_bottom_c = np.max(clos)
    95. return [left_top_c, left_top_r, right_bottom_c - left_top_c,
    96. right_bottom_r - left_top_r] # [x1,y1,w,h] 对应COCO的bbox格式
    97. def polygons_to_mask(self, img_shape, polygons):
    98. mask = np.zeros(img_shape, dtype=np.uint8)
    99. mask = PIL.Image.fromarray(mask)
    100. xy = list(map(tuple, polygons))
    101. PIL.ImageDraw.Draw(mask).polygon(xy=xy, outline=1, fill=1)
    102. mask = np.array(mask, dtype=bool)
    103. return mask
    104. def data2coco(self):
    105. data_coco = {}
    106. data_coco['images'] = self.images
    107. data_coco['categories'] = self.categories
    108. data_coco['annotations'] = self.annotations
    109. return data_coco
    110. def save_json(self):
    111. self.data_transfer()
    112. self.data_coco = self.data2coco()
    113. # 保存json文件
    114. json.dump(self.data_coco, open(self.save_json_path, 'w'), indent=4, cls=MyEncoder) # indent=4 更加美观显示
    115. if __name__ == '__main__':
    116. labelme_json = glob.glob('data/LabelmeData_frame_count/val2017/*.json') # labelme标注好的.json文件存放目录
    117. labelme2coco(labelme_json, 'data/coco_frame_count/annotations/instances_val2017.json') # 输出结果的存放目录

    4、修改训练模型参数

    先在pycharm中新建python脚本文件detr_r50_tf.py,代码如下:

    1. import torch
    2. pretrained_weights = torch.load('detr-r50-e632da11.pth')
    3. num_class = 1 # 类别数
    4. pretrained_weights["model"]["class_embed.weight"].resize_(num_class + 1, 256)
    5. pretrained_weights["model"]["class_embed.bias"].resize_(num_class + 1)
    6. torch.save(pretrained_weights, "detr-r50_%d.pth" % num_class)

    将其中类别数改成自己数据集的类别数即可,执行完成后会在目录下生成适合自己数据集类别的预训练模型:

    然后在models文件夹下打开detr.py,修改其中的类别数(一定要全部保持一致):

    最后打开main.py,修改其中的coco_path(数据存放路径)、output_dir(结果输出路径)、device(没有cuda就改为cpu)、resume(自己生成的预训练模型)。

    5、执行main.py来开始训练模型

    如果不想跑太多了轮可以修改epochs数:

    训练好的模型会保存在结果输出路径中:

    跑起来的效果是这样的:

    6、执行util/plot_utils.py来看训练效果

    在plot_utils.py的最后加上以下代码(其中路径要换成自己的输出路径):

    1. if __name__ == '__main__':
    2. files = list(Path(r'C:\Users\90539\Downloads\detr-main\detr-main\data\output\eval').glob('*.pth'))
    3. plot_precision_recall(files)
    4. plt.show()
    5. plot_logs(logs=Path(r'C:\Users\90539\Downloads\detr-main\detr-main\data\output'),fields=('class_error', 'loss_bbox_unscaled', 'mAP'), ewm_col=0, log_name='log.txt')
    6. plt.show()

    然后执行plot_utils.py得到训练的结果:

    可以看到我的pr曲线始终是一条为0的直线,说明没有训练出来(因为我只用了很少图片进行训练),而且loss曲线也很不好看。

  • 相关阅读:
    Mac解决鼠标滚轮反方向移动逻辑--Mos(又免费又好用哦~)
    JSTL标准标签库 EL表达式
    [PAT练级笔记] 31 Basic Level 1031 查验身份证
    C# Onnx 轻量实时的M-LSD直线检测
    解决java: 程序包org.springframework.boot不存在的解决方法
    docker的安装
    2024.8.7(SQL语句)
    java毕业设计爱宠医院管理系统mybatis+源码+调试部署+系统+数据库+lw
    jdk 8 List相关知识点
    JFrame中有关于DefaultCloseOperation的使用及参数说明(含源码阅读)
  • 原文地址:https://blog.csdn.net/Trisyp/article/details/139640965