EasyPR源码精析(2)——车牌定位之倾斜扭转
1.
2.
3.
4.
发布日期:2025-06-18 16:37:43
浏览次数:3
分类:精选文章
本文共 5197 字,大约阅读时间需要 17 分钟。
车牌倾斜矫正方法与实现
车牌识别系统中,倾斜矫正是提升识别准确率的关键步骤。本文将详细介绍车牌倾斜矫正的实现方法,包括多个核心功能函数的实现流程和代码解析。
实现流程概述
车牌倾斜矫正的实现流程如下:
颜色匹配旋转角度判断
判断车牌区域的旋转角度是否在60°范围内,是则进入下一步处理,否则直接忽略该车牌区域。安全矩阵计算
调用calcSafeRect函数,计算车牌区域的安全矩阵,确保车牌区域不会超过图像边界。若超出范围,则跳过该车牌区域。旋转角度细化判断
再次判断车牌区域的旋转角度是否在5°范围内,是则直接输出结果,不进行进一步矫正;否则进入下一步处理。矩形旋转矫正
调用rotation函数,对车牌区域进行旋转矫正,确保旋转后的车牌区域不会被截断。偏斜判断与仿射变换
调用isdeflection函数,判断车牌区域是否为平行四边形。若为平行四边形,则计算其斜率,并调用affine函数进行仿射变换矫正。车牌识别输出
经过上述多步处理后,输出最终的车牌信息。核心功能函数详解
1. calcSafeRect:安全矩阵计算
该函数的主要作用是计算车牌区域的安全矩阵,确保车牌区域不会超出图像边界。
bool calcSafeRect(const RotatedRect &roi_rect, const Mat &src, Rect_ float_ &safeBoundRect) { Rect_ float_ boudRect = roi_rect.boundingRect(); // 返回最小外接矩形 float tl_x = boudRect.x > 0 ? boudRect.x : 0; // 左上角坐标 float tl_y = boudRect.y > 0 ? boudRect.y : 0; float br_x = boudRect.x + boudRect.width < src.cols ? boudRect.x + boudRect.width - 1 : src.cols - 1; float br_y = boudRect.y + boudRect.height < src.rows ? boudRect.y + boudRect.height - 1 : src.rows - 1; float roi_width = br_x - tl_x; float roi_height = br_y - tl_y; if (roi_width <= 0 || roi_height <= 0) return false; safeBoundRect = Rect_ float_ (tl_x, tl_y, roi_width, roi_height); return true;} 代码解析:
- 函数首先计算车牌区域的最小外接矩形。
- 确定矩形的左上角(tl)和右下角(br)的坐标,确保坐标不超出图像边界。
- 计算矩形的宽度和高度,判断是否为有效区域。
- 返回安全矩阵,若无效则返回false。
2. rotation:矩形角度旋转矫正
该函数负责对车牌区域进行旋转矫正,确保旋转后的车牌区域不会被截断。
bool CPlateLocate::rotation(Mat &in, Mat &out, const Size rect_size, const Point2f center, const double angle) { Mat in_large; in_large.create(int(in.rows * 1.5), int(in.cols * 1.5), in.type()); float x = in_large.cols / 2 - center.x; float y = in_large.rows / 2 - center.y; float width = x + in.cols < in_large.cols ? in.cols : in_large.cols - x; float height = y + in.rows < in_large.rows ? in.rows : in_large.rows - y; if (width != in.cols || height != in.rows) return false; Mat imageRoi = in_large(Rect_(float)(x, y, width, height)); addWeighted(imageRoi, 0, in, 1, 0, imageRoi); Point2f new_center(in_large.cols / 2.f, in_large.rows / 2.f); Mat rot_mat = getRotationMatrix2D(new_center, angle, 1); Mat mat_rotated; warpAffine(in_large, mat_rotated, rot_mat, Size(in_large.cols, in_large.rows), CV_INTER_CUBIC); Mat img_crop; getRectSubPix(mat_rotated, Size(rect_size.width, rect_size.height), new_center, img_crop); out = img_crop; return true;} 代码解析:
- 创建放大1.5倍的图像,确保旋转操作不会导致车牌区域被截断。
- 计算旋转中心和目标旋转角度,生成旋转矩阵。
- 使用
warpAffine函数进行仿射变换,确保旋转后的车牌区域完整显示。 - 使用
getRectSubPix函数截取旋转后的车牌区域。
3. isdeflection:偏斜判断与斜率计算
该函数用于判断车牌区域是否为平行四边形,并计算其斜率。
bool CPlateLocate::isdeflection(const Mat &in, const double angle, double &slope) { int nRows = in.rows; int nCols = in.cols; int comp_index[3] = {nRows / 4, nRows / 4 * 2, nRows / 4 * 3}; int len[3]; for (int i = 0; i < 3; i++) { int index = comp_index[i]; const uchar *p = in.ptr (index); int j = 0; int value = 0; while (value == 0 && j < nCols) value = p[j++]; len[i] = j; } double maxlen = max(len[2], len[0]); double minlen = min(len[2], len[0]); double difflen = abs(len[2] - len[0]); double PI = 3.14159265; double g = tan(angle * PI / 180.0); if (maxlen - len[1] > nCols / 32 || len[1] - minlen > nCols / 32) { double slope_can_1 = (len[2] - len[0]) / (comp_index[1]); double slope_can_2 = (len[1] - len[0]) / (comp_index[0]); double slope_can_3 = (len[2] - len[1]) / (comp_index[0]); if (g >= 0) { slope = abs(slope_can_1 - g) <= abs(slope_can_2 - g) ? slope_can_1 : slope_can_2; } else { slope = abs(slope_can_3 - g) <= abs(slope_can_2 - g) ? slope_can_3 : slope_can_2; } return true; } else { slope = 0; } return false;} 代码解析:
- 从二值图像中选择特定行,计算每行的全为0的串长度。
- 计算最大长度、最小长度和差异长度,判断车牌区域是否为平行四边形。
- 计算斜率,根据旋转角度和实际斜率判断车牌是否为平行四边形。
4. affine:仿射变换矫正
该函数根据偏斜角度,进行仿射变换矫正,恢复车牌到正直状态。
void CPlateLocate::affine(const Mat &in, Mat &out, const double slope) { Point2f dstTri[3], plTri[3]; float height = in.rows; float width = in.cols; float xiff = abs(slope) * height; if (slope > 0) { plTri[0] = Point2f(0, 0); plTri[1] = Point2f(width - xiff - 1, 0); plTri[2] = Point2f(0 + xiff, height - 1); dstTri[0] = Point2f(xiff / 2, 0); dstTri[1] = Point2f(width - 1 - xiff / 2, 0); dstTri[2] = Point2f(xiff / 2, height - 1); } else { plTri[0] = Point2f(0 + xiff, 0); plTri[1] = Point2f(width - 1, 0); plTri[2] = Point2f(0, height - 1); dstTri[0] = Point2f(xiff / 2, 0); dstTri[1] = Point2f(width - 1 - xiff + xiff / 2, 0); dstTri[2] = Point2f(xiff / 2, height - 1); } Mat warp_mat = getAffineTransform(plTri, dstTri); Mat affine_mat; affine_mat.create((int)height, (int)width, TYPE); if (in.rows > HEIGHT || in.cols > WIDTH) { warpAffine(in, affine_mat, warp_mat, affine_mat.size(), CV_INTER_AREA); } else { warpAffine(in, affine_mat, warp_mat, affine_mat.size(), CV_INTER_CUBIC); } out = affine_mat;} 代码解析:
- 根据斜率计算仿射变换矩阵。
- 生成仿射变换后的图像,确保车牌区域恢复到正直状态。
总结
车牌倾斜矫正是车牌识别中的关键步骤,通过多个核心功能函数的协同工作,实现了车牌区域的旋转矫正和仿射变换矫正,确保车牌识别的准确性。这些功能函数不仅高效地解决了车牌倾斜问题,还通过优化算法和矩阵变换,实现了对车牌区域的精准处理。
发表评论
最新留言
路过按个爪印,很不错,赞一个!
[***.219.124.196]2026年06月13日 11时09分51秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
Pinia:$patch的使用场景
2023-03-02
Pinia:$subscribe()的使用场景
2023-03-02
Pinpoint对Kubernetes关键业务模块进行全链路监控
2023-03-02
Pinterest 大规模缓存集群的架构剖析
2023-03-02
PinYin4j库的使用
2023-03-02
PIP
2023-03-02
pip install mysqlclient报错
2023-03-02
pip install 出现报asciii码错误的解决
2023-03-02
pip throws TypeError: parse() got an unexpected keyword argument ‘transport_encoding‘ 在尝试安装新软件包时
2023-03-02
pip 下载慢
2023-03-02
pip 安装opencv-python卡死
2023-03-02
pip 安装出现异常
2023-03-02
Pip 安装失败:需要 SSL
2023-03-02
Pip 安装挂起
2023-03-02
pip 或 pip3 为 Python 3 安装包?
2023-03-02
pip 无法从 requirements.txt 安装软件包
2023-03-02