针对目标检测标注数据做的数据预处理操作。
# -*- coding: utf-8 -*-
"""
@File : rotate_images.py
@Time : 2020-8-19 21:21
@Author : qian733
@Description :
"""
import os
import cv2
import shutil
import argparse
import voc_utils
import numpy as np
class RotateProcessing(object):
def __init__(self, angle, center=0, scale=1.0):
"""
:param angle: 旋转角度
:param center: 旋转的中心点,默认为图像中心,以0代替
:param scale: 图像缩放因子
"""
self.angle = angle
self.center = center
self.scale = scale
def rotate(self, image):
(h, w) = image.shape[:2]
if not self.center:
(cx, cy) = (w / 2, h / 2)
else:
(cx, cy) = self.center # 二元元组
# 设置旋转矩阵
matrix = cv2.getRotationMatrix2D((cx, cy), -self.angle, self.scale)
cos = np.abs(matrix[0, 0])
sin = np.abs(matrix[0, 1])
# 计算图像旋转后的新边界
new_weight = int((h * sin) + (w * cos))
new_height = int((h * cos) + (w * sin))
# 调整旋转矩阵的移动距离(t_{x}, t_{y})
matrix[0, 2] += (new_weight / 2) - cx
matrix[1, 2] += (new_height / 2) - cy
return cv2.warpAffine(image, matrix, (new_weight, new_height))
def get_rotate_boxes(self, img_shape, gt_bboxes):
"""
:param img_shape:
:param gt_bboxes: 由TensorFlow返回的预测结果
:return:
"""
num_boxes = len(gt_bboxes)
(h, w) = img_shape[0], img_shape[1]
mask = np.zeros(shape=(h, w, num_boxes), dtype=np.uint8)
for i, box in enumerate(gt_bboxes):
y1, x1, y2, x2 = box
# 这里的gt是大于0的float,不需要分别乘以长宽,但是需要取整
y1, y2 = int(np.floor(y1)), int(np.ceil(y2))
x1, x2 = int(np.floor(x1)), int(np.ceil(x2))
mask[y1:y2, x1:x2, i] = 1
if not self.center:
(cx, cy) = (w / 2, h / 2)
else:
(cx, cy) = self.center # 二元元组
matrix = cv2.getRotationMatrix2D((cx, cy), -self.angle, self.scale)
cos = np.abs(matrix[0, 0])
sin = np.abs(matrix[0, 1])
new_weight = int((h * sin) + (w * cos))
new_height = int((h * cos) + (w * sin))
matrix[0, 2] += (new_weight / 2) - cx
matrix[1, 2] += (new_height / 2) - cy
new_mask = cv2.warpAffine(mask, matrix, (new_weight, new_height))
if num_boxes == 1:
new_mask = new_mask[:, :, np.newaxis]
new_gt_boxes = np.zeros_like(gt_bboxes)
for i in range(num_boxes):
hs, ws = np.where(new_mask[:, :, i] == 1)
y1, x1, y2, x2 = 0, 0, 0, 0
if len(hs) > 0:
y1, y2 = np.min(hs), np.max(hs)
if len(ws) > 0:
x1, x2 = np.min(ws), np.max(ws)
new_gt_boxes[i] = np.array([y1, x1, y2, x2])
return new_gt_boxes, new_height, new_weight
def rotate_images_special(args, out_anno, out_jpg, out_bg):
"""
:param args:
:param out_anno:
:param out_jpg:
:param out_bg:
:return:
"""
if args.subfile:
subfile_list = args.subfile.split("+")
else:
subfile_list = os.listdir(args.input_path)
angle_list = args.angle.split("+")
for sub in os.listdir(args.input_path):
if sub in subfile_list:
print(sub)
sub_dir = os.path.join(args.input_path, sub) # 如 root_dir/small_0511 下面放着图像和xml文件
anno_dir = sub_dir + "/Annotations/"
img_dir = sub_dir + "/JPEGImages/"
bg_list = []
for anno in os.listdir(anno_dir):
xml_path = os.path.join(anno_dir, anno) # xml
_, height, width, class_names, gt_boxes = voc_utils.parse_voc(xml_path)
# 如果class为空,则进行去重;如果class只有第一类,则进行旋转
if args.exclude_class not in class_names:
for angle in angle_list:
RP = RotateProcessing(int(angle))
image_ori = cv2.imread(img_dir + anno.split(".")[0] + ".jpg")
image_rotate = RP.rotate(image_ori)
cv2.imwrite(out_jpg + "/r" + angle + anno.split(".")[0] + ".jpg", image_rotate)
image_rotate_boxes, nH, nW = RP.get_rotate_boxes(image_ori.shape, gt_boxes)
voc_utils.save_to_xml_file("r" + angle + anno.split(".")[0] + ".jpg", nH, nW, class_names, image_rotate_boxes, out_anno)
def rotate_images(args, out_anno, out_jpg, out_bg):
"""
:param args:
:param out_anno:
:param out_jpg:
:param out_bg:
:return:
"""
if args.subfile:
subfile_list = args.subfile.split("+")
else:
subfile_list = os.listdir(args.input_path)
angle_list = args.angle.split("+")
for sub in os.listdir(args.input_path):
if sub in subfile_list:
print(sub)
sub_dir = os.path.join(args.input_path, sub) # 如 root_dir/small_0511 下面放着图像和xml文件
anno_dir = sub_dir + "/Annotations/"
img_dir = sub_dir + "/JPEGImages/"
bg_list = []
for anno in os.listdir(anno_dir):
xml_path = os.path.join(anno_dir, anno) # xml
_, height, width, class_names, gt_boxes = voc_utils.parse_voc(xml_path)
# 如果class为空,则旋转图像并修改xml中对应的图像宽高
if not class_names:
for angle in angle_list:
RP = RotateProcessing(int(angle))
image_ori = cv2.imread(img_dir + anno.split(".")[0] + ".jpg")
image_rotate = RP.rotate(image_ori)
cv2.imwrite(out_jpg + "/r" + anno.split(".")[0]+ "_" + angle + ".jpg", image_rotate)
print("touch " + anno.split(".")[0]+ "_" + angle + ".xml")
elif args.exclude_class not in class_names:
for angle in angle_list:
RP = RotateProcessing(int(angle))
image_ori = cv2.imread(img_dir + anno.split(".")[0] + ".jpg")
image_rotate = RP.rotate(image_ori)
cv2.imwrite(out_jpg + "/r" + anno.split(".")[0]+ "_" + angle + ".jpg", image_rotate)
image_rotate_boxes, nH, nW = RP.get_rotate_boxes(image_ori.shape, gt_boxes)
voc_utils.save_to_xml_file("r" + anno.split(".")[0]+ "_" + angle + ".jpg", nH, nW, class_names,
image_rotate_boxes, out_anno)
def main():
parse = argparse.ArgumentParser()
parse.add_argument("--input_path", type=str,
default="/sdb/tmp/Behavior_Normalization_Monitoring/data/Tagged_data_crop/small")
parse.add_argument("--out_path", type=str,
default="/sdb/tmp/Behavior_Normalization_Monitoring/data/Tagged_data_rotate/small")
parse.add_argument("--angle", type=str, default="90+180+270")
parse.add_argument("--subfile", type=str, default="")
parse.add_argument("--exclude_class", type=str, default="phone")
arg = parse.parse_args()
out_anno = arg.out_path + "/Annotations/"
out_jpg = arg.out_path + "/JPEGImages/"
out_bg = arg.out_path + "/bg/"
if not os.path.exists(arg.out_path):
os.makedirs(out_anno)
os.makedirs(out_jpg)
os.makedirs(out_bg)
rotate_images(arg, out_anno, out_jpg, out_bg)
if __name__ == '__main__':
main()