前言:
😊😊😊欢迎来到本博客😊😊😊
🌟🌟🌟 本专栏主要结合OpenCV和C++来实现一些基本的图像处理算法并详细解释各参数含义,适用于平时学习、工作快速查询等,随时更新。
😊😊😊 具体食用方式:可以点击本专栏【OpenCV快速查找(更新中)】–>搜索你要查询的算子名称或相关知识点,或者通过这篇博客👉通俗易懂OpenCV(C++版)详细教程——OpenCV函数快速查找(不断更新中)]查阅你想知道的知识,即可食用。
🎁🎁🎁支持:如果觉得博主的文章还不错或者您用得到的话,可以悄悄关注一下博主哈,如果三连收藏支持就更好啦!这就是给予我最大的支持!😙😙😙
文章目录
- 学习目标
- 一、投影变换原理
- 二、代码实现
- 三、 总结
 
学习目标
- 熟悉投影变换原理
- C++实现投影变换案例
一、投影变换原理
在对仿射变换的讨论中,校正物体都是在二维空间中完成的,如果物体在三维空间中发生了旋转,那么这种变换通常被称为投影变换。由于可能出现阴影或者遮挡,所以此投影变换是很难修正的。但是如果物体是平面的,那么就能通过二维投影变换对此物体三维变换进行模型化,这就是专用的二维投影变换,可由如下公式描述:
 
在OpenCV中,提供了函数:
getPerspectiveTransform(src,dst);
  计算投影变换矩阵,不同的是,这里需要输入四组对应的坐标变换,而不是三组,其中参数src代表原坐标,参数dst是与src相对应的变换后的坐标,返回值为3×3的投影矩阵。
对于该函数C++提供的API,提供了两个重载函数,与求仿射变换矩阵类似。
例:假设(0,0)、(200,0)、(0,200)、(200,200)是原坐标,通过某投影变换依次转换为(100,20)、(200,20)、(50,70)、(250,70)。
  第一种方式是将原位置坐标和对应的变换后的位置坐标分别保存在Point2f数组中,代码如下:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
int main() {
	//=========第一种方式
	//原坐标
	Point2f src[] = { Point2f(0,0),Point2f(200,0),Point2f(0,200),Point2f(200,200) };
	//经过投影变换后的坐标
	Point2f dst[] = { Point2f(100,20),Point2f(200,20),Point2f(50,70),Point2f(250,70) };
	Mat A=getPerspectiveTransform(src,dst);
	cout << "变换矩阵为:" << endl;
	cout << A << endl;
	return 0;
}
 
  注意:返回的投影矩阵的数据类型为CV_64F。
  
   第二种方式是将原位置坐标和对应的变换后的位置坐标分别保存在4×2的Mat中,每一行代表一个坐标,代码如下:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
int main() {
	//=========第二种方式
	Mat src = (Mat_<float>(4, 2) << 0, 0, 200, 0, 0, 200, 200, 200);//原坐标
	Mat dst = (Mat_<float>(4, 2) << 100, 20, 200, 20, 50, 70, 250, 70);//经过投影变换后的坐标
	Mat A = getPerspectiveTransform(src, dst);
	cout << "投影变换矩阵为:" << endl;
	cout << A << endl;
	return 0;
}
 
计算出投影变换矩阵后就可以完成图像的投影变换了,下面介绍在OpenCV中C++实现的投影变换函数案例:
二、代码实现
通过交互式的方式,利用OpenCV提供的鼠标事件,在原图和输出的画布上选取四组对应的坐标,然后计算投影变换矩阵完成图像的投影变换。代码如下:
//=========鼠标事件实现
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
Mat image;
Mat Pimage;
Point2f Ipoint, PIpoint;
int i = 0, j = 0;
Point2f src[4];//存储原坐标
Point2f dst[4];//存储变换后的坐标
//通过该事件,在原图中取四个坐标
void mouse_Image(int event, int x, int y, int flags, void *param) {
	
	switch (event)
	{
	case CV_EVENT_LBUTTONDOWN:
		Ipoint = Point2f(x, y);//记录坐标
		break;
	case CV_EVENT_LBUTTONUP:
		src[i] = Ipoint;
		circle(image,src[i],7,Scalar(0),3);//标记
		i += 1;
		break;
	default:
		break;
	}
}
//通过该事件,在输出的画布图中取对应的四个坐标
void mouse_PImage(int event, int x, int y, int flags, void *param) {
	switch (event)
	{
	case CV_EVENT_LBUTTONDOWN:
		PIpoint = Point2f(x, y);//记录坐标
		break;
	case CV_EVENT_LBUTTONUP:
		dst[j] = PIpoint;
		circle(Pimage, dst[j], 7, Scalar(0), 3);//标记
		j += 1;
		break;
	default:
		break;
	}
}
int main() {
	//输入原图
	image = imread("D:/VSCodeFile/OpenCV_CSDN/image/img.jpg", CV_LOAD_IMAGE_GRAYSCALE);
	if (!image.data)
	{
		return -1;
	}
	//输出原图
	Pimage = 255*Mat::ones(image.size(),CV_8UC1);
	//在原图窗口定义鼠标事件
	namedWindow("image", 1);
	setMouseCallback("image",mouse_Image,NULL);
	//在输出窗口定义鼠标事件
	namedWindow("Pimage", 1);
	setMouseCallback("Pimage", mouse_PImage, NULL);
	while (!(i==4 && j==4))
	{
		imshow("image", image);
		imshow("Pimage", Pimage);
		if (waitKey(50) == 'q')
		{
			break;
		}
	}
	imshow("image", image);
	imshow("Pimage", Pimage);
	//移除鼠标事件
	setMouseCallback("image", NULL , NULL);
	setMouseCallback("Pimage", NULL , NULL);
	//计算投影变换矩阵
	Mat A = getPerspectiveTransform(src, dst);
	//投影变换
	Mat result;
	warpPerspective(image, result, A, Pimage.size());
	imshow("投影后效果", result);
	waitKey(0);
	return 0;
}
 
利用以上程序校正上图(左)所示的三维图像,目的是能够看到图中上表面的正面,仿射变换是做不到的。运行程序时,首先,显示的是上图(左)的图像和上图(中)所示的画布;然后,利用鼠标在上图(左)中选取四个点,即黑色圆圈标注的点;接着,在画布上依次选取和在上图(左)中所选取的四个点对应的点,如上图(中)黑色圆圈标注的地方;最后,的输出图像上图(右)所示。
另:在该程序中利用了函数:
void circle(Mat & img,Point center,int radius,const Scalar & color,int thickness=1,int lineType=8,int shift=0)
  该函数用来在图中画圆,其中:img代表输入图像,center代表圆心,radius代表圆的半径,color代表画出的圆的颜色,thickness代表线的粗细,lineType代表线的类型。除了可以在图中画圆,OpenCV还提供了函数rectangle、ellipse、line分别用于在图中画矩形、椭圆形和线段这些基本的几何形状,其使用方法和circle类似,后续会有专栏更新相关内容。
三、 总结
最后,长话短说,大家看完就好好动手实践一下,切记不能三分钟热度、三天打鱼,两天晒网。OpenCV是学习图像处理理论知识比较好的一个途径,大家也可以自己尝试写写博客,来记录大家平时学习的进度,可以和网上众多学者一起交流、探讨,有什么问题希望大家可以积极评论交流,我也会及时更新,来督促自己学习进度。希望大家觉得不错的可以点赞、关注、收藏。
 



















