test_widerface.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import argparse
  2. import glob
  3. import time
  4. from pathlib import Path
  5. import os
  6. import cv2
  7. import torch
  8. import torch.backends.cudnn as cudnn
  9. from numpy import random
  10. import numpy as np
  11. from models.experimental import attempt_load
  12. from utils.datasets import letterbox
  13. from utils.general import check_img_size, check_requirements, non_max_suppression_face, apply_classifier, \
  14. scale_coords, xyxy2xywh, strip_optimizer, set_logging, increment_path
  15. from utils.plots import plot_one_box
  16. from utils.torch_utils import select_device, load_classifier, time_synchronized
  17. from tqdm import tqdm
  18. def dynamic_resize(shape, stride=64):
  19. max_size = max(shape[0], shape[1])
  20. if max_size % stride != 0:
  21. max_size = (int(max_size / stride) + 1) * stride
  22. return max_size
  23. def scale_coords_landmarks(img1_shape, coords, img0_shape, ratio_pad=None):
  24. # Rescale coords (xyxy) from img1_shape to img0_shape
  25. if ratio_pad is None: # calculate from img0_shape
  26. gain = min(img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1]) # gain = old / new
  27. pad = (img1_shape[1] - img0_shape[1] * gain) / 2, (img1_shape[0] - img0_shape[0] * gain) / 2 # wh padding
  28. else:
  29. gain = ratio_pad[0][0]
  30. pad = ratio_pad[1]
  31. coords[:, [0, 2, 4, 6, 8]] -= pad[0] # x padding
  32. coords[:, [1, 3, 5, 7, 9]] -= pad[1] # y padding
  33. coords[:, :10] /= gain
  34. #clip_coords(coords, img0_shape)
  35. coords[:, 0].clamp_(0, img0_shape[1]) # x1
  36. coords[:, 1].clamp_(0, img0_shape[0]) # y1
  37. coords[:, 2].clamp_(0, img0_shape[1]) # x2
  38. coords[:, 3].clamp_(0, img0_shape[0]) # y2
  39. coords[:, 4].clamp_(0, img0_shape[1]) # x3
  40. coords[:, 5].clamp_(0, img0_shape[0]) # y3
  41. coords[:, 6].clamp_(0, img0_shape[1]) # x4
  42. coords[:, 7].clamp_(0, img0_shape[0]) # y4
  43. coords[:, 8].clamp_(0, img0_shape[1]) # x5
  44. coords[:, 9].clamp_(0, img0_shape[0]) # y5
  45. return coords
  46. def show_results(img, xywh, conf, landmarks, class_num):
  47. h,w,c = img.shape
  48. tl = 1 or round(0.002 * (h + w) / 2) + 1 # line/font thickness
  49. x1 = int(xywh[0] * w - 0.5 * xywh[2] * w)
  50. y1 = int(xywh[1] * h - 0.5 * xywh[3] * h)
  51. x2 = int(xywh[0] * w + 0.5 * xywh[2] * w)
  52. y2 = int(xywh[1] * h + 0.5 * xywh[3] * h)
  53. cv2.rectangle(img, (x1,y1), (x2, y2), (0,255,0), thickness=tl, lineType=cv2.LINE_AA)
  54. clors = [(255,0,0),(0,255,0),(0,0,255),(255,255,0),(0,255,255)]
  55. for i in range(5):
  56. point_x = int(landmarks[2 * i] * w)
  57. point_y = int(landmarks[2 * i + 1] * h)
  58. cv2.circle(img, (point_x, point_y), tl+1, clors[i], -1)
  59. tf = max(tl - 1, 1) # font thickness
  60. label = str(int(class_num)) + ': ' + str(conf)[:5]
  61. cv2.putText(img, label, (x1, y1 - 2), 0, tl / 3, [225, 255, 255], thickness=tf, lineType=cv2.LINE_AA)
  62. return img
  63. def detect(model, img0):
  64. stride = int(model.stride.max()) # model stride
  65. imgsz = opt.img_size
  66. if imgsz <= 0: # original size
  67. imgsz = dynamic_resize(img0.shape)
  68. imgsz = check_img_size(imgsz, s=64) # check img_size
  69. img = letterbox(img0, imgsz)[0]
  70. # Convert
  71. img = img[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB, to 3x416x416
  72. img = np.ascontiguousarray(img)
  73. img = torch.from_numpy(img).to(device)
  74. img = img.float() # uint8 to fp16/32
  75. img /= 255.0 # 0 - 255 to 0.0 - 1.0
  76. if img.ndimension() == 3:
  77. img = img.unsqueeze(0)
  78. # Inference
  79. pred = model(img, augment=opt.augment)[0]
  80. # Apply NMS
  81. pred = non_max_suppression_face(pred, opt.conf_thres, opt.iou_thres)[0]
  82. gn = torch.tensor(img0.shape)[[1, 0, 1, 0]].to(device) # normalization gain whwh
  83. gn_lks = torch.tensor(img0.shape)[[1, 0, 1, 0, 1, 0, 1, 0, 1, 0]].to(device) # normalization gain landmarks
  84. boxes = []
  85. h, w, c = img0.shape
  86. if pred is not None:
  87. pred[:, :4] = scale_coords(img.shape[2:], pred[:, :4], img0.shape).round()
  88. pred[:, 5:15] = scale_coords_landmarks(img.shape[2:], pred[:, 5:15], img0.shape).round()
  89. for j in range(pred.size()[0]):
  90. xywh = (xyxy2xywh(pred[j, :4].view(1, 4)) / gn).view(-1)
  91. xywh = xywh.data.cpu().numpy()
  92. conf = pred[j, 4].cpu().numpy()
  93. landmarks = (pred[j, 5:15].view(1, 10) / gn_lks).view(-1).tolist()
  94. class_num = pred[j, 15].cpu().numpy()
  95. x1 = int(xywh[0] * w - 0.5 * xywh[2] * w)
  96. y1 = int(xywh[1] * h - 0.5 * xywh[3] * h)
  97. x2 = int(xywh[0] * w + 0.5 * xywh[2] * w)
  98. y2 = int(xywh[1] * h + 0.5 * xywh[3] * h)
  99. boxes.append([x1, y1, x2-x1, y2-y1, conf])
  100. return boxes
  101. if __name__ == '__main__':
  102. parser = argparse.ArgumentParser()
  103. parser.add_argument('--weights', nargs='+', type=str, default='runs/train/exp5/weights/last.pt', help='model.pt path(s)')
  104. parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)')
  105. parser.add_argument('--conf-thres', type=float, default=0.02, help='object confidence threshold')
  106. parser.add_argument('--iou-thres', type=float, default=0.5, help='IOU threshold for NMS')
  107. parser.add_argument('--device', default='0', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
  108. parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS')
  109. parser.add_argument('--augment', action='store_true', help='augmented inference')
  110. parser.add_argument('--update', action='store_true', help='update all models')
  111. parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --class 0, or --class 0 2 3')
  112. parser.add_argument('--project', default='runs/detect', help='save results to project/name')
  113. parser.add_argument('--name', default='exp', help='save results to project/name')
  114. parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
  115. parser.add_argument('--save_folder', default='./widerface_evaluate/widerface_txt/', type=str, help='Dir to save txt results')
  116. parser.add_argument('--dataset_folder', default='../WiderFace/val/images/', type=str, help='dataset path')
  117. parser.add_argument('--folder_pict', default='/yolov5-face/data/widerface/val/wider_val.txt', type=str, help='folder_pict')
  118. opt = parser.parse_args()
  119. print(opt)
  120. # changhy : read folder_pict
  121. pict_folder = {}
  122. with open(opt.folder_pict, 'r') as f:
  123. lines = f.readlines()
  124. for line in lines:
  125. line = line.strip().split('/')
  126. pict_folder[line[-1]] = line[-2]
  127. # Load model
  128. device = select_device(opt.device)
  129. model = attempt_load(opt.weights, map_location=device) # load FP32 model
  130. with torch.no_grad():
  131. # testing dataset
  132. testset_folder = opt.dataset_folder
  133. for image_path in tqdm(glob.glob(os.path.join(testset_folder, '*'))):
  134. if image_path.endswith('.txt'):
  135. continue
  136. img0 = cv2.imread(image_path) # BGR
  137. if img0 is None:
  138. print(f'ignore : {image_path}')
  139. continue
  140. boxes = detect(model, img0)
  141. # --------------------------------------------------------------------
  142. image_name = os.path.basename(image_path)
  143. txt_name = os.path.splitext(image_name)[0] + ".txt"
  144. save_name = os.path.join(opt.save_folder, pict_folder[image_name], txt_name)
  145. dirname = os.path.dirname(save_name)
  146. if not os.path.isdir(dirname):
  147. os.makedirs(dirname)
  148. with open(save_name, "w") as fd:
  149. file_name = os.path.basename(save_name)[:-4] + "\n"
  150. bboxs_num = str(len(boxes)) + "\n"
  151. fd.write(file_name)
  152. fd.write(bboxs_num)
  153. for box in boxes:
  154. fd.write('%d %d %d %d %.03f' % (box[0], box[1], box[2], box[3], box[4] if box[4] <= 1 else 1) + '\n')
  155. print('done.')