OpenCV西班牙车牌识别(一)

开发环境:Qt

车牌定位的大致流程:
  1. 读取原图
    imread();
  2. 灰度化操作
    cvtColor();
    或者在读取原图时直接做灰度化处理
  3. 滤波去噪
    blur();
  4. 梯度运算(sobel算子)
  5. 阈值化操作(二值化)
    threshold();// CV_THRESH_OTSU大律法
  6. 利用形态学闭运算(先膨胀再腐蚀),闭运算能够排除小型黑洞,将白色区域连成一块。膨胀操作会使物体的边界向外扩张,如果物体内部存在小空洞的话,经过膨胀操作这些洞将被补上,因而不再是边界了。再进行腐蚀操作时,外部边界将变回原来的样子,而这些内部空洞则永远消失了。
  7. 轮廓检测
        //Point->vector<Point>->vector<vector<Point>>//一个轮廓由几个点构成
        vector<vector<Point>> contours;//定义存储轮廓的向量
        //轮廓又是 点的向量
    
        //CV_RETR_EXTERNAL:只检测外轮廓
        //CV_CHAIN_APPROX_SIMPLE 近似方法:只存储垂直/水平/对角直线的起始点-->就是只需要边缘点
        findContours(closed,contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
  8. 绘制轮廓
  9. 求轮廓的最小外接矩形
  10. 验证过滤,通过面积和宽高比,验证是否为车牌区域
    int i;
        //  key(整型下标), value(旋转矩形-待选车牌区域)
        map<int, RotatedRect> imap; //映射/哈希,保存 待选车牌区域
        for(i=0; i<contours.size(); i++)
        {
            //                  所有轮廓   下标  颜色       线条粗细
            //drawContours(thresh, contours, i, Scalar(255), 3);
            //9.求轮廓最小外接矩形
            RotatedRect rect;//RotatedRect:旋转矩形
            rect = minAreaRect(contours[i]);
    
            //10.验证:过滤-->通过面积和宽高比 验证是否为车牌区域
            if(!verify(rect))
                continue;
            else
                imap[i] = rect;//哈希存储,用数组下标的方式-->第几个轮廓以及它的旋转矩形
            //https://www.cnblogs.com/fnlingnzb-learner/p/5833051.html
    
            //获取每个矩形的四个顶点
            Point2f vertices[4];
            rect.points(vertices);
            //画直线,看效果
            int j;
            for(j=0; j<4; j++)
                line(closed,vertices[j],vertices[(j+1)%4], Scalar(255), 3);
        }
  11. 利用矩形度来验证
    利用迭代器,通过循环找出矩形度和标准车牌最接近的矩形
     /*
        *    矩形度:周长的平方除以面积
        *    (2w+2h)*(2w+2h)/w*h
        *    (4w^2 + 4h^2 + 4w*h)/w*h
        *    4w/h + 4h/w + 8
        *    4*4.7272 + 4*(1/4.7272) + 4 = 27.75
        */
        float min_diff = 10000;//任意初始值
        int index = 0;
        map<int,RotatedRect>::iterator iter;//迭代器
        for(iter = imap.begin(); iter!=imap.end(); iter++)//通过循环,找出矩形度与标准车牌的矩形度最接近的
        {
            //求面积:
            int area = contourArea(contours[iter->first]);
            int perimeter = arcLength(contours[iter->first],true);
    
            if(area != 0)
            {
                int squareness = perimeter * perimeter / area;//矩形度
                float diff = abs(squareness - 27.75);//差值
                if(diff<min_diff)
                {
                    min_diff = diff;//当前最小差值
                    index = iter->first;//对应的下标
                }
            }
        }
  12. 绘制最接近的矩形
  13. 图像切割

以下为本模块全部代码:

#include "locate.h"
#include <opencv.hpp>
#include <map>

using namespace cv;

#define Debug 1

void myDebug(const string &winname, const Mat &image)//调试代码
{
#if Debug
    imshow(winname,image);
#endif
}

bool verify(RotatedRect rect)//宽高比的算法
{
    const float aspect = 4.7272;//宽高比(西班牙车牌)
    int min = 15 * aspect * 15;//面积下限(宽*高)
    int max = 125 * aspect * 125;//面积上限

    //宽高比误差范围
    float error = 0.1;
    float rmin = aspect - aspect * error; //误差
    float rmax = aspect + aspect * error;

    int area = rect.size.width * rect.size.height; //实际面积
    float rate = rect.size.width / rect.size.height; //实际宽高比

    return area>=min && area<=max && rate>=rmin && rate<=rmax;
}


void locate(const string &filename)
{
    //1.读入图像
    Mat image;
    image = imread(filename);
    if(image.empty())
        return;
    myDebug("src",image);

    //2.灰度化
    Mat gray;
    cvtColor(image,gray,COLOR_BGR2GRAY);
    myDebug("gray",gray);

    //3.滤波去噪
    Mat blured;
    blur(gray,blured,Size(5,5));
    myDebug("blured",blured);

    //4.梯度运算,求边缘--定位出可能的位置-->利用sobel算子
    Mat xsobel,ysobel;
    Sobel(blured, xsobel, CV_8U, 1, 0, 3);
    Sobel(blured, ysobel, CV_8U, 0, 1, 3);
    myDebug("xsobel",xsobel);
    myDebug("ysobel",ysobel);

    //5.阈值化--得到最优的二值图像
    Mat thresh;
    threshold(xsobel,thresh, 0, 255, CV_THRESH_OTSU);//大律法
    myDebug("thresh",thresh);

    //6.形态学闭运算--白色连成一片-->获取结构元素/闭运算
    //使用闭运算(先膨胀再腐蚀)连通临近区域,说白了就是将亮的区域连在一起
    Mat closed;
    Mat element = getStructuringElement(MORPH_RECT,Size(17,3));
    morphologyEx(thresh,closed,MORPH_CLOSE,element);
    myDebug("closed",closed);

    //7.轮廓检测
    //Point->vector<Point>->vector<vector<Point>>//一个轮廓由几个点构成
    vector<vector<Point>> contours;//定义存储轮廓的向量
    //轮廓又是 点的向量

    //CV_RETR_EXTERNAL:只检测外轮廓
    //CV_CHAIN_APPROX_SIMPLE 近似方法:只存储垂直/水平/对角直线的起始点-->就是只需要边缘点
    findContours(closed,contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    //8.绘制轮廓
    int i;
    //  key(整型下标), value(旋转矩形-待选车牌区域)
    map<int, RotatedRect> imap; //映射/哈希,保存 待选车牌区域
    for(i=0; i<contours.size(); i++)
    {
        //                  所有轮廓   下标  颜色       线条粗细
        //drawContours(thresh, contours, i, Scalar(255), 3);
        //9.求轮廓最小外接矩形
        RotatedRect rect;//RotatedRect:旋转矩形
        rect = minAreaRect(contours[i]);

        //10.验证:过滤-->通过面积和宽高比 验证是否为车牌区域
        if(!verify(rect))
            continue;
        else
            imap[i] = rect;//哈希存储,用数组下标的方式-->第几个轮廓以及它的旋转矩形
        //https://www.cnblogs.com/fnlingnzb-learner/p/5833051.html

        //获取每个矩形的四个顶点
        Point2f vertices[4];
        rect.points(vertices);
        //画直线,看效果
        int j;
        for(j=0; j<4; j++)
            line(closed,vertices[j],vertices[(j+1)%4], Scalar(255), 3);

    }
    myDebug("contours", closed);
    cout << imap.size() << endl;

    //11.利用矩形度来验证
    /*
        矩形度:周长的平方除以面积
        (2w+2h)*(2w+2h)/w*h
        (4w^2 + 4h^2 + 4w*h)/w*h
        4w/h + 4h/w + 8
        4*4.7272 + 4*(1/4.7272) + 4 = 27.75
    */
    float min_diff = 10000;//任意初始值
    int index = 0;
    map<int,RotatedRect>::iterator iter;//迭代器
    for(iter = imap.begin(); iter!=imap.end(); iter++)//通过循环,找出矩形度与标准车牌的矩形度最接近的
    {
        //求面积:
        int area = contourArea(contours[iter->first]);
        int perimeter = arcLength(contours[iter->first],true);

        if(area != 0)
        {
            int squareness = perimeter * perimeter / area;//矩形度
            float diff = abs(squareness - 27.75);//差值
            if(diff<min_diff)
            {
                min_diff = diff;//当前最小差值
                index = iter->first;//对应的下标
            }
        }

    }

    //12.绘制最接近的矩形
    RotatedRect rect2 = imap[index];
    Point2f vertices2[4];
    rect2.points(vertices2);
    for(int i=0; i<4; i++)
    {
        line(image,vertices2[i],vertices2[(i+1)%4], Scalar(0,255,0), 3);
    }
    myDebug("final",image);

    //13.图像切割 始终保持 宽 > 高
    Mat locate;
    RotatedRect rest = imap[index];
    Size size = rest.size;
    if(size.width < size.height)
        swap(size.width, size.height);
    //           原图像  大小(宽高) 中心点   输出结果
    getRectSubPix(image, size, rest.center, locate);
    imwrite("locate1.jpg",locate);

    waitKey(0);
}
欢迎交流学习,若有不足,还望指正!
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值