OpenCv图像仿射变换

转载 2013年12月02日 11:20:57

几何变换

几何变换可以看成图像中物体(或像素)空间位置改变,或者说是像素的移动。

几何运算需要空间变换和灰度级差值两个步骤的算法,像素通过变换映射到新的坐标位置,新的位置可能是在几个像素之间,即不一定为整数坐标。这时就需要灰度级差值将映射的新坐标匹配到输出像素之间。最简单的插值方法是最近邻插值,就是令输出像素的灰度值等于映射最近的位置像素,该方法可能会产生锯齿。这种方法也叫零阶插值,相应比较复杂的还有一阶和高阶插值。

插值算法感觉只要了解就可以了,图像处理中比较需要理解的还是空间变换。

空间变换

空间变换对应矩阵的仿射变换。一个坐标通过函数变换的新的坐标位置:


所以在程序中我们可以使用一个2*3的数组结构来存储变换矩阵:


以最简单的平移变换为例,平移(b1,b2)坐标可以表示为:


因此,平移变换的变换矩阵及逆矩阵记为:


缩放变换:将图像横坐标放大(或缩小)sx倍,纵坐标放大(或缩小)sy倍,变换矩阵及逆矩阵为:


选择变换:图像绕原点逆时针旋转a角,其变换矩阵及逆矩阵(顺时针选择)为:



OpenCV中的图像变换函数

基本的放射变换函数:

  1. void cvWarpAffine(   
  2.     const CvArr* src,//输入图像  
  3.     CvArr* dst, //输出图像  
  4.     const CvMat* map_matrix,   //2*3的变换矩阵  
  5.     int flags=CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,   //插值方法的组合  
  6.     CvScalar fillval=cvScalarAll(0)   //用来填充边界外的值  
  7. );  
另外一个比较类似的函数是cvGetQuadrangleSubPix:

  1. void cvGetQuadrangleSubPix(   
  2.        const CvArr* src,  //输入图像   
  3.        CvArr* dst,   // 提取的四边形  
  4.        const CvMat* map_matrix //2*3的变换矩阵  
  5. );  
这个函数用以提取输入图像中的四边形,并通过map_matrix变换存储到dst中,与WarpAffine变换意义相同,

即对应每个点的变换:


WarpAffine与 GetQuadrangleSubPix 不同的在于cvWarpAffine 要求输入和输出图像具有同样的数据类型,有更大的资源开销(因此对小图像不太合适)而且输出图像的部分可以保留不变。而 cvGetQuadrangleSubPix 可以精确地从8位图像中提取四边形到浮点数缓存区中,具有比较小的系统开销,而且总是全部改变输出图像的内容。


实践:图像旋转变换(原尺寸)

首先用cvWarpAffine实验将图像逆时针旋转degree角度。
  1. //逆时针旋转图像degree角度(原尺寸)  
  2. void rotateImage(IplImage* img, IplImage *img_rotate,int degree)  
  3. {  
  4.     //旋转中心为图像中心  
  5.     CvPoint2D32f center;    
  6.     center.x=float (img->width/2.0+0.5);  
  7.     center.y=float (img->height/2.0+0.5);  
  8.     //计算二维旋转的仿射变换矩阵  
  9.     float m[6];              
  10.     CvMat M = cvMat( 2, 3, CV_32F, m );  
  11.     cv2DRotationMatrix( center, degree,1, &M);  
  12.     //变换图像,并用黑色填充其余值  
  13.     cvWarpAffine(img,img_rotate, &M,CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,cvScalarAll(0) );  
  14. }  



或者
//旋转图像内容不变,尺寸相应变大  
void rotateImage(Mat& src, Mat& dst, double angle)
{   
//旋转加缩放  
    Point2f center(src.cols/2, src.rows/2);//旋转中心  
    double scale = 1.0;//缩放比例  
    Mat M2 = getRotationMatrix2D(center, angle, scale);//计算旋转加缩放的变换矩阵  
    warpAffine(src, dst, M2, src.size());//仿射变换  

逆时针旋转30度结果:
这里我们将新的图像还保留原来的图像尺寸。这样的效果显然不太好,我们通过计算相应放大图像尺寸。

实践:图像旋转变换(保留原图内容,放大尺寸)

需要计算新图的尺寸,示意图如下:

所以新图size为(width*cos(a)+height*sin(a), height*cos(a)+width*sin(a))
  1. //旋转图像内容不变,尺寸相应变大  
  2. IplImage* rotateImage1(IplImage* img,int degree){  
  3.     double angle = degree  * CV_PI / 180.; // 弧度    
  4.     double a = sin(angle), b = cos(angle);   
  5.     int width = img->width;    
  6.     int height = img->height;    
  7.     int width_rotate= int(height * fabs(a) + width * fabs(b));    
  8.     int height_rotate=int(width * fabs(a) + height * fabs(b));    
  9.     //旋转数组map  
  10.     // [ m0  m1  m2 ] ===>  [ A11  A12   b1 ]  
  11.     // [ m3  m4  m5 ] ===>  [ A21  A22   b2 ]  
  12.     float map[6];  
  13.     CvMat map_matrix = cvMat(2, 3, CV_32F, map);    
  14.     // 旋转中心  
  15.     CvPoint2D32f center = cvPoint2D32f(width / 2, height / 2);    
  16.     cv2DRotationMatrix(center, degree, 1.0, &map_matrix);    
  17.     map[2] += (width_rotate - width) / 2;    
  18.     map[5] += (height_rotate - height) / 2;    
  19.     IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), 8, 3);   
  20.     //对图像做仿射变换  
  21.     //CV_WARP_FILL_OUTLIERS - 填充所有输出图像的象素。  
  22.     //如果部分象素落在输入图像的边界外,那么它们的值设定为 fillval.  
  23.     //CV_WARP_INVERSE_MAP - 指定 map_matrix 是输出图像到输入图像的反变换,  
  24.     cvWarpAffine( img,img_rotate, &map_matrix, CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS, cvScalarAll(0));    
  25.     return img_rotate;  
  26. }  


