针对目标检测标注数据做的数据预处理操作。

    1. # -*- coding: utf-8 -*-
    2. """
    3. @File : rotate_images.py
    4. @Time : 2020-8-19 21:21
    5. @Author : qian733
    6. @Description :
    7. """
    8. import os
    9. import cv2
    10. import shutil
    11. import argparse
    12. import voc_utils
    13. import numpy as np
    14. class RotateProcessing(object):
    15. def __init__(self, angle, center=0, scale=1.0):
    16. """
    17. :param angle: 旋转角度
    18. :param center: 旋转的中心点,默认为图像中心,以0代替
    19. :param scale: 图像缩放因子
    20. """
    21. self.angle = angle
    22. self.center = center
    23. self.scale = scale
    24. def rotate(self, image):
    25. (h, w) = image.shape[:2]
    26. if not self.center:
    27. (cx, cy) = (w / 2, h / 2)
    28. else:
    29. (cx, cy) = self.center # 二元元组
    30. # 设置旋转矩阵
    31. matrix = cv2.getRotationMatrix2D((cx, cy), -self.angle, self.scale)
    32. cos = np.abs(matrix[0, 0])
    33. sin = np.abs(matrix[0, 1])
    34. # 计算图像旋转后的新边界
    35. new_weight = int((h * sin) + (w * cos))
    36. new_height = int((h * cos) + (w * sin))
    37. # 调整旋转矩阵的移动距离(t_{x}, t_{y})
    38. matrix[0, 2] += (new_weight / 2) - cx
    39. matrix[1, 2] += (new_height / 2) - cy
    40. return cv2.warpAffine(image, matrix, (new_weight, new_height))
    41. def get_rotate_boxes(self, img_shape, gt_bboxes):
    42. """
    43. :param img_shape:
    44. :param gt_bboxes: 由TensorFlow返回的预测结果
    45. :return:
    46. """
    47. num_boxes = len(gt_bboxes)
    48. (h, w) = img_shape[0], img_shape[1]
    49. mask = np.zeros(shape=(h, w, num_boxes), dtype=np.uint8)
    50. for i, box in enumerate(gt_bboxes):
    51. y1, x1, y2, x2 = box
    52. # 这里的gt是大于0的float,不需要分别乘以长宽,但是需要取整
    53. y1, y2 = int(np.floor(y1)), int(np.ceil(y2))
    54. x1, x2 = int(np.floor(x1)), int(np.ceil(x2))
    55. mask[y1:y2, x1:x2, i] = 1
    56. if not self.center:
    57. (cx, cy) = (w / 2, h / 2)
    58. else:
    59. (cx, cy) = self.center # 二元元组
    60. matrix = cv2.getRotationMatrix2D((cx, cy), -self.angle, self.scale)
    61. cos = np.abs(matrix[0, 0])
    62. sin = np.abs(matrix[0, 1])
    63. new_weight = int((h * sin) + (w * cos))
    64. new_height = int((h * cos) + (w * sin))
    65. matrix[0, 2] += (new_weight / 2) - cx
    66. matrix[1, 2] += (new_height / 2) - cy
    67. new_mask = cv2.warpAffine(mask, matrix, (new_weight, new_height))
    68. if num_boxes == 1:
    69. new_mask = new_mask[:, :, np.newaxis]
    70. new_gt_boxes = np.zeros_like(gt_bboxes)
    71. for i in range(num_boxes):
    72. hs, ws = np.where(new_mask[:, :, i] == 1)
    73. y1, x1, y2, x2 = 0, 0, 0, 0
    74. if len(hs) > 0:
    75. y1, y2 = np.min(hs), np.max(hs)
    76. if len(ws) > 0:
    77. x1, x2 = np.min(ws), np.max(ws)
    78. new_gt_boxes[i] = np.array([y1, x1, y2, x2])
    79. return new_gt_boxes, new_height, new_weight
    80. def rotate_images_special(args, out_anno, out_jpg, out_bg):
    81. """
    82. :param args:
    83. :param out_anno:
    84. :param out_jpg:
    85. :param out_bg:
    86. :return:
    87. """
    88. if args.subfile:
    89. subfile_list = args.subfile.split("+")
    90. else:
    91. subfile_list = os.listdir(args.input_path)
    92. angle_list = args.angle.split("+")
    93. for sub in os.listdir(args.input_path):
    94. if sub in subfile_list:
    95. print(sub)
    96. sub_dir = os.path.join(args.input_path, sub) # 如 root_dir/small_0511 下面放着图像和xml文件
    97. anno_dir = sub_dir + "/Annotations/"
    98. img_dir = sub_dir + "/JPEGImages/"
    99. bg_list = []
    100. for anno in os.listdir(anno_dir):
    101. xml_path = os.path.join(anno_dir, anno) # xml
    102. _, height, width, class_names, gt_boxes = voc_utils.parse_voc(xml_path)
    103. # 如果class为空,则进行去重;如果class只有第一类,则进行旋转
    104. if args.exclude_class not in class_names:
    105. for angle in angle_list:
    106. RP = RotateProcessing(int(angle))
    107. image_ori = cv2.imread(img_dir + anno.split(".")[0] + ".jpg")
    108. image_rotate = RP.rotate(image_ori)
    109. cv2.imwrite(out_jpg + "/r" + angle + anno.split(".")[0] + ".jpg", image_rotate)
    110. image_rotate_boxes, nH, nW = RP.get_rotate_boxes(image_ori.shape, gt_boxes)
    111. voc_utils.save_to_xml_file("r" + angle + anno.split(".")[0] + ".jpg", nH, nW, class_names, image_rotate_boxes, out_anno)
    112. def rotate_images(args, out_anno, out_jpg, out_bg):
    113. """
    114. :param args:
    115. :param out_anno:
    116. :param out_jpg:
    117. :param out_bg:
    118. :return:
    119. """
    120. if args.subfile:
    121. subfile_list = args.subfile.split("+")
    122. else:
    123. subfile_list = os.listdir(args.input_path)
    124. angle_list = args.angle.split("+")
    125. for sub in os.listdir(args.input_path):
    126. if sub in subfile_list:
    127. print(sub)
    128. sub_dir = os.path.join(args.input_path, sub) # 如 root_dir/small_0511 下面放着图像和xml文件
    129. anno_dir = sub_dir + "/Annotations/"
    130. img_dir = sub_dir + "/JPEGImages/"
    131. bg_list = []
    132. for anno in os.listdir(anno_dir):
    133. xml_path = os.path.join(anno_dir, anno) # xml
    134. _, height, width, class_names, gt_boxes = voc_utils.parse_voc(xml_path)
    135. # 如果class为空,则旋转图像并修改xml中对应的图像宽高
    136. if not class_names:
    137. for angle in angle_list:
    138. RP = RotateProcessing(int(angle))
    139. image_ori = cv2.imread(img_dir + anno.split(".")[0] + ".jpg")
    140. image_rotate = RP.rotate(image_ori)
    141. cv2.imwrite(out_jpg + "/r" + anno.split(".")[0]+ "_" + angle + ".jpg", image_rotate)
    142. print("touch " + anno.split(".")[0]+ "_" + angle + ".xml")
    143. elif args.exclude_class not in class_names:
    144. for angle in angle_list:
    145. RP = RotateProcessing(int(angle))
    146. image_ori = cv2.imread(img_dir + anno.split(".")[0] + ".jpg")
    147. image_rotate = RP.rotate(image_ori)
    148. cv2.imwrite(out_jpg + "/r" + anno.split(".")[0]+ "_" + angle + ".jpg", image_rotate)
    149. image_rotate_boxes, nH, nW = RP.get_rotate_boxes(image_ori.shape, gt_boxes)
    150. voc_utils.save_to_xml_file("r" + anno.split(".")[0]+ "_" + angle + ".jpg", nH, nW, class_names,
    151. image_rotate_boxes, out_anno)
    152. def main():
    153. parse = argparse.ArgumentParser()
    154. parse.add_argument("--input_path", type=str,
    155. default="/sdb/tmp/Behavior_Normalization_Monitoring/data/Tagged_data_crop/small")
    156. parse.add_argument("--out_path", type=str,
    157. default="/sdb/tmp/Behavior_Normalization_Monitoring/data/Tagged_data_rotate/small")
    158. parse.add_argument("--angle", type=str, default="90+180+270")
    159. parse.add_argument("--subfile", type=str, default="")
    160. parse.add_argument("--exclude_class", type=str, default="phone")
    161. arg = parse.parse_args()
    162. out_anno = arg.out_path + "/Annotations/"
    163. out_jpg = arg.out_path + "/JPEGImages/"
    164. out_bg = arg.out_path + "/bg/"
    165. if not os.path.exists(arg.out_path):
    166. os.makedirs(out_anno)
    167. os.makedirs(out_jpg)
    168. os.makedirs(out_bg)
    169. rotate_images(arg, out_anno, out_jpg, out_bg)
    170. if __name__ == '__main__':
    171. main()