**OpenCV: 单应矩阵的应用**

  • 例子1:透视矫正
  • 步骤:
  • 结果:
  • 代码:
  • 例子2:物体替换
  • 步骤:
  • 结果:
  • 代码:


在之前的文章中介绍过用OpenCV实现图像配准,主要包含兴趣点提取和利用单应矩阵配准两大方面。本文将主要介绍两个利用单应矩阵的应用例子。

opencv单应性变换bev opencv单应矩阵_Image


为了计算两幅图片之间的单应性,我们至少需要知道左边和右边两幅图片中4个对应点对的坐标信息(上图的黄,绿,红,橙分别表示四个对应的物理点的位置)。若我们有4个以上的对应点将更好。OpenCV可以非常准确的计算对应点对之间的单应矩阵。通常,这些对应点对是通过特征匹配算法SIFT,SURF等自动计算出来的,但在此我们将手动选取某些位置信息来检测OpenCV在单应矩阵上的应用效果。

例子1:透视矫正

opencv单应性变换bev opencv单应矩阵_opencv单应性变换bev_02

步骤:

上图中,我们想要选取书本中的红框区域进行选取并矫正,可以按照以下步骤实现透视矫正:

  1. 交互页面选取指定四个点来指定区域,将这些点称为pts_src;
  2. 提前设定校正后图像的高宽,比如设定为300*400,那么我们的目标点坐标(pts_dst)就是(0,0), (299,0), (299,399), (0,399);
  3. 利用pts_srcpts_dst计算单应矩阵;
  4. 将单应矩阵应用到原始图片上就可以得到期望的区域。

结果:

opencv单应性变换bev opencv单应矩阵_Image_03


以上是输入图片,红色点表示由鼠标选取的四个点,四个点的选取顺序是:左上角开始,顺时针选取。对选取区域透视矫正后的结果如下:

opencv单应性变换bev opencv单应矩阵_Image_04

代码:

//鼠标在图像中选区一个矩形,该矩形将被单独提取
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

struct userdata{
	Mat im;
	vector<Point2f> points;
};

void mouseHandler(int event, int x, int y, int flags, void* data_ptr)
{
	if (event == EVENT_LBUTTONDOWN)
	{
		userdata *data = ((userdata *)data_ptr);
		circle(data->im, Point(x, y), 3, Scalar(0, 0, 255), 5, CV_AA);//用圆圈标记出鼠标选点击的每一个点
		imshow("Image", data->im);
		if (data->points.size() < 4)
		{
			data->points.push_back(Point2f(x, y));//将鼠标选出的每一个点按照选取顺序push_back保存
		}
	}

}

int main(int argc, char** argv)
{

	// Read source image.
	Mat src = imread("src.jpg");
	int nrows = src.rows / 3;
	int ncols = src.cols / 3;
	Mat im_src(nrows, ncols, src.type());

	resize(src, im_src, im_src.size(), 0, 0, INTER_LINEAR);//对原始图片进行尺寸缩小
	// Destination image. The aspect ratio of the book is 3/4
	Size size(300, 400);
	Mat im_dst = Mat::zeros(size, CV_8UC3);


	// Create a vector of destination points.
	vector<Point2f> pts_dst;

	pts_dst.push_back(Point2f(0, 0));
	pts_dst.push_back(Point2f(size.width - 1, 0));
	pts_dst.push_back(Point2f(size.width - 1, size.height - 1));
	pts_dst.push_back(Point2f(0, size.height - 1));

	// Set data for mouse event
	Mat im_temp = im_src.clone();
	userdata data;
	data.im = im_temp;

	cout << "Click on the four corners of the book -- top left first and" << endl
		<< "bottom left last -- and then hit ENTER" << endl;

	// Show image and wait for 4 clicks. 
	imshow("Image", im_temp);
	// Set the callback function for any mouse event
	setMouseCallback("Image", mouseHandler, &data);
	waitKey(0);

	// Calculate the homography

Mat h = findHomography(data.points, pts_dst);

	// Warp source image to destination
	warpPerspective(im_src, im_dst, h, size);

	// Show image
	imwrite("选取结果.jpg", im_dst);
	//waitKey(0);

	return 0;
	}

