detect_plate.py.bk 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. import argparse
  2. import copy
  3. import os
  4. import time
  5. from pprint import pprint
  6. import cv2
  7. import numpy as np
  8. import torch
  9. from models.experimental import attempt_load
  10. from plate_recognition.double_plate_split_merge import get_split_merge
  11. from plate_recognition.plate_rec import (
  12. allFilePath,
  13. cv_imread,
  14. get_plate_result,
  15. init_model,
  16. )
  17. from utils.cv_puttext import cv2ImgAddText
  18. from utils.datasets import letterbox
  19. from utils.general import check_img_size, non_max_suppression_face, scale_coords
  20. clors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (0, 255, 255)]
  21. danger = ["危", "险"]
  22. def order_points(pts):
  23. rect = np.zeros((4, 2), dtype="float32")
  24. s = pts.sum(axis=1)
  25. rect[0] = pts[np.argmin(s)]
  26. rect[2] = pts[np.argmax(s)]
  27. diff = np.diff(pts, axis=1)
  28. rect[1] = pts[np.argmin(diff)]
  29. rect[3] = pts[np.argmax(diff)]
  30. return rect
  31. def four_point_transform(image, pts): # 透视变换得到车牌小图
  32. rect = pts.astype("float32")
  33. (tl, tr, br, bl) = rect
  34. widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
  35. widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
  36. maxWidth = max(int(widthA), int(widthB))
  37. heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
  38. heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
  39. maxHeight = max(int(heightA), int(heightB))
  40. dst = np.array(
  41. [[0, 0], [maxWidth - 1, 0], [maxWidth - 1, maxHeight - 1], [0, maxHeight - 1]],
  42. dtype="float32",
  43. )
  44. M = cv2.getPerspectiveTransform(rect, dst)
  45. warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
  46. return warped
  47. def load_model(weights, device): # 加载检测模型
  48. model = attempt_load(weights, map_location=device) # FP32 model
  49. return model
  50. def scale_coords_landmarks(
  51. img1_shape, coords, img0_shape, ratio_pad=None
  52. ): # 返回到原图坐标
  53. if ratio_pad is None: # calculate from img0_shape
  54. gain = min(
  55. img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1]
  56. ) # gain = old / new
  57. pad = (
  58. (img1_shape[1] - img0_shape[1] * gain) / 2,
  59. (img1_shape[0] - img0_shape[0] * gain) / 2,
  60. ) # wh padding
  61. else:
  62. gain = ratio_pad[0][0]
  63. pad = ratio_pad[1]
  64. coords[:, [0, 2, 4, 6]] -= pad[0] # x padding
  65. coords[:, [1, 3, 5, 7]] -= pad[1] # y padding
  66. coords[:, :8] /= gain
  67. coords[:, 0].clamp_(0, img0_shape[1]) # x1
  68. coords[:, 1].clamp_(0, img0_shape[0]) # y1
  69. coords[:, 2].clamp_(0, img0_shape[1]) # x2
  70. coords[:, 3].clamp_(0, img0_shape[0]) # y2
  71. coords[:, 4].clamp_(0, img0_shape[1]) # x3
  72. coords[:, 5].clamp_(0, img0_shape[0]) # y3
  73. coords[:, 6].clamp_(0, img0_shape[1]) # x4
  74. coords[:, 7].clamp_(0, img0_shape[0]) # y4
  75. # coords[:, 8].clamp_(0, img0_shape[1]) # x5
  76. # coords[:, 9].clamp_(0, img0_shape[0]) # y5
  77. return coords
  78. def get_plate_rec_landmark(
  79. img, xyxy, conf, landmarks, class_num, device, plate_rec_model, is_color=False
  80. ): # 获取车牌坐标以及四个角点坐标并识别车牌号
  81. h, w, c = img.shape
  82. result_dict = {}
  83. 1 or round(0.002 * (h + w) / 2) + 1
  84. x1 = int(xyxy[0])
  85. y1 = int(xyxy[1])
  86. x2 = int(xyxy[2])
  87. y2 = int(xyxy[3])
  88. height = y2 - y1
  89. landmarks_np = np.zeros((4, 2))
  90. rect = [x1, y1, x2, y2]
  91. for i in range(4):
  92. point_x = int(landmarks[2 * i])
  93. point_y = int(landmarks[2 * i + 1])
  94. landmarks_np[i] = np.array([point_x, point_y])
  95. class_label = int(class_num) # 车牌的的类型0代表单层车牌,1代表双层车牌
  96. roi_img = four_point_transform(img, landmarks_np) # 透视变换得到车牌小图
  97. if class_label: # 判断是否是双层车牌,是双牌的话进行分割后然后拼接
  98. roi_img = get_split_merge(roi_img)
  99. if not is_color:
  100. plate_number, rec_prob = get_plate_result(
  101. roi_img, device, plate_rec_model, is_color=is_color
  102. ) # 对车牌小图进行识别
  103. else:
  104. plate_number, rec_prob, plate_color, color_conf = get_plate_result(
  105. roi_img, device, plate_rec_model, is_color=is_color
  106. )
  107. # cv2.imwrite("roi.jpg",roi_img)
  108. result_dict["rect"] = rect # 车牌roi区域
  109. result_dict["detect_conf"] = conf # 检测区域置信度
  110. result_dict["landmarks"] = landmarks_np.tolist() # 车牌角点坐标
  111. result_dict["plate_no"] = plate_number # 车牌号
  112. result_dict["rec_conf"] = rec_prob # 每个字符的概率
  113. result_dict["roi_height"] = roi_img.shape[0] # 车牌高度
  114. result_dict["plate_color"] = ""
  115. if is_color:
  116. result_dict["plate_color"] = plate_color # 车牌颜色
  117. result_dict["color_conf"] = color_conf # 颜色置信度
  118. result_dict["plate_type"] = class_label # 单双层 0单层 1双层
  119. return result_dict
  120. def detect_Recognition_plate(
  121. model, orgimg, device, plate_rec_model, img_size, is_color=False
  122. ): # 获取车牌信息
  123. # img_size = opt_img_size
  124. conf_thres = 0.3 ##### 置信度阈值 #####
  125. iou_thres = 0.5 # nms的iou值
  126. dict_list = []
  127. img0 = copy.deepcopy(orgimg)
  128. assert orgimg is not None, "Image Not Found "
  129. h0, w0 = orgimg.shape[:2]
  130. r = img_size / max(h0, w0)
  131. if r != 1:
  132. interp = cv2.INTER_AREA if r < 1 else cv2.INTER_LINEAR
  133. img0 = cv2.resize(img0, (int(w0 * r), int(h0 * r)), interpolation=interp)
  134. imgsz = check_img_size(img_size, s=model.stride.max()) # 检查 img_size
  135. img = letterbox(img0, new_shape=imgsz)[
  136. 0
  137. ] # 检测前处理,图片长宽变为32倍数
  138. img = (
  139. img[:, :, ::-1].transpose(2, 0, 1).copy()
  140. ) # 图片的BGR排列转为RGB,然后将图片的H,W,C排列变为C,H,W排列
  141. t0 = time.time()
  142. img = torch.from_numpy(img).to(device)
  143. img = img.float()
  144. img /= 255.0 # 0 - 255 to 0.0 - 1.0
  145. if img.ndimension() == 3:
  146. img = img.unsqueeze(0)
  147. pred = model(img)[0]
  148. pred = non_max_suppression_face(pred, conf_thres, iou_thres)
  149. # 检测进程
  150. for i, det in enumerate(pred): # 对每张图片遍历
  151. if len(det):
  152. det[:, :4] = scale_coords(img.shape[2:], det[:, :4], orgimg.shape).round()
  153. # 打印结果
  154. for c in det[:, -1].unique():
  155. n = (det[:, -1] == c).sum() # 检测每个种类
  156. det[:, 5:13] = scale_coords_landmarks(
  157. img.shape[2:], det[:, 5:13], orgimg.shape
  158. ).round()
  159. for j in range(det.size()[0]):
  160. xyxy = det[j, :4].view(-1).tolist()
  161. conf = det[j, 4].cpu().numpy()
  162. landmarks = det[j, 5:13].view(-1).tolist()
  163. class_num = det[j, 13].cpu().numpy()
  164. result_dict = get_plate_rec_landmark(
  165. orgimg,
  166. xyxy,
  167. conf,
  168. landmarks,
  169. class_num,
  170. device,
  171. plate_rec_model,
  172. is_color=is_color,
  173. )
  174. dict_list.append(result_dict)
  175. return dict_list
  176. # cv2.imwrite('result.jpg', orgimg)
  177. def draw_result(orgimg, dict_list, is_color=True): # 将车牌结果画出
  178. result_str = ""
  179. if dict_list:
  180. pprint(dict_list)
  181. for result in dict_list:
  182. rect_area = result["rect"]
  183. x, y, w, h = (
  184. rect_area[0],
  185. rect_area[1],
  186. rect_area[2] - rect_area[0],
  187. rect_area[3] - rect_area[1],
  188. )
  189. padding_w = 0.05 * w
  190. padding_h = 0.11 * h
  191. rect_area[0] = max(0, int(x - padding_w))
  192. rect_area[1] = max(0, int(y - padding_h))
  193. rect_area[2] = min(orgimg.shape[1], int(rect_area[2] + padding_w))
  194. rect_area[3] = min(orgimg.shape[0], int(rect_area[3] + padding_h))
  195. height_area = result["roi_height"]
  196. landmarks = result["landmarks"]
  197. result_p = result["plate_no"]
  198. if result["plate_type"] == 0: # 单层
  199. result_p += " " + result["plate_color"]
  200. else: # 双层
  201. result_p += " " + result["plate_color"] + "双层"
  202. result_str += result_p + " "
  203. for i in range(4): # 关键点
  204. cv2.circle(
  205. orgimg, (int(landmarks[i][0]), int(landmarks[i][1])), 5, clors[i], -1
  206. )
  207. cv2.rectangle(
  208. orgimg,
  209. (rect_area[0], rect_area[1]),
  210. (rect_area[2], rect_area[3]),
  211. (0, 0, 255),
  212. 2,
  213. ) # 画框
  214. labelSize = cv2.getTextSize(
  215. result_p, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1
  216. ) # 获得字体大小
  217. if rect_area[0] + labelSize[0][0] > orgimg.shape[1]: # 防止文字越界
  218. rect_area[0] = int(orgimg.shape[1] - labelSize[0][0])
  219. orgimg = cv2.rectangle(
  220. orgimg,
  221. (rect_area[0], int(rect_area[1] - round(1.6 * labelSize[0][1])-30)),
  222. (
  223. int(rect_area[0] + round(1.2 * labelSize[0][0]))+50,
  224. rect_area[1] + labelSize[1],
  225. ),
  226. (255, 255, 255),
  227. cv2.FILLED ,
  228. ) # 画文字框,背景白色
  229. if len(result) >= 1:
  230. orgimg = cv2ImgAddText(
  231. orgimg,
  232. result_p,
  233. rect_area[0],
  234. int(rect_area[1] - round(1.6 * labelSize[0][1]))-30,
  235. (0, 0, 0),
  236. 30,
  237. )
  238. if result_str:
  239. print(result_str)
  240. return orgimg
  241. def get_second(capture):
  242. if capture.isOpened():
  243. rate = capture.get(5) # 帧速率
  244. FrameNumber = capture.get(7) # 视频文件的帧数
  245. duration = FrameNumber / rate
  246. return int(rate), int(FrameNumber), int(duration)
  247. def start(image_path="imgs"): # 测试图片路径
  248. parser = argparse.ArgumentParser()
  249. parser.add_argument(
  250. "--detect_model",
  251. nargs="+",
  252. type=str,
  253. default="weights/plate_detect.pt",
  254. help="model.pt path(s)",
  255. ) # 检测模型
  256. parser.add_argument(
  257. "--rec_model",
  258. type=str,
  259. default="weights/plate_rec_color.pth",
  260. help="model.pt path(s)",
  261. ) # 车牌识别+颜色识别模型
  262. parser.add_argument(
  263. "--is_color", type=bool, default=True, help="plate color"
  264. ) # 识别颜色
  265. parser.add_argument(
  266. "--image_path", type=str, default=image_path, help="source"
  267. ) # 图片路径
  268. parser.add_argument(
  269. "--img_size", type=int, default=640, help="inference size (pixels)"
  270. ) # 输入图片大小
  271. parser.add_argument(
  272. "--output", type=str, default="result", help="source"
  273. ) # 图片结果保存的位置
  274. parser.add_argument("--video", type=str, default="", help="source") # 视频的路径
  275. parser.add_argument(
  276. "--stream",
  277. type=str,
  278. default="",
  279. help="RTSP/RTMP video stream URL"
  280. ) # 视频流地址
  281. device = torch.device(
  282. "cuda" if torch.cuda.is_available() else "cpu"
  283. )
  284. # device =torch.device("cpu")
  285. opt = parser.parse_args()
  286. print(opt)
  287. save_path = opt.output
  288. count = 0
  289. if not os.path.exists(save_path):
  290. os.mkdir(save_path)
  291. detect_model = load_model(
  292. opt.detect_model, device
  293. ) # 初始化检测模型
  294. plate_rec_model = init_model(
  295. device, opt.rec_model, is_color=opt.is_color
  296. ) # 初始化识别模型
  297. # 计算参数量
  298. total = sum(p.numel() for p in detect_model.parameters())
  299. total_1 = sum(p.numel() for p in plate_rec_model.parameters())
  300. print("detect params: %.2fM,rec params: %.2fM" % (total / 1e6, total_1 / 1e6))
  301. # plate_color_model =init_color_model(opt.color_model,device)
  302. time_all = 0
  303. time_begin = time.time()
  304. # 处理视频流
  305. if opt.stream:
  306. cap = cv2.VideoCapture(opt.stream)
  307. if not cap.isOpened():
  308. print(f"无法打开视频流: {opt.stream}")
  309. return
  310. fps = cap.get(cv2.CAP_PROP_FPS) or 25
  311. width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
  312. height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
  313. out = cv2.VideoWriter("stream_result.mp4", cv2.VideoWriter_fourcc(*"MP4V"), fps, (width, height))
  314. frame_count = 0
  315. fps_all = 0
  316. print(f"开始处理视频流: {opt.stream}")
  317. while True:
  318. t1 = cv2.getTickCount()
  319. frame_count += 1
  320. ret, img = cap.read()
  321. if not ret:
  322. print("视频流读取结束或出错")
  323. break
  324. dict_list = detect_Recognition_plate(
  325. detect_model,
  326. img,
  327. device,
  328. plate_rec_model,
  329. opt.img_size,
  330. is_color=opt.is_color,
  331. )
  332. ori_img = draw_result(img, dict_list)
  333. t2 = cv2.getTickCount()
  334. infer_time = (t2 - t1) / cv2.getTickFrequency()
  335. fps = 1.0 / infer_time
  336. fps_all += fps
  337. str_fps = f"fps:{fps:.2f}"
  338. cv2.putText(ori_img, str_fps, (20, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
  339. cv2.imshow("Stream Result", ori_img)
  340. out.write(ori_img)
  341. if cv2.waitKey(1) & 0xFF == ord('q'):
  342. break
  343. cap.release()
  344. out.release()
  345. cv2.destroyAllWindows()
  346. print(f"总帧数: {frame_count}, 平均FPS: {fps_all / frame_count:.2f}")
  347. # 处理本地视频
  348. elif opt.video:
  349. video_name = opt.video
  350. capture = cv2.VideoCapture(video_name)
  351. fourcc = cv2.VideoWriter_fourcc(*"MP4V")
  352. fps = capture.get(cv2.CAP_PROP_FPS) # 帧数
  353. width, height = (
  354. int(capture.get(cv2.CAP_PROP_FRAME_WIDTH)),
  355. int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT)),
  356. ) # 宽高
  357. out = cv2.VideoWriter("result.mp4", fourcc, fps, (width, height)) # 写入视频
  358. frame_count = 0
  359. fps_all = 0
  360. rate, FrameNumber, duration = get_second(capture)
  361. if capture.isOpened():
  362. while True:
  363. t1 = cv2.getTickCount()
  364. frame_count += 1
  365. print(f"第{frame_count} 帧", end=" ")
  366. ret, img = capture.read()
  367. if not ret:
  368. break
  369. # if frame_count%rate==0:
  370. img0 = copy.deepcopy(img)
  371. dict_list = detect_Recognition_plate(
  372. detect_model,
  373. img,
  374. device,
  375. plate_rec_model,
  376. opt.img_size,
  377. is_color=opt.is_color,
  378. )
  379. ori_img = draw_result(img, dict_list)
  380. t2 = cv2.getTickCount()
  381. infer_time = (t2 - t1) / cv2.getTickFrequency()
  382. fps = 1.0 / infer_time
  383. fps_all += fps
  384. str_fps = f"fps:{fps:.4f}"
  385. cv2.putText(
  386. ori_img,
  387. str_fps,
  388. (20, 20),
  389. cv2.FONT_HERSHEY_SIMPLEX,
  390. 1,
  391. (0, 255, 0),
  392. 2,
  393. )
  394. cv2.imshow("haha", ori_img)
  395. cv2.waitKey(0)
  396. out.write(ori_img)
  397. # 处理图片
  398. else:
  399. if not os.path.isfile(opt.image_path): # 目录
  400. file_list = []
  401. allFilePath(
  402. opt.image_path, file_list
  403. ) # 将目录下的所有图片文件路径读取到file_list里面
  404. for img_path in file_list: # 遍历图片文件
  405. print(count, img_path, end=" ")
  406. time_b = time.time() # 开始时间
  407. img = cv_imread(img_path) # opencv 读取图片
  408. if img is None:
  409. continue
  410. if img.shape[-1] == 4: # 图片如果是4个通道的,将其转为3个通道
  411. img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)
  412. # detect_one(model,img_path,device)
  413. dict_list = detect_Recognition_plate(
  414. detect_model,
  415. img,
  416. device,
  417. plate_rec_model,
  418. opt.img_size,
  419. is_color=opt.is_color,
  420. ) # 检测以及识别车牌
  421. pprint(dict_list)
  422. ori_img = draw_result(img, dict_list) # 将结果画在图上
  423. img_name = os.path.basename(img_path)
  424. save_img_path = os.path.join(save_path, img_name) # 图片保存的路径
  425. time_e = time.time()
  426. time_gap = time_e - time_b # 计算单个图片识别耗时
  427. if count:
  428. time_all += time_gap
  429. if isinstance(ori_img, cv2.UMat):
  430. ori_img = cv2.UMat.get(ori_img)
  431. cv2.imwrite(save_img_path, ori_img) # opencv将识别的图片保存
  432. count += 1
  433. # cv2.namedWindow("result", cv2.WINDOW_NORMAL)
  434. # cv2.resizeWindow("result", 800, 600)
  435. cv2.imshow("result", ori_img)
  436. cv2.waitKey(0)
  437. cv2.destroyAllWindows()
  438. print(
  439. f"sumTime time is {time.time() - time_begin} s, average pic time is {time_all / (len(file_list) - 1)}"
  440. )
  441. else: # 单个图片
  442. print(count, opt.image_path, end=" ")
  443. img = cv_imread(opt.image_path)
  444. if img.shape[-1] == 4:
  445. img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)
  446. # detect_one(model,img_path,device)
  447. dict_list = detect_Recognition_plate(
  448. detect_model,
  449. img,
  450. device,
  451. plate_rec_model,
  452. opt.img_size,
  453. is_color=opt.is_color,
  454. )
  455. ori_img = draw_result(img, dict_list)
  456. img_name = os.path.basename(opt.image_path)
  457. save_img_path = os.path.join(save_path, img_name)
  458. cv2.imwrite(save_img_path, ori_img)
  459. if __name__ == '__main__':
  460. start()