多媒体制作技术大作业.docx
《多媒体制作技术大作业.docx》由会员分享,可在线阅读,更多相关《多媒体制作技术大作业.docx(18页珍藏版)》请在冰豆网上搜索。
多媒体制作技术大作业
姓名王涛涛学号0093997
专业09计算机班级
(1)班
实验项目名称车牌识别统设计
指导教师方志军
完成时间2011.12.23
一、实验目的及要求:
1)实验内容:
(1)通过在网上查阅资料,熟悉OpenCV的基本数据结构。
(2)利用OpenCV进行基本图像处理,实现图像预处理,包括对图像的平滑处理,边缘检测,二次线性插值等,最后实现对车牌识别。
2)实验要求:
利用OpenCV在VC6环境下开发。
写成命令行程序,无需设计图形用户界面。
保持良好的代码风格:
可读性、注释、变量命名、缩进等
二、实验方法与步骤
1、概要设计
(1)通过在网上查阅资料,熟悉OpenCV的基本数据结构。
(2)利用OpenCV进行基本图像处理,实现图像平滑处理、图像的分割,图像边缘提取算法,从而达到对车牌识别。
2、详细设计(文件组成、数据结构及函数设计、关键算法等)
2.1在图像预处理阶段
要实现的功能是提高得到图像的质量,首先是采用平滑滤波处理消除系统噪声,之后若要采用边缘提取,则在此步中要加强边缘。
现在已有的加强边缘的方法有:
(1)利用高通滤波器加强边缘后与原图像相加。
(2)利用低通滤波器取得背景后与原图像相减;对于已经进行过边缘加强的图像,再使用一定的方法进行阈算子等提取边缘。
(3)利用CANNY算子提取边缘。
2.2车牌识别系统流程图
输出结果
2.3图像数据结构
(i)灰度化函数cvCvtColor();
cvCvtColor()函数是将彩色图片转化成灰色进行处理,该灰度化函数为cvCvtColor(inputImg,grayImg,CV_BGR2GRAY);其中inputImg是输入的车牌号图片,要求是8-bit,16-bit或32-bit单倍精度浮点数影像;grayImg是处理后的灰度图片,输出的是8-bit,16-bit或32-bit单倍精度浮点数影像;CV_RGB2GRAY是色彩空间转换指标。
在OpenCV开发手册我们可以看到cvCvtColor(src,hsv,CV_BGR2HSV)基本用法如下所示。
#include"cv.h"
#include"highgui.h"
intmain(intargc,char**argv)
{
IplImage*src;
if(argc==2&&(src=cvLoadImage(argv[1],1))!
=0){
//计算HSV(hue色度saturation饱和度value亮度)并分解成为不同的平面
IplImage*hsv=cvCreateImage(cvGetSize(src),8,3);
cvCvtColor(src,hsv,CV_BGR2HSV);
//可以更改为CV_BGR2GRAY,可以自己尝试一下
cvNamedWindow("src",CV_WINDOW_AUTOSIZE);
cvShowImage("src",src);
cvNamedWindow("dst",CV_WINDOW_AUTOSIZE);
cvShowImage("dst",hsv);
cvWaitKey(0);
return0;
}
return0;
}
(ii)高斯滤波平滑处理函数CVSmooth()
在数字图像处理中,由于受到成像方法的限制,图像中的边缘、细节特征等重要信息常湮没于噪声信号中,给图像的后继处理带来很大的影响。
因此对含噪声图像进行适当的预处理是图像处理中的一个重要问题,对于改善图像质量具有重要的意义。
图像去噪是图像预处理中一项应用比较广泛的技术,其作用是为了提高图像的信噪比,突出图像的期望区域
各种方法的图像平滑
voidcvSmooth(constCvArr*src,CvArr*dst,intsmoothtype=CV_GAUS
SIAN,intparam1=3,intparam2=0,doubleparam3=0);
其中:
src输入图像.dst输出图像.smoothtype平滑方法:
.
(1)CV_BLUR_NO_SCALE(简单不带尺度变换的模糊)-对每个象素领域param1×param2求和。
如果邻域大小是变化的,可以事先利用函数cvIntegral计算积分图像。
(2)CV_BLUR(simpleblur)-对每个象素邻域param1×param2求和并做尺度变换1/(param1•param2).
(3)CV_GAUSSIAN(gaussianblur)-对图像进行核大小为param1×param2的高斯卷积
(4)CV_MEDIAN(medianblur)-发现邻域param1×param1的中值(i.e.邻域是方的).
(5)CV_BILATERAL(双滤波)-应用双向3x3滤波,彩色sigma=param1,空间sigma=param2.关于双向滤波,
param1:
平滑操作的第一个参数.
param2:
平滑操作的第二个参数param2为零对应简单的尺度变换和高斯模糊。
param3:
对应高斯参数的Gaussiansigma(标准差).如果为零,这由下面的核尺寸计算。
函数cvSmooth可使用上面任何一种方法平滑图像。
每一种方法都有自己的特点以及局限。
(iii)用于对图像的边缘检测(采用canny算法)。
1.Canny边缘检测基本原理
(1)图象边缘检测必须满足两个条件:
一能有效地抑制噪声;二必须尽量精确确定边缘的位置。
(2)根据对信噪比与定位乘积进行测度,得到最优化逼近算子。
这就是Canny边缘检测算子。
(3)类似与Marr(LoG)边缘检测方法,也属于先平滑后求导数的方法。
2.voidcvCanny(constCvArr*image,CvArr*edges,doublethreshold1,doublethreshold2,intaperture_size=3);
其中threshold1:
第一个阈,threshold2:
第二个阈值,aperture_sizeSobel:
算子内核大小.函数cvCanny采用Canny算法发现输入图像的边缘而且在输出图像中标识这些边缘。
threshold1和threshold2当中的小阈值用来控制边缘连接,大的阈值用来控制强边缘的初始分割。
3.在opencv中文论坛上我们可以找到关于Canny算法的详细过程
#include"cv.h"
#include"cxcore.h"
#include"highgui.h"
intmain(intargc,char**argv)
{
IplImage*pImg=NULL;//声明IplImage指针
IplImage*pCannyImg=NULL;
char*filename;
filename="F:
\\练习\\VC6.0\\opencv\\1.jpg";
pImg=cvLoadImage(filename,1);//载入图像,强制转化为Gray
if((pImg=cvLoadImage(filename,0))!
=0){
pCannyImg=cvCreateImage(cvGetSize(pImg),IPL_DEPTH_8U,1);
cvCanny(pImg,pCannyImg,50,150,3);
cvNamedWindow("src",1);//创建窗口
cvNamedWindow("canny",1);
cvShowImage("src",pImg);//显示图像
cvShowImage("canny",pCannyImg);
cvWaitKey(0);//等待按键
cvDestroyWindow("src");//销毁窗口
cvDestroyWindow("canny");//释放图像
cvReleaseImage(&pImg);
cvReleaseImage(&pCannyImg);
return0;
}
return-1;
}
图1Canny算法处理后
(iv)cvSetImageROI()函数
cvSetImageROI()函数基于给定的矩形设置感兴趣区域,也就是从图片中找到我们需要的部分。
voidcvSetImageROI(IplImage*image,CvRectrect);函数cvSetImageROI基于给定的矩形设置图像的ROI(感兴趣区域).如果ROI是NULL并且参数RECT的值不等于整个图像,ROI被分配.不像COI,大多数的OpenCV函数支持ROI并且处理它就像它是一个分离的图像(例如,所有的像素坐标从ROI的左上角或左下角(基于图像的结构)计算。
下面用一段代码来说明cvSetImageROI函数:
#include"cv.h"
#include"highgui.h"
#include"stdio.h"
intmain()
{
IplImage*img=cvLoadImage("F:
\\练习\\VC6.0\\opencv\\1.jpg");
IplImage*img1=cvLoadImage("F:
\\练习\\VC6.0\\opencv\\2.jpg");
cvSetImageROI(img1,cvRect(0,0,256,256));//设置img1的ROI区域
cvResize(img,img1);//缩放img图像,并将数据拷贝到img1
cvResetImageROI(img1);//这句是必须的,在img1的ROI区域显示img
cvNamedWindow("img1",1);
cvShowImage("img1",img1);
cvWaitKey(0);
cvDestroyAllWindows();
return0;
}
图2处理前图3处理后
3、程序的运行与实现
本程序采用的是一种很简单的识别方法,即将分割出的字符与模板想比较,得到结果图像后检测图像中非零像素的个数,当个数最少时即判定为匹配图像。
因为模板的大小是22*14的,所以需要将所得分割字符统一大小,之后再相比较,检测最小值。
具体实现方面还是让我痛苦了一段时间,如何用循环实现字符自动匹配和字符的显示比较的困难。
后来我采用的方法是建立字符串数组存储模板图片的文件名,利用数组来自动匹配,显示方面我采用将汉字分成若干个SWITCH语句,利用数组中的数字表示汉字序数,不同的序数执行不同的分支程序。
图4原图像
图5分割结果
三、实验感想
我的这套车牌识别系统是基于opencv建立的,在网上虽然有很多matlab的识别程序,但真正用opencv而非直接使用VC识别的系统很少,所以我采用的方法是在借鉴matlab程序的思路的基础上,自己搭建一个VC平台,其中的模块很多是在网上找的,但他们之间的连接和搭配,以及一些方法的选择也加入了我自己的想法。
通过这次的大作业,我对于图像处理课上讲授的内容有了更深的理解,对一些算法的应用有了更深的感受,比如开闭操作、投影直方图和最佳全局阈值等,而且在这个过程中,也熟悉了opencv和VC的编程方法,真的是收益良多。
四、附录
源代码
#include
#include
#include
#include
usingnamespacestd;
#pragmacomment(lib,"cv.lib")
#pragmacomment(lib,"cxcore.lib")
#pragmacomment(lib,"highgui.lib")
#defineT27
#defineT12
#defineS(image,x,y)((uchar*)(image->imageData+image->widthStep*(y)))[(x)]//S
voidmain()
{
IplImage*src;
IplImage*pImg8u=NULL;//灰度图
IplImage*pImg8uSmooth=NULL;//高斯滤波后的图
IplImage*pImgCanny=NULL;//二值化的图
IplImage*pImgHist=NULL;//直方图
inthist_size=155;
floatrange_0[]={0,256};
float*ranges[]={range_0};
inti,j,bin_w;
floatmax_value,min_value;
intmin_dx,max_dx;
introw_start,row_end;//用来记录车牌开始,结束行
intcol_start,col_end;//用来记录车牌开始,结束列
intcount=0;//用来记录行或列的白点个数
src=cvLoadImage("8.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);//二值化
row_start=0;
row_end=0;
col_start=0;
col_end=0;
introw[120];
intcol[340];
intk;
k=0;
boolflag=false;
for(j=0;jheight;j++)//找到上行开始
{
count=0;
for(i=0;iwidth-1;i++)
{
if(S(pImgCanny,i,j)!
=S(pImgCanny,i+1,j))//统计行跳数
count++;
if(count>T)
{
row[k]=j;
k++;
break;
}
}
}
for(i=0;i{
if((row[i]==row[i+1]-1)&&(row[i+1]==row[i+2]-1)){
row_start=row[i];
break;
}
}
cout<<"thestartrow:
"<cvLine(pImg8u,cvPoint(0,row_start),cvPoint(src->width,row_start),cvScalar(255,0,0),1,8,0);
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<<"theendrow:
"<cvLine(pImg8u,cvPoint(0,row_end),cvPoint(src->width,row_end),cvScalar(255,0,0),1,8,0);
flag=false;
for(i=10;iwidth;i++)//找到左列开始
{
count=0;
for(j=row_start;j{
if(S(pImgCanny,i,j)==255)
count++;
if(count>T1)
{
col_start=i;
flag=true;
break;
}
}
if(flag)break;
}
cout<<"thestartcol:
"<cvLine(pImg8u,cvPoint(col_start,row_start),cvPoint(col_start,row_end),cvScalar(255,0,0),1,8,0);
flag=false;
for(i=pImgCanny->width-10;i>col_start;i--)//找到右列开始
{
count=0;
for(j=row_start;j{
if(S(pImgCanny,i,j)==255)
count++;
if(count>T1)
{
col_end=i;
flag=true;
break;
}
}
if(flag)break;
}
cout<<"theendcol:
"<cvLine(pImg8u,cvPoint(col_end,row_start),cvPoint(col_end,row_end),cvScalar(255,0,0),1,8,0);
CvRectROI_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;
IplImage*pImg8uROI=NULL;//感兴趣的图片
cvSetImageROI(pImg8u,ROI_rect);
pImg8uROI=cvCreateImage(cvSize(ROI_rect.width,ROI_rect.height),IPL_DEPTH_8U,1);
cvCopy(pImg8u,pImg8uROI);
cvResetImageROI(pImg8u);
intnWidth=409;//(409,90)分别为感兴趣图像的宽度与高度
intnHeight=90;
IplImage*pImgResize=NULL;//归一化的灰度图
pImgResize=cvCreateImage(cvSize(nWidth,nHeight),IPL_DEPTH_8U,1);
cvResize(pImg8uROI,pImgResize,CV_INTER_LINEAR);//线性插值
intnCharWidth=45;
intnSpace=12;
for(i=0;i<7;i++)//得到每个字符的双边界
{
switch(i){
case0:
case1:
col[i*2]=i*nCharWidth+i*nSpace;
col[i*2+1]=(i+1)*nCharWidth+i*nSpace;
break;
case2:
case3:
case4:
case5:
case6:
col[i*2]=i*nCharWidth+i*nSpace+22;
col[i*2+1]=(i+1)*nCharWidth+i*nSpace+22;
break;
}
}
for(i=0;i<14;i++)//画出每个字符的区域
{
cvLine(pImgResize,cvPoint(col[i],0),cvPoint(col[i],nHeight),cvScalar(255,0,0),1,8,0);
}
IplImage*pImgCharOne=NULL;
IplImage*pImgCharTwo=NULL;
IplImage*pImgCharThree=NULL;
IplImage*pImgCharFour=NULL;
IplImage*pImgCharFive=NULL;
IplImage*pImgCharSix=NULL;
IplImage*pImgCharSeven=NULL;
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);
CvRectROI_rect1;
ROI_rect1.x=col[0];
ROI_rect1.y=0;
ROI_rect1.width=nCharWidth;
ROI_rect1.height=nHeight;
cvSetImageROI(pImgResize,ROI_rect1);
cvCopy(pImgResize,pImgCharOne,NULL);//获取第1个字符
cvResetImageROI(pImgResize);
ROI_rect1.x=col[2];
ROI_rect1.y=0;
ROI_rect1.width=nCharWidth;
ROI_r