det_infer.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. # Copyright (c) 2021 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 os
  15. import yaml
  16. import glob
  17. from functools import reduce
  18. import cv2
  19. import numpy as np
  20. import math
  21. import paddle
  22. from paddle.inference import Config
  23. from paddle.inference import create_predictor
  24. import sys
  25. # add deploy path of PadleDetection to sys.path
  26. parent_path = os.path.abspath(os.path.join(__file__, *(['..'])))
  27. sys.path.insert(0, parent_path)
  28. from benchmark_utils import PaddleInferBenchmark
  29. from picodet_postprocess import PicoDetPostProcess
  30. from preprocess import preprocess, Resize, NormalizeImage, Permute, PadStride, LetterBoxResize, decode_image
  31. from mot.visualize import visualize_box_mask
  32. from mot_utils import argsparser, Timer, get_current_memory_mb
  33. # Global dictionary
  34. SUPPORT_MODELS = {
  35. 'YOLO',
  36. 'PicoDet',
  37. 'JDE',
  38. 'FairMOT',
  39. 'DeepSORT',
  40. 'StrongBaseline',
  41. }
  42. def bench_log(detector, img_list, model_info, batch_size=1, name=None):
  43. mems = {
  44. 'cpu_rss_mb': detector.cpu_mem / len(img_list),
  45. 'gpu_rss_mb': detector.gpu_mem / len(img_list),
  46. 'gpu_util': detector.gpu_util * 100 / len(img_list)
  47. }
  48. perf_info = detector.det_times.report(average=True)
  49. data_info = {
  50. 'batch_size': batch_size,
  51. 'shape': "dynamic_shape",
  52. 'data_num': perf_info['img_num']
  53. }
  54. log = PaddleInferBenchmark(detector.config, model_info, data_info,
  55. perf_info, mems)
  56. log(name)
  57. class Detector(object):
  58. """
  59. Args:
  60. pred_config (object): config of model, defined by `Config(model_dir)`
  61. model_dir (str): root path of model.pdiparams, model.pdmodel and infer_cfg.yml
  62. device (str): Choose the device you want to run, it can be: CPU/GPU/XPU, default is CPU
  63. run_mode (str): mode of running(paddle/trt_fp32/trt_fp16)
  64. batch_size (int): size of pre batch in inference
  65. trt_min_shape (int): min shape for dynamic shape in trt
  66. trt_max_shape (int): max shape for dynamic shape in trt
  67. trt_opt_shape (int): opt shape for dynamic shape in trt
  68. trt_calib_mode (bool): If the model is produced by TRT offline quantitative
  69. calibration, trt_calib_mode need to set True
  70. cpu_threads (int): cpu threads
  71. enable_mkldnn (bool): whether to open MKLDNN
  72. output_dir (str): The path of output
  73. threshold (float): The threshold of score for visualization
  74. """
  75. def __init__(
  76. self,
  77. model_dir,
  78. device='CPU',
  79. run_mode='paddle',
  80. batch_size=1,
  81. trt_min_shape=1,
  82. trt_max_shape=1280,
  83. trt_opt_shape=640,
  84. trt_calib_mode=False,
  85. cpu_threads=1,
  86. enable_mkldnn=False,
  87. output_dir='output',
  88. threshold=0.5, ):
  89. self.pred_config = self.set_config(model_dir)
  90. self.predictor, self.config = load_predictor(
  91. model_dir,
  92. run_mode=run_mode,
  93. batch_size=batch_size,
  94. min_subgraph_size=self.pred_config.min_subgraph_size,
  95. device=device,
  96. use_dynamic_shape=self.pred_config.use_dynamic_shape,
  97. trt_min_shape=trt_min_shape,
  98. trt_max_shape=trt_max_shape,
  99. trt_opt_shape=trt_opt_shape,
  100. trt_calib_mode=trt_calib_mode,
  101. cpu_threads=cpu_threads,
  102. enable_mkldnn=enable_mkldnn)
  103. self.det_times = Timer()
  104. self.cpu_mem, self.gpu_mem, self.gpu_util = 0, 0, 0
  105. self.batch_size = batch_size
  106. self.output_dir = output_dir
  107. self.threshold = threshold
  108. def set_config(self, model_dir):
  109. return PredictConfig(model_dir)
  110. def preprocess(self, image_list):
  111. preprocess_ops = []
  112. for op_info in self.pred_config.preprocess_infos:
  113. new_op_info = op_info.copy()
  114. op_type = new_op_info.pop('type')
  115. preprocess_ops.append(eval(op_type)(**new_op_info))
  116. input_im_lst = []
  117. input_im_info_lst = []
  118. for im_path in image_list:
  119. im, im_info = preprocess(im_path, preprocess_ops)
  120. input_im_lst.append(im)
  121. input_im_info_lst.append(im_info)
  122. inputs = create_inputs(input_im_lst, input_im_info_lst)
  123. input_names = self.predictor.get_input_names()
  124. for i in range(len(input_names)):
  125. input_tensor = self.predictor.get_input_handle(input_names[i])
  126. input_tensor.copy_from_cpu(inputs[input_names[i]])
  127. return inputs
  128. def postprocess(self, inputs, result):
  129. # postprocess output of predictor
  130. np_boxes_num = result['boxes_num']
  131. if np_boxes_num[0] <= 0:
  132. print('[WARNNING] No object detected.')
  133. result = {'boxes': np.zeros([0, 6]), 'boxes_num': [0]}
  134. result = {k: v for k, v in result.items() if v is not None}
  135. return result
  136. def predict(self, repeats=1):
  137. '''
  138. Args:
  139. repeats (int): repeats number for prediction
  140. Returns:
  141. result (dict): include 'boxes': np.ndarray: shape:[N,6], N: number of box,
  142. matix element:[class, score, x_min, y_min, x_max, y_max]
  143. '''
  144. # model prediction
  145. np_boxes, np_boxes_num = None, None
  146. for i in range(repeats):
  147. self.predictor.run()
  148. output_names = self.predictor.get_output_names()
  149. boxes_tensor = self.predictor.get_output_handle(output_names[0])
  150. np_boxes = boxes_tensor.copy_to_cpu()
  151. boxes_num = self.predictor.get_output_handle(output_names[1])
  152. np_boxes_num = boxes_num.copy_to_cpu()
  153. result = dict(boxes=np_boxes, boxes_num=np_boxes_num)
  154. return result
  155. def merge_batch_result(self, batch_result):
  156. if len(batch_result) == 1:
  157. return batch_result[0]
  158. res_key = batch_result[0].keys()
  159. results = {k: [] for k in res_key}
  160. for res in batch_result:
  161. for k, v in res.items():
  162. results[k].append(v)
  163. for k, v in results.items():
  164. results[k] = np.concatenate(v)
  165. return results
  166. def get_timer(self):
  167. return self.det_times
  168. def predict_image(self,
  169. image_list,
  170. run_benchmark=False,
  171. repeats=1,
  172. visual=True):
  173. batch_loop_cnt = math.ceil(float(len(image_list)) / self.batch_size)
  174. results = []
  175. for i in range(batch_loop_cnt):
  176. start_index = i * self.batch_size
  177. end_index = min((i + 1) * self.batch_size, len(image_list))
  178. batch_image_list = image_list[start_index:end_index]
  179. if run_benchmark:
  180. # preprocess
  181. inputs = self.preprocess(batch_image_list) # warmup
  182. self.det_times.preprocess_time_s.start()
  183. inputs = self.preprocess(batch_image_list)
  184. self.det_times.preprocess_time_s.end()
  185. # model prediction
  186. result = self.predict(repeats=repeats) # warmup
  187. self.det_times.inference_time_s.start()
  188. result = self.predict(repeats=repeats)
  189. self.det_times.inference_time_s.end(repeats=repeats)
  190. # postprocess
  191. result_warmup = self.postprocess(inputs, result) # warmup
  192. self.det_times.postprocess_time_s.start()
  193. result = self.postprocess(inputs, result)
  194. self.det_times.postprocess_time_s.end()
  195. self.det_times.img_num += len(batch_image_list)
  196. cm, gm, gu = get_current_memory_mb()
  197. self.cpu_mem += cm
  198. self.gpu_mem += gm
  199. self.gpu_util += gu
  200. else:
  201. # preprocess
  202. self.det_times.preprocess_time_s.start()
  203. inputs = self.preprocess(batch_image_list)
  204. self.det_times.preprocess_time_s.end()
  205. # model prediction
  206. self.det_times.inference_time_s.start()
  207. result = self.predict()
  208. self.det_times.inference_time_s.end()
  209. # postprocess
  210. self.det_times.postprocess_time_s.start()
  211. result = self.postprocess(inputs, result)
  212. self.det_times.postprocess_time_s.end()
  213. self.det_times.img_num += len(batch_image_list)
  214. if visual:
  215. visualize(
  216. batch_image_list,
  217. result,
  218. self.pred_config.labels,
  219. output_dir=self.output_dir,
  220. threshold=self.threshold)
  221. results.append(result)
  222. if visual:
  223. print('Test iter {}'.format(i))
  224. results = self.merge_batch_result(results)
  225. return results
  226. def predict_video(self, video_file, camera_id):
  227. video_out_name = 'output.mp4'
  228. if camera_id != -1:
  229. capture = cv2.VideoCapture(camera_id)
  230. else:
  231. capture = cv2.VideoCapture(video_file)
  232. video_out_name = os.path.split(video_file)[-1]
  233. # Get Video info : resolution, fps, frame count
  234. width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH))
  235. height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT))
  236. fps = int(capture.get(cv2.CAP_PROP_FPS))
  237. frame_count = int(capture.get(cv2.CAP_PROP_FRAME_COUNT))
  238. print("fps: %d, frame_count: %d" % (fps, frame_count))
  239. if not os.path.exists(self.output_dir):
  240. os.makedirs(self.output_dir)
  241. out_path = os.path.join(self.output_dir, video_out_name)
  242. fourcc = cv2.VideoWriter_fourcc(* 'mp4v')
  243. writer = cv2.VideoWriter(out_path, fourcc, fps, (width, height))
  244. index = 1
  245. while (1):
  246. ret, frame = capture.read()
  247. if not ret:
  248. break
  249. print('detect frame: %d' % (index))
  250. index += 1
  251. results = self.predict_image([frame], visual=False)
  252. im = visualize_box_mask(
  253. frame,
  254. results,
  255. self.pred_config.labels,
  256. threshold=self.threshold)
  257. im = np.array(im)
  258. writer.write(im)
  259. if camera_id != -1:
  260. cv2.imshow('Mask Detection', im)
  261. if cv2.waitKey(1) & 0xFF == ord('q'):
  262. break
  263. writer.release()
  264. def create_inputs(imgs, im_info):
  265. """generate input for different model type
  266. Args:
  267. imgs (list(numpy)): list of images (np.ndarray)
  268. im_info (list(dict)): list of image info
  269. Returns:
  270. inputs (dict): input of model
  271. """
  272. inputs = {}
  273. im_shape = []
  274. scale_factor = []
  275. if len(imgs) == 1:
  276. inputs['image'] = np.array((imgs[0], )).astype('float32')
  277. inputs['im_shape'] = np.array(
  278. (im_info[0]['im_shape'], )).astype('float32')
  279. inputs['scale_factor'] = np.array(
  280. (im_info[0]['scale_factor'], )).astype('float32')
  281. return inputs
  282. for e in im_info:
  283. im_shape.append(np.array((e['im_shape'], )).astype('float32'))
  284. scale_factor.append(np.array((e['scale_factor'], )).astype('float32'))
  285. inputs['im_shape'] = np.concatenate(im_shape, axis=0)
  286. inputs['scale_factor'] = np.concatenate(scale_factor, axis=0)
  287. imgs_shape = [[e.shape[1], e.shape[2]] for e in imgs]
  288. max_shape_h = max([e[0] for e in imgs_shape])
  289. max_shape_w = max([e[1] for e in imgs_shape])
  290. padding_imgs = []
  291. for img in imgs:
  292. im_c, im_h, im_w = img.shape[:]
  293. padding_im = np.zeros(
  294. (im_c, max_shape_h, max_shape_w), dtype=np.float32)
  295. padding_im[:, :im_h, :im_w] = img
  296. padding_imgs.append(padding_im)
  297. inputs['image'] = np.stack(padding_imgs, axis=0)
  298. return inputs
  299. class PredictConfig():
  300. """set config of preprocess, postprocess and visualize
  301. Args:
  302. model_dir (str): root path of model.yml
  303. """
  304. def __init__(self, model_dir):
  305. # parsing Yaml config for Preprocess
  306. deploy_file = os.path.join(model_dir, 'infer_cfg.yml')
  307. with open(deploy_file) as f:
  308. yml_conf = yaml.safe_load(f)
  309. self.check_model(yml_conf)
  310. self.arch = yml_conf['arch']
  311. self.preprocess_infos = yml_conf['Preprocess']
  312. self.min_subgraph_size = yml_conf['min_subgraph_size']
  313. self.labels = yml_conf['label_list']
  314. self.mask = False
  315. self.use_dynamic_shape = yml_conf['use_dynamic_shape']
  316. if 'mask' in yml_conf:
  317. self.mask = yml_conf['mask']
  318. self.tracker = None
  319. if 'tracker' in yml_conf:
  320. self.tracker = yml_conf['tracker']
  321. if 'NMS' in yml_conf:
  322. self.nms = yml_conf['NMS']
  323. if 'fpn_stride' in yml_conf:
  324. self.fpn_stride = yml_conf['fpn_stride']
  325. self.print_config()
  326. def check_model(self, yml_conf):
  327. """
  328. Raises:
  329. ValueError: loaded model not in supported model type
  330. """
  331. for support_model in SUPPORT_MODELS:
  332. if support_model in yml_conf['arch']:
  333. return True
  334. raise ValueError("Unsupported arch: {}, expect {}".format(yml_conf[
  335. 'arch'], SUPPORT_MODELS))
  336. def print_config(self):
  337. print('----------- Model Configuration -----------')
  338. print('%s: %s' % ('Model Arch', self.arch))
  339. print('%s: ' % ('Transform Order'))
  340. for op_info in self.preprocess_infos:
  341. print('--%s: %s' % ('transform op', op_info['type']))
  342. print('--------------------------------------------')
  343. def load_predictor(model_dir,
  344. run_mode='paddle',
  345. batch_size=1,
  346. device='CPU',
  347. min_subgraph_size=3,
  348. use_dynamic_shape=False,
  349. trt_min_shape=1,
  350. trt_max_shape=1280,
  351. trt_opt_shape=640,
  352. trt_calib_mode=False,
  353. cpu_threads=1,
  354. enable_mkldnn=False):
  355. """set AnalysisConfig, generate AnalysisPredictor
  356. Args:
  357. model_dir (str): root path of __model__ and __params__
  358. device (str): Choose the device you want to run, it can be: CPU/GPU/XPU, default is CPU
  359. run_mode (str): mode of running(paddle/trt_fp32/trt_fp16/trt_int8)
  360. use_dynamic_shape (bool): use dynamic shape or not
  361. trt_min_shape (int): min shape for dynamic shape in trt
  362. trt_max_shape (int): max shape for dynamic shape in trt
  363. trt_opt_shape (int): opt shape for dynamic shape in trt
  364. trt_calib_mode (bool): If the model is produced by TRT offline quantitative
  365. calibration, trt_calib_mode need to set True
  366. Returns:
  367. predictor (PaddlePredictor): AnalysisPredictor
  368. Raises:
  369. ValueError: predict by TensorRT need device == 'GPU'.
  370. """
  371. if device != 'GPU' and run_mode != 'paddle':
  372. raise ValueError(
  373. "Predict by TensorRT mode: {}, expect device=='GPU', but device == {}"
  374. .format(run_mode, device))
  375. config = Config(
  376. os.path.join(model_dir, 'model.pdmodel'),
  377. os.path.join(model_dir, 'model.pdiparams'))
  378. if device == 'GPU':
  379. # initial GPU memory(M), device ID
  380. config.enable_use_gpu(200, 0)
  381. # optimize graph and fuse op
  382. config.switch_ir_optim(True)
  383. elif device == 'XPU':
  384. config.enable_lite_engine()
  385. config.enable_xpu(10 * 1024 * 1024)
  386. else:
  387. config.disable_gpu()
  388. config.set_cpu_math_library_num_threads(cpu_threads)
  389. if enable_mkldnn:
  390. try:
  391. # cache 10 different shapes for mkldnn to avoid memory leak
  392. config.set_mkldnn_cache_capacity(10)
  393. config.enable_mkldnn()
  394. except Exception as e:
  395. print(
  396. "The current environment does not support `mkldnn`, so disable mkldnn."
  397. )
  398. pass
  399. precision_map = {
  400. 'trt_int8': Config.Precision.Int8,
  401. 'trt_fp32': Config.Precision.Float32,
  402. 'trt_fp16': Config.Precision.Half
  403. }
  404. if run_mode in precision_map.keys():
  405. config.enable_tensorrt_engine(
  406. workspace_size=1 << 25,
  407. max_batch_size=batch_size,
  408. min_subgraph_size=min_subgraph_size,
  409. precision_mode=precision_map[run_mode],
  410. use_static=False,
  411. use_calib_mode=trt_calib_mode)
  412. if use_dynamic_shape:
  413. min_input_shape = {
  414. 'image': [batch_size, 3, trt_min_shape, trt_min_shape]
  415. }
  416. max_input_shape = {
  417. 'image': [batch_size, 3, trt_max_shape, trt_max_shape]
  418. }
  419. opt_input_shape = {
  420. 'image': [batch_size, 3, trt_opt_shape, trt_opt_shape]
  421. }
  422. config.set_trt_dynamic_shape_info(min_input_shape, max_input_shape,
  423. opt_input_shape)
  424. print('trt set dynamic shape done!')
  425. # disable print log when predict
  426. config.disable_glog_info()
  427. # enable shared memory
  428. config.enable_memory_optim()
  429. # disable feed, fetch OP, needed by zero_copy_run
  430. config.switch_use_feed_fetch_ops(False)
  431. predictor = create_predictor(config)
  432. return predictor, config
  433. def get_test_images(infer_dir, infer_img):
  434. """
  435. Get image path list in TEST mode
  436. """
  437. assert infer_img is not None or infer_dir is not None, \
  438. "--infer_img or --infer_dir should be set"
  439. assert infer_img is None or os.path.isfile(infer_img), \
  440. "{} is not a file".format(infer_img)
  441. assert infer_dir is None or os.path.isdir(infer_dir), \
  442. "{} is not a directory".format(infer_dir)
  443. # infer_img has a higher priority
  444. if infer_img and os.path.isfile(infer_img):
  445. return [infer_img]
  446. images = set()
  447. infer_dir = os.path.abspath(infer_dir)
  448. assert os.path.isdir(infer_dir), \
  449. "infer_dir {} is not a directory".format(infer_dir)
  450. exts = ['jpg', 'jpeg', 'png', 'bmp']
  451. exts += [ext.upper() for ext in exts]
  452. for ext in exts:
  453. images.update(glob.glob('{}/*.{}'.format(infer_dir, ext)))
  454. images = list(images)
  455. assert len(images) > 0, "no image found in {}".format(infer_dir)
  456. print("Found {} inference images in total.".format(len(images)))
  457. return images
  458. def visualize(image_list, result, labels, output_dir='output/', threshold=0.5):
  459. # visualize the predict result
  460. start_idx = 0
  461. for idx, image_file in enumerate(image_list):
  462. im_bboxes_num = result['boxes_num'][idx]
  463. im_results = {}
  464. if 'boxes' in result:
  465. im_results['boxes'] = result['boxes'][start_idx:start_idx +
  466. im_bboxes_num, :]
  467. start_idx += im_bboxes_num
  468. im = visualize_box_mask(
  469. image_file, im_results, labels, threshold=threshold)
  470. img_name = os.path.split(image_file)[-1]
  471. if not os.path.exists(output_dir):
  472. os.makedirs(output_dir)
  473. out_path = os.path.join(output_dir, img_name)
  474. im.save(out_path, quality=95)
  475. print("save result to: " + out_path)
  476. def print_arguments(args):
  477. print('----------- Running Arguments -----------')
  478. for arg, value in sorted(vars(args).items()):
  479. print('%s: %s' % (arg, value))
  480. print('------------------------------------------')
  481. def main():
  482. deploy_file = os.path.join(FLAGS.model_dir, 'infer_cfg.yml')
  483. with open(deploy_file) as f:
  484. yml_conf = yaml.safe_load(f)
  485. arch = yml_conf['arch']
  486. detector_func = 'Detector'
  487. detector = eval(detector_func)(FLAGS.model_dir,
  488. device=FLAGS.device,
  489. run_mode=FLAGS.run_mode,
  490. batch_size=FLAGS.batch_size,
  491. trt_min_shape=FLAGS.trt_min_shape,
  492. trt_max_shape=FLAGS.trt_max_shape,
  493. trt_opt_shape=FLAGS.trt_opt_shape,
  494. trt_calib_mode=FLAGS.trt_calib_mode,
  495. cpu_threads=FLAGS.cpu_threads,
  496. enable_mkldnn=FLAGS.enable_mkldnn,
  497. threshold=FLAGS.threshold,
  498. output_dir=FLAGS.output_dir)
  499. # predict from video file or camera video stream
  500. if FLAGS.video_file is not None or FLAGS.camera_id != -1:
  501. detector.predict_video(FLAGS.video_file, FLAGS.camera_id)
  502. else:
  503. # predict from image
  504. if FLAGS.image_dir is None and FLAGS.image_file is not None:
  505. assert FLAGS.batch_size == 1, "batch_size should be 1, when image_file is not None"
  506. img_list = get_test_images(FLAGS.image_dir, FLAGS.image_file)
  507. detector.predict_image(img_list, FLAGS.run_benchmark, repeats=10)
  508. if not FLAGS.run_benchmark:
  509. detector.det_times.info(average=True)
  510. else:
  511. mode = FLAGS.run_mode
  512. model_dir = FLAGS.model_dir
  513. model_info = {
  514. 'model_name': model_dir.strip('/').split('/')[-1],
  515. 'precision': mode.split('_')[-1]
  516. }
  517. bench_log(detector, img_list, model_info, name='DET')
  518. if __name__ == '__main__':
  519. paddle.enable_static()
  520. parser = argsparser()
  521. FLAGS = parser.parse_args()
  522. print_arguments(FLAGS)
  523. FLAGS.device = FLAGS.device.upper()
  524. assert FLAGS.device in ['CPU', 'GPU', 'XPU'
  525. ], "device should be CPU, GPU or XPU"
  526. main()