仿射变换介绍
仿射变换是指在向量空间中进行一次线性变换(乘以一个矩阵)并加上一个平移(加上一个向量),变换为另一个向量空间的过程。在有限维的情况下,每个仿射变换可以由一个矩阵A和一个向量b给出,它可以写作A和一个附加的列b。一个仿射变换对应于一个矩阵和一个向量的乘法,而仿射变换的复合对应于普通的矩阵乘法,只要加入一个额外的行到矩阵的底下,这一行全部是0除了最右边是一个1,而列向量的底下要加上一个1.
Affine Transform描述了一种二维仿射变换的功能,它是一种二维坐标之间的线性变换,保持二维图形的“平直性”(即变换后直线还是直线,圆弧还是圆弧)和“平行性”(其实是保持二维图形间的相对位置关系不变,平行线还是平行线,而直线上的点位置顺序不变,另特别注意向量间夹角可能会发生变化)。仿射变换可以通过一系列的原子变换的复合来实现包括:平移(Translation)、缩放(Scale)、翻转(Flip)、旋转(Rotation)和错切(Shear).
事实上,仿射变换代表的是两幅图之间的关系,我们通常使用2x3矩阵来表示仿射变换如下:
考虑到我们要使用矩阵A和B对二维向量做变换,所以也能表示为下列形式:
或
得到如下结果:
三种常见形式:
- 旋转,rotation(线性变换)
- 平移,translation(向量加)
- 缩放,scale(线性变换)
现在有两幅图像(如下图),图像二是图像一经过放射变化得来的。那问题来了,我们怎么从这两个图像信息里挖掘出两图之间的映射关系?
很简单,只要在图像一种拿出三个点(1,2,3),图像二也拿出对应的三个点(1,2,3),就可以求出两图间的映射关系!
接下来用代码实现一下。
OpenCV通过两个函数的组合使用来实现仿射变换:
- 使用warpAffine来实现简单重映射
- 使用getRotationMatrix2D来获得旋转矩阵
warpAffine函数
void cv::warpAffine ( InputArray src,
OutputArray dst,
InputArray M,
Size dsize,
int flags = INTER_LINEAR,
int borderMode = BORDER_CONSTANT,
const Scalar & borderValue = Scalar()
)
参数解释
. src: 输入图像
. dst: 输出图像,尺寸由dsize指定,图像类型与原图像一致
. M: 2X3的变换矩阵
. dsize: 指定图像输出尺寸
. flags: 插值算法标识符,有默认值INTER_LINEAR,如果插值算法为WARP_INVERSE_MAP, warpAffine函数使用如下矩阵进行图像转换
getRotationMatrix2D函数
Mat cv::getRotationMatrix2D ( Point2f center,
double angle,
double scale
)
参数解释
. center: Point2f类型,表示原图像的旋转中心
. angle: double类型,表示图像旋转角度,角度为正则表示逆时针旋转,角度为负表示逆时针旋转(坐标原点是图像左上角)
. scale: 缩放系数
函数计算如下矩阵:
其中
那么好了,接下来我们就试试吧。
Opencv里有两个方法,一个是用三点来确定变换矩阵,一种是直接指定旋转的角度和比例来确定变换矩阵。我个人觉得用第二种比较方便与直观。
// opencv_day6.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include<iostream>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
//仿射变换实验
int main()
{
Mat src = imread("1.jpg");
Mat dst_warp, dst_warpRotateScale;
Point2f srcPoints[3];//原图中的三点
Point2f dstPoints[3];//目标图中的三点
//第一种仿射变换的调用方式:三点法
//三个点对的值,上面也说了,只要知道你想要变换后图的三个点的坐标,就可以实现仿射变换
srcPoints[0] = Point2f(0, 0);
srcPoints[1] = Point2f(0, src.rows - 1);
srcPoints[2] = Point2f(src.cols - 1, 0);
//映射后的三个坐标值
dstPoints[0] = Point2f(0, src.rows*0.3);
dstPoints[1] = Point2f(src.cols*0.25, src.rows*0.75);
dstPoints[2] = Point2f(src.cols*0.75, src.rows*0.25);
Mat M1 = getAffineTransform(srcPoints, dstPoints);//由三个点对计算变换矩阵
warpAffine(src, dst_warp, M1, src.size());//仿射变换
//第二种仿射变换的调用方式:直接指定角度和比例
//旋转加缩放
Point2f center(src.cols / 2, src.rows / 2);//旋转中心
double angle = 45;//逆时针旋转45度
double scale = 0.5;//缩放比例
Mat M2 = getRotationMatrix2D(center, angle, scale);//计算旋转加缩放的变换矩阵
warpAffine(src, dst_warpRotateScale, M2, src.size());//仿射变换
imshow("原始图", src);
imshow("仿射变换1", dst_warp);
imshow("仿射变换2", dst_warpRotateScale);
waitKey(0);
return 0;
}