OpenCV仪表数据识别(四):图像倾斜矫正
2016-08-02 15:42
656 查看
在上一篇文章中我们得到了分行后的数据图像Row1.jpg-Row4.jpg,本篇文章介绍数字的倾斜矫正。
用Row1.jpg做示范。
本图数字倾斜情况并不明显。但是由于拍摄角度的问题,有的图像数字倾斜情况较为明显,而这里将采用的数字识别方法是特征识别,对于数字形状有较高要求,因此倾斜矫正是有必要的。
根据后文采用的识别方法,倾斜矫正的目的是要将数字的竖边矫正为竖直方向。即如图所示。
采用的方法是:
1. 利用hough变换找到与竖直方向夹角<30°的直线
2. 计算这些直线与竖直方向的夹角平均值avAng
3. 将图像旋转avAng
具体原理可以参考这篇文章:hough变换
利用hough变换我们就可以将原图上找到的直线转化为一组(θ−ρ) ,从上图中可以看出,θ 就是直线与竖直方向的夹角。
对canImage进行hough变换并且统计与竖直方向夹角<30°的直线数量、角度和,计算平均夹角。
hough变换需要分配储存空间和指向该空间的指针,这里分别为storage和lines。
用
获取二维放射变换矩阵并且对图像进行旋转,要求旋转后的图像保留原图像的全部内容,以免有数字部分被切掉。
这部分可以参考这篇文章,写得非常详细:
【OpenCV】图像几何变换:旋转、缩放、斜切
最后释放内存,并且返回旋转后的图像RotateRow。storage也需要释放。
1.方法
用Row1.jpg做示范。
本图数字倾斜情况并不明显。但是由于拍摄角度的问题,有的图像数字倾斜情况较为明显,而这里将采用的数字识别方法是特征识别,对于数字形状有较高要求,因此倾斜矫正是有必要的。
根据后文采用的识别方法,倾斜矫正的目的是要将数字的竖边矫正为竖直方向。即如图所示。
采用的方法是:
1. 利用hough变换找到与竖直方向夹角<30°的直线
2. 计算这些直线与竖直方向的夹角平均值avAng
3. 将图像旋转avAng
hough变换原理
hough变换简单来说就是将图像空间内的一条线转化为θ−ρ 参数空间的一个点。具体原理可以参考这篇文章:hough变换
利用hough变换我们就可以将原图上找到的直线转化为一组(θ−ρ) ,从上图中可以看出,θ 就是直线与竖直方向的夹角。
2.代码
/****************倾斜校正子程序*****************/ //函数名称:IplImage *Rotate(IplImage *RowImage) //功能:对每行数字进行倾斜校正 //入口参数:行图像RowImage //出口参数:旋转后的图像RotateRow /********************************************/ IplImage *Rotate(IplImage *RowImage) { //建立储存边缘检测结果图像canImage IplImage *canImage=cvCreateImage(cvGetSize(RowImage),IPL_DEPTH_8U,1); //进行边缘检测 cvCanny(RowImage,canImage,30,200,3); //进行hough变换 CvMemStorage *storage=cvCreateMemStorage(); CvSeq *lines=NULL; lines=cvHoughLines2(canImage,storage,CV_HOUGH_STANDARD,1,CV_PI/180,20,0,0); //统计与竖直夹角<30度的直线个数以及其夹角和 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; } } //计算出平均倾斜角,anAng为角度制 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); //建立输出图像RotateRow 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; }
3.解析
为减少计算量,对图像进行canny边缘检测。图像变换为canImage。//建立储存边缘检测结果图像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); //统计与竖直夹角<30度的直线个数以及其夹角和 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; } } //计算出平均倾斜角,anAng为角度制 float avAng=(sumAng/numLine)*180/CV_PI;
获取二维放射变换矩阵并且对图像进行旋转,要求旋转后的图像保留原图像的全部内容,以免有数字部分被切掉。
这部分可以参考这篇文章,写得非常详细:
【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); //建立输出图像RotateRow 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));
最后释放内存,并且返回旋转后的图像RotateRow。storage也需要释放。
cvReleaseImage(&canImage); cvReleaseMemStorage(&storage); return RotateRow;
相关文章推荐
- OpenCV仪表数据识别(四):图像倾斜矫正
- OpenCV仪表数据识别(一):整体思想
- 基于OpenCV及Python的数独问题识别与求解(二)边框识别与图像矫正
- OpenCV仪表数据识别(五):数字分割提取
- OpenCV仪表数据识别(二):数字区域自动定位
- OpenCV仪表数据识别(三):数据按行分割
- 图像识别与处理之Opencv——图像数据获取与存储(imread()和imwrite()函数)
- OpenCV的+安卓+号牌识别(OpenCV + Android + 图像水平矫正)
- 重温OpenCV图像数据访问
- 移动设备上使用opencv 1.10做图像识别的例子
- 移动设备上使用opencv 1.10做图像识别的例子
- Android-图像识别项目OpenCV(3):程序目标以及单独发布
- 关于OpenCV中利用函数cvConvert实现图像数据类型转换(8U->16S)
- Android-图像识别项目OpenCV(2):运行官方例子中的脸部识别程序
- 基于OpenCV的PHP图像人脸识别技术
- Android-图像识别项目OpenCV(2):运行官方例子中的脸部识别程序
- Android-图像识别项目OpenCV(3):程序目标以及单独发布
- 使用OpenCV实现内存中图像数据的RGB-->HSV转换
- Qt中用QLabel显示OpenCV中Mat图像数据出现扭曲现象的解决
- 图像处理和图像识别中常用的OpenCV函数2