基于棋盘格图片拼接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]);
		}
	}
}

在这里插入图片描述
重叠区域为空白部分。
拼接后显示:

在这里插入图片描述

小结

这种方式,类使用相机的水平双目标定,但是我们这里只是为了选择图像上的点,然后计算出该点在哪个对应的棋盘格内,对于有高度的作品,这种方式应该会有问题,根据相机成像原理,会存在一定的视场,作品的高度越高,这种差距应该越明显,以后再测试有高度的作品。

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