基于opencv的仿射变换


本文内容参考了http://blog.csdn.net/xiaowei_cqu/article/details/7616044

几何变换

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

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

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

空间变换

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


所以在程序中我们可以使用一个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角度:

#include<cv.h>
#include<cxcore.h>
#include<highgui.h>
#include<iostream>


int main ( int argc ,char* argv)
{
	IplImage* src = cvLoadImage("F:\\bb1.jpg");
	IplImage* dst = cvCloneImage( src);
	int degree = 30;//逆时针旋转30度
	CvPoint2D32f center ;//创建一个中心点
	center.x = src->width/2.0+0.5;
	center.y = src->height/2.0+0.5;
	float m[6];
	CvMat M = cvMat( 2 , 3 , CV_32F , m);//构建一个2*3的矩阵
	cv2DRotationMatrix( center , degree , 1 , &M);//计算围绕中心点旋转的映射矩阵
	cvWarpAffine( src , dst , &M , CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS, cvScalarAll(0));//仿射变换
	cvNamedWindow("src");
	cvShowImage("src" ,src);
	cvNamedWindow("dst");
	cvShowImage("dst" ,dst);
	cvWaitKey(0);

}
结果为



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

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

所以新图size为(width*cos(a)+height*sin(a), height*cos(a)+width*sin(a))

int main ( int argc ,char* argv)
{
	IplImage* src = cvLoadImage("F:\\bb1.jpg");
	int degree = 30;
	double angle = degree  * CV_PI / 180.; // 弧度 
	double a = sin(angle), b = cos(angle);   
    int width = src ->width;    
    int height = src->height;    
    int width_rotate= int(height * fabs(a) + width * fabs(b));    
    int height_rotate=int(width * fabs(a) + height * fabs(b));  

	float map[6];  
    CvMat map_matrix = cvMat(2, 3, CV_32F, map);    
    // 旋转中心  
    CvPoint2D32f center = cvPoint2D32f(width / 2, height / 2);    
    cv2DRotationMatrix(center, degree, 1.0, &map_matrix);    
    map[2] += (width_rotate - width) / 2;  //对图像进行平移  
    map[5] += (height_rotate - height) / 2;     //对图像进行平移  
    IplImage* dst = cvCreateImage(cvSize(width_rotate, height_rotate), 8, 3);   
    //对图像做仿射变换  
    //CV_WARP_FILL_OUTLIERS - 填充所有输出图像的象素。  
    //如果部分象素落在输入图像的边界外,那么它们的值设定为 fillval.  
    //CV_WARP_INVERSE_MAP - 指定 map_matrix 是输出图像到输入图像的反变换,  
    cvWarpAffine( src,dst, &map_matrix, CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS, cvScalarAll(0));    
	cvNamedWindow("src");
	cvShowImage("src" ,src);
	cvNamedWindow("dst");
	cvShowImage("dst" ,dst);
	cvWaitKey(0);
    return 0;  
}


结果为






发布了8 篇原创文章 · 获赞 3 · 访问量 2万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章