《ofo车牌识别》研发心得05-图像处理第3阶段:将7个数字抠出来并提取特征

举报
motozilog 发表于 2019/01/02 11:16:04 2019/01/02
【摘要】 图像处理的第3阶段是将7个数字抠出来并提取特征

1.png


s2-01将车牌抠出来

由于第一阶段裁出来的是彩色,需要进用新的参数进行处理。

而取边缘算法改为Canny(做到这时才知有这个算法......想重构,不过己经来不及),效果相当好。

来自,http://www.kongzhi.net/cases/caseview.php?id=2368 的说法:

Roberts算子检测方法对具有陡峭的低噪声的图像处理效果较好,但是利用roberts算子提取边缘的结果是边缘比较粗,因此边缘的定位不是很准确。

Sobel算子检测方法对灰度渐变和噪声较多的图像处理效果较好,sobel算子对边缘定位不是很准确,图像的边缘不止一个像素。

Prewitt算子检测方法对灰度渐变和噪声较多的图像处理效果较好。但边缘较宽,而且间断点多。

Laplacian算子法对噪声比较敏感,所以很少用该算子检测边缘,而是用来判断边缘像素视为与图像的明区还是暗区。

Canny方法不容易受噪声干扰,能够检测到真正的弱边缘。优点在于,使用两种不同的阈值分别检测强边缘和弱边缘,并且当弱边缘和强边缘相连时,才将弱边缘包含在输出图像中。

cv::medianBlur(ofo7CharReProcessImg,ofo7CharReProcessImg,11);//中值滤波
cv::cvtColor(ofo7CharReProcessImg,ofo7CharReProcessImg,COLOR_BGR2GRAY);//转灰度
cv::Canny(ofo7CharReProcessImg,ofo7CharReProcessImg,50,150,3);//取边缘

2.png

闭操作,用一个长条形将7个数字连起来

Mat elementOfo7CharReProcess = cv::getStructuringElement(MORPH_RECT,Size(25,1));//设置膨胀参数
cv::dilate(ofo7CharReProcessImg,ofo7CharReProcessImg,elementOfo7CharReProcess); //膨胀

3.png

接下来就是外接矩形,并判断是否为车牌

cv::threshold(ofo7CharReProcessImg,ofo7CharReProcessImg,0,255,CV_THRESH_OTSU+CV_THRESH_BINARY); //再次二值化,为绘制矩形做准备
vector< vector< Point> > contoursOfo7CharFind; //存放指针
cv::findContours(ofo7CharReProcessImg,contoursOfo7CharFind,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE); //找轮廓
for (int i=0;i<contoursOfo7CharFind.size();i++)
{
    //绘制最小外接矩形
    cv::Rect rectOfo7CharFind=cv::boundingRect(contoursOfo7CharFind[i]);
    //判断是否为7个字符
    if(
            (((float(rectOfo7CharFind.width))/rectOfo7CharFind.height)>3.2)&&
            (((float(rectOfo7CharFind.width))/rectOfo7CharFind.height)<4.8)&&
            ((rectOfo7CharFind.width>distanceQRx)||(rectOfo7CharFind.width>distanceQRy))
            )
    {
        cv::rectangle(ofo7CharReProcessImg,rectOfo7CharFind,Scalar(255,255,255));
        cout<<"Draw7Char: "<< rectOfo7CharFind.width <<"*"<<rectOfo7CharFind.height<<endl;
        Ofo7CharX=rectOfo7CharFind.x;
        Ofo7CharY=rectOfo7CharFind.y;
        Ofo7CharWidth=rectOfo7CharFind.width;
        Ofo7CharHeight=rectOfo7CharFind.height;
        Ofo7CharFind++;
    }
}
if(Ofo7CharFind!=1)
{
    cout<<"ERROR:Can't Find OFO 7 Char position"<<endl;
    continue;
}


4.png


s2-02将车牌抠出来

cv::Rect maskOfo7CharReProcessImg(Ofo7CharX, Ofo7CharY, Ofo7CharWidth,Ofo7CharHeight);
cv::Mat croppedOfo7CharReProcessImg(cropReProcessImg, maskOfo7CharReProcessImg);

5.png


s2-03将7个数字找出来并存放在数组中

cv::medianBlur(char7findImg,char7findImg,5);//中值滤波
cv::cvtColor(char7findImg,char7findImg,COLOR_BGR2GRAY);
cv::threshold(char7findImg,char7findImg,0,255,CV_THRESH_OTSU+CV_THRESH_BINARY); //再次二值化,为绘制矩形做准备
char7findImg.copyTo(char7findImgBw);
vector< vector< Point> > contourschar7findImg; //存放指针
cv::findContours(char7findImg,contourschar7findImg,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE); //找轮廓
for (int i=0;i<contourschar7findImg.size();i++)
{
    //绘制最小外接矩形
    cv::Rect rectRotateReProcess=cv::boundingRect(contourschar7findImg[i]);
    if (
            (((float(rectRotateReProcess.height))/rectRotateReProcess.width)>1.5)&&
            (((float(rectRotateReProcess.height))/rectRotateReProcess.width)<4)&&
            (rectRotateReProcess.height>((char7findImg.rows)*0.67))
            )
    {
        cv::rectangle(char7findImg,rectRotateReProcess,Scalar(255,255,255));
        charList[charListCount][1]=rectRotateReProcess.x;
        charList[charListCount][2]=rectRotateReProcess.y;
        charList[charListCount][3]=rectRotateReProcess.width;
        charList[charListCount][4]=rectRotateReProcess.height;
        charListCount++;
    }
}
if(charListCount!=7)
{
    cout<<"ERROR: Can't find ALL 7 char"<<endl;
    continue;
}


6.png


s2-04根据x坐标,将数组重排(因为找出来的不是从左到右)

//冒泡排序
int charSortTemp[5];
for (int i=0;i<7;i++)
{
    for (int j=i+1;j<7;j++)
    {
        if(charList[i][1]>charList[j][1])
        {
            for(int k=0;k<5;k++)
            {
                charSortTemp[k]=charList[i][k];
                charList[i][k]=charList[j][k];
                charList[j][k]=charSortTemp[k];
            }
        }
    }
}


s2-05将7个数字抠出来,并resize成 32*72

for (int i=0;i<7;i++)
{
    cout<<filename[i]<<endl;
    cv::Rect maskCharList(charList[i][1],charList[i][2],charList[i][3],charList[i][4]);
    cv::Mat croppedChar(croppedCharTempImg, maskCharList);
    croppedChar.copyTo(croppedCharListImg[i]);
    cv::resize(croppedCharListImg[i],croppedCharListImg[i],Size(32,72),0,0,INTER_CUBIC);
}

7.png


s2-06 将Mat矩阵转换成二维矩阵,因为Mat矩阵只能用一维指针来操作,太麻烦了

int imgArray[7][72][32];
for(int i=0;i<7;i++)
{
    //先二值化
    cv::threshold(croppedCharListImg[i],croppedCharListImg[i],0,255,CV_THRESH_OTSU+CV_THRESH_BINARY);
    croppedCharListImg[i].convertTo(croppedCharListImg[i],CV_8U);
    cv::MatIterator_<cv::Vec3b> it = croppedCharListImg[i].begin<cv::Vec3b>();
    int t=0;
    for(;it!=croppedCharListImg[i].end<cv::Vec3b>();it++)
    {
        if((((*it)[0])==255)&&(((*it)[1])==255)&&(((*it)[2])==255))
        {
            imgArray[i][t/32][t%32]=1;
        } else {
            imgArray[i][t/32][t%32]=0;
        }
        t++;
    }
}


s2-07计算feature2(8*8区域中白点数)

int feature2Extract[7][32/8*72/8];
for (int i=0;i<7;i++)
{
    int arrayNum=0;
    for(int j=0;j<72;j=j+8)
    {
        for (int k=0;k<32;k=k+8)
        {
            int countBW=0;
            for (int lj=0;lj<8;lj++)
            {
                for(int lk=0;lk<8;lk++)
                {
                    if ((imgArray[i][j+lj][k+lk])==1)
                    {
                        countBW++;
                    }
                }
            }
            feature2Extract[i][arrayNum]=countBW;
            arrayNum++;
        }
    }
}

8.png

至此图像预处理搞定,可以扔进libsvm做训练了。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。