基于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 运行前准备
- 请确保你已经下载了 凌智视觉模块车牌识别参数文件
- 请确保你已经下载了 凌智视觉模块车牌识别bin文件
- 请确保你已经下载了 凌智视觉模块车牌检测模型
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