1、仿射变换的原理
仿射变换是由平移、缩放、旋转、翻转和错切组合得到的,也称为三点变换。
仿射变换就是图像的旋转、平移和缩放操作的统称,可以表示为线性变换和平移变换的叠加。仿射变换的数学表示是先乘以一个线形变换矩阵再加上一个平移向量,其中线性变换矩阵为2×2的矩阵,平移向量为2×1的向量。
2、计算旋转矩阵
在OpenCV 4中并没有专门用于图像旋转的函数,而是通过图像的仿射变换实现图像的旋转。实现图像的旋转首先需要确定旋转角度和旋转中心,之后确定旋转矩阵,最终通过仿射变换实现图像旋转。针对这个过程,OpenCV 4提供了getRotationMatrix2D()函数用于计算旋转矩阵和warpAffine()函数用于实现图像的仿射变换。
Mat cv::getRotationMatrix2D (Point2f center,
double angle,
double scale
)
center:图像旋转的中心位置
angle:图像旋转的角度,单位为度,正值为逆时针旋转
scale:两个轴的比例因子,可以实现旋转过程中的图像缩放,不缩放输入1
该函数输入旋转角度和旋转中心,返回图像旋转矩阵,该返回值得数据类型为Mat类,是一个2×3的矩阵。
3、仿射变换
确定旋转矩阵后,通过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:仿射变换后输出图像,与src数据类型相同,但是尺寸与dsize相同
M:2×3的变换矩阵
dsize:输出图像的尺寸
flags:插值方法标志,可选参数及含义在表1和表2中给出
borderMode:像素边界外推方法的标志
borderValue:填充边界使用的数值,默认情况下为0
函数中第三个参数为前面求取的图像旋转矩阵。第四个参数是输出图像的尺寸。函数第五个参数是仿射变换插值方法的标志,表1。函数第六个参数为像素边界外推方法的标志,其可以的标志和对应的方法在表2中给出。第七个参数是外推标志选择BORDER_CONSTANT时的定值,默认情况下为0。
表1:图像仿射变换中的补充插值方法
表2:边界填充方法和对应标志
4、确定变换矩阵
仿射变换又称为三点变换,如果知道变换前后两张图像中三个像素点坐标的对应关系,就可以求得仿射变换中的变换矩阵,OpenCV 4提供了利用三个对应像素点来确定矩阵的函数getAffineTransform()。
Mat cv::getAffineTransform(const Point2f src[],
const Point2f dst[]
)
src[]:原图像中的三个像素坐标
dst[]:目标图像中的三个像素坐标
该函数两个输入量都是存放浮点坐标的数组,在生成数组的时候像素点的输入顺序无关,但是需要保证像素点的对应关系,函数的返回值是一个2×3的变换矩阵。
5、例子
#include <opencv2\opencv.hpp>
#include <iostream>
#include <vector>
using namespace std;
using namespace cv;
int main()
{
Mat img = imread("C:/opencv/1.bmp");
if (img.empty())
{
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
Mat rotation0, rotation1, img_warp0, img_warp1;
double angle = 30; //设置图像旋转的角度
Size dst_size(img.rows, img.cols); //设置输出图像的尺寸
Point2f center(img.rows / 2.0, img.cols / 2.0); //设置图像的旋转中心
rotation0 = getRotationMatrix2D(center, angle, 1); //计算放射变换矩阵
warpAffine(img, img_warp0, rotation0, dst_size); //进行仿射变换
imshow("img_warp0", img_warp0);
//根据定义的三个点进行仿射变换
Point2f src_points[3];
Point2f dst_points[3];
src_points[0] = Point2f(0, 0); //原始图像中的三个点
src_points[1] = Point2f(0, (float)(img.cols - 1));
src_points[2] = Point2f((float)(img.rows - 1), (float)(img.cols - 1));
//放射变换后图像中的三个点
dst_points[0] = Point2f((float)(img.rows)*0.11, (float)(img.cols)*0.20);
dst_points[1] = Point2f((float)(img.rows)*0.15, (float)(img.cols)*0.70);
dst_points[2] = Point2f((float)(img.rows)*0.81, (float)(img.cols)*0.85);
rotation1 = getAffineTransform(src_points, dst_points); //根据对应点求取仿射变换矩阵
warpAffine(img, img_warp1, rotation1, dst_size); //进行仿射变换
imshow("img_warp1", img_warp1);
waitKey(0);
return 0;
}