标签: opencv倾斜矫正图像旋转数字识别旋转变换
2016-08-02 15:42
980人阅读
收藏
举报
在上一篇文章中我们得到了分行后的数据图像Row1.jpg-Row4.jpg,本篇文章介绍数字的倾斜矫正。
1.方法
用Row1.jpg做示范。
本图数字倾斜情况并不明显。但是由于拍摄角度的问题,有的图像数字倾斜情况较为明显,而这里将采用的数字识别方法是特征识别,对于数字形状有较高要求,因此倾斜矫正是有必要的。
根据后文采用的识别方法,倾斜矫正的目的是要将数字的竖边矫正为竖直方向。即如图所示。
采用的方法是:
1. 利用hough变换找到与竖直方向夹角<30°的直线
2. 计算这些直线与竖直方向的夹角平均值avAng
3. 将图像旋转avAng
hough变换原理
hough变换简单来说就是将图像空间内的一条线转化为
θ−ρ
参数空间的一个点。
具体原理可以参考这篇文章:hough变换
利用hough变换我们就可以将原图上找到的直线转化为一组
(θ−ρ)
,从上图中可以看出,
θ
就是直线与竖直方向的夹角。
2.代码
/****************倾斜校正子程序*****************/
/********************************************/
IplImage *Rotate(IplImage *RowImage)
{
IplImage *canImage=cvCreateImage(cvGetSize(RowImage),IPL_DEPTH_8U,1);
cvCanny(RowImage,canImage,30,200,3);
CvMemStorage *storage=cvCreateMemStorage();
CvSeq *lines=NULL;
lines=cvHoughLines2(canImage,storage,CV_HOUGH_STANDARD,1,CV_PI/180,20,0,0);
int numLine=0;
float sumAng=0.0;
for(int i=0;i<lines->total;i++)
{
float *line=(float *)cvGetSeqElem(lines,i);
float theta=line[1];
if(theta<30*CV_PI/180 || (CV_PI-theta)<30*CV_PI/180 )
{
numLine++;
sumAng=sumAng+theta;
}
}
float avAng=(sumAng/numLine)*180/CV_PI;
CvPoint2D32f center;
center.x=float (RowImage->width/2.0);
center.y=float (RowImage->height/2.0);
float m[6];
CvMat M = cvMat( 2, 3, CV_32F, m );
cv2DRotationMatrix( center,avAng,1, &M);
double a=sin(sumAng/numLine);
double b=cos(sumAng/numLine);
int width_rotate=int (RowImage->height*fabs(a)+RowImage->width*fabs(b));
int height_rotate=int (RowImage->width*fabs(a)+RowImage->height*fabs(b));
IplImage *RotateRow=cvCreateImage(cvSize(width_rotate,height_rotate),IPL_DEPTH_8U,1);
m[2]+=(width_rotate-RowImage->width)/2;
m[5]+=(height_rotate-RowImage->height)/2;
cvWarpAffine(RowImage,RotateRow, &M,CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,cvScalarAll(0));
cvReleaseImage(&canImage);
cvReleaseMemStorage(&storage);
return RotateRow;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
3.解析
- 为减少计算量,对图像进行canny边缘检测。图像变换为canImage。
IplImage *canImage=cvCreateImage(cvGetSize(RowImage),IPL_DEPTH_8U,1);
cvCanny(RowImage,canImage,30,200,3);
- 对canImage进行hough变换并且统计与竖直方向夹角<30°的直线数量、角度和,计算平均夹角。
- hough变换需要分配储存空间和指向该空间的指针,这里分别为storage和lines。
cvHoughLines2
这个函数的第六个参数改变的是:将穿过多少个边缘像素点的直线确定为所需直线。该数越大,则产生的直线越少。本文中将其确定为20,对于不同的图片,可以对其进行调整。 - 用
cvGetSeqElem(lines,i)
函数时获取的是lines的第i个
(θ−ρ)
组,
θ
为弧度制。由于后面要用到的获取放射变换矩阵需要角度为角度制,故cvAng做一下转换。
CvMemStorage *storage=cvCreateMemStorage();
CvSeq *lines=NULL;
lines=cvHoughLines2(canImage,storage,CV_HOUGH_STANDARD,1,CV_PI/180,20,0,0);
int numLine=0;
float sumAng=0.0;
for(int i=0;i<lines->total;i++)
{
float *line=(float *)cvGetSeqElem(lines,i);
float theta=line[1];
if(theta<30*CV_PI/180 || (CV_PI-theta)<30*CV_PI/180 )
{
numLine++;
sumAng=sumAng+theta;
}
}
float avAng=(sumAng/numLine)*180/CV_PI;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 获取二维放射变换矩阵并且对图像进行旋转,要求旋转后的图像保留原图像的全部内容,以免有数字部分被切掉。
- 这部分可以参考这篇文章,写得非常详细:
【OpenCV】图像几何变换:旋转、缩放、斜切
CvPoint2D32f center;
center.x=float (RowImage->width/2.0);
center.y=float (RowImage->height/2.0);
float m[6];
CvMat M = cvMat( 2, 3, CV_32F, m );
cv2DRotationMatrix( center,avAng,1, &M);
double a=sin(sumAng/numLine);
double b=cos(sumAng/numLine);
int width_rotate=int (RowImage->height*fabs(a)+RowImage->width*fabs(b));
int height_rotate=int (RowImage->width*fabs(a)+RowImage->height*fabs(b));
IplImage *RotateRow=cvCreateImage(cvSize(width_rotate,height_rotate),IPL_DEPTH_8U,1);
m[2]+=(width_rotate-RowImage->width)/2;
m[5]+=(height_rotate-RowImage->height)/2;
cvWarpAffine(RowImage,RotateRow, &M,CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,cvScalarAll(0));
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 最后释放内存,并且返回旋转后的图像RotateRow。storage也需要释放。
cvReleaseImage(&canImage);
cvReleaseMemStorage(&storage);
return RotateRow;