【OpenCV】广告屏换图 文稿矫正 透视变换 C++ 案例实现

2023-11-23 18:50

本文主要是介绍【OpenCV】广告屏换图 文稿矫正 透视变换 C++ 案例实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

前言

一、透视变换的概念

二、透视变换工作原理

三、相关函数

1.findHomography函数

2.warpPerspective函数

3.getPerspectiveTransform函数

四、透视变换案例

案例1:文稿矫正

案例2:广告屏换图

五、总结


前言

本文以实现广告屏换图为目标,学习OpenCV透视变化相关知识,核心的内容包括:OpenCV透视变化概念透视变化工作原理,以及广告屏换图文稿矫正实现 

一、透视变换的概念

  • 仿射变换透视变换在图像还原、图像局部变化处理方面有重要意义
  • 通常情况下,在2D平面中,仿射变换的应用较多,在3D平面中,透视变换又有了自己的一席之地,两种变换原理相似,结果也类似,可针对不同的场合使用适当的变换
  • 仿射变换透视变换的数学原理不需深究,在应用层面,仿射变换是图像基于3个固定顶点的变换,如下图所示:

二、透视变换工作原理

  • 透视变换:本质是将图像投影到一个新的视平面
  • 其通用变换公式,如下图所示:

  • (u,v)为原始图像像素坐标,(x=x’/w’,y=y’/w’)为变换之后的图像像素坐标
  • 相关公式,本文不做过多阐述,图解如下:

  • 仿射变换:可以理解为透视变换的特殊形式
  • 数学表达式,如下图所示:

 

  • 所以,给定透视变换对应的四对像素点坐标,即可求得透视变换矩阵
  • 反之,给定透视变换矩阵,即可对图像或像素点坐标完成透视变换
  • 如下图所示:

 

三、相关函数

1.findHomography函数

📌函数功能:找到两个平面之间的变换矩阵

📌参数:如下表所示

2.warpPerspective函数

📌函数功能:对图像进行透视变换,变形

📌参数:

  • src – 输入图像
  • dst – 大小为dsize且类型与src相同的输出图像
  • M – 3×3变换矩阵
  • dsize – 输出图像的大小
  • 下图中红点即为固定顶点,在变换先后固定顶点的像素值不变
  • 图像整体则根据变换规则进行变换同理,透视变换是图像基于4个固定顶点的变换

3.getPerspectiveTransform函数

  • C++方式1:Mat getPerspectiveTransform(InputArray src, InputArray dst)  
  • C++方式2:Mat getPerspectiveTransform(const Point2f src[], const Point2f dst[]) 

📌函数功能:根据源图像和目标图像上的四对点坐标来计算从原图像透视变换到目标头像的透视变换矩阵

📌参数:

  • src – 源图像中四边形顶点的坐标    
  • dst – 目标图像中相应四边形顶点的坐标
  • 该函数计算透视变换的3乘3矩阵,以便实现以下计算:

四、透视变换案例

  • 首先,先试想一下,我们要将下图单独取一张平面出来(例如:A)该如何实现呢? 

案例1:文稿矫正

  • 这是本案例所用到的图片素材

  • 实现效果

  • 其他奇奇怪怪的东西的效果

