阅读(454) (11)

OpenCV仿射变换

2017-09-15 09:59:26 更新

目标

在本教程中,您将学习如何:

理论

什么是仿射变换(Affine Transformations)?

  • 可以以矩阵乘法(线性变换)的形式表示的转换,后跟向量加法(转换)。
  • 从上面我们可以使用仿射变换表达:
  1. 旋转(线性变换)
  2. 翻译(矢量添加)
  3. 缩放操作(线性变换)

您可以看到,实质上,仿射变换表示两个图像之间的关系。

  • 代表仿射变换的通常方法是使用2×3矩阵。

OpenCV仿射变换

考虑到我们要使用A和B来转换2D矢量,我们可以这样做:

OpenCV仿射变换

我们如何获得仿射变换?

  • 我们提到仿射变换基本上是两个图像之间的关系。关于这种关系的信息大概可以通过两种方式来实现:
  1. 我们知道X和T,我们也知道它们是相关的。那么我们的任务就是找M
  2. 我们知道M和X。为了获得T,我们只需要计算T= M⋅ X。其中M的是确定的(i.e.即具有2乘3矩阵),或者它可以作为点之间的几何关系。
  • 让我们以更好的方式来解释(b)。由于M和下面图像2有关,因此我们可以分析两个图像中三个点相关的最简单的情况。看下图:

OpenCV仿射变换

点1,2和3(在图像1中形成三角形)被映射到图像2中,仍然形成三角形,但是现在它们已经变得众所周知。如果我们发现这3个点的仿射变换(你可以根据需要选择它们),那么我们可以将这个发现的关系应用于图像中的所有像素。

Code

  1. 这个程序是做什么的?加载图像对图像应用仿射变换。该变换是从三点之间的关系得到的。为此,我们使用函数cv :: warpAffine。转换后对图像进行旋转。该旋转相对于图像中心等待用户退出程序
  2. 本教程的代码如下所示。您也可以在这里下载

#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
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], IMREAD_COLOR );
  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;
}

说明

  • 声明我们将使用的一些变量,例如存储我们的结果的矩阵和2个点数组,以存储定义我们的Affine变换的2D点。
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());
  • 仿射变换:如上所述,我们需要两组3点来推导仿射变换关系。看一看:
srcTri [0] = Point2f(0,0);
srcTri [1] = Point2f(src.cols  -  1,0);
srcTri [2] = Point2f(0,src.rows  -  1);
dstTri [0] = Point2f(src.cols * 0.0,src.rows * 0.33);
dstTri [1] = Point2f(src.cols * 0.85,src.rows * 0.25);
dstTri [2] = Point2f(src.cols * 0.15,src.rows * 0.7);

您可能想要绘制这些要点,以便更好地了解其变化。它们的位置与示例图中所示的位置大致相同(在理论部分)。您可能会注意到由3点定义的三角形的大小和方向改变。

warp_mat = getAffineTransform(srcTri,dstTri);

我们得到一个2×3矩阵作为输出(在这种情况下为warp_mat

  • 然后我们将刚刚发现的仿射变换应用到src图像
warpAffine(src,warp_dst,warp_mat,warp_dst.size());

具有以下参数:

  1. src:输入图像
  2. warp_dst:输出图像
  3. warp_mat:仿射变换
  4. warp_dst.size():输出图像所需的大小

我们刚刚得到了我们的第一个转换的形象 我们会显示一下。在此之前,我们也想旋转它...

  • 旋转:要旋转图像,我们需要知道两件事情:

  1. 相对于图像将旋转的中心
  2. 要旋转的角度。在OpenCV中,正角度是逆时针方向
  3. 可选:比例因子

我们使用以下代码片段定义这些参数:

Point center = Point( warp_dst.cols/2, warp_dst.rows/2 );
double angle = -50.0;
double scale = 0.6;

  • 我们用OpenCV函数cv :: getRotationMatrix2D生成旋转矩阵,它返回一个2×3矩阵(在这种情况下为rot_mat

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);

结果

在编译上面的代码之后,我们可以给它一个图像的路径作为参数。例如,对于像:

OpenCV仿射变换

在应用第一个仿射变换后,我们得到:

OpenCV仿射变换

最后,在应用负旋转(记住负方向顺时针)和比例因子后,我们得到:

OpenCV仿射变换