仿射变换-基于2x3矩阵进行的图像变换
一个任意的仿射变换可以表达为乘以一个矩阵再加上一个向量的形式。在OpenCV里,代表这种变换的标准形式是2x3矩阵。
######################################################
稠密仿射变换
void cvWarpAffine(
const CvArr* src,
CvArr* dst,
const CvMat* map_matrix,
int flags = CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS,
CvScalar fillval = cvScalarAll(0);
);
src和dst表示数组或图像,可以是单通道或三通道的任意类型(假定我们大小和类型是相同的)
map_matrix是一个对所需要的变换进行量化的2x3矩阵
flags控制插值的方法以及下面一个或两个附加选项(通常用布尔或操作来组合)
CV_WARP_FILL_OUTLINES: 通常,变换的src图像不能和dst图像完美匹配-从源图像映射的像素可能实际上并不存在。如果设置了标志位,这些失去的值就会由fillval补充
CV_WARP_INVERSE_MAP:这个标志位用于方便地进行从dst到src的逆向变形,而不是从src到dst的变换
cvWarpAffine()涉及开销问题。
另一种方法是利用cvGetQuadrangleSubPix()。这个函数的选项更少但优点更多,尤其是,它的开销比较小而且可以处理源图像是8位而目标图像是32位浮点图像的特殊情况。同时它能处理多通道图像。
void cvGetQuadrangleSubPix(
const CvArr* src,
CvArr* dst,
const CvMat* map_matrix
);
生成映射矩阵map_matrix。如果已有两幅图像要通过仿射变换发生关联或希望以那种方式来逼近可以使用如下函数:
CvMat* cvGetAffineTransform(
const CvPoint2D32f* pts_src,
const CvPoint2D32f* pts_dst,
CvMat* map_matrix
);
src和dst是包含三个三维点(x,y)的数组,map_matrix所表示的仿射变换就是通过这些点来计算。
cvGetAffineTransform()中的pts_src和pts_dst是其中包含三个点的数组,它们定义了两个平行四边形。描述仿射变换的简单方法就是把pts_src设置为源图像的三个角-例如:源图像的左上角和左下角以及右上角。从源图像到目标图像的映射完全由特定的pts_dst定义,这三个点的位置将会被映射到目标图像。一旦这三个独立点(实际上是指定一个“有代表性”的平行四边形)的映射完成,所有其他点会依次变形。
note:只需要三个点,因为对于仿射变换来说,只需要表示一个平行四边形。涉及透视变换时,就需要四个点以表示一个普通梯形。
cv2DRotationMatrix()
用来计算围绕任意点的旋转的映射矩阵和一个可选择的尺度
CvMat* cv2DRotationMatrix(
<span style="white-space:pre"> </span>CvPoint2D32f center,
double angle,
double scale,
CvMat* map_matrix
);
第一个参数center是旋转中心
第2个和第3个参数给出了旋转的角度和缩放尺度
最后一个参数map_matrix是输出的映射矩阵,总是一个浮点类型的2x3矩阵
旋转图像
void imageRotation(void)
{
CvPoint2D32f srcTri[3], dstTri[3];
CvMat* rot_mat = cvCreateMat(2, 3, CV_32FC1);
CvMat* warp_mat = cvCreateMat(2, 3, CV_32FC1);
IplImage *src, *dst;
src=cvLoadImage("lena.jpg");
dst=cvCloneImage(src);
dst->origin = src->origin;
cvZero(dst);
//Compute rotation matrix
//
CvPoint2D32f center=cvPoint2D32f(src->width/2, src->height/2);
double angle=45.0;//-50.0;
double scale=1.0;//0.6;
cv2DRotationMatrix(center, angle, scale, rot_mat);
//Do the transformation
//
cvWarpAffine(src, dst, rot_mat);
cvNamedWindow("Affine_Transform");
cvShowImage("Affine_Transform", dst);
cvWaitKey(0);
cvReleaseImage(&src);
cvReleaseImage(&dst);
cvReleaseMat(&rot_mat);
cvReleaseMat(&warp_mat);
cvDestroyAllWindows();
}
#############################################################################
参考:《OpenCV3编程入门》第7.4章
opencv2
仿射变换(Affine Transformation 或 Affine Map),又称仿射映射,是指在几何中,一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间的过程。它保持了二维图形的“平直性”(直线经过变换之后依然是直线)和“平行性”(二维图形之间的相对位置关系保持不变,平行线依然是平行线,且直线上点的位置顺序不变)。
一个任意的仿射变换都能表示为乘以一个矩阵(线性变换),接着再加上一个向量(平移)的形式。
用仿射变换能表示如下三种常见的变换形式:
1.旋转,rotation(线性变换)
2.平移,translation(向量加)
3.缩放,scale(线性变换)
通常使用2x3的矩阵来表示仿射变换
OpenCV仿射变换一般涉及到warpAffine和getRotationMatrix2D函数:
1.使用OpenCv函数warpAffine来实现一些简单的重映射
2.使用OpenCV函数getRotationMatrix2D来获得旋转矩阵
仿射变换函数:warpAffine()
//! warps the image using affine transformation
CV_EXPORTS_W void warpAffine( InputArray src, OutputArray dst,
InputArray M, Size dsize,
int flags=INTER_LINEAR,
int borderMode=BORDER_CONSTANT,
const Scalar& borderValue=Scalar());
参数解析:
src:InputArray类型的src,输入图像,即源图像,使用Mat类的对象即可
dst:OutputArray类型的dst,函数调用后的运算结果存在这里,需要和源图像有一样的尺寸和类型
M:InputArray类型的M,2x3大小的变换矩阵
dsize:Size类型的dsize,表示输出图像的尺寸
flags:int类型的flags,插值方法的标识符。默认为INTER_LINEAR(线性插值)
可选的插值方法如下:
INTER_NEAREST:最近邻插值
INTER_LINEAR:线性插值(默认)
INTER_AREA:区域插值
INTER_CUBIC:三次样条插值
INTER_LANCZOS4:Lanczos插值
CV_WARP_FILL_OUTLIERS:填充所有输出图像的像素。如果部分像素落在输入图像的边界外,那么它们的值设定为fillval
CV_WARP_INVERSE_MAP:表示M为输出图像到输入图像的反变换。因此可以直接用来做像素插值。否则,warpAffine函数从M矩阵得到反变换
borderMode:int类型,边界像素模式,默认值为BORDER_CONSTANT
borderValue:const Scalar& 类型,在恒定的边界情况下取的值,默认值为Scalar(),即0。
二维旋转变换矩阵:getRotationMatrix2D矩阵
//! returns 2x3 affine transformation matrix for the planar rotation.
CV_EXPORTS_W Mat getRotationMatrix2D( Point2f center, double angle, double scale );
center:Point2f类型,表示源图像的旋转中心
angle:double类型,旋转角度。角度为正值表示向逆时针旋转(坐标原点是左上角)
scale:double类型,缩放系数
其他函数:
//! returns 2x3 affine transformation for the corresponding 3 point pairs.
CV_EXPORTS Mat getAffineTransform( const Point2f src[], const Point2f dst[] );
src:Point2f类型的数组,里面包含源图像的3个点的坐标
dst:Point2f类型的数组,里面包含和src数组中对应的三个点的坐标
利用输入的src和dst,返回2x3大小的仿射变换矩阵
新建头文件warpAffineTransform.h:
#ifndef WARPAFFINE_H_
#define WARPAFFINE_H_
//包含程序依赖的头文件和命名空间
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
//定义一些辅助宏
#define WINDOW_NAME1 "[原始图窗口]"
#define WINDOW_NAME2 "[经过Warp后的图像]"
#define WINDOW_NAME3 "[经过Warp和Rotate后的图像]"
//仿射变换
int showWarpAffine();
#endif
新建cpp文件warpAffineTransform.cpp:
#include "warpAffineTransform.h"
//显示欢迎和帮助文字
void ShowHelpText();
int showWarpAffine() {
ShowHelpText();
//参数准备
//定义两组点,代表两个三角形
Point2f srcTriangle[3];
Point2f dstTriangle[3];
//定义一些Mat变量
Mat rotMat(2, 3, CV_32FC1);
Mat warpMat(2, 3, CV_32FC1);
Mat srcImage, dstImage_warp, dstImage_warp_rotate;
//加载源图像并做一些初始化
srcImage = imread("lena.jpg", 1);
if (!srcImage.data) { //读取图像不成功
cout<<"读取图片错误,请确定目录下是否有imread函数指定的图片存在~!"<<endl;
cin.get();
return false;
}
//设置目标图像的大小和类型与源图像一致
dstImage_warp=Mat::zeros(srcImage.rows, srcImage.cols, srcImage.type());
//设置源图像和目标图像上的三组点以计算仿射变换
srcTriangle[0] = Point2f(0, 0);
srcTriangle[1] = Point2f(static_cast<float>(srcImage.cols-1), 0);
srcTriangle[2] = Point2f(0, static_cast<float>(srcImage.rows-1));
dstTriangle[0] = Point2f(static_cast<float>(srcImage.cols*0.5), static_cast<float>(srcImage.rows*0.0));
dstTriangle[1] = Point2f(static_cast<float>(srcImage.cols*1.0), static_cast<float>(srcImage.rows*0.5));
dstTriangle[2] = Point2f(static_cast<float>(srcImage.cols*0.0), static_cast<float>(srcImage.rows*0.5));
//求得仿射变换
warpMat = getAffineTransform(srcTriangle, dstTriangle);
//对源图像应用刚刚求得的仿射变换
warpAffine(srcImage, dstImage_warp, warpMat, dstImage_warp.size());
//对图像进行缩放后再旋转
//计算图像中顺时针旋转50度缩放因子为0.6的旋转矩阵
Point center = Point(dstImage_warp.cols/2, dstImage_warp.rows/2);
double angle = -50.0;
double scale = 0.6;
//通过上面的旋转细节信息求得旋转矩阵
rotMat = getRotationMatrix2D(center, angle, scale);
//旋转已缩放后的图像
warpAffine(dstImage_warp, dstImage_warp_rotate, rotMat, dstImage_warp.size());
//显示结果
imshow(WINDOW_NAME1, srcImage);
imshow(WINDOW_NAME2, dstImage_warp);
imshow(WINDOW_NAME3, dstImage_warp_rotate);
//等待用户按任意键退出程序
waitKey(0);
return 0;
}
//输出一些帮助信息
void ShowHelpText() {
//输出一些帮助信息
cout<<"\n\n\n\t 欢迎来到【仿射变换】示例程序"<<endl<<endl;
cout<<"\t当前使用的OpenCV版本为 OpenCV "<<CV_VERSION<<endl;
}
结果为:
##############################################################################