今天学习到了邮政编码识别,找到了相关理论与实际的代码,分析了一下,有些地方还不是很懂。
理论是看的杨淑莹的《图像识别与项目实践——VC++、matlab计术实现》 第四章–邮政编码识别。
下面是网上找到的字符识别相关程序
#include <cv.h>
#include <highgui.h>
#include <math.h>
#include <stdio.h>
#include <iostream>
#include <cxcore.h>
using namespace std;
IplImage* rotateImage2(IplImage* img, double angle)
{
//double angle = degree /180*CV_PI;
int step;
int i,j,k;
uchar*data ;
cout<<"angle="<<angle<<endl; // 角度 angle
double a = sin(angle), b = cos(angle);
int width=img->width, height=img->height;
//旋转后的新图尺寸
int width_rotate= int(height * fabs(a) + width * fabs(b)); //旋转后的宽度,函数:fabs(a):求浮点数a的绝对值 ,强制类型转换
int height_rotate=int(width * fabs(a) + height * fabs(b));
IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), img->depth, img->nChannels);
cvZero(img_rotate); //好像是清除
step=img->widthStep/sizeof(uchar);
data = (uchar*)img->imageData;
for(i=0;i<img->height;i++)
for(j=0;j<img->width;j++)
for(k=0;k<img->nChannels;k++)
data[i*step+j*img->nChannels+k]=255-data[i*step+j*img->nChannels+k];//反色
//保证原图可以任意角度旋转的最小尺寸
int tempLength = sqrt((double)width * width + (double)height *height) + 10;
int tempX = (tempLength + 1) / 2 - width / 2;
int tempY = (tempLength + 1) / 2 - height / 2;
IplImage* temp = cvCreateImage(cvSize(tempLength, tempLength), img->depth, img->nChannels);
cvZero(temp);
//将原图复制到临时图像tmp中心
cvSetImageROI(temp, cvRect(tempX, tempY, width, height));
cvCopy(img, temp, NULL);
cvResetImageROI(temp);
//旋转数组map
// [ m0 m1 m2 ] ===> [ A11 A12 b1 ]
// [ m3 m4 m5 ] ===> [ A21 A22 b2 ]
float m[6];
int w = temp->width;
int h = temp->height;
m[0] = b;
m[1] = a;
m[3] = -m[1];
m[4] = m[0];
// 将旋转中心移至图像中间
m[2] = w * 0.5f;
m[5] = h * 0.5f;
CvMat M = cvMat(2, 3, CV_32F, m);
cvGetQuadrangleSubPix(temp, img_rotate, &M);
step=img_rotate->widthStep/sizeof(uchar);
data = (uchar*)img_rotate->imageData;
for(i=0;i<img_rotate->height;i++)
for(j=0;j<img_rotate->width;j++)
for(k=0;k<img_rotate->nChannels;k++)
data[i*step+j*img_rotate->nChannels+k]=255-data[i*step+j*img_rotate->nChannels+k];//反色
cvReleaseImage(&temp);
return img_rotate;
}
int otsu(const IplImage *src)
{
double sum=0.0;
double w0=0.0;
double w1=0.0;
double u0_temp=0.0;
double u1_temp=0.0;
double u0=0.0;
double u1=0.0;
double delta_temp=0.0;
double delta_max=0.0;
int pixel_count[256]={0};
float pixel_pro[256]={0};
int threshold=0;
uchar *data=(uchar *)src->imageData;
for(int i=0;i<src->height;i++)
{
for(int j=0;j<src->width;j++)
{
pixel_count[(int)data[i*src->width+j]]++;
sum+=(int)data[i*src->width+j];
}
}
cout<<"平均灰度:"<<sum/(src->height*src->width)<<endl;
for(int i=0;i<256;i++)
{
pixel_pro[i]=(float)pixel_count[i]/(src->height*src->width);
}
for(int i=0;i<256;i++)
{
w0=w1=u0_temp=u1_temp=u0=u1=delta_temp=0;
for(int j=0;j<256;j++)
{
if(j<=i)
{
w0+=pixel_pro[j];
u0_temp+=j*pixel_pro[j];
}
else
{
w1+=pixel_pro[j];
u1_temp+=j*pixel_pro[j];
}
}
u0=u0_temp/w0;
u1=u1_temp/w1;
delta_temp=(float)(w0*w1*pow((u0-u1),2));
if(delta_temp>delta_max)
{
delta_max=delta_temp;
threshold=i;
}
}
return threshold;
}
void FillInternalContours(IplImage *pBinary,double dAreaThre)
{
CvSeq *pContour=NULL;
int rol_y0=pBinary->height;
int rol_y1=0;
int rol_x1=0;
int rol_x0=pBinary->width;
CvSeq *pConInner=NULL;
CvRect rect;
CvMemStorage *pStorage=NULL;
if(pBinary)
{
pStorage=cvCreateMemStorage(0);
cvFindContours(pBinary,pStorage,&pContour,sizeof(CvContour),CV_RETR_EXTERNAL,CV_LINK_RUNS);
cvDrawContours(pBinary,pContour,CV_RGB(255,255,255),CV_RGB(255,255,255),2,CV_FILLED,8,cvPoint(0,0));
//CvRect rect=cvBoundingRect(pContour,0);
for(;pContour!=NULL;pContour=pContour->h_next)
{
//rect=cvBoundingRect(pContour,0);
for(int k=0;k<pContour->total;k++)
{
CvPoint *pt=(CvPoint *)cvGetSeqElem(pContour,k);
if(pt->y<rol_y0)
{
rol_y0=pt->y;
}
if(pt->x<rol_x0)
{
rol_x0=pt->x;
}
if(pt->x>rol_x1)
{
rol_x1=pt->x;
}
if(pt->y>rol_y1)
{
rol_y1=pt->y;
}
}
//cvRectangle(pBinary,cvPoint(rect.x,rect.y),cvPoint(rect.x+rect.width,rect.y+rect.height),CV_RGB(255,0,0),1,8,0);
}
rect.x=rol_x0;
rect.y=rol_y0;
rect.height = (rol_y1 - rol_y0);
rect.width = (rol_x1 - rol_x0);
cvRectangle(pBinary,cvPoint(rect.x,rect.y),cvPoint(rect.x+rect.width,rect.y+rect.height),CV_RGB(255,255,255),1,8,0);
cvReleaseMemStorage(&pStorage);
pStorage=NULL;
}
}
IplImage * rotateimage(IplImage *image)
{
IplImage *img1=cvCreateImage(cvGetSize(image),IPL_DEPTH_8U,1);
cvSmooth(image,img1,CV_GAUSSIAN,3,0,0,0);
int threshold=otsu(img1);
IplImage *img2=cvCreateImage(cvGetSize(img1),IPL_DEPTH_8U,1);
cvThreshold(img1,img2,threshold,255,CV_THRESH_BINARY);
FillInternalContours(img2,200);
return img2;
}
void FillInternalContours1(IplImage *image,double dAreaThre)
{
//cvCvtColor(image,image,CV_RGB2GRAY);
IplImage *image_bin=cvCreateImage(cvGetSize(image),8,1);
int Threshold=otsu(image);
cvThreshold(image, image_bin, Threshold, 255, CV_THRESH_BINARY);
double conarea=0.0;
CvSeq *pContour=NULL;
CvSeq *pConInner=NULL;
CvMemStorage *storage0=NULL;
int id=0;
char fgname[56]={0};
if(image)
{
storage0=cvCreateMemStorage(0);
cvFindContours(image_bin, storage0, &pContour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
// 填充所有轮廓
cvDrawContours(image_bin, pContour, CV_RGB(255, 255, 255), CV_RGB(255, 255, 255), 2, CV_FILLED, 8, cvPoint(0, 0));
for (; pContour != NULL; pContour = pContour->h_next)
{
// 内轮廓循环
for (pConInner = pContour->v_next; pConInner != NULL; pConInner = pConInner->h_next)
{
conarea = fabs(cvContourArea(pConInner, CV_WHOLE_SEQ));
if(conarea<2000)
{
printf("%f\n", conarea);
CvRect rect = cvBoundingRect(pConInner,0);
cvRectangle(image_bin, cvPoint(rect.x, rect.y), cvPoint(rect.x + rect.width, rect.y + rect.height),CV_RGB(255,255, 250), 1, 8, 0);
cout<<"1="<<rect.width<<endl;
IplImage *imgfenge=cvCreateImage(cvSize(rect.width,rect.height),8,1);
cvSetImageROI(image_bin,rect);
cvCopyImage(image_bin,imgfenge);
CvSize size_img;
size_img.height=60;
size_img.width=40;
IplImage *imgfg=cvCreateImage(size_img,8,1);
cvResize(imgfenge,imgfg);
sprintf(fgname, "wnd_%d", id++);
cvNamedWindow(fgname);
cvShowImage(fgname,imgfg);
cvReleaseImage(&imgfg);
}
}
}
cvReleaseMemStorage(&storage0);
storage0 = NULL;
}
}
int main()
{
IplImage* src;
src=cvLoadImage("F:\\save18.jpg",0);
IplImage* dst = cvCreateImage( cvGetSize(src), 8, 1 );
IplImage* color_dst = cvCreateImage( cvGetSize(src), 8, 3 );
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* lines = 0;
int i;
IplImage* src1=cvCreateImage(cvSize(src->width,src->height),IPL_DEPTH_8U,1);
// cvCvtColor(src, src1, CV_BGR2GRAY);
cvCopy(src,src1);
cvCanny( src1, dst, 50, 200, 3 );
cvCvtColor( dst, color_dst, CV_GRAY2BGR );
lines = cvHoughLines2( dst, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 10,30,15);
int dis=0;
int max=0;
int j=0;
CvPoint *line;
CvPoint pointOne;
CvPoint pointTwo;
int a[1003]={0};
for(i=0;i<lines->total;i++)
{
line = (CvPoint*)cvGetSeqElem(lines,i);
dis=(line[1].y-line[0].y)*(line[1].y-line[0].y)+(line[1].x-line[0].x)*(line[1].x-line[0].x);
a[4*i]=line[0].x;
a[4*i+1]=line[0].y;
a[4*i+2]=line[1].x;
a[4*i+3]=line[1].y;
if(dis>max)
{
max=dis;
j=i;
}
}
pointOne.x=a[4*j];
//cout<<"pointOne.x="<<pointOne.x<<endl;
pointOne.y=a[4*j+1];
//cout<<pointOne.y<<endl;
pointTwo.x=a[4*j+2];
//cout<<pointTwo.x<<endl;
pointTwo.y=a[4*j+3];
//cout<<pointTwo.y<<endl;
cvLine( color_dst, pointOne, pointTwo, CV_RGB(255,0,0), 3, 8 ); //画出最长的直线
double Degree=0.0;
Degree = atan2(fabs((double)(pointTwo.y-pointOne.y)),fabs((double)(pointTwo.x-pointOne.x))); //得到最长直线与水平夹角
cout<<"pointTwo.x="<<pointTwo.x<<";pointOne.x="<<pointOne.x<<";pointTwo.y="<<pointTwo.y<<";pointOne.y="<<pointOne.y<<endl;
cout<<"Degree="<<Degree<<endl;
if((pointTwo.x>pointOne.x) &&( pointTwo.y>pointOne.y))
{
Degree=-Degree;
}
cout<<"弧度= "<<Degree<<endl;
cvNamedWindow( "Source", 1 );
cvShowImage( "Source", src );
cvNamedWindow( "Hough", 1 );
cvShowImage( "Hough", color_dst );
IplImage *rotate2=rotateimage(src);
cvNamedWindow("rotate2",1);
cvShowImage("rotate2",rotate2);
IplImage *img_rotate=rotateImage2(rotate2,Degree);
cvNamedWindow("xzh");
cvShowImage("xzh",img_rotate);
/*IplImage *FS=fanse(img_rotate);
cvNamedWindow("FS");
cvShowImage("FS",img_rotate);*/
FillInternalContours1(img_rotate,200);
cvReleaseMemStorage(&storage);
cvWaitKey(0);
cvReleaseImage(&rotate2);
cvReleaseImage(&src);
cvReleaseImage(&color_dst);
cvReleaseImage(&dst);
cvReleaseImage(&img_rotate);
cvDestroyAllWindows();
}
下面来一部分一部分的分析
1.
cout<<"angle="<<angle<<endl; // 角度 angle
double a = sin(angle), b = cos(angle);
int width=img->width, height=img->height;
//旋转后的新图尺寸
int width_rotate= int(height * fabs(a) + width * fabs(b)); //旋转后的宽度,函数:fabs(a):求浮点数a的绝对值 ,强制类型转换
int height_rotate=int(width * fabs(a) + height * fabs(b));
IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), img->depth, img->nChannels);
cvZero(img_rotate); //好像是清除
这部分程序的作用应该是图像旋转,相关原理可以看下面:
上面第二张图片,就是将第一张逆时针旋转了30度,可以看到整体图像尺寸没有改变,所以部分图像看不到了,所以我们应该扩大图像尺寸。
需要计算新图的尺寸,示意图如下:
结果
今天暂时到这里,对opencv还不是很懂
2.