【图像处理OpenCV(C++版)】——3.2 几何变换之投影变换

news2025/1/14 18:27:53

前言

😊😊😊欢迎来到本博客😊😊😊

🌟🌟🌟 本专栏主要结合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是学习图像处理理论知识比较好的一个途径,大家也可以自己尝试写写博客,来记录大家平时学习的进度,可以和网上众多学者一起交流、探讨,有什么问题希望大家可以积极评论交流,我也会及时更新,来督促自己学习进度。希望大家觉得不错的可以点赞、关注、收藏。


🚶🚶🚶 今天的文章就到这里啦~
喜欢的话,点赞👍、收藏⭐️、关注💟哦 ~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/141310.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端高频vue面试题总结

created和mounted的区别 created:在模板渲染成html前调用&#xff0c;即通常初始化某些属性值&#xff0c;然后再渲染成视图。mounted:在模板渲染成html后调用&#xff0c;通常是初始化页面完成后&#xff0c;再对html的dom节点进行一些需要的操作。 如何从真实DOM到虚拟DOM …

springboot 全局 Date参数接收 String格式 转换异常报错

报错&#xff1a; .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type java.util.Date from String "2022-12-13 11:22:11": not a v…

C语言实现c++对象和私有成员

C语言实现c对象和私有成员 C语言实现c对象 类是C中面向对象编程思想中比较重要的组成部分&#xff0c;与结构体一样类只是一个模板只有在创建对象时才会申请内存空间&#xff0c;类其实是把具有共同特性的数据或方法&#xff08;面向对象编程中&#xff0c;一般把函数称为方法…

录屏软件哪个好用?10个免费好用的「录屏软件」推荐

想知道如何录制自己的流媒体视频吗&#xff1f;有几个选项可以让您免费录制流媒体视频&#xff1a;桌面屏幕录像机、在线工具、浏览器扩展、iOS 应用程序和 Android 应用程序。 查看下表以获取有关不同直播流媒体录像机的更多信息&#xff1a;它们的主要用途和运行的操作系统。…

基于YOLOV5的火灾检测系统(含模型)+GUI界面

基于YOLOV5的火灾检测系统 本期我们带来的内容是基于YOLOV5的火灾检测系统&#xff0c;火灾检测系统还是比较有实际意义的&#xff0c;也方便大家在背景描述中展开。废话不多说&#xff0c;还是先看效果。 完整代码下载地址:基于YOLOV5的火灾检测系统&#xff08;含模型&…

MySQL高级 SQL优化【limitcountupdate优化】

目录 1&#xff1a;SQL优化 1.1&#xff1a;limit优化 1.2&#xff1a;count优化 1.2.1&#xff1a;概述 1.2.2&#xff1a;count用法 1.3&#xff1a;update优化 1&#xff1a;SQL优化 1.1&#xff1a;limit优化 在数据量比较大时&#xff0c;如果进行limit分页查询&a…

拉伯配资|战略新兴产业火了,高增长低估值股曝光

导读&#xff1a;2022年&#xff0c;A股商场值得记录的前史性大事件不断。这一年&#xff0c;A股商场上市公司数量正式打破5000家&#xff0c;战略新兴工业上市公司数量打破2500家&#xff0c;占比初次打破50%大关。这一年&#xff0c;A股商场顶住杂乱的外部环境要素&#xff0…

一文读懂:什么是CRM?企业如何通过CRM盈利?

今天和大家分享一篇干货文章&#xff0c;主要探讨什么是CRM&#xff0c;用大白话解释企业究竟如何通过CRM盈利。文章有点长&#xff0c;但看完&#xff0c;相信你会有所收获。 一、什么是CRM CRM——客户关系管理系统&#xff0c;它不仅是一个系统&#xff0c;一个技术解决方案…

云原生安全系列 4:6个 Kubernetes 安全最佳实践

引言&#xff1a; Kubernetes为我们提供了一套很好的核心软件安全原则&#xff0c;但我们仍然需要理解并实施它们。对于 Kubernetes 集群分布式部署&#xff0c;攻击向量的数量也会增加&#xff0c;了解并尽可能限制这些攻击面的最佳实践非常重要。 即使在使用托管的 Kuberne…

