圖像拼接(七):OpenCV單應變換模型拼接多幅圖像

上篇博客圖像拼接(六):OpenCV單應變換模型拼接兩幅圖像 實現了兩幅圖像的拼接,主要是使用了單應矩陣和warpPerspective()這個庫函數。


#求取每相鄰兩幅圖像的單應矩陣
拼接多幅圖像,需要計算每相鄰兩幅圖像的單應矩陣,上篇已經封裝了求取單應矩陣的類,可以拿來用。
現有4幅圖像:img1img1img2img2img3img3img4img4。依次從右向左排列,拼接圖像以最左側的img4img4爲參考圖像。

Homography homo12(img1,img2);
Homography homo23(img2, img3);
Homography homo34(img3, img4);

Mat h12 = homo12.getHomography();
Mat h23 = homo23.getHomography();
Mat h34 = homo34.getHomography();

#透視變換

warpPerspective(InputArray src, OutputArray dst, InputArray M, Size dsize)

這個函數卻顯得不夠靈活。一方面,目標投影的圖像不能夠選取興趣區域,使得每拼接兩幅都會產生一個結果圖像;另一方面,投影參考原點爲圖像左上角,如果投影后的圖像在左方,就不能顯示出,所以需要左側的圖像爲參考圖像。

爲了實現方便,拼接4幅圖像以最左側圖像作爲參考(其實這種方案會產生累加誤差和圖像變形,最佳的方案應該是選擇中間幅作爲參考基準),計算其餘3幅圖像到它的變換。

	Mat h24 = h34*h23;
	Mat h14 = h24*h12;

	float scale_h24 = h24.at<double>(2, 2);
	float scale_h14 = h14.at<double>(2, 2);
	h24 = h24 / scale_h24;
	h14 = h14 / scale_h14;

	Mat warp1;
	warpPerspective(img1_color, warp1, h14, Size(img1.cols * 4, img1.rows));
	Mat warp2;
	warpPerspective(img2_color, warp2, h24, Size(img1.cols * 4, img1.rows));
	Mat warp3;
	warpPerspective(img3_color, warp3, h34, Size(img1.cols * 4, img1.rows));

投影結果:

img3->img4
這裏寫圖片描述

img2->img4
這裏寫圖片描述

img1->img4
這裏寫圖片描述

再把上述中間結果放在一個最終的結果圖像中。怎麼自動地確定水平方向剪切的邊界呢?我的一個解決思路是:根據單應矩陣的8個參數,提取出水平位移量。將單應變換公式展開

$\begin{bmatrix} x^\prime \ y^\prime \1 \end{bmatrix} =\begin{bmatrix} 1+h_{00} &h_{01} & h_{02} \ h_{10} & 1+h_{11}&h_{12} \h_{20}&h_{21}&1 \end{bmatrix} \begin{bmatrix} x \ y \1 \end{bmatrix} $

x=(1+h00)x+h01y+h02h20x+h21y+1x^\prime=\dfrac{(1+h_{00})x+h_{01}y+h_{02}}{h_{20}x+h_{21}y+1}

對不同的xxyy,位移量不同,不可能提取出一個統一的值。這裏採取粗略的方式,取x=0x=0y=0y=0時得出的xx^\prime值作爲水平位移量。因爲在邊緣剪容易出現黑色像素,所以再加上25\frac{2}{5}原始圖像寬度,選取大概靠圖像中間的位置作爲剪切位置。當然這個中間位置的計算是及其粗略的,但至少避開了邊緣。

剪切代碼

	int d = img1.cols*2/5;

	int x3 = h34.at<double>(0,2)+d;
	int x2 = x3 + h23.at<double>(0, 2);
	int x1 = x2 + h12.at<double>(0, 2);

	Mat canvas(img1.rows,img1.cols*4,CV_8UC3);
	img4_color.copyTo(canvas(Range::all(), Range(0, img1.cols)));
	warp3(Range::all(), Range(x3, x2)).copyTo(canvas(Range::all(), Range(x3, x2)));
	warp2(Range::all(), Range(x2, x1)).copyTo(canvas(Range::all(), Range(x2, x1)));
	warp1(Range::all(), Range(x1, x1 + img1.cols)).copyTo(canvas(Range::all(), Range(x1, x1 + img1.cols)));

最終結果

這裏寫圖片描述

由拼接結果可以看出,圖像越往右側,圖像變形越大,出現了明顯的拼接誤差,這個可以通過後期融合來消除。另一方面,由於接縫位置選擇不當,第4張圖像只顯示了一部分和出現了不對齊的情況,可以採用最佳拼接縫算法,使不對齊的情況最小化。本篇博客未實現接縫融合和最佳拼接縫尋找。


#完整代碼和圖像素材

#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();*/

	Mat h24 = h34*h23;
	Mat h14 = h24*h12;

	float scale_h24 = h24.at<double>(2, 2);
	float scale_h14 = h14.at<double>(2, 2);
	h24 = h24 / scale_h24;
	h14 = h14 / scale_h14;

	Mat warp1;
	warpPerspective(img1_color, warp1, h14, Size(img1.cols * 4, img1.rows));
	Mat warp2;
	warpPerspective(img2_color, warp2, h24, Size(img1.cols * 4, img1.rows));
	Mat warp3;
	warpPerspective(img3_color, warp3, h34, Size(img1.cols * 4, img1.rows));

	imshow("warp1", warp1);
	imshow("warp2",warp2);
	imshow("warp3", warp3);
	
	imwrite("warp1.jpg", warp1);
	imwrite("warp2.jpg", warp2);
	imwrite("warp3.jpg", warp3);

	int d = img1.cols*2/5;
	int x3 = h34.at<double>(0,2)+d;
	int x2 = x3 + h23.at<double>(0, 2);
	int x1 = x2 + h12.at<double>(0, 2);

	Mat canvas(img1.rows,img1.cols*4,CV_8UC3);
	img4_color.copyTo(canvas(Range::all(), Range(0, img1.cols)));
	warp3(Range::all(), Range(x3, x2)).copyTo(canvas(Range::all(), Range(x3, x2)));
	warp2(Range::all(), Range(x2, x1)).copyTo(canvas(Range::all(), Range(x2, x1)));
	warp1(Range::all(), Range(x1, x1 + img1.cols)).copyTo(canvas(Range::all(), Range(x1, x1 + img1.cols)));
	

	imwrite("canvas.jpg",canvas);
	imshow("canvas",canvas);

	waitKey(0);
	return 0;
}

img4
這裏寫圖片描述

img3
這裏寫圖片描述

img2
這裏寫圖片描述

img1
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章