博客
关于我
EasyPR源码精析(2)——车牌定位之倾斜扭转
阅读量:798 次
发布时间:2023-04-15

本文共 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;}

    代码解析:

    • 根据斜率计算仿射变换矩阵。
    • 生成仿射变换后的图像,确保车牌区域恢复到正直状态。

    总结

    车牌倾斜矫正是车牌识别中的关键步骤,通过多个核心功能函数的协同工作,实现了车牌区域的旋转矫正和仿射变换矫正,确保车牌识别的准确性。这些功能函数不仅高效地解决了车牌倾斜问题,还通过优化算法和矩阵变换,实现了对车牌区域的精准处理。

    转载地址:http://cgrfk.baihongyu.com/

    你可能感兴趣的文章
    mqtt broker服务端
    查看>>
    MQTT 保留消息
    查看>>
    MQTT 持久会话与 Clean Session 详解
    查看>>
    MQTT工作笔记0007---剩余长度
    查看>>
    MQTT工作笔记0009---订阅主题和订阅确认
    查看>>
    Mqtt搭建代理服务器进行通信-浅析
    查看>>
    MS Edge浏览器“STATUS_INVALID_IMAGE_HASH“兼容性问题
    查看>>
    ms sql server 2008 sp2更新异常
    查看>>
    MS UC 2013-0-Prepare Tool
    查看>>
    MSBuild 教程(2)
    查看>>
    msbuild发布web应用程序
    查看>>
    MSB与LSB
    查看>>
    MSCRM调用外部JS文件
    查看>>
    MSCRM调用外部JS文件
    查看>>
    MSEdgeDriver (Chromium) 不适用于版本 >= 79.0.313 (Canary)
    查看>>
    MsEdgeTTS开源项目使用教程
    查看>>
    msf
    查看>>
    MSSQL数据库查询优化(一)
    查看>>
    MSSQL数据库迁移到Oracle(二)
    查看>>
    MSSQL日期格式转换函数(使用CONVERT)
    查看>>