以系统思维推进零信任架构演进

声明 本文是学习零信任数据动态授权桔皮书. 下载地址 http://github5.com/view/55013而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 以工程化思维推进零信任架构演进 前文所述零信任数据动态授权能力&#xff0c;是围绕数据本身的库、表、字段构建安…

光点数据中台能干啥?怎么做?用在哪?_光点科技

如今&#xff0c;数据营销已经成为许多公司数字化转型的必要选择。数据中台的支持是企业中任何高层次的业务应用不可或缺的。数据中心面向的公司业务不再是单一业务线&#xff0c;而是从公司整体角度审视业务全景&#xff0c;寻找可重用的沉淀能力。 今天&#xff0c;让我们来了…

一个 Qml MenuBar 的问题

基本情况 使用 QQuick.Control 中的 MenuBar 实现主菜单栏。菜单栏包括 File、Edit、View、Help 菜单项。点击菜单项&#xff0c;会弹出对应的菜单。 ApplicationWindow {id: windowwidth: 320height: 260visible: truemenuBar: MenuBar {Menu {title: qsTr("&File&…

List集合

首先看这个框架图&#xff1a; List集合代表一个元素有序&#xff0c;可重复的集合&#xff0c;集合中每个元素都有对应的顺序索引。List接口中增加了一些根据索引操作元素的方法&#xff1a; void add(int index,E element ) 在列表的指定位置插入该元素。 boolean addAll(in…

【数据结构】LinkedList模拟实现与简单使用

文章目录模拟实现LinkedListLinkedList的简单使用LinkedList的一些方法LinkedList的遍历简单对比一下LinkedList和ArrayList模拟实现LinkedList 在上一篇的博客中&#xff0c;我们讲解了链表的基础知识&#xff0c;并且模拟实现了一个无头单向不循环链表&#xff0c;链表的基础…

12.动态内存

文章目录动态内存12.1动态内存和智能指针12.1.1shared_ptr类make_shared函数shared_ptr的拷贝和赋值shared_ptr自动销毁所管理的对象shared_ptr还自动释放相关联的内存使用了动态生存期的资源的类12.1.2直接管理内存使用new动态分配和初始化对象动态分配的const对象内存耗尽指针…

Java集合类ArrayList应用 | 如何在字符串s1中删除有在字符串s2出现的字符?

目录 一、题干 二、题解 1. 思路 ArrayList实现 2. 代码 ArrayList实现 StringBuilder实现-1 StringBuilder实现-2 三、总结 一、题干 面试的编程题&#xff1a; s1: "welcome to Zhejiang" s2: "come" 要求输出从字符串s1删除s2中存在的字符之后…

如何更好使用markdown输出pdf

如何更好的使用markdown输出PDF 背景&#xff1a;当前很多人比较常用的 markdown 编辑方式是用vscode编写&#xff0c;再使用 vscode 中 的 MPE 的进行预览。有时候会出现这种情况&#xff1a;想要最终输出PDF给客户&#xff0c;但是通过 vscode 中的markdown 转 PDF插件生成 P…

云计算与云原生

如今是云时代&#xff0c;云计算&#xff0c;大数据&#xff0c;人工智能等新的名词在最近爆火。今天我们来了解一下&#xff0c;云计算与云原生。 在了解云原生之前&#xff0c;我们必须要了解云计算。 云计算 什么是云计算&#xff1f; 在了解云计算之前我们需要先了解一…

嵌入式实时操作系统的设计与开发(三)

基本调度机制 用户在基于RTOS开发应用前&#xff0c;首先要创建线程。 aCoral中&#xff0c;用户创建一个线程时须指定用户希望采用的调度策略&#xff0c;例如&#xff0c;用户想创建一个周期性执行的线程并希望通过周期来触发多线程的调度。 //创建一个周期性的线程 acoral…

视频播放中动画

CSS filter属性CSS的filter属性主要用于设置图像的视觉效果。语法&#xff1a;filter: none|blur()|brightness()|contrast()|drop-shadow()|grayscale()|hue-rotate()|invert()|opacity()|saturate()|sepia()|url();Filter 函数注意&#xff1a; 滤镜通常使用百分比 (如&#…