基於棋盤格圖片拼接1

棋盤格拼接

傳統圖像拼接方式

目前有一個需求,需要根據背景圖即棋盤格圖進行前景圖的拼接。一般來講圖像的拼接技術採用以下步驟:以兩幅圖爲例

  1. 分別提取兩幅圖的Sift特徵點,sift 具有旋轉角度不變性,常用於圖像特徵點的提取。
  2. 計算特徵點周圍的描述子。也就是128爲的特徵向量。
  3. 根據特徵向量,查找匹配的點,可以用到Kd-tree進行查找。
  4. 對匹配的特徵點進行進一步篩選,可採用Ransac方法。
  5. 計算匹配後的兩組特徵點之間的透視變換矩陣即單應性矩陣H。
  6. 根據H矩陣,然後進行換算,進行拼接。

但是,這種拼接造成了圖像會發生變形,從而造成定位會發生偏差。爲了解決這個問題我們提出了兩個方案:

  1. 利用相機標定,先對相機進行標定,然後把棋盤格當中世界座標,通過多視角幾何模型的原則進行計算。
  2. 由於我們操作的場景比較特殊,相機兩次拍攝都在同一個軸水平軸上,不同的位置拍攝,相機相對於棋盤格平面是水平的。所以基於這個原則可以採樣最簡單,最暴力的方式,直接先糾正棋盤格到標準的,然後把對應的前景圖也按照進行糾正。最後因爲只知道兩個之間的差距,進行拼接。
    下面先採用第二種方式。

簡單模式

具體做法:

  1. 糾正棋盤格,
    由於鏡頭會略帶畸變,所以根據每個格子的4個角點進行透視變換到標準的格子中,透視變換我這裏就不多說了,會用到OpenCV的兩個函數
//獲得透視變換矩陣
Mat transform = getPerspectiveTransform(srcPts, dstPts);
//根據變換矩陣進行映射
cv::warpPerspective(roi, perspective, transform, Size(celw, celw), cv::INTER_LINEAR);

具體過程:

void extractAndRectify(Mat& src, Mat& dst, string filename) {
	
	//1.提取角點
	vector<Point2f> corners;
	bool ok = CameraCalibration::extract_corners(src, borderSize, corners);
	//加載文件中的
	//corners = loadPoints(filename);
	//2.創建輸出圖像大小
	dst.create(Size(celw * (borderSize.width-1), celw * (borderSize.height-1)), CV_8UC3);
	
	//3. 初次拼接
	for (size_t r = 0; r < borderSize.height - 1; r++)
	{
		for (size_t c = 0; c < borderSize.width - 1; c++)
		{

			{
				
				Point2f pt0 = corners[r * borderSize.width + c];
				Point2f pt1 = corners[r * borderSize.width + c + 1];
				Point2f pt2 = corners[(r + 1) * borderSize.width + c];
				Point2f pt3 = corners[(r + 1) * borderSize.width + c + 1];
				//計算roi區域
				auto itx = minmax({ pt0.x, pt1.x, pt2.x, pt3.x });
				auto ity = minmax({ pt0.y, pt1.y, pt2.y, pt3.y });
				float _width = abs(itx.first - itx.second);
				float _height = abs(ity.first - ity.second);
				Mat roi = src(Rect(itx.first, ity.first, _width, _height));
				//計算點在ROI區域中的座標位置
				vector<Point2f> srcPts, dstPts;
				srcPts.push_back(Point2f(pt0.x - itx.first, pt0.y - ity.first));
				srcPts.push_back(Point2f(pt1.x - itx.first, pt1.y - ity.first));
				srcPts.push_back(Point2f(pt2.x - itx.first, pt2.y - ity.first));
				srcPts.push_back(Point2f(pt3.x - itx.first, pt3.y - ity.first));

				//目標點的最表位置 celw 固定的寬度
				dstPts.push_back(Point2f(0, 0));
				dstPts.push_back(Point2f(celw, 0));
				dstPts.push_back(Point2f(0, celw));
				dstPts.push_back(Point2f(celw,celw));

				//計算變換矩陣H
				Mat transform = getPerspectiveTransform(srcPts, dstPts);
				//填充計算
				Mat perspective;
				cv::warpPerspective(roi, perspective, transform, Size(celw, celw), cv::INTER_LINEAR);

				int _r = r * celw;
				int _c = c * celw;


				for (size_t i = 0; i < celw; i++)
				{
					for (size_t j = 0; j < celw; j++)
					{
						dst.at<cv::Vec3b>(i+ _r, j + _c ) = perspective.at<Vec3b>(i, j);
					}
				}
			}
	}
}

  1. 同理也要對前景圖進行同樣的變換
    矯正之後的兩幅前景圖如下:

在這裏插入圖片描述
在這裏插入圖片描述

  1. 圖像拼接
    拼接的話,兩個圖像直接的偏差是已知的相差14個棋盤格,所以gap=14135 每個格子大小爲135135。
    左右圖拼接,還要考慮重疊區域像素的處理,這裏採用距離作爲權重,即:像素點座標距離原圖越近權重越大,越遠權重越小。公式爲:
 pix = w1* pix1 + w2 * pix2

具體計算方式:

void lrjoin(Mat& left, Mat& right,Mat& out, int gap) {
	gap = gap * celw;
	int rows = left.rows;
	int cols = left.cols + right.cols - gap;
	out.create(rows, cols, CV_8UC3);

	for (size_t r = 0; r < rows; r++)
	{
		Vec3b* t1 = out.ptr<Vec3b>(r);
		Vec3b* dl = left.ptr<Vec3b>(r); // 左圖
		Vec3b* dr = right.ptr<Vec3b>(r); // 右圖

		//左圖非重疊部分
		for (size_t c = 0; c < left.cols-gap; c++)
		{
			t1[c] = dl[c];
		}
		//右圖非重疊部分
		for (size_t c = gap; c < right.cols; c++)
		{
			t1[c + left.cols-gap ] = dr[c];
		}
		//左右重疊部分
		float w1, w2;
		for (size_t i = 0; i < gap; i++)
		{
			Vec3b& pl = dl[left.cols - gap + i];
			Vec3b& pr = dr[i];
			Vec3b& it = t1[left.cols - gap + i];
			w2 = (i + 1)*1.0 / gap; //右圖權重
			w1 = (gap - i - 1)*1.0 / gap; //左圖權重

			it[0] = static_cast<uchar>(w1 * pl[0] + w2 * pr[0]);
			it[1] = static_cast<uchar>(w1 * pl[1] + w2 * pr[1]);
			it[2] = static_cast<uchar>(w1 * pl[2] + w2 * pr[2]);
		}
	}
}

在這裏插入圖片描述
重疊區域爲空白部分。
拼接後顯示:

在這裏插入圖片描述

小結

這種方式,類使用相機的水平雙目標定,但是我們這裏只是爲了選擇圖像上的點,然後計算出該點在哪個對應的棋盤格內,對於有高度的作品,這種方式應該會有問題,根據相機成像原理,會存在一定的視場,作品的高度越高,這種差距應該越明顯,以後再測試有高度的作品。

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