vehicle_plate.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  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 os
  15. import yaml
  16. import glob
  17. from functools import reduce
  18. import time
  19. import cv2
  20. import numpy as np
  21. import math
  22. import paddle
  23. import sys
  24. parent_path = os.path.abspath(os.path.join(__file__, *(['..'] * 3)))
  25. sys.path.insert(0, parent_path)
  26. from python.infer import get_test_images
  27. from python.preprocess import preprocess, NormalizeImage, Permute, Resize_Mult32
  28. from pipeline.ppvehicle.vehicle_plateutils import create_predictor, get_infer_gpuid, get_rotate_crop_image, draw_boxes
  29. from pipeline.ppvehicle.vehicleplate_postprocess import build_post_process
  30. from pipeline.cfg_utils import merge_cfg, print_arguments, argsparser
  31. class PlateDetector(object):
  32. def __init__(self, args, cfg):
  33. self.args = args
  34. self.pre_process_list = {
  35. 'Resize_Mult32': {
  36. 'limit_side_len': cfg['det_limit_side_len'],
  37. 'limit_type': cfg['det_limit_type'],
  38. },
  39. 'NormalizeImage': {
  40. 'mean': [0.485, 0.456, 0.406],
  41. 'std': [0.229, 0.224, 0.225],
  42. 'is_scale': True,
  43. },
  44. 'Permute': {}
  45. }
  46. postprocess_params = {}
  47. postprocess_params['name'] = 'DBPostProcess'
  48. postprocess_params["thresh"] = 0.3
  49. postprocess_params["box_thresh"] = 0.6
  50. postprocess_params["max_candidates"] = 1000
  51. postprocess_params["unclip_ratio"] = 1.5
  52. postprocess_params["use_dilation"] = False
  53. postprocess_params["score_mode"] = "fast"
  54. self.postprocess_op = build_post_process(postprocess_params)
  55. self.predictor, self.input_tensor, self.output_tensors, self.config = create_predictor(
  56. args, cfg, 'det')
  57. def preprocess(self, im_path):
  58. preprocess_ops = []
  59. for op_type, new_op_info in self.pre_process_list.items():
  60. preprocess_ops.append(eval(op_type)(**new_op_info))
  61. input_im_lst = []
  62. input_im_info_lst = []
  63. im, im_info = preprocess(im_path, preprocess_ops)
  64. input_im_lst.append(im)
  65. input_im_info_lst.append(im_info['im_shape'] / im_info['scale_factor'])
  66. return np.stack(input_im_lst, axis=0), input_im_info_lst
  67. def order_points_clockwise(self, pts):
  68. rect = np.zeros((4, 2), dtype="float32")
  69. s = pts.sum(axis=1)
  70. rect[0] = pts[np.argmin(s)]
  71. rect[2] = pts[np.argmax(s)]
  72. diff = np.diff(pts, axis=1)
  73. rect[1] = pts[np.argmin(diff)]
  74. rect[3] = pts[np.argmax(diff)]
  75. return rect
  76. def clip_det_res(self, points, img_height, img_width):
  77. for pno in range(points.shape[0]):
  78. points[pno, 0] = int(min(max(points[pno, 0], 0), img_width - 1))
  79. points[pno, 1] = int(min(max(points[pno, 1], 0), img_height - 1))
  80. return points
  81. def filter_tag_det_res(self, dt_boxes, image_shape):
  82. img_height, img_width = image_shape[0:2]
  83. dt_boxes_new = []
  84. for box in dt_boxes:
  85. box = self.order_points_clockwise(box)
  86. box = self.clip_det_res(box, img_height, img_width)
  87. rect_width = int(np.linalg.norm(box[0] - box[1]))
  88. rect_height = int(np.linalg.norm(box[0] - box[3]))
  89. if rect_width <= 3 or rect_height <= 3:
  90. continue
  91. dt_boxes_new.append(box)
  92. dt_boxes = np.array(dt_boxes_new)
  93. return dt_boxes
  94. def filter_tag_det_res_only_clip(self, dt_boxes, image_shape):
  95. img_height, img_width = image_shape[0:2]
  96. dt_boxes_new = []
  97. for box in dt_boxes:
  98. box = self.clip_det_res(box, img_height, img_width)
  99. dt_boxes_new.append(box)
  100. dt_boxes = np.array(dt_boxes_new)
  101. return dt_boxes
  102. def predict_image(self, img_list):
  103. st = time.time()
  104. dt_batch_boxes = []
  105. for image in img_list:
  106. img, shape_list = self.preprocess(image)
  107. if img is None:
  108. return None, 0
  109. self.input_tensor.copy_from_cpu(img)
  110. self.predictor.run()
  111. outputs = []
  112. for output_tensor in self.output_tensors:
  113. output = output_tensor.copy_to_cpu()
  114. outputs.append(output)
  115. preds = {}
  116. preds['maps'] = outputs[0]
  117. #self.predictor.try_shrink_memory()
  118. post_result = self.postprocess_op(preds, shape_list)
  119. # print("post_result length:{}".format(len(post_result)))
  120. org_shape = image.shape
  121. dt_boxes = post_result[0]['points']
  122. dt_boxes = self.filter_tag_det_res(dt_boxes, org_shape)
  123. dt_batch_boxes.append(dt_boxes)
  124. et = time.time()
  125. return dt_batch_boxes, et - st
  126. class TextRecognizer(object):
  127. def __init__(self, args, cfg, use_gpu=True):
  128. self.rec_image_shape = cfg['rec_image_shape']
  129. self.rec_batch_num = cfg['rec_batch_num']
  130. word_dict_path = cfg['word_dict_path']
  131. use_space_char = True
  132. postprocess_params = {
  133. 'name': 'CTCLabelDecode',
  134. "character_dict_path": word_dict_path,
  135. "use_space_char": use_space_char
  136. }
  137. self.postprocess_op = build_post_process(postprocess_params)
  138. self.predictor, self.input_tensor, self.output_tensors, self.config = \
  139. create_predictor(args, cfg, 'rec')
  140. self.use_onnx = False
  141. def resize_norm_img(self, img, max_wh_ratio):
  142. imgC, imgH, imgW = self.rec_image_shape
  143. assert imgC == img.shape[2]
  144. imgW = int((imgH * max_wh_ratio))
  145. if self.use_onnx:
  146. w = self.input_tensor.shape[3:][0]
  147. if w is not None and w > 0:
  148. imgW = w
  149. h, w = img.shape[:2]
  150. ratio = w / float(h)
  151. if math.ceil(imgH * ratio) > imgW:
  152. resized_w = imgW
  153. else:
  154. resized_w = int(math.ceil(imgH * ratio))
  155. resized_image = cv2.resize(img, (resized_w, imgH))
  156. resized_image = resized_image.astype('float32')
  157. resized_image = resized_image.transpose((2, 0, 1)) / 255
  158. resized_image -= 0.5
  159. resized_image /= 0.5
  160. padding_im = np.zeros((imgC, imgH, imgW), dtype=np.float32)
  161. padding_im[:, :, 0:resized_w] = resized_image
  162. return padding_im
  163. def predict_text(self, img_list):
  164. img_num = len(img_list)
  165. # Calculate the aspect ratio of all text bars
  166. width_list = []
  167. for img in img_list:
  168. width_list.append(img.shape[1] / float(img.shape[0]))
  169. # Sorting can speed up the recognition process
  170. indices = np.argsort(np.array(width_list))
  171. rec_res = [['', 0.0]] * img_num
  172. batch_num = self.rec_batch_num
  173. st = time.time()
  174. for beg_img_no in range(0, img_num, batch_num):
  175. end_img_no = min(img_num, beg_img_no + batch_num)
  176. norm_img_batch = []
  177. imgC, imgH, imgW = self.rec_image_shape
  178. max_wh_ratio = imgW / imgH
  179. # max_wh_ratio = 0
  180. for ino in range(beg_img_no, end_img_no):
  181. h, w = img_list[indices[ino]].shape[0:2]
  182. wh_ratio = w * 1.0 / h
  183. max_wh_ratio = max(max_wh_ratio, wh_ratio)
  184. for ino in range(beg_img_no, end_img_no):
  185. norm_img = self.resize_norm_img(img_list[indices[ino]],
  186. max_wh_ratio)
  187. norm_img = norm_img[np.newaxis, :]
  188. norm_img_batch.append(norm_img)
  189. norm_img_batch = np.concatenate(norm_img_batch)
  190. norm_img_batch = norm_img_batch.copy()
  191. if self.use_onnx:
  192. input_dict = {}
  193. input_dict[self.input_tensor.name] = norm_img_batch
  194. outputs = self.predictor.run(self.output_tensors, input_dict)
  195. preds = outputs[0]
  196. else:
  197. self.input_tensor.copy_from_cpu(norm_img_batch)
  198. self.predictor.run()
  199. outputs = []
  200. for output_tensor in self.output_tensors:
  201. output = output_tensor.copy_to_cpu()
  202. outputs.append(output)
  203. if len(outputs) != 1:
  204. preds = outputs
  205. else:
  206. preds = outputs[0]
  207. rec_result = self.postprocess_op(preds)
  208. for rno in range(len(rec_result)):
  209. rec_res[indices[beg_img_no + rno]] = rec_result[rno]
  210. return rec_res, time.time() - st
  211. class PlateRecognizer(object):
  212. def __init__(self, args, cfg):
  213. use_gpu = args.device.lower() == "gpu"
  214. self.platedetector = PlateDetector(args, cfg)
  215. self.textrecognizer = TextRecognizer(args, cfg, use_gpu=use_gpu)
  216. def get_platelicense(self, image_list):
  217. plate_text_list = []
  218. plateboxes, det_time = self.platedetector.predict_image(image_list)
  219. for idx, boxes_pcar in enumerate(plateboxes):
  220. plate_pcar_list = []
  221. for box in boxes_pcar:
  222. plate_images = get_rotate_crop_image(image_list[idx], box)
  223. plate_texts = self.textrecognizer.predict_text([plate_images])
  224. plate_pcar_list.append(plate_texts)
  225. plate_text_list.append(plate_pcar_list)
  226. return self.check_plate(plate_text_list)
  227. def check_plate(self, text_list):
  228. plate_all = {"plate": []}
  229. for text_pcar in text_list:
  230. platelicense = ""
  231. for text_info in text_pcar:
  232. text = text_info[0][0][0]
  233. if len(text) > 2 and len(text) < 10:
  234. platelicense = self.replace_cn_code(text)
  235. plate_all["plate"].append(platelicense)
  236. return plate_all
  237. def replace_cn_code(self, text):
  238. simcode = {
  239. '浙': 'ZJ-',
  240. '粤': 'GD-',
  241. '京': 'BJ-',
  242. '津': 'TJ-',
  243. '冀': 'HE-',
  244. '晋': 'SX-',
  245. '蒙': 'NM-',
  246. '辽': 'LN-',
  247. '黑': 'HLJ-',
  248. '沪': 'SH-',
  249. '吉': 'JL-',
  250. '苏': 'JS-',
  251. '皖': 'AH-',
  252. '赣': 'JX-',
  253. '鲁': 'SD-',
  254. '豫': 'HA-',
  255. '鄂': 'HB-',
  256. '湘': 'HN-',
  257. '桂': 'GX-',
  258. '琼': 'HI-',
  259. '渝': 'CQ-',
  260. '川': 'SC-',
  261. '贵': 'GZ-',
  262. '云': 'YN-',
  263. '藏': 'XZ-',
  264. '陕': 'SN-',
  265. '甘': 'GS-',
  266. '青': 'QH-',
  267. '宁': 'NX-',
  268. '闽': 'FJ-',
  269. '·': ' '
  270. }
  271. for _char in text:
  272. if _char in simcode:
  273. text = text.replace(_char, simcode[_char])
  274. return text
  275. def main():
  276. cfg = merge_cfg(FLAGS)
  277. print_arguments(cfg)
  278. vehicleplate_cfg = cfg['VEHICLE_PLATE']
  279. detector = PlateRecognizer(FLAGS, vehicleplate_cfg)
  280. # predict from image
  281. img_list = get_test_images(FLAGS.image_dir, FLAGS.image_file)
  282. for img in img_list:
  283. image = cv2.imread(img)
  284. results = detector.get_platelicense([image])
  285. print(results)
  286. if __name__ == '__main__':
  287. paddle.enable_static()
  288. parser = argsparser()
  289. FLAGS = parser.parse_args()
  290. FLAGS.device = FLAGS.device.upper()
  291. assert FLAGS.device in ['CPU', 'GPU', 'XPU'
  292. ], "device should be CPU, GPU or XPU"
  293. main()