英文版原文链接
先修教程:Remapping(重映射)
下一教程:Histogram Equalization(直方图均衡化)
结果
- 在编译代码之后,我们可以将图像的路径作为参数提供给它。例如,像这样的图片:
- 应用第一个仿射变换后得到:
- 最后,在应用一个负的旋转(记住负的意思是顺时针)和一个比例因子之后,我们得到:
目标
此教程我们将学会:
- 使用OpenCV函数 cv::warpAffine 实现简单的重新映射例程。
- 使用OpenCV函数 cv::getRotationMatrix2D 获得一个 2×3 旋转矩阵
原理
什么是仿射变换?
1、以矩阵乘法(线性变换)和向量加法(平移变换)的形式表示的一种变换。
2、由上可知,我们可以用仿射变换来表示:
- a、旋转(线性变换)
- b、平移(向量加法)
- c、尺度变化(线性变换)
可以看到,在本质上,仿射变换表示两个图像之间的关系。
3、表示仿射变换的常用方法是使用一个2 ×
3的矩阵。
A
=
[
a
00
a
01
a
10
a
11
]
2
×
2
B
=
[
b
00
b
10
]
2
×
1
A = \begin{bmatrix} a_{00} & a_{01} \\ a_{10} & a_{11} \end{bmatrix}_{2 \times 2} B = \begin{bmatrix} b_{00} \\ b_{10} \end{bmatrix}_{2 \times 1}
A=[a00a10a01a11]2×2B=[b00b10]2×1
M
=
[
A
B
]
=
[
a
00
a
01
b
00
a
10
a
11
b
10
]
2
×
3
M = \begin{bmatrix} A & B \end{bmatrix} = \begin{bmatrix} a_{00} & a_{01} & b_{00} \\ a_{10} & a_{11} & b_{10} \end{bmatrix}_{2 \times 3}
M=[AB]=[a00a10a01a11b00b10]2×3
考虑到我们要变换一个二维向量
X
=
[
x
y
]
X = \begin{bmatrix}x \\ y\end{bmatrix}
X=[xy] ,通过使用A和B,我们也可以这样做:
T
=
A
⋅
[
x
y
]
+
B
T = A \cdot \begin{bmatrix}x \\ y\end{bmatrix} + B
T=A⋅[xy]+B 或者
T
=
M
⋅
[
x
,
y
,
1
]
T
T = M \cdot [x, y, 1]^{T}
T=M⋅[x,y,1]T
T
=
[
a
00
x
+
a
01
y
+
b
00
a
10
x
+
a
11
y
+
b
10
]
T = \begin{bmatrix} a_{00}x + a_{01}y + b_{00} \\ a_{10}x + a_{11}y + b_{10} \end{bmatrix}
T=[a00x+a01y+b00a10x+a11y+b10]
我们如何得到一个仿射变换?
1、我们提到过仿射变换基本上是两个像之间的关系。关于这种关系的信息大致可以通过两种方式获得:
- a、我们知道
X
X
X和
T
T
T,我们也知道它们是相关的。那么我们的任务就是找到
M
M
M
- b、我们知道
M
M
M和
X
X
X。为了得到
T
T
T,我们只需要应用
T
=
M
∗
X
T= M*X
T=M∗X。我们对于
M
M
M的信息可以是显式的(即具有2×3矩阵),也可以是点之间的几何关系。
2、让我们用更好的方式来解释(b)。因为
M
M
M与2张图像相关,我们可以分析它与两张图像中的3个点相关的最简单的情况。请看下图
点1、2和3(在图1中形成一个三角形)被映射到图2中,仍然形成一个三角形,但是现在它们已经显著地改变了。如果我们找到这3个点的仿射变换(您可以选择您喜欢的),那么我们可以将这个找到的关系应用到图像中的所有像素。
代码
这个程序是做什么的?
- 加载一张图像
- 对图像应用仿射变换。这个变换矩阵是由三个点之间的关系得到的。为此,我们使用了函数
cv::warpAffine
。 - 在仿射变换后对图像应用旋转。这个旋转是以图像中心为旋转点的。
- 等待,直到用户退出程序
代码如下所示:
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
int main( int argc, char** argv )
{
CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );
Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ) );
if( src.empty() )
{
cout << "Could not open or find the image!\n" << endl;
cout << "Usage: " << argv[0] << " <Input image>" << endl;
return -1;
}
Point2f srcTri[3];
srcTri[0] = Point2f( 0.f, 0.f );
srcTri[1] = Point2f( src.cols - 1.f, 0.f );
srcTri[2] = Point2f( 0.f, src.rows - 1.f );
Point2f dstTri[3];
dstTri[0] = Point2f( 0.f, 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 );
Mat warp_mat = getAffineTransform( srcTri, dstTri );
Mat warp_dst = Mat::zeros( src.rows, src.cols, src.type() );
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;
Mat rot_mat = getRotationMatrix2D( center, angle, scale );
Mat warp_rotate_dst;
warpAffine( warp_dst, warp_rotate_dst, rot_mat, warp_dst.size() );
imshow( "Source image", src );
imshow( "Warp", warp_dst );
imshow( "Warp + Rotate", warp_rotate_dst );
waitKey();
return 0;
}
解释
CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );
Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ) );
if( src.empty() )
{
cout << "Could not open or find the image!\n" << endl;
cout << "Usage: " << argv[0] << " <Input image>" << endl;
return -1;
}
- 仿射变换:正如我们在上面的行中所解释的,我们需要两组3个点的集合来导出仿射变换矩阵。看一看:
Point2f srcTri[3];
srcTri[0] = Point2f( 0.f, 0.f );
srcTri[1] = Point2f( src.cols - 1.f, 0.f );
srcTri[2] = Point2f( 0.f, src.rows - 1.f );
Point2f dstTri[3];
dstTri[0] = Point2f( 0.f, 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 );
你可以画出这些点来更好地了解它们是如何变化的。它们的位置与示例图(在原理部分)中描述的位置大致相同。您可能会注意到由3个点定义的三角形的大小和方向会发生变化。
- 利用这两组点,我们利用OpenCV函数 cv::getAffineTransform:
Mat warp_mat = getAffineTransform( srcTri, dstTri );
我们得到一个
2
×
3
2×3
2×3矩阵作为输出(在本例中为 warp_mat)
Mat warp_dst = Mat::zeros( src.rows, src.cols, src.type() );
warpAffine( src, warp_dst, warp_mat, warp_dst.size() );
有以下参数:
1)src:输入图像
2)warp_dst:输出图像
3)warp_mat:仿射变换
4) warp_dst.size():输出图像的期望大小
我们刚刚得到了我们的第一个变换图像!我们将在 1bit 中显示它。在那之前,我们还想旋转它…
- 旋转:要旋转一个图像,我们需要知道两件事:
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
2×3
2×3矩阵(在本例中为rot_mat)。
Mat rot_mat = getRotationMatrix2D( center, angle, scale );
Mat warp_rotate_dst;
warpAffine( warp_dst, warp_rotate_dst, rot_mat, warp_dst.size() );
imshow( "Source image", src );
imshow( "Warp", warp_dst );
imshow( "Warp + Rotate", warp_rotate_dst );
waitKey();
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)