[OpenCv]使用getPerspectiveTransform()函数实现Perspective Effect(透视效果)

一、Perspective Effect(透视效果)是什么?

  • 用过3Dmax 的同学,应该很清楚,在工作空间界面,一共有四个部分:俯视图、左视图、主视图、还有就是Perspective试图;在Perspective视图中,我们使用鼠标拖动模型,就可以换个角度看这个模型,这个效果是3D 的透视,我们使用OpenCv实现的2D 图片的透视效果和这个是差不多的;图1 是3Dmax的工作空间

    [OpenCv]使用getPerspectiveTransform()函数实现Perspective Effect(透视效果)_第1张图片


  • 我们实现的2D 图片的透视效果,如下面4张图展示。
    做gif动态图还是有些麻烦,所以只有将运行过程中的几张图片放出来,如果感兴趣,可以点击下载exe文件,运行查看效果,不过需要注意以下,运行的时候选择初始区域的四个点和结束区域的四个点的顺序必须是:左上、右上、左下、右下(这个和OPenCv中处理函数要求的数据相关或者点击查看展示视频。
    图 2:
    [OpenCv]使用getPerspectiveTransform()函数实现Perspective Effect(透视效果)_第2张图片

    图 3:
    [OpenCv]使用getPerspectiveTransform()函数实现Perspective Effect(透视效果)_第3张图片

    图 4:


    图 5:


二、代码实现思路

  1. 选择一张需要被透视的图片,在这个图片上面,我们需要选择透视的初始区域(展示window中显示的第一帧)、结束区域(展示window中显示的最后一帧),我们主要做的工作就是将从初始区域--->结束区域过程中每一帧展示到window中,如下图所示:
    [OpenCv]使用getPerspectiveTransform()函数实现Perspective Effect(透视效果)_第4张图片


  2. 如何将不规则的四边行区域变换称然后展示在一个长方形window中?如下图所示,就是变换的实现过程,是从右边展示window中的像素点对应到左边原图中选择区域(四边形)中的像素点;
    从右边对应到左边的原因有二:
    A.   左边这个不规则的四边行区域要遍历它很难;
    B.   如果左边的总的像素点少,那么要变换到右边展示窗口,
          右边窗口中一定会有像素点不会被赋值到左边的某个像素
          点的RGB值,这样在右边展示window中出现黑点

    [OpenCv]使用getPerspectiveTransform()函数实现Perspective Effect(透视效果)_第5张图片

    具体为什么变换中是通过 一个3*3的矩阵P进行变换?这个矩阵如何去求?请查看本人OpenCv类别中相应的博客。在这个程序的实现代码中,使用的是OpenCv中自带的函数 getPerspectiveTransform()去求的这个变换矩阵 P,详细内容可以看代码。


  3. 如何实现在原图中,将每个帧的四边形画出来?如第一部分中展示的效果图 2。实现原理图如下:
    [OpenCv]使用getPerspectiveTransform()函数实现Perspective Effect(透视效果)_第6张图片

  4. 讲了着么多的原理,最后上代码(注释应当足够让有一些OpenCv基础的同学看懂),这里就不详细的解释了,见谅!

    #include 
    #include 
    #include "opencv2/opencv.hpp"
    #include "opencv2/videoio.hpp"
    #include "opencv2/highgui.hpp"
    #include "opencv2/imgproc.hpp"
    
    using namespace std;
    using namespace cv;
    
    // 展示window的 宽 和 高
    int show_width = 400;
    int show_height = 500;
    
    // 用来记录现在获取了多少个点
    int pointNum = 0;  
    // 用来保存获取到的8个点的坐标
    vector vec_points;
    
    // 总共需要展示的帧的数量
    int frame_num = 80;
    
    // 设置为全局变量,可以在函数中使用
    Mat img ;
    char * window = "OriginalWindwo";
    
    //将透视过程封装成为函数
    void ProcessOfPerspective(Mat originalImg,int show_width, int show_height,int frame_num,
    	vector vec) {
    
    	// 起始地四个点
    	int s_ulx, s_uly, s_urx, s_ury, s_dlx, s_dly, s_drx, s_dry;
    	s_ulx = vec[0].x;
    	s_uly = vec[0].y;
    	s_urx = vec[1].x;
    	s_ury = vec[1].y;
    	s_dlx = vec[2].x;
    	s_dly = vec[2].y;
    	s_drx = vec[3].x;
    	s_dry = vec[3].y;
    
    
    	// 终止的四个点
    	int e_ulx, e_uly, e_urx, e_ury, e_dlx, e_dly, e_drx, e_dry;
    	e_ulx = vec[4].x;
    	e_uly = vec[4].y;
    	e_urx = vec[5].x;
    	e_ury = vec[5].y;
    	e_dlx = vec[6].x;
    	e_dly = vec[6].y;
    	e_drx = vec[7].x;
    	e_dry = vec[7].y;
    
    	int ulx_gap, uly_gap, urx_gap, ury_gap, dlx_gap, dly_gap, drx_gap, dry_gap;
    
    	// 计算每取一个帧 四个点需要移动的offset
    	ulx_gap = (e_ulx - s_ulx) / frame_num;
    	uly_gap = (e_uly - s_uly) / frame_num;  // up_left
    	urx_gap = (e_urx - s_urx) / frame_num;
    	ury_gap = (e_ury - s_ury) / frame_num;  // up_right
    	dlx_gap = (e_dlx - s_dlx) / frame_num;
    	dly_gap = (e_dly - s_dly) / frame_num;  // down_left
    	drx_gap = (e_drx - s_drx) / frame_num;
    	dry_gap = (e_dry - s_dry) / frame_num;  // down_right
    
    
    	// 变换过后窗口的四点的值
    	vector corners_trans(4);
    	corners_trans[0] = Point2f(0, 0);
    	corners_trans[1] = Point2f(show_width - 1, 0);
    	corners_trans[2] = Point2f(0, show_height - 1);
    	corners_trans[3] = Point2f(show_width - 1, show_height - 1);
    
    
    	// 创建一个 ‘Image’命名的窗口,用来展示图片
    	cv::namedWindow("resultImg", 1);
    
    	for (int i = 0; i < frame_num; i++)
    	{
    		// 将要被变换的矩阵的四点的值
    		vector corners(4);
    		corners[0] = Point2f(s_ulx + i*ulx_gap, s_uly + i*uly_gap);
    		corners[1] = Point2f(s_urx + i*urx_gap, s_ury + i*ury_gap);
    		corners[2] = Point2f(s_dlx + i*dlx_gap, s_dly + i*dly_gap);
    		corners[3] = Point2f(s_drx + i*drx_gap, s_dry + i*dry_gap);
    
    		line(img, corners[0], corners[1], cv::Scalar(0, 0, 255));
    		line(img, corners[0], corners[2], cv::Scalar(0, 0, 255));
    		line(img, corners[1], corners[3], cv::Scalar(0, 0, 255));
    		line(img, corners[2], corners[3], cv::Scalar(0, 0, 255));
    		imshow(window, img);
    
    		// 获取到变换调整矩阵
    		Mat transform = getPerspectiveTransform(corners_trans, corners);
    		cout << transform << endl;
    
    		vector ponits, points_trans;
    		for (int i = 0; i(i);
    
    			for (int j = 0; j(y);
    
    				// 复制到变换之后的图形之中
    				p[j * 3 + 0] = t[x * 3];
    				p[j * 3 + 1] = t[x * 3 + 1];
    				p[j * 3 + 2] = t[x * 3 + 2];
    
    				count++;
    			}
    		}
    
    		cout << "transform frame " << i << "successfully!!! " << endl;
    
    		cv::imshow("resultImg", img_trans);
    		cv::waitKey(30);
    
    	}
    	cv::destroyWindow("resultImg");
    	cv::waitKey(0);
    	return ;
    }
    
    
    void onMouse(int Event, int x, int y, int flags, void* param)
    {
    
    	// 左键松开响应事件
    	if (Event == 4&& pointNum<8) {
    		// 记录已经记录了一个点
    		pointNum++;
    
    		// 将点保存起来
    		Point2f p(x,y);
    		vec_points.push_back(p);
    
    
    		// 将点展示出来
    		circle(img, p, 2, cv::Scalar(0, 0, 225));
    		imshow(window, img);
    
    		// 展示出来用来测试
    		printf("( %d, %d) ", x, y);
    		printf("The Event is : %d ", Event);
    		printf("The flags is : %d ", flags);
    		printf("The param is : %d\n", param);
    	}
    }
    
    
    
    
    int main()
    {
    	//读取工程根目录下的图片 tt.jpg
    	img = imread("tt2.jpg");
    
    	namedWindow(window, WINDOW_NORMAL);
    	imshow(window,img);
    	setMouseCallback(window, onMouse, NULL);
    	waitKey(0);
    
    	while (true) {
    		if (vec_points.size() == 8) {
    
    			Mat originalImg = imread("tt2.jpg");
    			ProcessOfPerspective(originalImg, show_width, show_height, frame_num,
    				vec_points);
    			break;
    		}
    	}
    	return 0;
    }


三、备注

如有错误,还请你不吝指正,在此多谢。

如需交流,可私信新浪微博@IT技术N

 

           

你可能感兴趣的:(opencv)