在ARM-Linux下实现车牌识别(一)------车牌提取

你好!这里是风筝的博客,

欢迎和我一起交流。


最近在弄车牌识别这个项目,对于机器视觉有些了解的人都知道,这个东西算是比较成熟了,在书里也有代码。
网上能找到的资料也比较多,所及借着这个机会在ARM开发板上实现以下车牌识别。
反正对于神经网络这些什么的我是不知道了,所以代码也是网上借鉴了的,我稍微整理注释了下。
先放下移植opencv的步骤:移植opencv到嵌入式arm详细过程
第一步做的就是车牌提取:
opencv
代码如下:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <string>
#include <cvaux.h> 
#include <stdio.h> 
#include <opencv2/gpu/gpu.hpp>
#include <opencv2/ml/ml.hpp>

using namespace cv; 
using namespace std;

//车牌宽高比为520/110=4.727272左右,误差不超过40%
//车牌高度范围在15~125之间,视摄像头距离而定(图像大小)
bool verifySizes_closeImg(const RotatedRect & candidate)
{
    float error = 0.4;//误差40%
    const float aspect = 4.7272;//44/14; //长宽比
    int min = 15*aspect*15;//20*aspect*20; //面积下限,最小区域
    int max = 125*aspect*125;//180*aspect*180;  //面积上限,最大区域
    float rmin = aspect - aspect*error; //考虑误差后的最小长宽比
    float rmax = aspect + aspect*error; //考虑误差后的最大长宽比

    int area = candidate.size.height * candidate.size.width;//计算面积
    float r = (float)candidate.size.width/(float)candidate.size.height;//计算宽高比
    if(r <1)
        r = 1/r;

    if( (area < min || area > max) || (r< rmin || r > rmax)  )//满足条件才认为是车牌候选区域
        return false;
    else
        return true;
}

void RgbConvToGray(const Mat& inputImage,Mat & outpuImage)  //g = 0.3R+0.59G+0.11B
{
    outpuImage = Mat(inputImage.rows ,inputImage.cols ,CV_8UC1);  

    for (int i = 0 ;i<inputImage.rows ;++ i)
    {
        uchar *ptrGray = outpuImage.ptr<uchar>(i); 
        const Vec3b * ptrRgb = inputImage.ptr<Vec3b>(i);
        for (int j = 0 ;j<inputImage.cols ;++ j)
        {
            ptrGray[j] = 0.3*ptrRgb[j][2]+0.59*ptrRgb[j][1]+0.11*ptrRgb[j][0];  
        }
    }
}

void normal_area(Mat &intputImg, vector<RotatedRect> &rects_optimal, vector <Mat>& output_area )
{
    float r,angle;
    for (int i = 0 ;i< rects_optimal.size() ; ++i)
    {
        //旋转区域
        angle = rects_optimal[i].angle;
        r = (float)rects_optimal[i].size.width / (float) (float)rects_optimal[i].size.height;
        if(r<1)
            angle = 90 + angle;//旋转图像使其得到长大于高度图像。
        Mat rotmat = getRotationMatrix2D(rects_optimal[i].center , angle,1);//获得变形矩阵对象
        Mat img_rotated;
        warpAffine(intputImg ,img_rotated,rotmat, intputImg.size(),CV_INTER_CUBIC);
        imwrite("car_rotated.jpg",img_rotated);//得到旋转图像

        //裁剪图像
        Size rect_size = rects_optimal[i].size;
        if(r<1)
            swap(rect_size.width, rect_size.height); //交换高和宽
        Mat  img_crop;
        getRectSubPix(img_rotated ,rect_size,rects_optimal[i].center , img_crop );//图像切割

        //用光照直方图调整所有裁剪得到的图像,使具有相同宽度和高度,适用于训练和分类
        Mat resultResized;
        //别人写的:
        /*resultResized.create(33,144,CV32FC1);
        resize(img_crop , resultResized,resultResized.size() , 0,0,INTER_CUBIC);
        resultResized.convertTo(resultResized, CV32FC1);
        resultResized = resultResized.reshape(1,1);*/

        resultResized.create(33,144,CV_8UC3);//CV32FC1????
        resize(img_crop , resultResized,resultResized.size() , 0,0,INTER_CUBIC);
        Mat grayResult;
        RgbConvToGray(resultResized ,grayResult);
        //blur(grayResult ,grayResult,Size(3,3));
        equalizeHist(grayResult,grayResult);

        output_area.push_back(grayResult);
    }
}


