上篇博客圖像拼接(六):OpenCV單應變換模型拼接兩幅圖像 實現了兩幅圖像的拼接,主要是使用了單應矩陣和warpPerspective()
這個庫函數。
#求取每相鄰兩幅圖像的單應矩陣
拼接多幅圖像,需要計算每相鄰兩幅圖像的單應矩陣,上篇已經封裝了求取單應矩陣的類,可以拿來用。
現有4幅圖像:,,,。依次從右向左排列,拼接圖像以最左側的爲參考圖像。
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} $
對不同的和,位移量不同,不可能提取出一個統一的值。這裏採取粗略的方式,取和時得出的值作爲水平位移量。因爲在邊緣剪容易出現黑色像素,所以再加上原始圖像寬度,選取大概靠圖像中間的位置作爲剪切位置。當然這個中間位置的計算是及其粗略的,但至少避開了邊緣。
剪切代碼
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