OpenCV4入门系列教程37:仿射变换

索引地址:系列教程索引地址

上一篇:OpenCV4入门系列教程36:图像混合

几何操作

仿射变换是指在向量空间中进行一次线性变换(乘以一个矩阵)并加上一个平移(加上一个向量),变换为另一个向量空间的过程。在有限维的情况下,每个仿射变换可以由一个矩阵A和一个向量b给出,它可以写作A和一个附加的列b。一个仿射变换对应于一个矩阵和一个向量的乘法,而仿射变换的复合对应于普通的矩阵乘法,只要加入一个额外的行到矩阵的底下,这一行全部是0除了最右边是一个1,而列向量的底下要加上一个1。

Affine Transform描述了一种二维仿射变换的功能,它是一种二维坐标之间的线性变换,保持二维图形的“平直性”(即变换后直线还是直线,圆弧还是圆弧)和“平行性”(其实是保持二维图形间的相对位置关系不变,平行线还是平行线,而直线上的点位置顺序不变,另特别注意向量间夹角可能会发生变化)。仿射变换可以通过一系列的原子变换的复合来实现包括:平移(Translation)、缩放(Scale)、翻转(Flip)、旋转(Rotation)和错切(Shear).

本篇文章使用的是仿射变换函数。

利用opencv实现仿射变换一般会涉及到warpAffine和getRotationMatrix2D两个函数,其中warpAffine可以实现一些简单的重映射,而getRotationMatrix2D可以获得旋转矩阵。

函数原型:

1
2
3
4
5
6
7
8
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_LINER

平移

图像的平移也分为两步:首先定义好图像的平移矩阵,分别指定x方向和y方向上的平移量tx和ty,平移矩阵的形式如下:

M=[10tx01ty]M = \begin{bmatrix} 1 & 0 & t_{x} \\ 0 & 1 & t_{y} \end{bmatrix}

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <opencv2/opencv.hpp>

using namespace cv;

int main() {
cv::Mat src = cv::imread("1.jpg");
cv::Mat dst;

cv::Size dst_sz = src.size();

//定义平移矩阵
cv::Mat t_mat = cv::Mat::zeros(2, 3, CV_32FC1);

t_mat.at<float>(0, 0) = 1;
t_mat.at<float>(0, 2) = 20; //水平平移量tx
t_mat.at<float>(1, 1) = 1;
t_mat.at<float>(1, 2) = 10; //竖直平移量ty

//根据平移矩阵进行仿射变换
cv::warpAffine(src, dst, t_mat, dst_sz);

//显示平移效果
cv::imshow("image", src);
cv::imshow("result", dst);

cv::waitKey(0);

return 0;
}

测试效果如图:

move

镜像

在opencv2和opencv中,cv::flip()支持图像的翻转(上下翻转、左右翻转,以及同时均可)。

函数原型:

1
2
3
4
5
void cv::flip(
cv::InputArray src, // 输入图像
cv::OutputArray dst, // 输出
int flipCode = 0 // >0: 沿y-轴翻转, 0: 沿x-轴翻转, <0: x、y轴同时翻转
);

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <opencv2/opencv.hpp>
using namespace cv;

int main() {
cv::Mat image = cv::imread("1.jpg");

cv::Mat image_fliped;
cv::flip(image, image_fliped, -1);

cv::imshow("original", image);
cv::imshow("fliped", image_fliped);

cv::waitKey(0);
return 0;
}

上下翻转效果:

flip

旋转

图像的旋转具体实现分为两步:先根据旋转角度和旋转中心获取旋转矩阵;然后根据旋转矩阵进行仿射变换,即可实现任意角度和任意中心的旋转效果。旋转矩阵的形式如下:

M=[αβ(1α)center.xβcenter.yβαβcenter.x(1α)center.y]M = \begin{bmatrix} \alpha & \beta & (1-\alpha) center.x & \beta center.y \\ -\beta & \alpha & \beta center.x & (1-\alpha) center.y \end{bmatrix}

其中:

α=scale cosθ\alpha = scale \ cos{\theta}

β=scale sinθ\beta = scale \ sin{\theta}

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <opencv2/opencv.hpp>

using namespace cv;

int main() {
cv::Mat src = cv::imread("1.jpg");
cv::Mat dst;

//旋转角度
double angle = 45;

cv::Size src_sz = src.size();
cv::Size dst_sz(src_sz.height, src_sz.width);
int len = std::max(src.cols, src.rows);

//指定旋转中心
cv::Point2f center(len / 2., len / 2.);

//获取旋转矩阵(2x3矩阵)
cv::Mat rot_mat = cv::getRotationMatrix2D(center, angle, 1.0);

//根据旋转矩阵进行仿射变换
cv::warpAffine(src, dst, rot_mat, dst_sz);

//显示旋转效果
cv::imshow("image", src);
cv::imshow("result", dst);

cv::waitKey(0);

return 0;
}

测试效果:

rotate

缩放

函数原型:

1
2
3
void resize( InputArray src, OutputArray dst,
Size dsize, double fx = 0, double fy = 0,
int interpolation = INTER_LINEAR );

其中第一,二个参数是输入和输出的图像;

第三个参数为调整之后的图像尺寸;

第四个参数fx为x方向的缩放因子,若fx为0,fx = dsize.width/src.cols;

第五个参数fy为y方向的缩放因子,若fy为1,fy = dsize.height/src.rows;

第六个参数interpolation:这个是指定插值的方式,图像缩放之后,肯定像素要进行重新计算的,就靠这个参数来指定重新计算像素的方式,有以下几种:

  • INTER_NEAREST - 最邻近插值
  • INTER_LINEAR - 双线性插值,如果最后一个参数你不指定,默认使用这种方法
  • INTER_AREA - resampling using pixel area relation. It may be a preferred method for image decimation, as it gives moire’-free results. But when the image is zoomed, it is similar to the INTER_NEAREST method.
  • INTER_CUBIC - 4x4像素邻域内的双立方插值
  • INTER_LANCZOS4 - 8x8像素邻域内的Lanczos插值

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;

// 图像按比例缩放
///@ width 输入要缩放图像的宽度
void Resize(const Mat &srcImage, Mat &destImage, int width)
{
int ncols = width;
int nrows = srcImage.rows*width / srcImage.cols;
resize(srcImage, destImage, cv::Size(nrows, ncols));
}

int main()
{
//读入图像,并判断图像是否读入正确
cv::Mat srcImage = imread("1.jpg");
if (!srcImage.data)
return -1;
imshow("srcImage", srcImage);

//将图片按比例缩放至宽为250像素的大小
Mat destImage;
double angle = 9.9;//角度
Resize(srcImage, destImage, 400);
imshow("dst", destImage);

waitKey(0);
return 0;
}

测试效果:

resize

错切

图像的错切变换实际上是平面景物在投影平面上的非垂直投影效果。图像错切变换也称为图像剪切、错位或错移变换。图像错切的原理就是保持图像上各点的某一坐标不变,将另一个坐标进行线性变换,坐标不变的轴称为依赖轴,坐标变换的轴称为方向轴。图像错切一般分为两种情况:水平方向错切和垂直方向错切。

水平方向的错切,即沿x轴方向关于y的错切,即x=x0+c*y0, y=y0

若c>0,则沿+x轴方向错切,反之,如果c<0,则沿-x轴方向错切.

下一篇:OpenCV4入门系列教程38:线性变换