int main(int argc, char* argv[])
{
    Mat img_input= imread("./car.jpg");//加载图片
    if(img_input.empty())//如果读入图像失败
    {
        cout << "Can not load image" << endl;
        return -1;
    }

    Mat hsvImg ;
    cvtColor(img_input,hsvImg,CV_BGR2HSV);//RGB模型转换成HSV模型
    imwrite("car_hsv.jpg",hsvImg);//看下hsv效果

    vector <Mat> hsvSplit;
    split(hsvImg,hsvSplit);//将图像的各个通道分离
    equalizeHist(hsvSplit[2],hsvSplit[2]);//直方图均衡化,提高图像的质量
    merge(hsvSplit,hsvImg);//将分离的多个单通道合成一幅多通道图像
    imwrite("car_hsv1.jpg",hsvImg);//看下处理效果

    const int min_blue =100;//最小蓝车区域
    const int max_blue =240;//最大蓝车区域
    int avg_h = (min_blue+max_blue)/2;
    int channels = hsvImg.channels();
    int nRows = hsvImg.rows;
    //图像数据列需要考虑通道数的影响;
    int nCols = hsvImg.cols * channels;

    if (hsvImg.isContinuous())//连续存储的数据,按一行处理
    {
        nCols *= nRows;
        nRows = 1;
    }

    int i, j;
    unsigned char* p;
    const float  minref_sv = 64; //参考的S V的值
    const float max_sv = 255; // S V 的最大值

    for (i = 0; i < nRows; ++i)//根据蓝色在HSV在的区域每个通道的取值范围将此作为阈值,提取出图片中蓝色部分作为备选区域
    {
        p = hsvImg.ptr<uchar>(i);//有效提高了车牌和车色颜色在不相差较大的情况下的识别率
        for (j = 0; j < nCols; j += 3)//致命问题:蓝色的车和蓝色的牌照?
        {
            int H = int(p[j]); //0-180
            int S = int(p[j + 1]);  //0-255
            int V = int(p[j + 2]);  //0-255
            bool colorMatched = false;

            if (H > min_blue && H < max_blue)
            {
                int Hdiff = 0;
                float Hdiff_p = float(Hdiff) / 40;
                float min_sv = 0;

                if (H > avg_h)
                {
                    Hdiff = H - avg_h;
                }   
                else
                {
                    Hdiff = avg_h - H;
                }
                min_sv = minref_sv - minref_sv / 2 * (1 - Hdiff_p);
                if ((S > 70&& S < 255) &&(V > 70 && V < 255))
                    colorMatched = true;
            }
            if (colorMatched == true) 
            {
                p[j] = 0; p[j + 1] = 0; p[j + 2] = 255;
            }
            else 
            {
                p[j] = 0; p[j + 1] = 0; p[j + 2] = 0;
            }
        }
    }

    Mat src_grey;
    Mat img_threshold;
    vector<Mat> hsvSplit_done;

    split(hsvImg, hsvSplit_done);
    src_grey = hsvSplit_done[2];//提取黑色分量
    imwrite("car_hsvSplit.jpg",src_grey);//查看分离通道出来的车牌
    vector <RotatedRect>  rects;
    Mat element = getStructuringElement(MORPH_RECT ,Size(17 ,3));  //闭形态学的结构元素
    morphologyEx(src_grey ,img_threshold,CV_MOP_CLOSE,element); //闭运算,先膨胀后腐蚀,连通近邻区域(填补白色区域的间隙)
    morphologyEx(img_threshold,img_threshold,MORPH_OPEN,element);//形态学处理
    imwrite("car_morphology.jpg",img_threshold);//查看threshold

    vector< vector <Point> > contours;//寻找车牌区域的轮廓
    findContours(img_threshold ,contours,CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);//只检测外轮廓。存储所以轮廓点
    //绘制轮廓
    /*for(int find=0; find < contours.size(); find++)
        drawContours(img_threshold, contours, find, Scalar(255), 2);
    imwrite("car_contours.jpg",img_threshold);//查看轮廓*/

    //对候选的轮廓进行进一步筛选
    vector< vector <Point> > ::iterator itc = contours.begin();
    while( itc != contours.end())
    {
        RotatedRect mr = minAreaRect(Mat( *itc )); //返回每个轮廓的最小有界矩形区域
        if(!verifySizes_closeImg(mr))  //判断矩形轮廓是否符合要求
        {
            itc = contours.erase(itc);
        }
        else     
        {

            rects.push_back(mr);
            ++itc;
        }      
    }
    vector <Mat> output_area;
    normal_area(img_input ,rects,output_area);  //获得144*33的候选车牌区域output_area
    imwrite("car_area.jpg",output_area[0]);//得到候选区域,这里可能会获得多个候选区域,最好使用svm训练一下

    return 0;

}

我们看下效果:
原图car.jpg,我在学校随便拍的一张图:
car

看下hsv的效果图car_hsv.jpg:
hsv

接着直方图均衡化后的图car_hsv1.jpg:
hsv1

提取蓝车区域后,分离出来,提取分量 car_hsvSplit.jpg:
split
可以看出,找到了车牌部分。我拍的这图质量还好,当车辆与车牌都是蓝色时就不好弄出这个了

接着看下形态学处理 car_morphology.jpg:
morphology
白色区域都被联通了。

然后看下旋转的图像 car_rotated.jpg:
rotated

最后,剪切出车牌即可 car_area.jpg:
area
这样就提取车了一张车牌。

因为这图比较理想,事实上,可能识别出多个蓝车区域多为车牌,也就是多个候选区域。这里我们图像理想,只识别出一个候选区域,所以我直接把候选区域0,即output_area[0]打印出来就是车牌了,事实上,我们应该加入SVM训练,作为最后判断是否是车牌的依据,当然,这个我放到下一篇文章吧……

未完,待续…….

猜你喜欢

转载自blog.csdn.net/guet_kite/article/details/79702169
今日推荐