deploy.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. # copyright (c) 2020 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 os.path as osp
  16. import cv2
  17. import numpy as np
  18. import yaml
  19. import multiprocessing as mp
  20. import paddlex
  21. import paddle.fluid as fluid
  22. from paddlex.cv.transforms import build_transforms
  23. from paddlex.cv.models import BaseClassifier
  24. from paddlex.cv.models import PPYOLO, FasterRCNN, MaskRCNN
  25. from paddlex.cv.models import DeepLabv3p
  26. import paddlex.utils.logging as logging
  27. class Predictor:
  28. def __init__(self,
  29. model_dir,
  30. use_gpu=True,
  31. gpu_id=0,
  32. use_mkl=True,
  33. mkl_thread_num=4,
  34. use_trt=False,
  35. use_glog=False,
  36. memory_optimize=True,
  37. max_trt_batch_size=1):
  38. """ 创建Paddle Predictor
  39. Args:
  40. model_dir: 模型路径(必须是导出的部署或量化模型)
  41. use_gpu: 是否使用gpu,默认True
  42. gpu_id: 使用gpu的id,默认0
  43. use_mkl: 是否使用mkldnn计算库,CPU情况下使用,默认False
  44. mkl_thread_num: mkldnn计算线程数,默认为4
  45. use_trt: 是否使用TensorRT,默认False
  46. use_glog: 是否启用glog日志, 默认False
  47. memory_optimize: 是否启动内存优化,默认True
  48. max_trt_batch_size: 在使用TensorRT时配置的最大batch size,默认1
  49. """
  50. if not osp.isdir(model_dir):
  51. raise Exception("[ERROR] Path {} not exist.".format(model_dir))
  52. if not osp.exists(osp.join(model_dir, "model.yml")):
  53. raise Exception("There's not model.yml in {}".format(model_dir))
  54. with open(osp.join(model_dir, "model.yml")) as f:
  55. self.info = yaml.load(f.read(), Loader=yaml.Loader)
  56. self.status = self.info['status']
  57. if self.status != "Quant" and self.status != "Infer":
  58. raise Exception("[ERROR] Only quantized model or exported "
  59. "inference model is supported.")
  60. self.model_dir = model_dir
  61. self.model_type = self.info['_Attributes']['model_type']
  62. self.model_name = self.info['Model']
  63. self.num_classes = self.info['_Attributes']['num_classes']
  64. self.labels = self.info['_Attributes']['labels']
  65. if self.info['Model'] == 'MaskRCNN':
  66. if self.info['_init_params']['with_fpn']:
  67. self.mask_head_resolution = 28
  68. else:
  69. self.mask_head_resolution = 14
  70. transforms_mode = self.info.get('TransformsMode', 'RGB')
  71. if transforms_mode == 'RGB':
  72. to_rgb = True
  73. else:
  74. to_rgb = False
  75. self.transforms = build_transforms(self.model_type,
  76. self.info['Transforms'], to_rgb)
  77. self.predictor = self.create_predictor(
  78. use_gpu, gpu_id, use_mkl, mkl_thread_num, use_trt, use_glog,
  79. memory_optimize, max_trt_batch_size)
  80. # 线程池,在模型在预测时用于对输入数据以图片为单位进行并行处理
  81. # 主要用于batch_predict接口
  82. thread_num = mp.cpu_count() if mp.cpu_count() < 8 else 8
  83. self.thread_pool = mp.pool.ThreadPool(thread_num)
  84. self.input_channel = 3
  85. if 'input_channel' in self.info['_init_params']:
  86. self.input_channel = self.info['_init_params']['input_channel']
  87. def reset_thread_pool(self, thread_num):
  88. self.thread_pool.close()
  89. self.thread_pool.join()
  90. self.thread_pool = mp.pool.ThreadPool(thread_num)
  91. def create_predictor(self,
  92. use_gpu=True,
  93. gpu_id=0,
  94. use_mkl=False,
  95. mkl_thread_num=4,
  96. use_trt=False,
  97. use_glog=False,
  98. memory_optimize=True,
  99. max_trt_batch_size=1):
  100. config = fluid.core.AnalysisConfig(
  101. os.path.join(self.model_dir, '__model__'),
  102. os.path.join(self.model_dir, '__params__'))
  103. if use_gpu:
  104. # 设置GPU初始显存(单位M)和Device ID
  105. config.enable_use_gpu(100, gpu_id)
  106. if use_trt:
  107. config.enable_tensorrt_engine(
  108. workspace_size=1 << 10,
  109. max_batch_size=max_trt_batch_size,
  110. min_subgraph_size=3,
  111. precision_mode=fluid.core.AnalysisConfig.Precision.Float32,
  112. use_static=False,
  113. use_calib_mode=False)
  114. else:
  115. config.disable_gpu()
  116. if use_mkl and not use_gpu:
  117. if self.model_name not in ["HRNet", "DeepLabv3p", "PPYOLO"]:
  118. config.enable_mkldnn()
  119. config.set_cpu_math_library_num_threads(mkl_thread_num)
  120. else:
  121. logging.warning(
  122. "HRNet/DeepLabv3p/PPYOLO are not supported for the use of mkldnn\n"
  123. )
  124. if use_glog:
  125. config.enable_glog_info()
  126. else:
  127. config.disable_glog_info()
  128. if memory_optimize:
  129. config.enable_memory_optim()
  130. # 开启计算图分析优化,包括OP融合等
  131. config.switch_ir_optim(True)
  132. # 关闭feed和fetch OP使用,使用ZeroCopy接口必须设置此项
  133. config.switch_use_feed_fetch_ops(False)
  134. predictor = fluid.core.create_paddle_predictor(config)
  135. return predictor
  136. def preprocess(self, image, thread_pool=None):
  137. """ 对图像做预处理
  138. Args:
  139. image(list|tuple): 数组中的元素可以是图像路径,也可以是解码后的排列格式为(H,W,C)
  140. 且类型为float32且为BGR格式的数组。
  141. """
  142. res = dict()
  143. if self.model_type == "classifier":
  144. im = BaseClassifier._preprocess(
  145. image,
  146. self.transforms,
  147. self.model_type,
  148. self.model_name,
  149. thread_pool=thread_pool)
  150. res['image'] = im
  151. elif self.model_type == "detector":
  152. if self.model_name in ["PPYOLO", "YOLOv3"]:
  153. im, im_size = PPYOLO._preprocess(
  154. image,
  155. self.transforms,
  156. self.model_type,
  157. self.model_name,
  158. thread_pool=thread_pool,
  159. input_channel=self.input_channel)
  160. res['image'] = im
  161. res['im_size'] = im_size
  162. if self.model_name.count('RCNN') > 0:
  163. im, im_resize_info, im_shape = FasterRCNN._preprocess(
  164. image,
  165. self.transforms,
  166. self.model_type,
  167. self.model_name,
  168. thread_pool=thread_pool,
  169. input_channel=self.input_channel)
  170. res['image'] = im
  171. res['im_info'] = im_resize_info
  172. res['im_shape'] = im_shape
  173. elif self.model_type == "segmenter":
  174. im, im_info = DeepLabv3p._preprocess(
  175. image,
  176. self.transforms,
  177. self.model_type,
  178. self.model_name,
  179. thread_pool=thread_pool,
  180. input_channel=self.input_channel)
  181. res['image'] = im
  182. res['im_info'] = im_info
  183. return res
  184. def postprocess(self,
  185. results,
  186. topk=1,
  187. batch_size=1,
  188. im_shape=None,
  189. im_info=None):
  190. """ 对预测结果做后处理
  191. Args:
  192. results (list): 预测结果
  193. topk (int): 分类预测时前k个最大值
  194. batch_size (int): 预测时图像批量大小
  195. im_shape (list): MaskRCNN的图像输入大小
  196. im_info (list):RCNN系列和分割网络的原图大小
  197. """
  198. def offset_to_lengths(lod):
  199. offset = lod[0]
  200. lengths = [
  201. offset[i + 1] - offset[i] for i in range(len(offset) - 1)
  202. ]
  203. return [lengths]
  204. if self.model_type == "classifier":
  205. true_topk = min(self.num_classes, topk)
  206. preds = BaseClassifier._postprocess([results[0][0]], true_topk,
  207. self.labels)
  208. elif self.model_type == "detector":
  209. res = {'bbox': (results[0][0], offset_to_lengths(results[0][1])), }
  210. res['im_id'] = (np.array(
  211. [[i] for i in range(batch_size)]).astype('int32'), [[]])
  212. if self.model_name in ["PPYOLO", "YOLOv3"]:
  213. preds = PPYOLO._postprocess(res, batch_size, self.num_classes,
  214. self.labels)
  215. elif self.model_name == "FasterRCNN":
  216. preds = FasterRCNN._postprocess(res, batch_size,
  217. self.num_classes, self.labels)
  218. elif self.model_name == "MaskRCNN":
  219. res['mask'] = (results[1][0], offset_to_lengths(results[1][1]))
  220. res['im_shape'] = (im_shape, [])
  221. preds = MaskRCNN._postprocess(
  222. res, batch_size, self.num_classes,
  223. self.mask_head_resolution, self.labels)
  224. elif self.model_type == "segmenter":
  225. res = [results[0][0], results[1][0]]
  226. preds = DeepLabv3p._postprocess(res, im_info)
  227. return preds
  228. def raw_predict(self, inputs):
  229. """ 接受预处理过后的数据进行预测
  230. Args:
  231. inputs(tuple): 预处理过后的数据
  232. """
  233. for k, v in inputs.items():
  234. try:
  235. tensor = self.predictor.get_input_tensor(k)
  236. except:
  237. continue
  238. tensor.copy_from_cpu(v)
  239. self.predictor.zero_copy_run()
  240. output_names = self.predictor.get_output_names()
  241. output_results = list()
  242. for name in output_names:
  243. output_tensor = self.predictor.get_output_tensor(name)
  244. output_tensor_lod = output_tensor.lod()
  245. output_results.append(
  246. [output_tensor.copy_to_cpu(), output_tensor_lod])
  247. return output_results
  248. def predict(self, image, topk=1, transforms=None):
  249. """ 图片预测
  250. Args:
  251. image(str|np.ndarray): 图像路径;或者是解码后的排列格式为(H, W, C)且类型为float32且为BGR格式的数组。
  252. topk(int): 分类预测时使用,表示预测前topk的结果。
  253. transforms (paddlex.cls.transforms): 数据预处理操作。
  254. """
  255. if transforms is not None:
  256. self.transforms = transforms
  257. preprocessed_input = self.preprocess([image])
  258. model_pred = self.raw_predict(preprocessed_input)
  259. im_shape = None if 'im_shape' not in preprocessed_input else preprocessed_input[
  260. 'im_shape']
  261. im_info = None if 'im_info' not in preprocessed_input else preprocessed_input[
  262. 'im_info']
  263. results = self.postprocess(
  264. model_pred,
  265. topk=topk,
  266. batch_size=1,
  267. im_shape=im_shape,
  268. im_info=im_info)
  269. return results[0]
  270. def batch_predict(self, image_list, topk=1, transforms=None):
  271. """ 图片预测
  272. Args:
  273. image_list(list|tuple): 对列表(或元组)中的图像同时进行预测,列表中的元素可以是图像路径
  274. 也可以是解码后的排列格式为(H,W,C)且类型为float32且为BGR格式的数组。
  275. topk(int): 分类预测时使用,表示预测前topk的结果。
  276. transforms (paddlex.cls.transforms): 数据预处理操作。
  277. """
  278. if transforms is not None:
  279. self.transforms = transforms
  280. preprocessed_input = self.preprocess(image_list, self.thread_pool)
  281. model_pred = self.raw_predict(preprocessed_input)
  282. im_shape = None if 'im_shape' not in preprocessed_input else preprocessed_input[
  283. 'im_shape']
  284. im_info = None if 'im_info' not in preprocessed_input else preprocessed_input[
  285. 'im_info']
  286. results = self.postprocess(
  287. model_pred,
  288. topk=topk,
  289. batch_size=len(image_list),
  290. im_shape=im_shape,
  291. im_info=im_info)
  292. return results