实践:图像旋转变换(保留原图内容,放大尺寸)-2

试一下用cvGetQuadrangleSubPix函数:
  1. //旋转图像内容不变,尺寸相应变大  
  2. IplImage* rotateImage2(IplImage* img, int degree)    
  3. {    
  4.     double angle = degree  * CV_PI / 180.;   
  5.     double a = sin(angle), b = cos(angle);   
  6.     int width=img->width, height=img->height;  
  7.     //旋转后的新图尺寸  
  8.     int width_rotate= int(height * fabs(a) + width * fabs(b));    
  9.     int height_rotate=int(width * fabs(a) + height * fabs(b));    
  10.     IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), img->depth, img->nChannels);    
  11.     cvZero(img_rotate);    
  12.     //保证原图可以任意角度旋转的最小尺寸  
  13.     int tempLength = sqrt((double)width * width + (double)height *height) + 10;    
  14.     int tempX = (tempLength + 1) / 2 - width / 2;    
  15.     int tempY = (tempLength + 1) / 2 - height / 2;    
  16.     IplImage* temp = cvCreateImage(cvSize(tempLength, tempLength), img->depth, img->nChannels);    
  17.     cvZero(temp);    
  18.     //将原图复制到临时图像tmp中心  
  19.     cvSetImageROI(temp, cvRect(tempX, tempY, width, height));    
  20.     cvCopy(img, temp, NULL);    
  21.     cvResetImageROI(temp);    
  22.     //旋转数组map  
  23.     // [ m0  m1  m2 ] ===>  [ A11  A12   b1 ]  
  24.     // [ m3  m4  m5 ] ===>  [ A21  A22   b2 ]  
  25.     float m[6];    
  26.     int w = temp->width;    
  27.     int h = temp->height;    
  28.     m[0] = b;    
  29.     m[1] = a;    
  30.     m[3] = -m[1];    
  31.     m[4] = m[0];    
  32.     // 将旋转中心移至图像中间    
  33.     m[2] = w * 0.5f;    
  34.     m[5] = h * 0.5f;    
  35.     CvMat M = cvMat(2, 3, CV_32F, m);    
  36.     cvGetQuadrangleSubPix(temp, img_rotate, &M);    
  37.     cvReleaseImage(&temp);    
  38.     return img_rotate;  
  39. }    


实践:图像放射变换(通过三点确定变换矩阵)

在OpenCV 2.3的参考手册中《opencv_tutorials》介绍了另一种确定变换矩阵的方法,通过三个点变换的几何关系映射实现变换。
变换示意图如下:

即通过三个点就可以确定一个变换矩阵。(矩形变换后一定为平行四边形)
以下是基于OpenCV 2.3的代码(需至少2.0以上版本的支持)
  1. int main( )  
  2. {  
  3.     Point2f srcTri[3];  
  4.     Point2f dstTri[3];  
  5.     Mat rot_mat( 2, 3, CV_32FC1 );  
  6.     Mat warp_mat( 2, 3, CV_32FC1 );  
  7.     Mat src, warp_dst, warp_rotate_dst;  
  8.     //读入图像  
  9.     src = imread( "baboon.jpg", 1 );  
  10.     warp_dst = Mat::zeros( src.rows, src.cols, src.type() );  
  11.     // 用3个点确定A仿射变换  
  12.     srcTri[0] = Point2f( 0,0 );  
  13.     srcTri[1] = Point2f( src.cols - 1, 0 );  
  14.     srcTri[2] = Point2f( 0, src.rows - 1 );  
  15.     dstTri[0] = Point2f( src.cols*0.0, src.rows*0.33 );  
  16.     dstTri[1] = Point2f( src.cols*0.85, src.rows*0.25 );  
  17.     dstTri[2] = Point2f( src.cols*0.15, src.rows*0.7 );  
  18.     warp_mat = getAffineTransform( srcTri, dstTri );  
  19.     warpAffine( src, warp_dst, warp_mat, warp_dst.size() );  
  20.     /// 旋转矩阵  
  21.     Point center = Point( warp_dst.cols/2, warp_dst.rows/2 );  
  22.     double angle = -50.0;  
  23.     double scale = 0.6;  
  24.     rot_mat = getRotationMatrix2D( center, angle, scale );  
  25.     warpAffine( warp_dst, warp_rotate_dst, rot_mat, warp_dst.size() );  
  26.     ////OpenCV 1.0的形式  
  27.     //IplImage * img=cvLoadImage("baboon.jpg");  
  28.     //IplImage *img_rotate=cvCloneImage(img);  
  29.     //CvMat M =warp_mat;  
  30.     //cvWarpAffine(img,img_rotate, &M,CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,cvScalarAll(0) );  
  31.     //cvShowImage("Wrap2",img_rotate);  
  32.   
  33.     namedWindow( "Source", CV_WINDOW_AUTOSIZE );  
  34.     imshow( "Source", src );  
  35.     namedWindow( "Wrap", CV_WINDOW_AUTOSIZE );  
  36.     imshow( "Wrap", warp_dst );  
  37.     namedWindow("Wrap+Rotate", CV_WINDOW_AUTOSIZE );  
  38.     imshow( "Wrap+Rotate", warp_rotate_dst );  
  39.     waitKey(0);  
  40.     return 0;  
  41. }  
变换结果:

【OpenCV】图像变换(五)-仿射变换和透视变换

在上篇的博文中,我们重点讨论了基于霍夫变换的线段和圆检测。其实在图像的变换中,还有一部分是几何操作,这些操作包括各种方式的拉伸,包括一致性缩放和非一致性缩放(即扭曲)。对于平面区域,有两种方式的几何转...
  • w12345_ww
  • w12345_ww
  • 2015年04月28日 10:28
  • 13606

图像仿射变换及图像扭曲(Image Warping)

空间图像几何变换包括两个主要步骤: (1) 空间坐标变换 (2)变换坐标的赋值、插值运算 空间坐标变换一般可以表达为如下式子: 对于用得普遍的仿射变换,可以表达为如下式子: ...
  • ZYTTAE
  • ZYTTAE
  • 2015年01月07日 23:33
  • 11118

opencv图像旋转--矩阵旋转和仿射变换相比较

opencv图像旋转--矩阵旋转和仿射变换相比较 这段时间在用opencv实现一些图像基本功能,旋转啊,对比度,亮度增强等,在该篇文章做的实验时图像旋转时,发现有两种方法(我这里都会介绍),我将两种...
  • liumangmao1314
  • liumangmao1314
  • 2016年11月27日 22:00
  • 1434

OpenCV实现仿射变换--通过三个点进行变换

什么是仿射变换?¶ 一个任意的仿射变换都能表示为 乘以一个矩阵 (线性变换) 接着再 加上一个向量 (平移). 综上所述, 我们能够用仿射变换来表示: 旋转 (线性变换)...
  • u013713010
  • u013713010
  • 2015年05月28日 10:10
  • 1623

opencv之仿射变换

http://www.cnblogs.com/shijibao001/articles/1225962.html
  • skeeee
  • skeeee
  • 2014年05月05日 11:35
  • 3811

【OpenCV入门教程之十八】OpenCV仿射变换 & SURF特征点描述合辑

本系列文章由@浅墨_毛星云 出品,转载请注明出处。   文章链接: http://blog.csdn.net/poem_qianmo/article/details/33320997 作...
  • yuzeze
  • yuzeze
  • 2016年05月26日 11:17
  • 2471

opencv 图像仿射变换 计算仿射变换后对应特征点的新坐标 图像旋转、缩放、平移

常常需要最图像进行仿射变换,仿射变换后,我们可能需要将原来图像中的特征点坐标进行重新计算,获得原来图像中例如眼睛瞳孔坐标的新的位置,用于在新得到图像中继续利用瞳孔位置坐标。 仿射变换...
  • u011867581
  • u011867581
  • 2014年04月08日 11:42
  • 1256

opencv-第六章-图像变换-重映射、仿射变换、透视变换

opencv-第六章-图像变换-重映射、仿射变换、透视变换
  • gdut2015go
  • gdut2015go
  • 2015年10月28日 11:27
  • 3085

【opencv+python】图像处理之二、几何变换(仿射与投影)的应用

该系列文章为 OpenCV+Python Tutorials的学习笔记 代码托管在Github 转载请注明: http://blog.csdn.net/a352611/article/detail...
  • a352611
  • a352611
  • 2016年05月15日 20:34
  • 7423

图像处理中的旋转、平移、缩放——仿射变换的原理和优化

仿射变换的原理在条形码识别软件中有图像预览的功能。有时预览的图像需要进行转置(旋转180度或者90度)、缩放、镜像(左右反转)等操作。OpenCV提供了相应的函数进行以上操作。例如: 转置:cv::...
  • bytekiller
  • bytekiller
  • 2015年08月20日 10:08
  • 3667
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:OpenCv图像仿射变换
举报原因:
原因补充:

(最多只允许输入30个字)