| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509 |
- import argparse
- import copy
- import os
- import time
- from pprint import pprint
- import cv2
- import numpy as np
- import torch
- from models.experimental import attempt_load
- from plate_recognition.double_plate_split_merge import get_split_merge
- from plate_recognition.plate_rec import (
- allFilePath,
- cv_imread,
- get_plate_result,
- init_model,
- )
- from utils.cv_puttext import cv2ImgAddText
- from utils.datasets import letterbox
- from utils.general import check_img_size, non_max_suppression_face, scale_coords
- clors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (0, 255, 255)]
- danger = ["危", "险"]
- def order_points(pts):
- rect = np.zeros((4, 2), dtype="float32")
- s = pts.sum(axis=1)
- rect[0] = pts[np.argmin(s)]
- rect[2] = pts[np.argmax(s)]
- diff = np.diff(pts, axis=1)
- rect[1] = pts[np.argmin(diff)]
- rect[3] = pts[np.argmax(diff)]
- return rect
- def four_point_transform(image, pts): # 透视变换得到车牌小图
- rect = pts.astype("float32")
- (tl, tr, br, bl) = rect
- widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
- widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
- maxWidth = max(int(widthA), int(widthB))
- heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
- heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
- maxHeight = max(int(heightA), int(heightB))
- dst = np.array(
- [[0, 0], [maxWidth - 1, 0], [maxWidth - 1, maxHeight - 1], [0, maxHeight - 1]],
- dtype="float32",
- )
- M = cv2.getPerspectiveTransform(rect, dst)
- warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
- return warped
- def load_model(weights, device): # 加载检测模型
- model = attempt_load(weights, map_location=device) # FP32 model
- return model
- def scale_coords_landmarks(
- img1_shape, coords, img0_shape, ratio_pad=None
- ): # 返回到原图坐标
- if ratio_pad is None: # calculate from img0_shape
- gain = min(
- img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1]
- ) # gain = old / new
- pad = (
- (img1_shape[1] - img0_shape[1] * gain) / 2,
- (img1_shape[0] - img0_shape[0] * gain) / 2,
- ) # wh padding
- else:
- gain = ratio_pad[0][0]
- pad = ratio_pad[1]
- coords[:, [0, 2, 4, 6]] -= pad[0] # x padding
- coords[:, [1, 3, 5, 7]] -= pad[1] # y padding
- coords[:, :8] /= gain
- coords[:, 0].clamp_(0, img0_shape[1]) # x1
- coords[:, 1].clamp_(0, img0_shape[0]) # y1
- coords[:, 2].clamp_(0, img0_shape[1]) # x2
- coords[:, 3].clamp_(0, img0_shape[0]) # y2
- coords[:, 4].clamp_(0, img0_shape[1]) # x3
- coords[:, 5].clamp_(0, img0_shape[0]) # y3
- coords[:, 6].clamp_(0, img0_shape[1]) # x4
- coords[:, 7].clamp_(0, img0_shape[0]) # y4
- # coords[:, 8].clamp_(0, img0_shape[1]) # x5
- # coords[:, 9].clamp_(0, img0_shape[0]) # y5
- return coords
- def get_plate_rec_landmark(
- img, xyxy, conf, landmarks, class_num, device, plate_rec_model, is_color=False
- ): # 获取车牌坐标以及四个角点坐标并识别车牌号
- h, w, c = img.shape
- result_dict = {}
- 1 or round(0.002 * (h + w) / 2) + 1
- x1 = int(xyxy[0])
- y1 = int(xyxy[1])
- x2 = int(xyxy[2])
- y2 = int(xyxy[3])
- height = y2 - y1
- landmarks_np = np.zeros((4, 2))
- rect = [x1, y1, x2, y2]
- for i in range(4):
- point_x = int(landmarks[2 * i])
- point_y = int(landmarks[2 * i + 1])
- landmarks_np[i] = np.array([point_x, point_y])
- class_label = int(class_num) # 车牌的的类型0代表单层车牌,1代表双层车牌
- roi_img = four_point_transform(img, landmarks_np) # 透视变换得到车牌小图
- if class_label: # 判断是否是双层车牌,是双牌的话进行分割后然后拼接
- roi_img = get_split_merge(roi_img)
- if not is_color:
- plate_number, rec_prob = get_plate_result(
- roi_img, device, plate_rec_model, is_color=is_color
- ) # 对车牌小图进行识别
- else:
- plate_number, rec_prob, plate_color, color_conf = get_plate_result(
- roi_img, device, plate_rec_model, is_color=is_color
- )
- # cv2.imwrite("roi.jpg",roi_img)
- result_dict["rect"] = rect # 车牌roi区域
- result_dict["detect_conf"] = conf # 检测区域置信度
- result_dict["landmarks"] = landmarks_np.tolist() # 车牌角点坐标
- result_dict["plate_no"] = plate_number # 车牌号
- result_dict["rec_conf"] = rec_prob # 每个字符的概率
- result_dict["roi_height"] = roi_img.shape[0] # 车牌高度
- result_dict["plate_color"] = ""
- if is_color:
- result_dict["plate_color"] = plate_color # 车牌颜色
- result_dict["color_conf"] = color_conf # 颜色置信度
- result_dict["plate_type"] = class_label # 单双层 0单层 1双层
- return result_dict
- def detect_Recognition_plate(
- model, orgimg, device, plate_rec_model, img_size, is_color=False
- ): # 获取车牌信息
- # img_size = opt_img_size
- conf_thres = 0.3 ##### 置信度阈值 #####
- iou_thres = 0.5 # nms的iou值
- dict_list = []
- img0 = copy.deepcopy(orgimg)
- assert orgimg is not None, "Image Not Found "
- h0, w0 = orgimg.shape[:2]
- r = img_size / max(h0, w0)
- if r != 1:
- interp = cv2.INTER_AREA if r < 1 else cv2.INTER_LINEAR
- img0 = cv2.resize(img0, (int(w0 * r), int(h0 * r)), interpolation=interp)
- imgsz = check_img_size(img_size, s=model.stride.max()) # 检查 img_size
- img = letterbox(img0, new_shape=imgsz)[
- 0
- ] # 检测前处理,图片长宽变为32倍数
- img = (
- img[:, :, ::-1].transpose(2, 0, 1).copy()
- ) # 图片的BGR排列转为RGB,然后将图片的H,W,C排列变为C,H,W排列
- t0 = time.time()
- img = torch.from_numpy(img).to(device)
- img = img.float()
- img /= 255.0 # 0 - 255 to 0.0 - 1.0
- if img.ndimension() == 3:
- img = img.unsqueeze(0)
- pred = model(img)[0]
- pred = non_max_suppression_face(pred, conf_thres, iou_thres)
- # 检测进程
- for i, det in enumerate(pred): # 对每张图片遍历
- if len(det):
- det[:, :4] = scale_coords(img.shape[2:], det[:, :4], orgimg.shape).round()
- # 打印结果
- for c in det[:, -1].unique():
- n = (det[:, -1] == c).sum() # 检测每个种类
- det[:, 5:13] = scale_coords_landmarks(
- img.shape[2:], det[:, 5:13], orgimg.shape
- ).round()
- for j in range(det.size()[0]):
- xyxy = det[j, :4].view(-1).tolist()
- conf = det[j, 4].cpu().numpy()
- landmarks = det[j, 5:13].view(-1).tolist()
- class_num = det[j, 13].cpu().numpy()
- result_dict = get_plate_rec_landmark(
- orgimg,
- xyxy,
- conf,
- landmarks,
- class_num,
- device,
- plate_rec_model,
- is_color=is_color,
- )
- dict_list.append(result_dict)
- return dict_list
- # cv2.imwrite('result.jpg', orgimg)
- def draw_result(orgimg, dict_list, is_color=True): # 将车牌结果画出
- result_str = ""
- if dict_list:
- pprint(dict_list)
-
- for result in dict_list:
- rect_area = result["rect"]
- x, y, w, h = (
- rect_area[0],
- rect_area[1],
- rect_area[2] - rect_area[0],
- rect_area[3] - rect_area[1],
- )
- padding_w = 0.05 * w
- padding_h = 0.11 * h
- rect_area[0] = max(0, int(x - padding_w))
- rect_area[1] = max(0, int(y - padding_h))
- rect_area[2] = min(orgimg.shape[1], int(rect_area[2] + padding_w))
- rect_area[3] = min(orgimg.shape[0], int(rect_area[3] + padding_h))
- height_area = result["roi_height"]
- landmarks = result["landmarks"]
- result_p = result["plate_no"]
- if result["plate_type"] == 0: # 单层
- result_p += " " + result["plate_color"]
- else: # 双层
- result_p += " " + result["plate_color"] + "双层"
- result_str += result_p + " "
- for i in range(4): # 关键点
- cv2.circle(
- orgimg, (int(landmarks[i][0]), int(landmarks[i][1])), 5, clors[i], -1
- )
- cv2.rectangle(
- orgimg,
- (rect_area[0], rect_area[1]),
- (rect_area[2], rect_area[3]),
- (0, 0, 255),
- 2,
- ) # 画框
- labelSize = cv2.getTextSize(
- result_p, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1
- ) # 获得字体大小
- if rect_area[0] + labelSize[0][0] > orgimg.shape[1]: # 防止文字越界
- rect_area[0] = int(orgimg.shape[1] - labelSize[0][0])
- orgimg = cv2.rectangle(
- orgimg,
- (rect_area[0], int(rect_area[1] - round(1.6 * labelSize[0][1])-30)),
- (
- int(rect_area[0] + round(1.2 * labelSize[0][0]))+50,
- rect_area[1] + labelSize[1],
- ),
- (255, 255, 255),
- cv2.FILLED ,
- ) # 画文字框,背景白色
- if len(result) >= 1:
- orgimg = cv2ImgAddText(
- orgimg,
- result_p,
- rect_area[0],
- int(rect_area[1] - round(1.6 * labelSize[0][1]))-30,
- (0, 0, 0),
- 30,
- )
- if result_str:
- print(result_str)
- return orgimg
- def get_second(capture):
- if capture.isOpened():
- rate = capture.get(5) # 帧速率
- FrameNumber = capture.get(7) # 视频文件的帧数
- duration = FrameNumber / rate
- return int(rate), int(FrameNumber), int(duration)
- def start(image_path="imgs"): # 测试图片路径
- parser = argparse.ArgumentParser()
- parser.add_argument(
- "--detect_model",
- nargs="+",
- type=str,
- default="weights/plate_detect.pt",
- help="model.pt path(s)",
- ) # 检测模型
- parser.add_argument(
- "--rec_model",
- type=str,
- default="weights/plate_rec_color.pth",
- help="model.pt path(s)",
- ) # 车牌识别+颜色识别模型
- parser.add_argument(
- "--is_color", type=bool, default=True, help="plate color"
- ) # 识别颜色
- parser.add_argument(
- "--image_path", type=str, default=image_path, help="source"
- ) # 图片路径
- parser.add_argument(
- "--img_size", type=int, default=640, help="inference size (pixels)"
- ) # 输入图片大小
- parser.add_argument(
- "--output", type=str, default="result", help="source"
- ) # 图片结果保存的位置
- parser.add_argument("--video", type=str, default="", help="source") # 视频的路径
- parser.add_argument(
- "--stream",
- type=str,
- default="",
- help="RTSP/RTMP video stream URL"
- ) # 视频流地址
- device = torch.device(
- "cuda" if torch.cuda.is_available() else "cpu"
- )
- # device =torch.device("cpu")
- opt = parser.parse_args()
- print(opt)
- save_path = opt.output
- count = 0
- if not os.path.exists(save_path):
- os.mkdir(save_path)
- detect_model = load_model(
- opt.detect_model, device
- ) # 初始化检测模型
- plate_rec_model = init_model(
- device, opt.rec_model, is_color=opt.is_color
- ) # 初始化识别模型
- # 计算参数量
- total = sum(p.numel() for p in detect_model.parameters())
- total_1 = sum(p.numel() for p in plate_rec_model.parameters())
- print("detect params: %.2fM,rec params: %.2fM" % (total / 1e6, total_1 / 1e6))
- # plate_color_model =init_color_model(opt.color_model,device)
- time_all = 0
- time_begin = time.time()
- # 处理视频流
- if opt.stream:
- cap = cv2.VideoCapture(opt.stream)
- if not cap.isOpened():
- print(f"无法打开视频流: {opt.stream}")
- return
- fps = cap.get(cv2.CAP_PROP_FPS) or 25
- width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
- height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
- out = cv2.VideoWriter("stream_result.mp4", cv2.VideoWriter_fourcc(*"MP4V"), fps, (width, height))
- frame_count = 0
- fps_all = 0
- print(f"开始处理视频流: {opt.stream}")
- while True:
- t1 = cv2.getTickCount()
- frame_count += 1
- ret, img = cap.read()
- if not ret:
- print("视频流读取结束或出错")
- break
- dict_list = detect_Recognition_plate(
- detect_model,
- img,
- device,
- plate_rec_model,
- opt.img_size,
- is_color=opt.is_color,
- )
- ori_img = draw_result(img, dict_list)
- t2 = cv2.getTickCount()
- infer_time = (t2 - t1) / cv2.getTickFrequency()
- fps = 1.0 / infer_time
- fps_all += fps
- str_fps = f"fps:{fps:.2f}"
- cv2.putText(ori_img, str_fps, (20, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
- cv2.imshow("Stream Result", ori_img)
- out.write(ori_img)
- if cv2.waitKey(1) & 0xFF == ord('q'):
- break
- cap.release()
- out.release()
- cv2.destroyAllWindows()
- print(f"总帧数: {frame_count}, 平均FPS: {fps_all / frame_count:.2f}")
- # 处理本地视频
- elif opt.video:
- video_name = opt.video
- capture = cv2.VideoCapture(video_name)
- fourcc = cv2.VideoWriter_fourcc(*"MP4V")
- fps = capture.get(cv2.CAP_PROP_FPS) # 帧数
- width, height = (
- int(capture.get(cv2.CAP_PROP_FRAME_WIDTH)),
- int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT)),
- ) # 宽高
- out = cv2.VideoWriter("result.mp4", fourcc, fps, (width, height)) # 写入视频
- frame_count = 0
- fps_all = 0
- rate, FrameNumber, duration = get_second(capture)
- if capture.isOpened():
- while True:
- t1 = cv2.getTickCount()
- frame_count += 1
- print(f"第{frame_count} 帧", end=" ")
- ret, img = capture.read()
- if not ret:
- break
- # if frame_count%rate==0:
- img0 = copy.deepcopy(img)
- dict_list = detect_Recognition_plate(
- detect_model,
- img,
- device,
- plate_rec_model,
- opt.img_size,
- is_color=opt.is_color,
- )
- ori_img = draw_result(img, dict_list)
- t2 = cv2.getTickCount()
- infer_time = (t2 - t1) / cv2.getTickFrequency()
- fps = 1.0 / infer_time
- fps_all += fps
- str_fps = f"fps:{fps:.4f}"
- cv2.putText(
- ori_img,
- str_fps,
- (20, 20),
- cv2.FONT_HERSHEY_SIMPLEX,
- 1,
- (0, 255, 0),
- 2,
- )
- cv2.imshow("haha", ori_img)
- cv2.waitKey(0)
- out.write(ori_img)
- # 处理图片
- else:
- if not os.path.isfile(opt.image_path): # 目录
- file_list = []
- allFilePath(
- opt.image_path, file_list
- ) # 将目录下的所有图片文件路径读取到file_list里面
- for img_path in file_list: # 遍历图片文件
- print(count, img_path, end=" ")
- time_b = time.time() # 开始时间
- img = cv_imread(img_path) # opencv 读取图片
- if img is None:
- continue
- if img.shape[-1] == 4: # 图片如果是4个通道的,将其转为3个通道
- img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)
- # detect_one(model,img_path,device)
- dict_list = detect_Recognition_plate(
- detect_model,
- img,
- device,
- plate_rec_model,
- opt.img_size,
- is_color=opt.is_color,
- ) # 检测以及识别车牌
- pprint(dict_list)
- ori_img = draw_result(img, dict_list) # 将结果画在图上
- img_name = os.path.basename(img_path)
- save_img_path = os.path.join(save_path, img_name) # 图片保存的路径
- time_e = time.time()
- time_gap = time_e - time_b # 计算单个图片识别耗时
- if count:
- time_all += time_gap
- if isinstance(ori_img, cv2.UMat):
- ori_img = cv2.UMat.get(ori_img)
- cv2.imwrite(save_img_path, ori_img) # opencv将识别的图片保存
- count += 1
- # cv2.namedWindow("result", cv2.WINDOW_NORMAL)
- # cv2.resizeWindow("result", 800, 600)
- cv2.imshow("result", ori_img)
- cv2.waitKey(0)
- cv2.destroyAllWindows()
- print(
- f"sumTime time is {time.time() - time_begin} s, average pic time is {time_all / (len(file_list) - 1)}"
- )
- else: # 单个图片
- print(count, opt.image_path, end=" ")
- img = cv_imread(opt.image_path)
- if img.shape[-1] == 4:
- img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)
- # detect_one(model,img_path,device)
- dict_list = detect_Recognition_plate(
- detect_model,
- img,
- device,
- plate_rec_model,
- opt.img_size,
- is_color=opt.is_color,
- )
- ori_img = draw_result(img, dict_list)
- img_name = os.path.basename(opt.image_path)
- save_img_path = os.path.join(save_path, img_name)
- cv2.imwrite(save_img_path, ori_img)
- if __name__ == '__main__':
- start()
|