OpenCV仪表数据识别(四):图像倾斜矫正

OpenCV仪表数据识别(四):图像倾斜矫正

标签: opencv倾斜矫正图像旋转数字识别旋转变换
  980人阅读  评论(0)  收藏  举报
  分类:

目录(?)[+]

在上一篇文章中我们得到了分行后的数据图像Row1.jpg-Row4.jpg,本篇文章介绍数字的倾斜矫正。

1.方法

Row1 
用Row1.jpg做示范。 
本图数字倾斜情况并不明显。但是由于拍摄角度的问题,有的图像数字倾斜情况较为明显,而这里将采用的数字识别方法是特征识别,对于数字形状有较高要求,因此倾斜矫正是有必要的。 
根据后文采用的识别方法,倾斜矫正的目的是要将数字的竖边矫正为竖直方向。即如图所示。 
Rotate1 
采用的方法是: 
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;
}
   
   
  • 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。
    //建立储存边缘检测结果图像canImage
    IplImage *canImage=cvCreateImage(cvGetSize(RowImage),IPL_DEPTH_8U,1);
    //进行边缘检测
    cvCanny(RowImage,canImage,30,200,3);
   
   
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4
  • 对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; 
   
   
  • 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);  
    //建立输出图像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));
   
   
  • 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;
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值