理论
- 任何变换都可以以矩阵乘法(线性变换)的形式表示,然后是矢量加法(平移)。
- 从上面,我们可以使用仿射变换来表达:
- 旋转(线性变换)
- 转换(矢量加法)
- 比例运算(线性变换)
- 表示仿射变换的常用方法是使用2×3矩阵。
如何得到仿射变换?
- 我们提到仿射变换基本上是两个图像之间的关系。 关于这种关系的信息大致可以通过两种方式得出:
- 我们知道X和T,我们也知道它们是相关的。 然后我们的工作是找到M.
- 我们知道M和X.要获得T,我们只需要应用T =M⋅X。 我们对M的信息可以是明确的(即具有2乘3矩阵),或者它可以作为点之间的几何关系。
- 让我们解释一下(b)。 由于M涉及02图像,我们可以分析两个图像中三个点相关的最简单的情况。 请看下图:
- 点1,2和3(在图像1中形成三角形)被映射到图像2中,仍然形成三角形,但现在它们已经变得众所周知。 如果我们找到具有这3个点的仿射变换(您可以根据需要选择它们),那么我们可以将这个找到的关系应用于图像中的整个像素。
代码
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
using namespace cv;
using namespace std;
const char* source_window = "Source image";
const char* warp_window = "Warp";
const char* warp_rotate_window = "Warp + Rotate";
int main( int, char** argv )
{
Point2f srcTri[3];
Point2f dstTri[3];
Mat rot_mat( 2, 3, CV_32FC1 );
Mat warp_mat( 2, 3, CV_32FC1 );
Mat src, warp_dst, warp_rotate_dst;
src = imread( argv[1], 1 );
warp_dst = Mat::zeros( src.rows, src.cols, src.type() );
srcTri[0] = Point2f( 0,0 );
srcTri[1] = Point2f( src.cols - 1.f, 0 );
srcTri[2] = Point2f( 0, src.rows - 1.f );
dstTri[0] = Point2f( src.cols*0.0f, src.rows*0.33f );
dstTri[1] = Point2f( src.cols*0.85f, src.rows*0.25f );
dstTri[2] = Point2f( src.cols*0.15f, src.rows*0.7f );
warp_mat = getAffineTransform( srcTri, dstTri );
warpAffine( src, warp_dst, warp_mat, warp_dst.size() );
Point center = Point( warp_dst.cols/2, warp_dst.rows/2 );
double angle = -50.0;
double scale = 0.6;
rot_mat = getRotationMatrix2D( center, angle, scale );
warpAffine( warp_dst, warp_rotate_dst, rot_mat, warp_dst.size() );
namedWindow( source_window, WINDOW_AUTOSIZE );
imshow( source_window, src );
namedWindow( warp_window, WINDOW_AUTOSIZE );
imshow( warp_window, warp_dst );
namedWindow( warp_rotate_window, WINDOW_AUTOSIZE );
imshow( warp_rotate_window, warp_rotate_dst );
waitKey(0);
return 0;
}
解释
- 声明我们将使用的一些变量,例如用于存储结果的矩阵和用于存储定义我们的仿射变换的2D点的2个点阵列。
- 加载图片
- 将目标图像初始化为与源具有相同的大小和类型:
- 仿射变换:正如我们在上面解释的那样,我们需要两组3个点来推导仿射变换关系。 看一看:
- 使用两组点,我们使用OpenCV函数cv :: getAffineTransform计算仿射变换:
- 我们将刚刚找到的仿射变换应用于src图像
(1)src:输入图像
(2)warp_dst:输出图像
(3)warp_mat:仿射变换
(4)warp_dst.size():输出图像的所需大小
- 旋转:要旋转图像,我们需要知道两件事:
- 图像将旋转的中心
- 要旋转的角度。 在OpenCV中,正角度是逆时针的
- 可选:比例因子
- 我们使用OpenCV函数cv :: getRotationMatrix2D生成旋转矩阵,它返回一个2×3矩阵.
- 我们现在将找到的旋转应用于我们之前的Transformation的输出。
- 最后,我们将结果显示在两个窗口加上原始图像中以获得良好的衡量标准:
- 我们只需要等到用户退出程序
效果
- 在编译上面的代码之后,我们可以给它一个图像的路径作为参数。 例如,对于像这样的图片:
- 在应用第一个仿射变换后,我们获得:
- 最后,在应用负旋转(记住负方向顺时针)和比例因子后,我们得到: