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

来源:互联网 发布:赵敏 知乎 编辑:程序博客网 时间:2024/04/30 17:30

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

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




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


    图 3:


    图 4:


    图 5:


二、代码实现思路

  1. 选择一张需要被透视的图片,在这个图片上面,我们需要选择透视的初始区域(展示window中显示的第一帧)、结束区域(展示window中显示的最后一帧),我们主要做的工作就是将从初始区域--->结束区域过程中每一帧展示到window中,如下图所示:



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



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


  3. 如何实现在原图中,将每个帧的四边形画出来?如第一部分中展示的效果图 2。实现原理图如下:


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

    #include <iostream>#include <stdio.h>#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<Point2f> 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<Point2f> 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;// 计算每取一个帧 四个点需要移动的offsetulx_gap = (e_ulx - s_ulx) / frame_num;uly_gap = (e_uly - s_uly) / frame_num;  // up_lefturx_gap = (e_urx - s_urx) / frame_num;ury_gap = (e_ury - s_ury) / frame_num;  // up_rightdlx_gap = (e_dlx - s_dlx) / frame_num;dly_gap = (e_dly - s_dly) / frame_num;  // down_leftdrx_gap = (e_drx - s_drx) / frame_num;dry_gap = (e_dry - s_dry) / frame_num;  // down_right// 变换过后窗口的四点的值vector<Point2f> 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<Point2f> 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<Point2f> ponits, points_trans;for (int i = 0; i<show_width; i++) {for (int j = 0; j<show_height; j++) {points_trans.push_back(Point2f(j, i));}}// ponits 中保存的是 points_trans变换过来的点perspectiveTransform(points_trans, ponits, transform);Mat img_trans = Mat::zeros(show_width, show_height, CV_8UC3);int count = 0;for (int i = 0; i<show_width; i++) {// 获取图像的一行 uchar* p = img_trans.ptr<uchar>(i);for (int j = 0; j<show_height; j++) {int y = ponits[count].y;int x = ponits[count].x;// 获取原图像中的一行uchar* t = originalImg.ptr<uchar>(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.jpgimg = 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

 

           
2 0
原创粉丝点击