例子2:物体替换

利用单应矩阵可以将例子1中的书本《Effective C++中文版》替换成其他的,可以用以下步骤实现:

步骤:

  1. 交互页面选取指定四个点来指定需要替换的物体所在的区域,将这些点称为pts_src;
  2. 待替换进去的物体宽高尺寸为w和h,那么pts_dst的坐标信息为(0,0),(w-1,0),(w-1,h-1), (0,h-1);
  3. 利用pts_srcpts_dst计算单应矩阵;
  4. 利用单应矩阵将待替换物体融合原始图片中;

结果:

opencv单应性变换bev opencv单应矩阵_opencv单应性变换bev_05


opencv单应性变换bev opencv单应矩阵_opencv_06


以上是原始图片和待替换进去的物体,原始图片中黄色的点是用鼠标选取的四个点,选取顺序:左上角开始,顺时针选取。对选区区域进行替换后的结果如下:

opencv单应性变换bev opencv单应矩阵_opencv单应性变换bev_07

代码:

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

struct userdata{
	Mat im;
	vector<Point2f> points;
};


void mouseHandler(int event, int x, int y, int flags, void* data_ptr)
{
	if (event == EVENT_LBUTTONDOWN)
	{
		userdata *data = ((userdata *)data_ptr);
		circle(data->im, Point(x, y), 3, Scalar(0, 255, 255), 5, CV_AA);
		imshow("Image", data->im);
		if (data->points.size() < 4)
		{
			data->points.push_back(Point2f(x, y));
		}
	}

}

int main(int argc, char** argv)
{

	// Read in the image.
	Mat src = imread("aligned5.jpg");

	int nrows = src.rows / 3;
	int ncols = src.cols / 3;
	Mat im_src(nrows, ncols, src.type());
	resize(src, im_src, im_src.size(), 0, 0, INTER_LINEAR);
	Size size = im_src.size();
	// Create a vector of points.
	vector<Point2f> pts_src;
	pts_src.push_back(Point2f(0, 0));
	pts_src.push_back(Point2f(size.width - 1, 0));
	pts_src.push_back(Point2f(size.width - 1, size.height - 1));
	pts_src.push_back(Point2f(0, size.height - 1));

	// Destination image
	Mat dst = imread("src2.jpg");
	Mat im_dst(dst.rows / 3, dst.cols / 3, dst.type());
	resize(dst, im_dst, im_dst.size(), 0, 0, INTER_LINEAR);
	// Set data for mouse handler
	Mat im_temp = im_dst.clone();
	userdata data;
	data.im = im_temp;
	
	//show the image
	imshow("Image", im_temp);
	imwrite("原始图.jpg", im_temp);
	cout << "Click on four corners(start from left-top to left-bottom) of a billboard and then press ENTER" << endl;
	//set the callback function for any mouse event
	setMouseCallback("Image", mouseHandler, &data);
	waitKey(0);

	// Calculate Homography between source and destination points
	Mat h = findHomography(pts_src, data.points);

	// Warp source image
	warpPerspective(im_src, im_temp, h, im_temp.size());

	// Extract four points from mouse data
	Point pts_dst[4];
	for (int i = 0; i < 4; i++)
	{
		pts_dst[i] = data.points[i];
	}
	
	// Black out polygonal area in destination image.
	fillConvexPoly(im_dst, pts_dst, 4, Scalar(0), CV_AA);

	// Add warped source image to destination image.
	im_dst = im_dst + im_temp;

	// Display image.
	imwrite("替换.jpg", im_dst);
	waitKey(0);

	return 0;
}