透视变换MATLAB仿真,OpenCV验证

问题:

image-20220311102049452

Matlab仿真

思路

将原图像的四个顶点分别左乘变换矩阵,并对其归一化。然后使用一个矩形将这个四边形框在内,矩形的选取规则为:左下顶点为所有xy的最小值,右上顶点为所有xy的最大值。

代码

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
% 原图像F的四个顶点坐标
x0y0 = [0;0;1];
xnyn = [640;480;1];
x0yn = [0;480;1];
xny0 = [640;0;1];
% 变换矩阵
A = [6.01 -1.65 537;0.188 2.88 554;0.000584 -0.000620 1];
% 计算透视变换后图像的四个顶点坐标
toushi_x0y0 = (A * x0y0) / (A(3,1) * x0y0(1,1) + A(3,2) * x0y0(2,1) + 1);
toushi_xnyn = (A * xnyn) / (A(3,1) * xnyn(1,1) + A(3,2) * xnyn(2,1) + 1);
toushi_x0yn = (A * x0yn) / (A(3,1) * x0yn(1,1) + A(3,2) * x0yn(2,1) + 1);
toushi_xny0 = (A * xny0) / (A(3,1) * xny0(1,1) + A(3,2) * xny0(2,1) + 1);

% 画图
figure(1);
% 原图像(黑色)
plot(x0y0(1,1),x0y0(2,1),'k*');hold on;
plot(x0yn(1,1),x0yn(2,1),'k*');hold on;
plot(xnyn(1,1),xnyn(2,1),'k*');hold on;
plot(xny0(1,1),xny0(2,1),'k*');hold on;
line([x0y0(1,1),x0yn(1,1)],[x0y0(2,1),x0yn(2,1)],'color','k');hold on;
line([x0yn(1,1),xnyn(1,1)],[x0yn(2,1),xnyn(2,1)],'color','k');hold on;
line([xnyn(1,1),xny0(1,1)],[xnyn(2,1),xny0(2,1)],'color','k');hold on;
line([xny0(1,1),x0y0(1,1)],[xny0(2,1),x0y0(2,1)],'color','k');hold on;

% 变换后图像(蓝色)
plot(toushi_x0y0(1,1),toushi_x0y0(2,1),'b*');hold on;
plot(toushi_x0yn(1,1),toushi_x0yn(2,1),'b*');hold on;
plot(toushi_xnyn(1,1),toushi_xnyn(2,1),'b*');hold on;
plot(toushi_xny0(1,1),toushi_xny0(2,1),'b*');hold on;
line([toushi_x0y0(1,1),toushi_x0yn(1,1)],[toushi_x0y0(2,1),toushi_x0yn(2,1)],'color','b');hold on;
line([toushi_x0yn(1,1),toushi_xnyn(1,1)],[toushi_x0yn(2,1),toushi_xnyn(2,1)],'color','b');hold on;
line([toushi_xnyn(1,1),toushi_xny0(1,1)],[toushi_xnyn(2,1),toushi_xny0(2,1)],'color','b');hold on;
line([toushi_xny0(1,1),toushi_x0y0(1,1)],[toushi_xny0(2,1),toushi_x0y0(2,1)],'color','b');hold on;

% 用一个矩形框起来(绿色)
all_x = [toushi_x0y0(1,1),toushi_x0yn(1,1),toushi_xny0(1,1),toushi_xnyn(1,1)];
all_y = [toushi_x0y0(2,1),toushi_x0yn(2,1),toushi_xny0(2,1),toushi_xnyn(2,1)];
new_x0y0 = [min(all_x);min(all_y)]
new_x0yn = [min(all_x);max(all_y)];
new_xnyn = [max(all_x);max(all_y)]
new_xny0 = [max(all_x);min(all_y)];
plot(new_x0y0(1,1),new_x0y0(2,1),'g*');hold on;
plot(new_x0yn(1,1),new_x0yn(2,1),'g*');hold on;
plot(new_xnyn(1,1),new_xnyn(2,1),'g*');hold on;
plot(new_xny0(1,1),new_xny0(2,1),'g*');hold on;
line([new_x0y0(1,1),new_x0yn(1,1)],[new_x0y0(2,1),new_x0yn(2,1)],'color','g');hold on;
line([new_x0yn(1,1),new_xnyn(1,1)],[new_x0yn(2,1),new_xnyn(2,1)],'color','g');hold on;
line([new_xnyn(1,1),new_xny0(1,1)],[new_xnyn(2,1),new_xny0(2,1)],'color','g');hold on;
line([new_xny0(1,1),new_x0y0(1,1)],[new_xny0(2,1),new_x0y0(2,1)],'color','g');hold on;

%输出G的尺寸
delta_x = max(all_x) - min(all_x)
delta_y = max(all_y) - min(all_y)

结果

运行代码,输出图像,下图中黑色矩形为原图像F,蓝色矩形为透视变换后的图像,绿色矩形是用矩形框起来的最终图像G:

image-20220308173653334

同时MATLAB控制台输出delta_x = 3.7003e+03delta_y = 2.2660e+03

也就是说经过透视变换后的图像G的尺寸为3700.3*2266

OpenCV验证

思路

随便选取一张图片,将其拉伸到640*480,再对其进行上述变换矩阵指定的透视变换,最后输出图片。

代码

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
33
34
35
36
37
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
string path = "res/test_image.jpg";
Mat img = imread(path);

// 伸缩图片至640*480
Mat imgResize;
resize(img, imgResize, Size(640,480));

// 变换前与变换后的顶点坐标
Point2f src[4] = { {0,0},{640,480},{0,480},{640,0} };
Point2f dst[4] = { {537.0f,554.0f},{3337.2f,1911.2f},{-363.0f,2756.8},{3190.8,490.9} };

//创建变换矩阵
Mat matrix = getPerspectiveTransform(src, dst);

// 透视变换
Mat imgWarp;
warpPerspective(imgResize, imgWarp, matrix, Size(4000, 3000));

// 标记原始图像位置
rectangle(imgWarp, Point(0, 0), Point(640, 480), Scalar(255, 255, 255), FILLED);
putText(imgWarp, "Origin Image", Point(100, 200), FONT_HERSHEY_COMPLEX, 2, Scalar(0, 69, 255),2);

// 输出变换后图像
imwrite("res/output.jpg", imgWarp);

waitKey(0);
}

输出结果

下图中左上角为原始图像(合并显示有点麻烦所以只画了一个跟原始图像一样大的白色矩形代替)

output