首页 > 其他 > 详细

车牌识别系统开发记录(二) 车牌定位

时间:2014-04-10 03:09:39      阅读:793      评论:0      收藏:0      [点我收藏+]

这一部分我们来谈一谈如何检测车牌,即是车牌定位。

在上文部分,我讲了车牌定位的四种方法:

(1)基于颜色的分割方法;

(2)基于纹理的分割方法;

(3)基于边缘检测的分割方法;

(4)基于数学形态法的分割方法。

这里面我要具体介绍的检测车牌方法的步骤如下:

  1. 首先利用Sobel滤波器对灰度图像进行滤波,突出图像中的垂直边缘信息
  2. 利用数学形态学方法: Close(先膨胀再腐蚀,填充内部空隙)
  3. 利用findContours函数寻找车牌的潜在轮廓
  4. 结合车牌的长宽比例的先验知识剔除不符合的轮廓
  5. 利用floodfill算法结合车牌的颜色信息提取可能的车牌区域
  6. 再次结合车牌的长宽比例的先验知识剔除不符合的轮廓
  7. 根据上面初步提取出可能的车牌区域,下一步利用SVM进行判断是否是车牌
好了,下面一步步来分析:

提取垂直边缘
先看如何凸显垂直边缘信息。这个主要是利用Sobel算子对图像进行卷积,学过图像处理的同志们对这个肯定不陌生。这里面用的是OpenCV的Sobel函数。
C++: void Sobel(InputArray src, OutputArray dst, int ddepth, int dx, int dy, intksize=3, double scale=1, double delta=0, int borderType=BORDER_DEFAULT )
因为要提取垂直边缘信息,所以令dx=1,dy=0,如下所示:
bubuko.com,布布扣
在凸显完垂直边缘信息后,我们再次利用阈值化处理,将灰度图像变成二值图像,为下面的形态学处理做准备。

Code:
    Mat img_gray;
    cvtColor(input, img_gray, CV_BGR2GRAY);
    blur(img_gray, img_gray, Size(5,5));    

    // 突出垂直边缘
    Mat img_sobel;
    Sobel(img_gray, img_sobel, CV_8U, 1, 0, 3, 1, 0, BORDER_DEFAULT);
    if(showSteps)
        imshow("Sobel", img_sobel);

    // 阈值化处理
    Mat img_threshold;
    threshold(img_sobel, img_threshold, 0, 255, CV_THRESH_OTSU+CV_THRESH_BINARY);
    if(showSteps)
        imshow("Threshold", img_threshold);

效果:
bubuko.com,布布扣bubuko.com,布布扣

形态学处理:CLOSE
现在利用数学形态学处理中的闭运算(先膨胀再腐蚀),填充上面得到的二值图像的空隙,为下面寻找轮廓做准备。
我们知道,结构元素各向同性的开运算用于消除图像中小于结构元素的细节部分——物体的局部形状保持不变。闭运算用来连接邻近的物体,填补小空洞,填平窄缝隙使得物体边缘变得平滑。
对形态学不熟悉的同学,请击中这个链接(形态学运算)
在这里我们利用OpenCV中的morphologyEx来进行形态学处理。因为车牌是长方形的,所以我们更希望的是对横向连接,故而我们将结构元素element大小设为(17×3).

Code:
    // 形态学close处理(先膨胀再腐蚀,填充内部空隙)
    Mat element = getStructuringElement(MORPH_RECT, Size(17, 3) );
    morphologyEx(img_threshold, img_threshold, CV_MOP_CLOSE, element);
    if(showSteps)
        imshow("Close", img_threshold);

效果:
bubuko.com,布布扣

