首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐
2025年6月15日 星期日 8:35am

python 基于HyperLPR3和OpenCV实时RTSP视频流中文车牌识别(实现ONNX、OpenVINO、NCNN车牌框检测)

  • 25-04-25 02:41
  • 4281
  • 12612
blog.csdn.net

1、HyperLPR3简介

HyperLPR3是一款高性能开源中文车牌识别。源码:https://github.com/szad670401/HyperLPR

2、快速部署与简单使用

参考文章:HyperLPR3车牌识别-五分钟搞定: 中文车牌识别光速部署与使用

3、利用OpenCV对RTSP视频流实时读取

我是参考yolo5的streamload使用python迭代器实现视频流读取,视频流读取部分可以按照自己想法继续优化,例如视频流读取分离。

dataset.py

  1. import math
  2. import time
  3. import threading
  4. import platform
  5. import cv2
  6. import numpy as np
  7. from utils.my_logger import logger
  8. class LoadStreamsByRTSP:
  9. def __init__(self,camera_list):
  10. self.camera_list = camera_list
  11. n = len(camera_list)
  12. # 原尺寸图像 初始化图片 fps 总帧数(无穷大) 线程数 相机ip数组
  13. self.orgImgs,self.imgs, self.fps, self.frames, self.threads,self.camIps,self.ids =[None] * n, [None] * n, [0] * n, [0] * n, [None] * n,[None]*n,[None]*n
  14. self.sources = [] # clean source names for later
  15. self.countFrm = {} #统计帧数,索引,帧数
  16. for i,item in enumerate(camera_list):
  17. # Start thread to read frames from video stream
  18. st = f'{i + 1}/{n}: {item[0]}... '
  19. rtspUrl = item[2]
  20. cap = cv2.VideoCapture(rtspUrl)
  21. if cap.isOpened():
  22. print(f'{rtspUrl} open success !')
  23. self.sources.append(rtspUrl) # rtsp地址
  24. # 如果当前Rtsp流打开失败,记录错误日志,并循环下一个流
  25. else :
  26. logger.error(f'{st}Failed to open {item[0]}')
  27. continue
  28. w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) # 获取视频宽度
  29. h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 获取视频高度
  30. fps = cap.get(cv2.CAP_PROP_FPS) # warning: may return 0 or nan
  31. self.frames[i] = max(int(cap.get(cv2.CAP_PROP_FRAME_COUNT)), 0) or float('inf') # infinite stream fallback
  32. self.fps[i] = max((fps if math.isfinite(fps) else 0) % 100, 0) or 30 # 30 FPS fallback
  33. _, self.imgs[i] = cap.read() # guarantee first frame
  34. self.orgImgs[i] = self.imgs[i].copy()
  35. self.countFrm[i] = 0
  36. self.camIps[i] = item[0] # 记录当前相机IP,用于业务判断
  37. self.ids[i] = item[1] # 记录当前相机编号
  38. self.threads[i] = threading.Thread(target=self.update, args=([i, cap, rtspUrl]), daemon=True)
  39. logger.info(f"{st} Success ({self.frames[i]} frames {w}x{h} at {self.fps[i]:.2f} FPS)")
  40. print(f"{st} Success ({self.frames[i]} frames {w}x{h} at {self.fps[i]:.2f} FPS)")
  41. self.threads[i].start()
  42. print(' ') # newline
  43. def update(self, i, cap, stream):
  44. if cap.isOpened():
  45. while True:
  46. success, im = cap.read()
  47. if success:
  48. self.orgImgs[i] = im #原始图像
  49. else:
  50. logger.warning(f'WARNING: Video stream[{stream}] unresponsive, please check your IP camera connection.15s restart stream...')
  51. self.orgImgs[i] = np.zeros_like(self.orgImgs[i])
  52. time.sleep(15)
  53. cap = cv2.VideoCapture(stream)
  54. if cap.isOpened():
  55. logger.info(f'{stream} restart success !')
  56. #time.sleep(1 / self.fps[i]) # wait time if videofile
  57. def __iter__(self):
  58. #self.count = -1
  59. return self
  60. def initImgs(self):
  61. # 需保证imgs里没有None的图片
  62. while self.imgs[0] is None:
  63. time.sleep(1)
  64. for i,im in enumerate(self.imgs):
  65. if self.imgs[i] is None:
  66. self.imgs[i] = np.zeros_like(self.imgs[0])
  67. while self.orgImgs[0] is None:
  68. time.sleep(1)
  69. for i,im in enumerate(self.orgImgs):
  70. if self.orgImgs[i] is None:
  71. self.orgImgs[i] = np.zeros_like(self.orgImgs[0])
  72. def __next__(self):
  73. beginTime = time.time()
  74. orgImgs = self.orgImgs.copy()
  75. return orgImgs, beginTime,self.camIps,self.ids
  76. def __len__(self):
  77. return len(self.sources)

4、根据自定义修改一些默认设置(使用默认onnx推理也可跳过该步骤)

4.1、下载hyperlpr3

从Github下载Prj-Python源码,并将其中的hyperlpr3文件夹放入自己项目中。

4.2、修改模型文件夹默认位置

修改其中config文件夹下的setting.py,将模型文件夹路径设置为当前项目下,这一步也可以不做,默认是在家目录下面。

  1. import os
  2. import sys
  3. _MODEL_VERSION_ = "20230229"
  4. if 'win32' in sys.platform:
  5. #_DEFAULT_FOLDER_ = os.path.join(os.environ['HOMEPATH'], ".hyperlpr3")
  6. _DEFAULT_FOLDER_ = './'
  7. else:
  8. #_DEFAULT_FOLDER_ = os.path.join(os.environ['HOME'], ".hyperlpr3")
  9. _DEFAULT_FOLDER_ = './'
  10. _ONLINE_URL_ = "http://hyperlpr.tunm.top/raw/"
  11. onnx_runtime_config = dict(
  12. det_model_path_320x=os.path.join(_MODEL_VERSION_, "onnx", "y5fu_320x_sim.onnx"),
  13. det_model_path_640x=os.path.join(_MODEL_VERSION_, "onnx", "y5fu_640x_sim.onnx"),
  14. rec_model_path=os.path.join(_MODEL_VERSION_, "onnx", "rpv3_mdict_160_r3.onnx"),
  15. cls_model_path=os.path.join(_MODEL_VERSION_, "onnx", "litemodel_cls_96x_r1.onnx"),
  16. )
  17. onnx_model_maps = ["det_model_path_320x", "det_model_path_640x", "rec_model_path", "cls_model_path"]
  18. _REMOTE_URL_ = "https://github.com/szad670401/HyperLPR/blob/master/resource/models/onnx/"

之后将模型文件夹复制到当前项目根目录下

5、对RTSP视频流进行实时车牌识别

plateRecognizer.py

  1. # 导入cv相关库
  2. import cv2
  3. import os
  4. import json
  5. import time
  6. import traceback
  7. # 导入依赖包
  8. import hyperlpr3 as lpr3
  9. from hyperlpr3.common.typedef import *
  10. from utils.my_logger import logger
  11. from utils.dataset import LoadStreamsByRTSP
  12. import utils.plateUtil as plateUtil
  13. #将10位时间戳或者13位转换为时间字符串,默认为2017-10-01 13:37:04格式
  14. def timestamp_to_date(time_stamp, format_string="%Y%m%d%H%M%S"):
  15. time_array = time.localtime(time_stamp)
  16. other_style_time = time.strftime(format_string, time_array)
  17. return other_style_time
  18. if __name__ == '__main__':
  19. try:
  20. camera_list = []
  21. if not os.path.exists('images'):
  22. os.mkdir('images')
  23. camPlateInfoRecord = {} #记录当前相机识别到的车牌号
  24. camPlateInfoRecordTime = {}
  25. #catcher = {} # 识别对象
  26. with open("config.json", encoding='utf-8') as f:
  27. jsonData = json.load(f)
  28. for ipkey in jsonData:
  29. camera_list.append([ipkey,jsonData[ipkey]['id'],jsonData[ipkey]['rtspUrl']])
  30. for item in camera_list:
  31. print(item)
  32. camPlateInfoRecord[item[0]] = [] # 初始化
  33. camPlateInfoRecordTime[item[0]] = None
  34. # catcher[item[0]] = lpr3.LicensePlateCatcher()
  35. if not os.path.exists('./images/'+item[0]):
  36. os.mkdir('./images/'+item[0])
  37. # 实例化识别对象
  38. catcher = lpr3.LicensePlateCatcher() # detect_level=lpr3.DETECT_LEVEL_HIGH
  39. dataset = LoadStreamsByRTSP(camera_list)
  40. dataset.initImgs()
  41. frameSkip = 5 # 跳帧
  42. for orgImgs, beginTime,camIps,ids in dataset:
  43. recordTime = time.time()
  44. for i,orgImg in enumerate(orgImgs) :
  45. results = catcher(orgImg)
  46. print(results)
  47. # print(f'catcher Done. ({time.time() - beginTime:.3f}s)') # 打印时间
  48. if len(results)>0:
  49. plateInfo = sorted(results,key=lambda x : x[1], reverse=True)[0]# 根据置信度进行降序排序,并取置信度最高的一项
  50. plateNo = plateUtil.get_plate_color(plateInfo[2]) + plateInfo[0] # 车牌号
  51. plateConf = plateInfo[1] # 置信度
  52. camPlateInfoRecordTime[camIps[i]] = recordTime
  53. if plateConf > 0.97:
  54. # TODO 可直接判定为最佳车牌
  55. camPlateInfoRecord[camIps[i]].append((plateNo,plateConf,orgImg))
  56. break
  57. elif plateConf > 0.8:
  58. camPlateInfoRecord[camIps[i]].append((plateNo,plateConf,orgImg))
  59. pass
  60. else:
  61. # TODO 未识别到车牌
  62. pass
  63. #cv2.imshow(camIps[i], cv2.resize(orgImg,(1280,720))) # cv2.resize(im0,(800,600))
  64. #cv2.waitKey(1) # 1 millisecond
  65. if camPlateInfoRecordTime[camIps[i]] is not None:
  66. currentTime = time.time()
  67. # 判断时间是否大于2秒
  68. if recordTime - camPlateInfoRecordTime[camIps[i]] > 2 and len(camPlateInfoRecord[camIps[i]]) > 0:
  69. # 选取置信度最大的车牌信息保存
  70. savePlateInfo = sorted(camPlateInfoRecord[camIps[i]], key=lambda e: e[1], reverse=True)[0]
  71. save_path = './images/'+camIps[i]+'/'+timestamp_to_date(int(currentTime),'%Y%m%d%H%M%S')+'_'+savePlateInfo[0]+'_'+ids[i]+'.jpg'
  72. cv2.imencode('.jpg', savePlateInfo[2])[1].tofile(save_path)
  73. logger.info(f"{camIps[i]} 识别到车牌:{savePlateInfo[0]},置信度:{savePlateInfo[1]},保存路径:{save_path}")
  74. camPlateInfoRecordTime[camIps[i]] = None # 清空时间记录
  75. camPlateInfoRecord[camIps[i]] = [] # 清空车牌记录
  76. # 显示FPS
  77. fps_time = time.time()-beginTime
  78. if(fps_time > 0):
  79. video_fps = int(1/fps_time)
  80. print(f'{fps_time:.3f}s'+" fps= %.2f"%(video_fps))
  81. #进行跳帧休眠处理
  82. loopTime = int(round(fps_time * 1000))
  83. if loopTime < 40*frameSkip:
  84. time.sleep(0.04*frameSkip-loopTime/1000)
  85. except BaseException:
  86. logger.error("处理时出错!"+traceback.format_exc())
  87. time.sleep(1)

代码解释:

1)、frameSkip = 5 ,我这里使用了跳帧方式去识别所读取到的视频流,主要是避免过多过快的车牌识别检测导致的CPU过高或者其他系统性能问题,一般情况下每5帧识别一次已经足够,可根据自身需求进行调整。

2)、车牌真实性判断逻辑,我这里是当车牌的置信度大于0.8则保存该帧的车牌信息及图像,当该视频流2s内不再出现大于0.8置信度的车牌时,保存图像及车牌信息至文件夹中。该逻辑可根据自身需求进行修改代码,例如通过有效帧次数来保存识别最佳的车牌信息等。

其他文件:

config.json

  1. {
  2. "192.168.**.**": {"id":"XXX001","rtspUrl":"相机的RTSP地址"}
  3. }

plateUtil.py

  1. from hyperlpr3.common.typedef import *
  2. def get_plate_color(plate_type):
  3. plateColor = "蓝"
  4. if plate_type == UNKNOWN:
  5. plateColor = '未知'
  6. elif plate_type == BLUE:
  7. plateColor = "蓝"
  8. elif plate_type == YELLOW_SINGLE:
  9. plateColor = "黄"
  10. elif plate_type == WHILE_SINGLE:
  11. plateColor = "白"
  12. elif plate_type == GREEN:
  13. plateColor = "绿"
  14. return plateColor

6、使用OpenVINO检测车牌框

pip install openvino

6.1 转换为OpenVINO格式的模型文件

onnx2openvino.py

  1. # coding:UTF-8
  2. import os
  3. import openvino as ov
  4. # 定义 ONNX 模型路径
  5. onnx_model_path = "y5fu_320x_sim.onnx"
  6. # 定义输出目录路径
  7. output_dir = "./openvino"
  8. # 检查输出目录是否存在,如果不存在则创建
  9. if not os.path.exists(output_dir):
  10. os.makedirs(output_dir)
  11. print(f"Created output directory at: {output_dir}")
  12. else:
  13. print(f"Output directory already exists at: {output_dir}")
  14. # 设置模型转换参数
  15. try:
  16. # 转换 ONNX 模型为 OpenVINO 格式
  17. ov_model = ov.convert_model(onnx_model_path)
  18. # 保存 OpenVINO 模型
  19. ir_path = './openvino/y5fu_320x_sim.xml'
  20. ov.save_model(ov_model, ir_path)
  21. print(f"Model successfully converted and saved to: {output_dir}")
  22. except Exception as e:
  23. print(f"Error during model conversion: {e}")

执行后即可得到OpenVINO推理文件

6.2、添加OpenVINO推理类

找到 hyperlpr3\inference\multitask_detect.py

添加:

  1. class MultiTaskDetectorOpenVINO(HamburgerABC):
  2. def __init__(self, openvino_path, box_threshold: float = 0.5, nms_threshold: float = 0.6, *args, **kwargs):
  3. super().__init__(*args, **kwargs)
  4. import openvino as ov
  5. self.box_threshold = box_threshold
  6. self.nms_threshold = nms_threshold
  7. # 👇Create OpenVINO Core
  8. core = ov.Core()
  9. # 👇读取模型
  10. self.model = core.read_model(model=openvino_path, weights=openvino_path.replace(".xml", ".bin"))
  11. # 👇加载模型,如果用intel的显卡就把CPU改成GPU,但是要确保你的显卡驱动安装好
  12. self.compiled_model = core.compile_model(model=self.model, device_name="CPU")
  13. self.inputs_option = self.model.input(0)
  14. self.outputs_option = self.model.output(0)
  15. #input_option = self.inputs_option[0]
  16. input_size_ = tuple(self.inputs_option.shape[2:])
  17. self.input_size = tuple(self.input_size)
  18. if not self.input_size:
  19. self.input_size = input_size_
  20. assert self.input_size == input_size_, 'The dimensions of the input do not match the model expectations.'
  21. assert self.input_size[0] == self.input_size[1]
  22. self.input_name = self.inputs_option.names
  23. def _run_session(self, data):
  24. result = self.compiled_model(data)[0]
  25. return result
  26. def _postprocess(self, data):
  27. r, left, top = self.tmp_pack
  28. return post_precessing(data, r, left, top)
  29. def _preprocess(self, image):
  30. img, r, left, top = detect_pre_precessing(image, self.input_size)
  31. self.tmp_pack = r, left, top
  32. return img

6.3、添加OpenVINO推理方法

找到 hyperlpr3\hyperlpr3.py

  1. from .config.settings import onnx_runtime_config as ort_cfg
  2. from .inference.pipeline import LPRMultiTaskPipeline
  3. from .common.typedef import *
  4. from os.path import join
  5. from .config.settings import _DEFAULT_FOLDER_
  6. from .config.configuration import initialization
  7. initialization()
  8. class LicensePlateCatcher(object):
  9. def __init__(self,
  10. detect_inference: int = INFER_ONNX_RUNTIME,
  11. inference: int = INFER_ONNX_RUNTIME,
  12. folder: str = _DEFAULT_FOLDER_,
  13. detect_level: int = DETECT_LEVEL_LOW,
  14. logger_level: int = 3,
  15. full_result: bool = False):
  16. if inference == INFER_ONNX_RUNTIME:
  17. from hyperlpr3.inference.recognition import PPRCNNRecognitionORT
  18. from hyperlpr3.inference.classification import ClassificationORT
  19. import onnxruntime as ort
  20. ort.set_default_logger_severity(logger_level)
  21. if detect_level == DETECT_LEVEL_LOW:
  22. if detect_inference == INFER_ONNX_RUNTIME:
  23. from hyperlpr3.inference.multitask_detect import MultiTaskDetectorORT
  24. det = MultiTaskDetectorORT(join(folder, ort_cfg['det_model_path_320x']), input_size=(320, 320))
  25. elif detect_inference == INFER_OPENVINO:
  26. from hyperlpr3.inference.multitask_detect import MultiTaskDetectorOpenVINO
  27. det = MultiTaskDetectorOpenVINO('./openvino/y5fu_320x_sim.xml', input_size=(320, 320))
  28. elif detect_inference == INFER_NCNN:
  29. from hyperlpr3.inference.multitask_detect import MultiTaskDetectorNCNN
  30. det = MultiTaskDetectorNCNN('./ncnn/y5fu_320x_sim.ncnn.bin', input_size=(320, 320))
  31. elif detect_level == DETECT_LEVEL_HIGH:
  32. det = MultiTaskDetectorORT(join(folder, ort_cfg['det_model_path_640x']), input_size=(640, 640))
  33. else:
  34. raise NotImplemented
  35. rec = PPRCNNRecognitionORT(join(folder, ort_cfg['rec_model_path']), input_size=(48, 160))
  36. cls = ClassificationORT(join(folder, ort_cfg['cls_model_path']), input_size=(96, 96))
  37. self.pipeline = LPRMultiTaskPipeline(detector=det, recognizer=rec, classifier=cls, full_result=full_result)
  38. else:
  39. raise NotImplemented
  40. def __call__(self, image: np.ndarray, *args, **kwargs):
  41. return self.pipeline(image)

6.4、设置为OpenVINO推理

plateRecognizer.py 中

catcher = lpr3.LicensePlateCatcher(detect_inference=INFER_OPENVINO) # detect_level=lpr3.DETECT_LEVEL_HIGH

7、使用NCNN检测车牌框

