openvino_benchmark.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. # Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import cv2
  15. import numpy as np
  16. import time
  17. import argparse
  18. from scipy.special import softmax
  19. from openvino.runtime import Core
  20. def image_preprocess(img_path, re_shape):
  21. img = cv2.imread(img_path)
  22. img = cv2.resize(
  23. img, (re_shape, re_shape), interpolation=cv2.INTER_LANCZOS4)
  24. img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
  25. img = np.transpose(img, [2, 0, 1]) / 255
  26. img = np.expand_dims(img, 0)
  27. img_mean = np.array([0.485, 0.456, 0.406]).reshape((3, 1, 1))
  28. img_std = np.array([0.229, 0.224, 0.225]).reshape((3, 1, 1))
  29. img -= img_mean
  30. img /= img_std
  31. return img.astype(np.float32)
  32. def draw_box(img, results, class_label, scale_x, scale_y):
  33. label_list = list(
  34. map(lambda x: x.strip(), open(class_label, 'r').readlines()))
  35. for i in range(len(results)):
  36. print(label_list[int(results[i][0])], ':', results[i][1])
  37. bbox = results[i, 2:]
  38. label_id = int(results[i, 0])
  39. score = results[i, 1]
  40. if (score > 0.20):
  41. xmin, ymin, xmax, ymax = [
  42. int(bbox[0] * scale_x), int(bbox[1] * scale_y),
  43. int(bbox[2] * scale_x), int(bbox[3] * scale_y)
  44. ]
  45. cv2.rectangle(img, (xmin, ymin), (xmax, ymax), (0, 255, 0), 3)
  46. font = cv2.FONT_HERSHEY_SIMPLEX
  47. label_text = label_list[label_id]
  48. cv2.rectangle(img, (xmin, ymin), (xmax, ymin - 60), (0, 255, 0), -1)
  49. cv2.putText(img, "#" + label_text, (xmin, ymin - 10), font, 1,
  50. (255, 255, 255), 2, cv2.LINE_AA)
  51. cv2.putText(img,
  52. str(round(score, 3)), (xmin, ymin - 40), font, 0.8,
  53. (255, 255, 255), 2, cv2.LINE_AA)
  54. return img
  55. def hard_nms(box_scores, iou_threshold, top_k=-1, candidate_size=200):
  56. """
  57. Args:
  58. box_scores (N, 5): boxes in corner-form and probabilities.
  59. iou_threshold: intersection over union threshold.
  60. top_k: keep top_k results. If k <= 0, keep all the results.
  61. candidate_size: only consider the candidates with the highest scores.
  62. Returns:
  63. picked: a list of indexes of the kept boxes
  64. """
  65. scores = box_scores[:, -1]
  66. boxes = box_scores[:, :-1]
  67. picked = []
  68. indexes = np.argsort(scores)
  69. indexes = indexes[-candidate_size:]
  70. while len(indexes) > 0:
  71. current = indexes[-1]
  72. picked.append(current)
  73. if 0 < top_k == len(picked) or len(indexes) == 1:
  74. break
  75. current_box = boxes[current, :]
  76. indexes = indexes[:-1]
  77. rest_boxes = boxes[indexes, :]
  78. iou = iou_of(
  79. rest_boxes,
  80. np.expand_dims(
  81. current_box, axis=0), )
  82. indexes = indexes[iou <= iou_threshold]
  83. return box_scores[picked, :]
  84. def iou_of(boxes0, boxes1, eps=1e-5):
  85. """Return intersection-over-union (Jaccard index) of boxes.
  86. Args:
  87. boxes0 (N, 4): ground truth boxes.
  88. boxes1 (N or 1, 4): predicted boxes.
  89. eps: a small number to avoid 0 as denominator.
  90. Returns:
  91. iou (N): IoU values.
  92. """
  93. overlap_left_top = np.maximum(boxes0[..., :2], boxes1[..., :2])
  94. overlap_right_bottom = np.minimum(boxes0[..., 2:], boxes1[..., 2:])
  95. overlap_area = area_of(overlap_left_top, overlap_right_bottom)
  96. area0 = area_of(boxes0[..., :2], boxes0[..., 2:])
  97. area1 = area_of(boxes1[..., :2], boxes1[..., 2:])
  98. return overlap_area / (area0 + area1 - overlap_area + eps)
  99. def area_of(left_top, right_bottom):
  100. """Compute the areas of rectangles given two corners.
  101. Args:
  102. left_top (N, 2): left top corner.
  103. right_bottom (N, 2): right bottom corner.
  104. Returns:
  105. area (N): return the area.
  106. """
  107. hw = np.clip(right_bottom - left_top, 0.0, None)
  108. return hw[..., 0] * hw[..., 1]
  109. class PicoDetPostProcess(object):
  110. """
  111. Args:
  112. input_shape (int): network input image size
  113. ori_shape (int): ori image shape of before padding
  114. scale_factor (float): scale factor of ori image
  115. enable_mkldnn (bool): whether to open MKLDNN
  116. """
  117. def __init__(self,
  118. input_shape,
  119. ori_shape,
  120. scale_factor,
  121. strides=[8, 16, 32, 64],
  122. score_threshold=0.4,
  123. nms_threshold=0.5,
  124. nms_top_k=1000,
  125. keep_top_k=100):
  126. self.ori_shape = ori_shape
  127. self.input_shape = input_shape
  128. self.scale_factor = scale_factor
  129. self.strides = strides
  130. self.score_threshold = score_threshold
  131. self.nms_threshold = nms_threshold
  132. self.nms_top_k = nms_top_k
  133. self.keep_top_k = keep_top_k
  134. def warp_boxes(self, boxes, ori_shape):
  135. """Apply transform to boxes
  136. """
  137. width, height = ori_shape[1], ori_shape[0]
  138. n = len(boxes)
  139. if n:
  140. # warp points
  141. xy = np.ones((n * 4, 3))
  142. xy[:, :2] = boxes[:, [0, 1, 2, 3, 0, 3, 2, 1]].reshape(
  143. n * 4, 2) # x1y1, x2y2, x1y2, x2y1
  144. # xy = xy @ M.T # transform
  145. xy = (xy[:, :2] / xy[:, 2:3]).reshape(n, 8) # rescale
  146. # create new boxes
  147. x = xy[:, [0, 2, 4, 6]]
  148. y = xy[:, [1, 3, 5, 7]]
  149. xy = np.concatenate(
  150. (x.min(1), y.min(1), x.max(1), y.max(1))).reshape(4, n).T
  151. # clip boxes
  152. xy[:, [0, 2]] = xy[:, [0, 2]].clip(0, width)
  153. xy[:, [1, 3]] = xy[:, [1, 3]].clip(0, height)
  154. return xy.astype(np.float32)
  155. else:
  156. return boxes
  157. def __call__(self, scores, raw_boxes):
  158. batch_size = raw_boxes[0].shape[0]
  159. reg_max = int(raw_boxes[0].shape[-1] / 4 - 1)
  160. out_boxes_num = []
  161. out_boxes_list = []
  162. for batch_id in range(batch_size):
  163. # generate centers
  164. decode_boxes = []
  165. select_scores = []
  166. for stride, box_distribute, score in zip(self.strides, raw_boxes,
  167. scores):
  168. box_distribute = box_distribute[batch_id]
  169. score = score[batch_id]
  170. # centers
  171. fm_h = self.input_shape[0] / stride
  172. fm_w = self.input_shape[1] / stride
  173. h_range = np.arange(fm_h)
  174. w_range = np.arange(fm_w)
  175. ww, hh = np.meshgrid(w_range, h_range)
  176. ct_row = (hh.flatten() + 0.5) * stride
  177. ct_col = (ww.flatten() + 0.5) * stride
  178. center = np.stack((ct_col, ct_row, ct_col, ct_row), axis=1)
  179. # box distribution to distance
  180. reg_range = np.arange(reg_max + 1)
  181. box_distance = box_distribute.reshape((-1, reg_max + 1))
  182. box_distance = softmax(box_distance, axis=1)
  183. box_distance = box_distance * np.expand_dims(reg_range, axis=0)
  184. box_distance = np.sum(box_distance, axis=1).reshape((-1, 4))
  185. box_distance = box_distance * stride
  186. # top K candidate
  187. topk_idx = np.argsort(score.max(axis=1))[::-1]
  188. topk_idx = topk_idx[:self.nms_top_k]
  189. center = center[topk_idx]
  190. score = score[topk_idx]
  191. box_distance = box_distance[topk_idx]
  192. # decode box
  193. decode_box = center + [-1, -1, 1, 1] * box_distance
  194. select_scores.append(score)
  195. decode_boxes.append(decode_box)
  196. # nms
  197. bboxes = np.concatenate(decode_boxes, axis=0)
  198. confidences = np.concatenate(select_scores, axis=0)
  199. picked_box_probs = []
  200. picked_labels = []
  201. for class_index in range(0, confidences.shape[1]):
  202. probs = confidences[:, class_index]
  203. mask = probs > self.score_threshold
  204. probs = probs[mask]
  205. if probs.shape[0] == 0:
  206. continue
  207. subset_boxes = bboxes[mask, :]
  208. box_probs = np.concatenate(
  209. [subset_boxes, probs.reshape(-1, 1)], axis=1)
  210. box_probs = hard_nms(
  211. box_probs,
  212. iou_threshold=self.nms_threshold,
  213. top_k=self.keep_top_k, )
  214. picked_box_probs.append(box_probs)
  215. picked_labels.extend([class_index] * box_probs.shape[0])
  216. if len(picked_box_probs) == 0:
  217. out_boxes_list.append(np.empty((0, 4)))
  218. out_boxes_num.append(0)
  219. else:
  220. picked_box_probs = np.concatenate(picked_box_probs)
  221. # resize output boxes
  222. picked_box_probs[:, :4] = self.warp_boxes(
  223. picked_box_probs[:, :4], self.ori_shape[batch_id])
  224. im_scale = np.concatenate([
  225. self.scale_factor[batch_id][::-1],
  226. self.scale_factor[batch_id][::-1]
  227. ])
  228. picked_box_probs[:, :4] /= im_scale
  229. # clas score box
  230. out_boxes_list.append(
  231. np.concatenate(
  232. [
  233. np.expand_dims(
  234. np.array(picked_labels),
  235. axis=-1), np.expand_dims(
  236. picked_box_probs[:, 4], axis=-1),
  237. picked_box_probs[:, :4]
  238. ],
  239. axis=1))
  240. out_boxes_num.append(len(picked_labels))
  241. out_boxes_list = np.concatenate(out_boxes_list, axis=0)
  242. out_boxes_num = np.asarray(out_boxes_num).astype(np.int32)
  243. return out_boxes_list, out_boxes_num
  244. def detect(img_file, compiled_model, re_shape, class_label):
  245. output = compiled_model.infer_new_request({0: test_image})
  246. result_ie = list(output.values()) #[0]
  247. test_im_shape = np.array([[re_shape, re_shape]]).astype('float32')
  248. test_scale_factor = np.array([[1, 1]]).astype('float32')
  249. np_score_list = []
  250. np_boxes_list = []
  251. num_outs = int(len(result_ie) / 2)
  252. for out_idx in range(num_outs):
  253. np_score_list.append(result_ie[out_idx])
  254. np_boxes_list.append(result_ie[out_idx + num_outs])
  255. postprocess = PicoDetPostProcess(test_image.shape[2:], test_im_shape,
  256. test_scale_factor)
  257. np_boxes, np_boxes_num = postprocess(np_score_list, np_boxes_list)
  258. image = cv2.imread(img_file, 1)
  259. scale_x = image.shape[1] / test_image.shape[3]
  260. scale_y = image.shape[0] / test_image.shape[2]
  261. res_image = draw_box(image, np_boxes, class_label, scale_x, scale_y)
  262. cv2.imwrite('res.jpg', res_image)
  263. cv2.imshow("res", res_image)
  264. cv2.waitKey()
  265. def benchmark(test_image, compiled_model):
  266. # benchmark
  267. loop_num = 100
  268. warm_up = 8
  269. timeall = 0
  270. time_min = float("inf")
  271. time_max = float('-inf')
  272. for i in range(loop_num + warm_up):
  273. time0 = time.time()
  274. #perform the inference step
  275. output = compiled_model.infer_new_request({0: test_image})
  276. time1 = time.time()
  277. timed = time1 - time0
  278. if i >= warm_up:
  279. timeall = timeall + timed
  280. time_min = min(time_min, timed)
  281. time_max = max(time_max, timed)
  282. time_avg = timeall / loop_num
  283. print('inference_time(ms): min={}, max={}, avg={}'.format(
  284. round(time_min * 1000, 2),
  285. round(time_max * 1000, 1), round(time_avg * 1000, 1)))
  286. if __name__ == '__main__':
  287. parser = argparse.ArgumentParser()
  288. parser.add_argument(
  289. '--benchmark', type=int, default=1, help="0:detect; 1:benchmark")
  290. parser.add_argument(
  291. '--img_path',
  292. type=str,
  293. default='../../../../demo/000000014439.jpg',
  294. help="image path")
  295. parser.add_argument(
  296. '--onnx_path',
  297. type=str,
  298. default='out_onnxsim/picodet_s_320_processed.onnx',
  299. help="onnx filepath")
  300. parser.add_argument('--in_shape', type=int, default=320, help="input_size")
  301. parser.add_argument(
  302. '--class_label',
  303. type=str,
  304. default='coco_label.txt',
  305. help="class label file")
  306. args = parser.parse_args()
  307. ie = Core()
  308. net = ie.read_model(args.onnx_path)
  309. test_image = image_preprocess(args.img_path, args.in_shape)
  310. compiled_model = ie.compile_model(net, 'CPU')
  311. if args.benchmark == 0:
  312. detect(args.img_path, compiled_model, args.in_shape, args.class_label)
  313. if args.benchmark == 1:
  314. benchmark(test_image, compiled_model)