提取轮廓
在形态学处理之后,我们对得到的二值图像提取潜在的车牌轮廓。
所谓的(简单的)轮廓提取算法就是:系统性地扫描图像直到遇到连通区域的一个点,以它为起始点,跟踪它的轮廓,标记边界上的像素。当轮廓完整闭合,扫描回到上一个位置,直到再次发现新的成分。
这里面我们利用OpenCV中的findContours函数:
C++: void findContours(InputOutputArray image, OutputArrayOfArrays contours, int mode, int method, Point offset=Point())
这个函数输入端是一幅二值图像,而输出的是轮廓的向量(轮廓本身是cv::Points的向量),另外还涉及了两个参数mode和method。其中mode=CV_RETR_EXTERNEL标记了仅返回外部轮廓,即物体的洞将会被忽视。第二个参数method=CV_CHAIN_APPROX_NONE要求给出更复杂的链式轮廓近似,以得到更紧密的表示方式。
更多相关信息可参考:

Code:
    // 利用findContours函数寻找车牌可能的轮廓
    vector< vector< Point> > contours;
    findContours(img_threshold,
            contours,			// 轮廓的数组
            CV_RETR_EXTERNAL,	// 获取外轮廓
            CV_CHAIN_APPROX_NONE); // 获取每个轮廓的每个像素
	

	// 排除非车牌的可能轮廓
    vector<vector<Point> >::iterator itc= contours.begin();
    vector<RotatedRect> rects;  
    while (itc!=contours.end()) {
		// minAreaRect函数计算最小包围旋转矩形
        RotatedRect mr= minAreaRect(Mat(*itc));
        if( !verifySizes(mr)){
            itc= contours.erase(itc);
        }else{
            ++itc;
            rects.push_back(mr);
        }
    }

	// 画出轮廓
    cv::Mat result;
    input.copyTo(result);
    cv::drawContours(result,contours,
            -1, // draw all contours
            cv::Scalar(255,0,0), // in blue
            1); // with a thickness of 1





    for(int i = 0; i < rects.size(); i++){

	// 现在进一步利用车牌的颜色信息,运用floodfill算法。
	// flood fill 算法是从一个区域中提取若干个连通的点与其他相邻区域分开的经典算法。它接收三个参数:起始结点,
	// 目标颜色和替换颜色。
        circle(result, rects[i].center, 3, Scalar(0,255,0), -1);

        //get the min size between width and height
        float minSize = (rects[i].size.width < rects[i].size.height)? rects[i].size.width : rects[i].size.height;
        minSize=minSize-minSize*0.5;

        //initialize rand and get 5 points around center for floodfill algorithm
        srand ( time(NULL) );
        //Initialize floodfill parameters and variables
        Mat mask;
        mask.create(input.rows + 2, input.cols + 2, CV_8UC1);
        mask= Scalar::all(0);
        int loDiff = 30;
        int upDiff = 30;
        int connectivity = 4;
        int newMaskVal = 255;
        int NumSeeds = 10;
        Rect ccomp;
        int flags = connectivity + (newMaskVal << 8 ) + CV_FLOODFILL_FIXED_RANGE + CV_FLOODFILL_MASK_ONLY;

        for(int j=0; j<NumSeeds; j++){
            Point seed;
            seed.x=rects[i].center.x+rand()%(int)minSize-(minSize/2);
            seed.y=rects[i].center.y+rand()%(int)minSize-(minSize/2);
            circle(result, seed, 1, Scalar(0,255,255), -1);
            int area = floodFill(input, mask, seed, Scalar(255,0,0), &ccomp, Scalar(loDiff, loDiff, loDiff), Scalar(upDiff, upDiff, upDiff), flags);
        }
        if(showSteps)
            imshow("MASK", mask);
        //cvWaitKey(0);
bubuko.com,布布扣


本文地址:http://blog.csdn.net/linj_m/article/details/23281403

更多图像处理、机器视觉资源请关注 博客:LinJM-机器视觉 微博:林建民-机器视觉


车牌识别系统开发记录(二) 车牌定位,布布扣,bubuko.com

车牌识别系统开发记录(二) 车牌定位

原文:http://blog.csdn.net/linj_m/article/details/23281403

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!