在图像拼接(七):OpenCV单应变换模型拼接多幅图像 这篇博客中实现了用单应变换模型拼接多幅图像,图像拼接使用的OpenCV库函数warpPerspective()
。
因为这个函数只有在右侧图像变换到左侧图像时才能完整显示,所以拼接过程选择了以最左侧图像为参考帧。由于累加误差,最右侧的图像出现严重的变形。如下所示:
在这篇博客中,我们以中间幅图像为参考图像,实现多幅拼接。图像数量为4张,从左到右分别为 i m g 4 img4 img4, i m g 3 img3 img3, i m g 2 img2 img2, i m g 1 img1 img1。以左侧第2幅图像,即 i m g 3 img3 img3为参考图像。
从右到左,
i
m
g
2
img2
img2,
i
m
g
1
img1
img1的单应变形仍可直接使用warpPerspective()
,那么从左到右,
i
m
g
4
img4
img4到
i
m
g
3
img3
img3怎么办呢?
解决的办法是对
i
m
g
3
img3
img3添加一个向右距离为img3.width
的平移变换。这样变形后的
i
m
g
4
img4
img4即可显示在画布中了。
这个平移变换矩阵为:
T x = [ 1 0 i m g 3. c o l s 0 1 0 0 0 1 ] Tx=\begin{bmatrix} 1&0&img3.cols\\0&1&0\\0&0&1\end{bmatrix} Tx=⎣⎡100010img3.cols01⎦⎤
若
i
m
g
3
img3
img3 点坐标设为
X
′
X'
X′,
i
m
g
4
img4
img4点坐标设为
X
X
X,则他们之间的关系为:
T
x
∗
X
′
=
T
x
∗
H
43
∗
X
Tx*X'=Tx*H_{43}*X
Tx∗X′=Tx∗H43∗X
所以 i m g 4 img4 img4到 i m g 3 img3 img3变换后的图像如下,注意 i m g 3 img3 img3的原点向右位移至中间。在之后的拼接中要考虑这一点。
i
m
g
4
img4
img4->
i
m
g
3
img3
img3
其它图像之间的变换:
i
m
g
2
img2
img2->
i
m
g
3
img3
img3
i
m
g
1
img1
img1->
i
m
g
3
img3
img3
所有的4幅图像的拼接结果为:
整体观感相比图像拼接(七):OpenCV单应变换模型拼接多幅图像的结果提升不少,最右侧图像变形失真减轻。
代码
#include"Homography.h"
int main()
{
//从右向左升序
string imgPath1 = "trees_003.jpg";
string imgPath2 = "trees_002.jpg";
string imgPath3 = "trees_001.jpg";
string imgPath4 = "trees_000.jpg";
Mat img1 = imread(imgPath1, CV_LOAD_IMAGE_GRAYSCALE);
Mat img2 = imread(imgPath2, CV_LOAD_IMAGE_GRAYSCALE);
Mat img3 = imread(imgPath3, CV_LOAD_IMAGE_GRAYSCALE);
Mat img4 = imread(imgPath4, CV_LOAD_IMAGE_GRAYSCALE);
Mat img1_color = imread(imgPath1, CV_LOAD_IMAGE_COLOR);
Mat img2_color = imread(imgPath2, CV_LOAD_IMAGE_COLOR);
Mat img3_color = imread(imgPath3, CV_LOAD_IMAGE_COLOR);
Mat img4_color = imread(imgPath4, CV_LOAD_IMAGE_COLOR);
Homography homo12(img1, img2);
Homography homo23(img2, img3);
Homography homo34(img3, img4);
Mat h12 = homo12.getHomography();
Mat h23 = homo23.getHomography();
Mat h34 = homo34.getHomography();
/*homo12.drawMatches();
homo23.drawMatches();
homo34.drawMatches();*/
//以从左至右第2幅图像为参考,即img3
Mat h13 = h23*h12;
Mat h43 = h34.inv(DECOMP_LU);
double scale_h13 = h13.at<double>(2, 2);
h13 = h13 / scale_h13;
double scale_h43 = h43.at<double>(2, 2);
h43 = h43 / scale_h43;
Mat warp1;
warpPerspective(img1_color, warp1, h13, Size(img3.cols * 4, img3.rows));
Mat warp2;
warpPerspective(img2_color, warp2, h23, Size(img3.cols * 4, img3.rows));
Mat warp4;
Mat Tx = (Mat_<double>(3, 3) << 1, 0, img3.cols, 0, 1, 0, 0, 0, 1);
warpPerspective(img4_color, warp4, Tx*h43, Size(img3.cols * 2, img3.rows));
imshow("warp1", warp1);
imshow("warp2", warp2);
imshow("warp4", warp4);
imwrite("warp1.jpg", warp1);
imwrite("warp2.jpg", warp2);
imwrite("warp4.jpg", warp4);
int d = img3.cols * 2 / 5;
int x2 = h23.at<double>(0, 2) + d;
int x1 = x2 + h12.at<double>(0, 2);
Mat canvas(img3.rows, img3.cols * 4, CV_8UC3);
img3_color.copyTo(canvas(Range::all(), Range(img3.cols, img3.cols*2)));
warp2(Range::all(), Range(x2, x1)).copyTo(canvas(Range::all(), Range(x2 + img3.cols, x1 + img3.cols)));
warp1(Range::all(), Range(x1, x1+img3.cols)).copyTo(canvas(Range::all(), Range(x1 + img3.cols, x1+img3.cols*2)));
warp4(Range::all(), Range(0, img3.cols)).copyTo(canvas(Range::all(), Range(0, img3.cols)));
canvas(Range::all(), Range(x1 + img3.cols * 2, img3.cols * 4)) = Scalar::all(0);
imwrite("canvas.jpg", canvas);
imshow("canvas", canvas);
waitKey(0);
return 0;
}