案例1 完整代码

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;struct imagedata
{Mat img;//目标图像vector <Point2f> points;//3D点
};//鼠标操作函数:用于选择四个角的点(使用方法有顺序的,从左上角顺时针选择,选完之后回车)
void mouseHundle(int event,int x,int y,int flag,void *per)
{struct imagedata * d=(struct imagedata*)per;if(event==EVENT_LBUTTONDOWN){//确定按下的是鼠标左键//用圆形标记一下鼠标按下左键标记的位置circle(d->img,Point(x,y),3,Scalar(0,255,0),3,CV_AA);//在图上标记,圆心为点击的位置imshow("image",d->img);//原窗口上显示if(d->points.size()<4){d->points.push_back(Point2f(x,y));//把点击的点存起来}}
}void example_1()
{Mat image=imread("C:/Users/86177/Desktop/image/777.jpg");Mat result=Mat::zeros(400,500,CV_8UC1);//400*500的大小,但是里面没有东西//存放四个转换以后的坐标vector <Point2f>obj;obj.push_back(Point2f(0,0));obj.push_back(Point2f(500,0));obj.push_back(Point2f(500,400));obj.push_back(Point2f(0,400));//转换后的坐标imshow("image",image);struct imagedata data;data.img=image;setMouseCallback("image",mouseHundle,&data);//鼠标处理的回调函数waitKey(0);//按任意键关闭当前显示的窗口,显示下一个窗口Mat res=findHomography(data.points,obj,CV_RANSAC);//利用RANSAC算法计算出来一个小矩阵warpPerspective(image,result,res,result.size());  //结果转换imshow("result",result);waitKey(0);
}int main(int argc, char *argv[])
{example_1();return 0;
}

案例2:广告屏换图

  • 这是本案例所用到的图片素材 

  • 实现效果

  • 变换过程,如下图所示: 

案例2 完整代码

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;struct imagedata
{Mat img;//目标图像vector <Point2f> points;//3D点
};//鼠标操作函数:用于选择四个角的点(使用方法有顺序的,从左上角顺时针选择,选完之后回车)
void mouseHundle(int event,int x,int y,int flag,void *ptr)
{struct imagedata * d=(struct imagedata*)ptr;if(event==EVENT_LBUTTONDOWN){//确定按下的是鼠标左键//用圆形标记一下鼠标按下左键标记的位置circle(d->img,Point(x,y),3,Scalar(0,255,0),3,CV_AA);//在图上标记,圆心为点击的位置//imshow("image",d->img);//原窗口上显示imshow("dst",d->img);//原窗口上显示if(d->points.size()<4)//只存下来,最先点的前四个点{d->points.push_back(Point2f(x,y));//把鼠标操作点击的点存起来}}
}void example_2()
{Mat image1=imread("C:/Users/86177/Desktop/image/2222.jpg");//广告屏替换图片路径Mat image2=imread("C:/Users/86177/Desktop/image/city.jpg");//背景图片路径Mat dst=image2.clone();//克隆vector <Point2f>obj;obj.push_back(Point2f(0,0));obj.push_back(Point2f(image1.cols,0));obj.push_back(Point2f(image1.cols,image1.rows));obj.push_back(Point2f(0,image1.rows));//转换后的坐标imshow("dst",dst);struct imagedata data;data.img =dst;setMouseCallback("dst",mouseHundle,&data);//鼠标处理的回调函数waitKey(0);//按任意键关闭当前显示的窗口,显示下一个窗口Mat res=findHomography(obj,data.points,CV_RANSAC);//利用RANSAC算法,3*3变换矩阵warpPerspective(image1,dst,res,dst.size());//透视转换imshow("warpPerspective",dst);Point pts[4];for(int i=0;i<4;i++){pts[i]=data.points[i];}fillConvexPoly(image2,pts,4,Scalar(0),CV_AA);image2+=dst;imshow("final",image2);waitKey(0);
}int main(int argc, char *argv[])
{example_2();return 0;
}

PS: 案例代码使用说明,需要选择四个点,且必须顺时针选择,因为代码中只设置存储4个点,超出选择无效,选择完毕后按 Enter 即可显示效果

五、总结

  • 本文核心内容包括:OpenCV透视变化概念透视变化工作原理
  • 广告屏换图文稿矫正实现为例子,讲解了OpenCV透视变化的实战案例,案例完成的效果还是挺有意思的!
  • 在我们的日常生活中,已经有很多软件用到了我们的透视变换的知识,例如,我们办公扫描文件时用的扫描软件,很大程度上,节省了我们的时间,提高了工作效率!

以上就是本文的全部内容啦!如果对您有帮助,麻烦点赞啦!收藏啦!欢迎各位评论区留言!! !

这篇关于【OpenCV】广告屏换图 文稿矫正 透视变换 C++ 案例实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/420014

相关文章

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi