基于RV1106 PaddleDetection ncnn 的车牌检测识别系统

序论

车牌的识别和检测一直有着广泛的应用场景,不论是在简单的停车场收费系统中,还是大到交通监控系统中,均有广泛的应用,在本次实验中,我们采用了 PaddleDet和rcnn分别做了车牌的检测和识别,可以在检测阶段做到25帧,在识别阶段5帧的效果,几乎可以说具备了较为良好的部署体验了。

车牌识别

本章节在 Lockzhiner Vision Module 上基于 PaddleDet 目标检测类和rcnn文本识别模型,实现了一个车牌识别案例。

1. 基本知识简介

1.1 车牌识别简介

车牌识别是一种基于计算机视觉和深度学习的技术,通过图像处理、字符分割和光学字符识别算法,自动提取车辆牌照中的文字与数字信息。该技术可实时识别不同光照、角度和复杂背景下的车牌,广泛应用于智能交通管理、停车场收费系统、电子警察执法等场景。

1.2 车牌识别流程

  • ​​车牌检测:
    • ​深度学习​​:YOLO、Faster R-CNN等检测模型定位车牌区域。
    • ​​传统方法:颜色空间分析(蓝色/黄色车牌)+边缘检测。
  • ​​车牌矫正:透视变换调整倾斜角度,Hough直线检测旋转角度。
  • ​字符分割:​​垂直投影法分割字符,连通域分析处理粘连字符。
  • 字符识别:
    • ​CRNN​​:CNN+RNN+CTC结构,端到端识别。
    • OCR模型​​:Tesseract、PaddleOCR等开源工具。

2. C++ API文档

2.1 PaddleDetection 类

2.1.1 头文件
#include <lockzhiner_vision_module/vision/deep_learning/detection/paddle_det.h>
2.1.2 构造函数
lockzhiner_vision_module::vision::PaddleDetection();
  • 作用:
    • 创建一个 PaddleDetection 对象,并初始化相关成员变量。
  • 参数:
  • 返回值:
2.1.3 Initialize函数
bool Initialize(const std::string& model_path);
  • 作用:
    • 加载预训练的 PaddleDetection 模型。
  • 参数:
    • model_path:模型路径,包含模型文件和参数文件。
  • 返回值:
    • true:模型加载成功。
    • false:模型加载失败。
2.1.4 SetThreshold函数
void SetThreshold(float score_threshold = 0.5, float nms_threshold = 0.3);
  • 作用:
    • 设置目标检测的置信度阈值和NMS阈值。
  • 参数:
    • score_threshold:置信度阈值,默认值为0.5。
    • nms_threshold:NMS阈值,默认值为0.3。
  • 返回值:
2.1.5 Predict函数
std::vector<lockzhiner_vision_module::vision::DetectionResult> Predict(const cv::Mat& image);
  • 作用:
    • 使用加载的模型对输入图像进行目标检测,返回检测结果。
  • 参数:
    • input_mat (const cv::Mat&): 输入的图像数据,通常是一个 cv::Mat 变量。
  • 返回值:
    • 返回一个包含多个 DetectionResult 对象的向量,每个对象表示一个检测结果。

2.2 Net类

2.2.1 头文件
#include <ncnn/net.h>
  • 作用:用于声明Net类,使得Net类可以在当前文件中使用。
2.2.2 构造类函数
ncnn::Net ocr_net;
  • 作用:创建一个Net类型的对象实例,用于加载模型和参数等。
  • 参数说明:无
  • 返回值:无
2.3.3 load_param函数
int load_param(const DataReader& dr);
  • 参数说明:
    • dr:传入的参数文件路径。
  • 返回值:
    • 返回值为0表示加载参数文件成功。
2.2.4 load_model函数
int load_model(const DataReader& dr);
  • 参数说明:
    • dr:传入的模型文件路径。
  • 返回值:返回值为0表示加载模型成功。
2.2.5 from_pixels函数
ncnn::Mat::from_pixels(plate_img.data, ncnn::Mat::PIXEL_BGR, plate_img.cols, 
                                        plate_img.rows);
  • ​​作用​​:将原始图像像素数据转换为 ncnn::Mat 对象,​​同时进行缩放操作​​,适配神经网络的输入尺寸要求。
  • 参数说明:
    • plate_img.data:输入图像的像素数据指针。
    • ncnn::Mat::PIXEL_BGR:输入像素的颜色格式。
    • plate_img.cols, plate_img.rows:原始图像的宽度和高度。
  • 返回值​​:返回一个 ncnn::Mat 对象,包含缩放后的图像数据,格式为 ​​CHW 排列。

2.3 Extractor类

2.3.1 头文件
#include <ncnn/net.h>
  • 作用:用于声明Extractor类,使得Extractor类可以在当前文件中使用。
2.3.2 构造类函数
ncnn::Extractor ex = ocr_net.create_extractor();
  • 作用:从已经加载了神经网络模型的 net 中创建一个 Extractor 实例,用于执行车牌识别的推理任务。
  • 参数说明:无
  • 返回值:无

3. 车牌识别代码解析

3.1 流程图

开始
│
├── 参数检查 (argc == 4 或 5)
│   └── 错误 → 输出Usage并退出
│
├── 初始化车牌检测模型 (PaddleDet)
│   ├── 使用 argv[1] 加载模型
│   └── 失败 → 错误退出
│
├── 初始化OCR模型 (ncnn)
│   ├── 使用 argv[2] 和 argv[3] 加载 param 和 bin 文件
│   └── 失败 → 错误退出
│
├── 设置字体与显示参数
│
├── 判断运行模式
│   ├── argc == 5 → 图片处理模式
│   │   ├── 读取图像文件
│   │   ├── 执行车牌检测
│   │   ├── 绘制检测框
│   │   ├── 对每个车牌调用 RecognizePlate()
│   │   ├── 叠加识别结果并显示
│   │   ├── 控制台输出详细信息
│   │   └── 等待按键退出
│   │
│   └── argc == 4 → 实时摄像头模式
│       ├── 启动设备连接服务
│       ├── 打开摄像头
│       │
│       └── 实时处理循环
│           ├── 读取一帧图像
│           ├── 执行车牌检测
│           ├── 绘制检测框
│           ├── 对每个车牌识别字符
│           ├── 叠加识别结果
│           ├── 控制台输出 + 时间戳
│           ├── 显示图像
│           ├── 按 ESC 退出循环
│
└── 程序结束

3.2 核心代码解析

  • 初始化车牌检测模型
lockzhiner_vision_module::vision::PaddleDet detector;
if (!detector.Initialize(argv[1])) {
    cerr << "Failed to load detection model: " << argv[1] << endl;
    return 1;
}
  • 车牌检测模型推理
auto results = detector.Predict(image);
  • 可视化并显示推理结果
cv::Mat output_image = image.clone();
for (const auto& det : results) {
    cv::Rect rect(
        static_cast<int>(det.box.x),
        static_cast<int>(det.box.y),
        static_cast<int>(det.box.width),
        static_cast<int>(det.box.height)
    );
    cv::rectangle(
        output_image,
        rect,
        cv::Scalar(0, 255, 0),
        1,
        cv::LINE_AA
    );
}
cv::imshow("Detection Result", output_image);
  • 加载字符识别参数和模型
ocr_net.load_param(param_path.c_str())
ocr_net.load_model(model_path.c_str())
  • 归一化处理
const float mean[3] = {127.5f, 127.5f, 127.5f};
const float norm[3] = {0.0078125f, 0.0078125f, 0.0078125f};
in.substract_mean_normalize(mean, norm);
  • 解码预测结果
string license;
vector<int> preb;
for (int c = 0; c < feat.c; c++) {
    const float* data = feat.channel(c);
    for (int w = 0; w < feat.w; w++) {
        float max_val = -FLT_MAX;
        int max_idx = 0;
        for (int h = 0; h < feat.h; h++) {
            float val = data[w + h * feat.w];
            if (val > max_val) {
                max_val = val;
                max_idx = h;
            }
        }
        preb.push_back(max_idx);
    }
}

// 后处理去重
vector<int> valid_labels;
int prev = -1;
for (size_t i = 0; i < preb.size(); ++i) {
    if (preb[i] != 67 && preb[i] != prev) {
        valid_labels.push_back(preb[i]);
    }
    prev = preb[i];
}

for (int idx : valid_labels) {
    license += plate_chars[idx];
}

自定义函数说明

  • 车牌字符识别
string RecognizePlate(cv::Mat plate_img);
  • 作用:对分割出来的车牌进行识别。
  • 参数说明:
    • plate_image:待识别的车牌图像。
  • 返回值:返回一串字符串类型的数据,表示识别到的车牌是什么。

3.3 完整代码实现

👉 点击获取完整源码


4. 编译调试

4.1 编译环境搭建

4.2 Cmake介绍

cmake_minimum_required(VERSION 3.10)

project(plate_recognize)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 定义项目根目录路径
set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..")
message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH})

include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake")

# 定义 OpenCV SDK 路径
set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module")
set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4")
find_package(OpenCV REQUIRED)
set(OPENCV_LIBRARIES "${OpenCV_LIBS}")

# 定义 LockzhinerVisionModule SDK 路径
set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk")
set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module")
find_package(LockzhinerVisionModule REQUIRED)

# ncnn配置
set(NCNN_ROOT_DIR "${PROJECT_ROOT_PATH}/third_party/ncnn-20240820-lockzhiner-vision-module")  # 确保third_party层级存在
message(STATUS "Checking ncnn headers in: ${NCNN_ROOT_DIR}/include/ncnn")

# 验证头文件存在
if(NOT EXISTS "${NCNN_ROOT_DIR}/include/ncnn/net.h")
    message(FATAL_ERROR "ncnn headers not found. Confirm the directory contains ncnn: ${NCNN_ROOT_DIR}")
endif()

set(NCNN_INCLUDE_DIRS "${NCNN_ROOT_DIR}/include")
set(NCNN_LIBRARIES "${NCNN_ROOT_DIR}/lib/libncnn.a")

add_executable(Test-plate_recognize plate_recognize.cc)
target_include_directories(Test-plate_recognize PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS}  ${NCNN_INCLUDE_DIRS})
target_link_libraries(Test-plate_recognize PRIVATE ${OPENCV_LIBRARIES} ${NCNN_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES})

install(
    TARGETS Test-plate_recognize
    RUNTIME DESTINATION .  
)

4.3 编译项目

使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目

# 进入Demo所在目录
cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/D09_plate_recognize
# 创建编译目录
rm -rf build && mkdir build && cd build
# 配置交叉编译工具链
export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf"
# 使用cmake配置项目
cmake ..
# 执行编译项目
make -j8 && make install

在执行完上述命令后,会在build目录下生成可执行文件。


5. 执行结果

5.1 运行前准备

5.2 运行过程

chmode 777 Test-plate_recognize
# 对车牌图片进行识别
./Test-plate_recognize Plate_recognize.rknn lpr2d.param lpr2d.bin image_path
# 摄像头实时识别
./Test-plate_recognize Plate_recognize.rknn lpr2d.param lpr2d.bin

5.3 运行结果

  • 原始图像

在这里插入图片描述

  • 分割出来的车牌图像

在这里插入图片描述

  • 识别结果

在这里插入图片描述


6. 总结

通过上述内容,我们成功实现了一个车牌识别系统,包括:

  • 加载车牌检测模型和rcnn字符识别模型
  • 进行车牌检测并将车牌分割出来。
  • 将分割出来的车牌进行字符识别。
  • 保存识别结果。

🚀 开发者生态

🔧 问题反馈

遇到技术问题?请到凌智视觉模块官方仓库**凌智视觉模块** 提交Issue

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值