7.1、转换为NCNN格式的模型文件

注意:网上大部分都是说要下载protobuf和ncnn源码进行编译,再使用onnx2ncnn.exe进行转换,实际验证该方法对onnx转换为ncnn格式时,会报错误Unsupported slice axes !。

建议使用pnnx方式直接转换。

pip install pnnx
pnnx y5fu_320x_sim.onnx inputshape=[1,3,320,320]

执行后会生成一堆文件,选择ncnn的两个文件即可

在项目根目录新建一个ncnn文件夹,并上述两个模型文件放入。

7.2、添加NCNN推理类

找到 hyperlpr3\inference\multitask_detect.py

添加:

  1. import ncnn
  2. class MultiTaskDetectorNCNN(HamburgerABC):
  3. def __init__(self, ncnn_path, box_threshold: float = 0.5, nms_threshold: float = 0.6, *args, **kwargs):
  4. super().__init__(*args, **kwargs)
  5. self.box_threshold = box_threshold
  6. self.nms_threshold = nms_threshold
  7. # 加载ncnn模型
  8. self.net = ncnn.Net()
  9. self.net.opt.use_vulkan_compute = False
  10. self.net.load_param(ncnn_path.replace(".bin", ".param"))
  11. self.net.load_model(ncnn_path)
  12. self.input_size = (320,320)
  13. def _run_session(self, data):
  14. input_data = ncnn.Mat(data[0])
  15. # 创建提取器
  16. extractor = self.net.create_extractor()
  17. extractor.input("in0", input_data)
  18. # 执行推理
  19. ret1, mat_out1 = extractor.extract("out0")
  20. # 处理输出
  21. result = np.expand_dims(np.array(mat_out1), axis=0)
  22. return result
  23. def _postprocess(self, data):
  24. r, left, top = self.tmp_pack
  25. return post_precessing(data, r, left, top)
  26. def _preprocess(self, image):
  27. img, r, left, top = detect_pre_precessing(image, self.input_size)
  28. self.tmp_pack = r, left, top
  29. return img

7.3、添加NCNN推理方法

同6.3

7.4、设置为NCNN推理

plateRecognizer.py 中

catcher = lpr3.LicensePlateCatcher(detect_inference=INFER_NCNN) # detect_level=lpr3.DETECT_LEVEL_HIGH

8、ONNX、OpenVINO、NCNN 推理速度记录(包含预处理和后处理)

本人电脑硬件如下:

内存:16G

CPU:AMD Ryzen 5 4600U with Radeon Graphics  2.10 GHz

无独立显卡

输入视频流大小:1920*1080

8.1、在ONNX下的单帧车牌框检测速度

8.2、在OpenVINO下的单帧车牌框检测速度

不太稳定。。。。

8.3、在NCNN下的单帧车牌框检测速度

9、打包成exe可执行文件进行部署

9.1、安装auto-py-to-exe

  1. pip install auto-py-to-exe
  2. auto-py-to-exe

9.2、部署ONNX推理方式

注意:不包含ncnn部分的代码可以直接打包

9.3、部署OpenVINO推理方式

其余一致,需在--collect-data添加openvino

9.4、部署NCNN推理方式

与9.2一样直接打包可能执行会出现错误:

Library not found: could no resolve或者显示ncnn的DLL load的问题

需要打开你的python环境所在目录找到 Lib\site-packages\ncnn.libs,将里面的DLL复制到Lib同一层目录下

再次打包即可通过并正常运行

10、结语

本人实际测试中,将上述车牌识别代码在Win732位下也部署了一套,其中OpenVINO因为不支持win7无法部署,实测发现ncnn的推理速度大概是onnx的1.5倍左右,并且ncnn推理占用CPU资源也比较低。

注:本文转载自blog.csdn.net的向往光的地方的文章"https://blog.csdn.net/Ding_Jie_/article/details/143425882"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

114
音视频
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top