opencv3.0 车牌号字符的分割

  1.车牌号字符的分割

第一步要解决的问题是去除车牌的边框。方法: 对车牌二值化图像行和列扫描。当行扫描时跳变小于某个阈值时,则认为这行数据中不包含字符信息,这行的像素直接置0。这样去除掉上、下边框和铆钉的影响。当扫描完行之后,直接在前端和末端抹掉两列信息。这是为了去掉左右边框的影响。

2.一些函数的用法

(1)cvCvtColor()色彩空间转换函数。格式:cvCvtColor(源图像,输出图像,code),code是指色彩空间转换的模式.
(2)cvSmooth()图像平滑函数。格式:cvSmooth(源图像,输出图像,smoothtype)smoothtype是指平滑方法.
        cvSmooth(pImg8u,pImg8uSmooth,CV_GAUSSIAN,3,0,0) = cvSmooth(pImg8u,pImg8uSmooth);
        后三个参数是默认值。CV_GAUSSIAN (gaussian blur) - 对图像进行核大小为 param1×param2 的高斯卷积
(3)cvCanny() 边缘检测,使用Canny算法。 格式:cvCanny(源图像,输出图像,lowThresh,highThresh,int apertureSize)
        aperture:['æpətʃə; -tj(ʊ)ə] n.孔,光圈,孔径.源图像必须是灰度图像.输出图像也是灰度图像,或黑白图像,或者按着书       上说的是布尔图像.用于检测轮廓必须要区分轮廓像素与区域的像素:
     像素的梯度>上限阈值highThresh    => 边缘像素
     像素的梯度<下限阈值lowThresh    => 区域的像素 (会被抛弃)
  (4)cvSobel()Sobel微分算子.格式:cvSobel(输入图像,输出图像,xorder,yorder)
       void cvSobel( const CvArr* src, CvArr* dst, int xorder, int yorder, int aperture_size=3 );
        src: 输入灰度以及高斯去噪之后的图像.xorder,yorder : 指的是求导的阶数,0表示这个方向不求导,1表示求1阶            导数,最多求2阶导数.
    1,0 表示对x方向求导,过滤掉"横线",留下"竖线";
    0,1 表示对y方向求导,过滤掉"竖线",留下"横线".
 (5)IplImage* pImg; //声明IplImage指针

3.代码

#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
#include <iostream>
using namespace std;


#define T 27                            //判断一行是不是车牌有效信息的阈值
#define T12                            //判断一列是不是车牌有效信息的阈值
#define S(image,x,y) ((uchar*)(image->imageData + image->widthStep*(y)))[(x)]
//S(image,x,y)指该图像(x,y)像素点的像素值,[(x)]是数组,类似于a[i]
IplImage *src;
IplImage *pImg8u = NULL;             //灰度图
IplImage *pImg8uSmooth = NULL;       //高斯滤波后的图
IplImage *pImgCanny = NULL;          //二值化的图
IplImage *pImg8uROI = NULL;         //原图
IplImage *pImgResize = NULL;        //归一化的灰度图


IplImage *pImgCharOne = NULL;
IplImage *pImgCharTwo = NULL;
IplImage *pImgCharThree = NULL;
IplImage *pImgCharFour = NULL;
IplImage *pImgCharFive = NULL;
IplImage *pImgCharSix = NULL;
IplImage *pImgCharSeven = NULL;




int i, j;
int row_start, row_end;             //用来记录车牌开始,结束行
int col_start, col_end;             //用来记录车牌开始,结束列
int row[120];                      //row[]存放含有有效车牌信息的第j行,把所有有效行放一个数组里面,统一管理,有利于判断。
int col[340];                      //存放每个字符双边界(列)的位置
int k = 0;                           //含有有效车牌信息的行数
int nCharWidth = 45;                 //每个字符的列,也就是宽度
int nSpace = 12;                     //字符之间的间隙
int nWidth = 409;                    //(409,90)分别为原图像的宽度与高度
int nHeight = 90;


void find_UpAndDown_row(IplImage *src_son_row);
//定义一个子函数,找到图片中含有车牌有效信息的最上行和最下行
void find_LeftAndRight_col(IplImage *src_son_col);
//定义一个子函数,找到图片中含有车牌有效信息的最左列和最右列
void find_ROI(IplImage *before_ROI, IplImage *after_ROI);
//定义一个子函数find_ROI,找到图片中只含有目标区域的部分
void cut_and_drawLine(IplImage *befour_cut_image);
//定义一个子函数cut_and_drawLine,对含有车牌有效信息的图片分割出字符,并画出分割线
void show_every_char(IplImage *showChar);
//定义一个子函数show_every_char,显示出所有分割出来的字符
void main()
{
src = cvLoadImage("车牌.jpg", -1);
pImg8uSmooth = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
pImg8u = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
pImgCanny = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
cvCvtColor(src, pImg8u, CV_RGB2GRAY);                //灰度化
cvSmooth(pImg8u, pImg8uSmooth, CV_GAUSSIAN, 3, 0, 0);   //高斯滤波
cvCanny(pImg8uSmooth, pImgCanny, 100, 200, 3);         //二值化
cvDilate(pImgCanny, pImgCanny, 0, 1);
cvErode(pImgCanny, pImgCanny, 0, 1);
cvNamedWindow("cvcanny", 1);
cvShowImage("cvcanny", pImgCanny);




row_start = 0;
row_end = 0;
col_start = 0;
col_end = 0;
cout << "图片的高度值(即像素的行数)为:" << pImgCanny->height << endl;
cout << "图片的宽度值(即像素的列数)为:" << pImgCanny->width << endl;




find_UpAndDown_row(pImgCanny);   //找到图片中含有车牌有效信息的最上行和最下行
find_LeftAndRight_col(pImgCanny);//找到图片中含有车牌有效信息的最左列和最右列
find_ROI(pImg8u, pImg8uROI);      //找到图片中只含有目标区域的部分


pImgResize = cvCreateImage(cvSize(nWidth, nHeight), IPL_DEPTH_8U, 1);
cvResize(pImg8uROI, pImgResize, CV_INTER_LINEAR); //线性插值
cvNamedWindow("感兴趣图像的宽度与高度", 1);
cvShowImage("感兴趣图像的宽度与高度", pImgResize);


cut_and_drawLine(pImgResize);    //对含有车牌有效信息的图片分割出字符,并画出分割线
show_every_char(pImgResize);      //显示出所有分割出来的字符
cvWaitKey(0);


cvReleaseImage(&pImgResize);
cvReleaseImage(&pImg8uROI);
cvReleaseImage(&pImgCharOne);
cvReleaseImage(&pImgCharTwo);
cvReleaseImage(&pImgCharThree);
cvReleaseImage(&pImgCharFour);
cvReleaseImage(&pImgCharFive);
cvReleaseImage(&pImgCharSix);
cvReleaseImage(&pImgCharSeven);


cvDestroyAllWindows();
}


/******************************************************************************************\
定义一个子函数find_UpAndDown_row,找到图片中含有车牌有效信息的最上行和最下行
\******************************************************************************************/
void find_UpAndDown_row(IplImage *src_son_row) {
/判断每行是不是含有车牌信息的行是通过查看黑点白点变化的次数来确定的


for (j = 0; j<src_son_row->height; j++)
//遍历整幅图的行和列,寻找包含车牌信息的行数
{
int count = 0;                                       //  count/2  记录每行白点(水平的白线看做一个点)的个数
for (i = 0; i<src_son_row->width; i++)
{
if (S(src_son_row, i, j) != S(src_son_row, i + 1, j))  //统计行跳数
count++;
if (count>T)                               //把含有车牌有效信息的行j存放到row[k]
{
row[k] = j;
k++;                                  //记录含有有效车牌信息的行数
break;
}
}
//cout<<"count值为:"<<count<<endl;
}
cout << "有效行k值为:" << k << endl;
for (i = 0; i<k; i++)                                 //从上边开始,3行连续时认为是起始行
{
if ((row[i] == row[i + 1] - 1) && (row[i + 1] == row[i + 2] - 1)) {
row_start = row[i];
// cout<<"the start row123:"<<row_start<<endl;
break;
}
}
cout << "the start row:" << row_start << endl;
cvLine(pImg8u, cvPoint(0, row_start), cvPoint(src->width, row_start), cvScalar(255, 0, 0), 1, 8, 0);
cvNamedWindow("划线_上", 1);
cvShowImage("划线_上", pImg8u);


for (i = k - 1; i>row_start; i--)     //从下边开始,3行连续时认为是起始行
{
if ((row[i] == row[i - 1] + 1) && (row[i - 1] == row[i - 2] + 1)) {
row_end = row[i];
break;
}
}
cout << "the end row:" << row_end << endl;
cvLine(pImg8u, cvPoint(0, row_end), cvPoint(src->width, row_end), cvScalar(255, 0, 0), 1, 8, 0);
cvNamedWindow("划线_上下", 1);
cvShowImage("划线_上下", pImg8u);
}
/******************************************************************************************\
定义一个子函数find_LeftAndRight_col,找到图片中含有车牌有效信息的最左列和最右列
\******************************************************************************************/
void find_LeftAndRight_col(IplImage *src_son_col) {
/判断每列是不是含有车牌有效信息是查看每列中含有白点像素的个数
bool flag = false;
for (i = 10; i<src_son_col->width; i++)           //找到左列开始???i为什么是10???
{
int count = 0;
for (j = row_start; j<row_end; j++)
{
if (S(src_son_col, i, j) == 255)
count++;
if (count>T1)
{
col_start = i;
flag = true;
break;
}
}
if (flag) break;
}
cout << "the start col:" << col_start << endl;
cvLine(pImg8u, cvPoint(col_start, row_start), cvPoint(col_start, row_end), cvScalar(255, 0, 0), 1, 8, 0);
cvNamedWindow("划线_左", 1);
cvShowImage("划线_左", pImg8u);


flag = false;
for (i = src_son_col->width - 10; i>col_start; i--)           //找到右列开始
{
int count = 0;
for (j = row_start; j<row_end; j++)
{
if (S(src_son_col, i, j) == 255)
count++;
if (count>T1)
{
col_end = i;
flag = true;
break;
}
}
if (flag) break;
}
cout << "the end col:" << col_end << endl;
cvLine(pImg8u, cvPoint(col_end, row_start), cvPoint(col_end, row_end), cvScalar(255, 0, 0), 1, 8, 0);
cvNamedWindow("划线_左右", 1);
cvShowImage("划线_左右", pImg8u);
}
/******************************************************************************************\
定义一个子函数find_ROI,找到图片中只含有目标区域的部分
\******************************************************************************************/
void find_ROI(IplImage *before_ROI, IplImage *after_ROI) {
CvRect ROI_rect;                 //获得图片感兴趣区域
ROI_rect.x = col_start;
ROI_rect.y = row_start;
ROI_rect.width = col_end - col_start;
ROI_rect.height = row_end - row_start;
cvSetImageROI(pImg8u, ROI_rect);
pImg8uROI = cvCreateImage(cvSize(ROI_rect.width, ROI_rect.height), IPL_DEPTH_8U, 1);
cvCopy(pImg8u, pImg8uROI);
cvResetImageROI(pImg8u);
}
/******************************************************************************************\
定义一个子函数cut_and_drawLine,对含有车牌有效信息的图片分割出字符,并画出分割线
\******************************************************************************************/
void cut_and_drawLine(IplImage *befour_cut_image) {
///得到每个字符的双边界
//七个字符的间距坐标间距,只看水平方向。分别为:0(0,45),1(57,102),2(136,181),3(193,238)
//                                              4(250,295),5(307,352),6(364,409)
//并存放在col[]里面
for (i = 0; i<7; i++)
{
switch (i) {
case 0:                                          //0是省份名占45个像素
case 1:                                          //1是市名的字母占45个像素
col[i * 2] = i*nCharWidth + i*nSpace;
cout << col[i * 2] << endl;
col[i * 2 + 1] = (i + 1)*nCharWidth + i*nSpace;
cout << col[i * 2 + 1] << endl;
break;                                       //1与2之间有个点宽度是34个像素
case 2:                                          //2---6是剩余的字母和数字各自占45个像素
case 3:
case 4:
case 5:
case 6:
col[i * 2] = i*nCharWidth + i*nSpace + 22;
cout << col[i * 2] << endl;
col[i * 2 + 1] = (i + 1)*nCharWidth + i*nSpace + 22;
cout << col[i * 2 + 1] << endl;
break;
}


}
for (i = 0; i<14; i++)        //画出每个字符的区域
{
cvLine(befour_cut_image, cvPoint(col[i], 0), cvPoint(col[i], nHeight), cvScalar(255, 0, 0), 1, 8, 0);
//cout<<col[i*2]<<" "<<col[2*i+1]<<" ";
}
//cvNamedWindow("画出每个字符的区域",1);
//cvShowImage("画出每个字符的区域",befour_cut_image);


}
/******************************************************************************************\
定义一个子函数show_every_char,显示出所有分割出来的字符
\******************************************************************************************/
void show_every_char(IplImage *showChar) {


pImgCharOne = cvCreateImage(cvSize(nCharWidth, nHeight), IPL_DEPTH_8U, 1);
pImgCharTwo = cvCreateImage(cvSize(nCharWidth, nHeight), IPL_DEPTH_8U, 1);
pImgCharThree = cvCreateImage(cvSize(nCharWidth, nHeight), IPL_DEPTH_8U, 1);
pImgCharFour = cvCreateImage(cvSize(nCharWidth, nHeight), IPL_DEPTH_8U, 1);
pImgCharFive = cvCreateImage(cvSize(nCharWidth, nHeight), IPL_DEPTH_8U, 1);
pImgCharSix = cvCreateImage(cvSize(nCharWidth, nHeight), IPL_DEPTH_8U, 1);
pImgCharSeven = cvCreateImage(cvSize(nCharWidth, nHeight), IPL_DEPTH_8U, 1);


CvRect ROI_rect1;
ROI_rect1.x = col[0];
ROI_rect1.y = 0;
ROI_rect1.width = nCharWidth;
ROI_rect1.height = nHeight;
cvSetImageROI(showChar, ROI_rect1);
cvCopy(showChar, pImgCharOne, NULL); //获取第1个字符
cvResetImageROI(showChar);


ROI_rect1.x = col[2];
ROI_rect1.y = 0;
ROI_rect1.width = nCharWidth;
ROI_rect1.height = nHeight;
cvSetImageROI(showChar, ROI_rect1);
cvCopy(showChar, pImgCharTwo, NULL); //获取第2个字符
cvResetImageROI(showChar);


ROI_rect1.x = col[4];
ROI_rect1.y = 0;
ROI_rect1.width = nCharWidth;
ROI_rect1.height = nHeight;
cvSetImageROI(showChar, ROI_rect1);
cvCopy(showChar, pImgCharThree, NULL); //获取第3个字符
cvResetImageROI(showChar);


ROI_rect1.x = col[6];
ROI_rect1.y = 0;
ROI_rect1.width = nCharWidth;
ROI_rect1.height = nHeight;
cvSetImageROI(showChar, ROI_rect1);
cvCopy(showChar, pImgCharFour, NULL); //获取第4个字符
cvResetImageROI(showChar);


ROI_rect1.x = col[8];
ROI_rect1.y = 0;
ROI_rect1.width = nCharWidth;
ROI_rect1.height = nHeight;
cvSetImageROI(showChar, ROI_rect1);
cvCopy(showChar, pImgCharFive, NULL); //获取第5个字符
cvResetImageROI(showChar);


ROI_rect1.x = col[10];
ROI_rect1.y = 0;
ROI_rect1.width = nCharWidth;
ROI_rect1.height = nHeight;
cvSetImageROI(showChar, ROI_rect1);
cvCopy(showChar, pImgCharSix, NULL); //获取第6个字符
cvResetImageROI(showChar);


ROI_rect1.x = col[12];
ROI_rect1.y = 0;
ROI_rect1.width = nCharWidth;
ROI_rect1.height = nHeight;
cvSetImageROI(showChar, ROI_rect1);
cvCopy(showChar, pImgCharSeven, NULL); //获取第7个字符
cvResetImageROI(showChar);


cvNamedWindow("分割后的车牌", 1);
cvShowImage("分割后的车牌", showChar);
cvNamedWindow("one", CV_WINDOW_AUTOSIZE);
cvShowImage("one", pImgCharOne);
cvNamedWindow("two", 1);
cvShowImage("two", pImgCharTwo);
cvNamedWindow("three", 1);
cvShowImage("three", pImgCharThree);
cvNamedWindow("four", 1);
cvShowImage("four", pImgCharFour);
cvNamedWindow("five", 1);
cvShowImage("five", pImgCharFive);
cvNamedWindow("six", 1);
cvShowImage("six", pImgCharSix);
cvNamedWindow("seven", 1);

cvShowImage("seven", pImgCharSeven);

}

4.运行结果
}







  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值