OpenCV 实现重新映射(53)

返回:OpenCV系列文章目录(持续更新中......)

上一篇:OpenCV 实现霍夫圆变换(52)
下一篇 :OpenCV实现仿射变换(54)

目标

在本教程中,您将学习如何:

一个。使用 OpenCV 函数 cv::remap 实现简单的重新映射例程。

理论

什么是重映射?

  • 它是从图像中的一个位置获取像素并将它们定位在新图像中的另一个位置的过程。
  • 为了完成映射过程,可能需要对非整数像素位置进行一些插值,因为源图像和目标图像之间并不总是存在一对一的像素对应关系。
  • 我们可以表示每个像素位置的重新映射(x,y)如:

      

    哪里g()是重新映射的图像,f()源图像和ℎ(x,y)是操作的映射函数(x,y).

  • 让我们举个简单的例子。想象一下,我们有一个图像我而且,比如说,我们想做一个重新映射,以便:

      

    会发生什么?很容易看出,图像会在x方向。例如,考虑输入图像:

观察红色圆圈相对于 x 如何改变位置(考虑到x水平方向):

  • 在 OpenCV 中,函数 cv::remap 提供了一个简单的重新映射实现。

C++代码
 

  • 这个程序是做什么的?
    • 加载图像
    • 每秒将 4 个不同的重新映射过程中的 1 个应用于图像,并在窗口中无限期地显示它们。
    • 等待用户退出程序
    • 教程代码如下所示。您也可以从这里下载
     
     
    #include "opencv2/imgcodecs.hpp"
    #include "opencv2/highgui.hpp"
    #include "opencv2/imgproc.hpp"
    #include <iostream>using namespace cv;void update_map( int &ind, Mat &map_x, Mat &map_y );int main(int argc, const char** argv)
    {CommandLineParser parser(argc, argv, "{@image |chicky_512.png|input image name}");std::string filename = parser.get<std::string>(0);Mat src = imread( samples::findFile( filename ), IMREAD_COLOR );if (src.empty()){std::cout << "Cannot read image: " << filename << std::endl;return -1;}Mat dst(src.size(), src.type());Mat map_x(src.size(), CV_32FC1);Mat map_y(src.size(), CV_32FC1);const char* remap_window = "Remap demo";namedWindow( remap_window, WINDOW_AUTOSIZE );int ind = 0;for(;;){update_map(ind, map_x, map_y);remap( src, dst, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0) );imshow( remap_window, dst );char c = (char)waitKey( 1000 );if( c == 27 ){break;}}return 0;
    }void update_map( int &ind, Mat &map_x, Mat &map_y )
    {for( int i = 0; i < map_x.rows; i++ ){for( int j = 0; j < map_x.cols; j++ ){switch( ind ){case 0:if( j > map_x.cols*0.25 && j < map_x.cols*0.75 && i > map_x.rows*0.25 && i < map_x.rows*0.75 ){map_x.at<float>(i, j) = 2*( j - map_x.cols*0.25f ) + 0.5f;map_y.at<float>(i, j) = 2*( i - map_x.rows*0.25f ) + 0.5f;}else{map_x.at<float>(i, j) = 0;map_y.at<float>(i, j) = 0;}break;case 1:map_x.at<float>(i, j) = (float)j;map_y.at<float>(i, j) = (float)(map_x.rows - i);break;case 2:map_x.at<float>(i, j) = (float)(map_x.cols - j);map_y.at<float>(i, j) = (float)i;break;case 3:map_x.at<float>(i, j) = (float)(map_x.cols - j);map_y.at<float>(i, j) = (float)(map_x.rows - i);break;default:break;} // end of switch}}ind = (ind+1) % 4;
    }

解释

  • 加载图像

     Mat src = imread( samples::findFile( filename ), IMREAD_COLOR );if (src.empty()){std::cout << "Cannot read image: " << filename << std::endl;return -1;}

  • 创建目标映像和两个映射矩阵(对于 x 和 y )

    Mat dst(src.size(), src.type());Mat map_x(src.size(), CV_32FC1);Mat map_y(src.size(), CV_32FC1);

  • 创建一个窗口以显示结果

     const char* remap_window = "Remap demo";namedWindow( remap_window, WINDOW_AUTOSIZE );

  • 建立循环。每隔 1000 毫秒,我们就会更新映射矩阵(mat_x 和 mat_y),并将它们应用于我们的源图像:

     int ind = 0;for(;;){update_map(ind, map_x, map_y);remap( src, dst, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0) );imshow( remap_window, dst );char c = (char)waitKey( 1000 );if( c == 27 ){break;}}

void update_map( int &ind, Mat &map_x, Mat &map_y )
{for( int i = 0; i < map_x.rows; i++ ){for( int j = 0; j < map_x.cols; j++ ){switch( ind ){case 0:if( j > map_x.cols*0.25 && j < map_x.cols*0.75 && i > map_x.rows*0.25 && i < map_x.rows*0.75 ){map_x.at<float>(i, j) = 2*( j - map_x.cols*0.25f ) + 0.5f;map_y.at<float>(i, j) = 2*( i - map_x.rows*0.25f ) + 0.5f;}else{map_x.at<float>(i, j) = 0;map_y.at<float>(i, j) = 0;}break;case 1:map_x.at<float>(i, j) = (float)j;map_y.at<float>(i, j) = (float)(map_x.rows - i);break;case 2:map_x.at<float>(i, j) = (float)(map_x.cols - j);map_y.at<float>(i, j) = (float)i;break;case 3:map_x.at<float>(i, j) = (float)(map_x.cols - j);map_y.at<float>(i, j) = (float)(map_x.rows - i);break;default:break;} // end of switch}}ind = (ind+1) % 4;
}

结果

  1. 编译上面的代码后,您可以执行它,并给出一个图像路径作为参数。例如,使用下图:

  1. 这是将其减小到一半大小并居中的结果:

  1. 把它颠倒过来:

  1. 在 x 方向上反映它

  1. 在两个方向上反映它:


参考文献:

1、《Remapping》------Ana Huamán

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里低代码引擎学习记录

官网 一、关于设计器 1、从设计器入手进行低代码开发 设计器就是我们用拖拉拽的方法&#xff0c;配合少量代码进行页面或者应用开发的在线工具。 阿里官方提供了以下八个不同类型的设计器Demo&#xff1a; 综合场景Demo&#xff08;各项能力相对完整&#xff0c;使用Fusion…

Gitea 上传用户签名

在 Gitea 的用户管理部分&#xff0c;有一个 SSH 和 GPG 的选项。 单击这个选项&#xff0c;可以在选项上添加 Key。 Key 的来源 如是 Windows 的用户&#xff0c;可以选择 Kleopatra 这个软件。 通过这个软件生成的 Key 的界面中有一个导出功能。 单击这个导出&#xff0c;…

区块链 | IPFS:Merkle DAG

&#x1f98a;原文&#xff1a;IPFS: Merkle DAG 数据结构 - 知乎 &#x1f98a;写在前面&#xff1a;本文属于搬运博客&#xff0c;自己留存学习。 1 Merkle DAG 的简介 Merkle DAG 是 IPFS 系统的核心概念之一。虽然 Merkle DAG 并不是由 IPFS 团队发明的&#xff0c;它来自…

【UnityRPG游戏制作】Unity_RPG项目_玩家逻辑相关

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;就业…

自动驾驶-第02课软件环境基础(ROSCMake)

1. 什么是ros 2. 为什么使用ros 3. ROS通信 3.1 Catkin编译系统

MT3608B 航天民芯代理 1.2Mhz 24V输入 升压转换器

深圳市润泽芯电子有限公司为航天民芯一级代理商 技术支持欢迎试样~Tel&#xff1a;18028786817 简述 MT3608B是恒定频率的6针SOT23电流模式升压转换器&#xff0c;用于小型、低功耗应用。MT3608B开关频率为1.2MHz&#xff0c;允许使用微小、低电平成本电容器和电感器高度不…

正版Office-Word使用时却提示无网络连接请检查你的网络设置 然后重试

这是购买电脑时自带的已经安装好的word。看纸箱外壳有office标记&#xff0c;但是好像没有印系列号。 某天要使用。提示&#xff1a;无网络连接请检查你的网络设置。 经过网上高手的提示&#xff1a; 说要勾选勾选ssl3.0、TLS1.0、1.1、1.2。 我的截图 我电脑进去就缺1.2. …

Go协程的底层原理(图文详解)

为什么要有协程 什么是进程 操作系统“程序”的最小单位进程用来占用内存空间进程相当于厂房&#xff0c;占用工厂空间 什么是线程 进程如果比作厂房&#xff0c;线程就是厂房里面的生产线&#xff1a; 每个进程可以有多个线程线程使用系统分配给进程的内存&#xff0c;线…

深度学习之基于Vgg16卷积神经网络印度交警手势识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 随着智能交通系统的不断发展&#xff0c;手势识别技术在其中扮演着越来越重要的角色。特别是在印度等…

Golang | Leetcode Golang题解之第58题最后一个单词的长度

题目&#xff1a; 题解&#xff1a; func lengthOfLastWord(s string) (ans int) {index : len(s) - 1for s[index] {index--}for index > 0 && s[index] ! {ansindex--}return }

AST原理(反混淆)

一、AST原理 jscode var a "\u0068\u0065\u006c\u006c\u006f\u002c\u0041\u0053\u0054";在上述代码中&#xff0c;a 是一个变量&#xff0c;它被赋值为一个由 Unicode 转义序列组成的字符串。Unicode 转义序列在 JavaScript 中以 \u 开头&#xff0c;后跟四个十六进…

JavaScript 如何理解柯里化函数结构及调用

文章目录 柯里化函数是什么逐步理解柯里化函数 柯里化函数是什么 柯里化&#xff08;Currying&#xff09;函数&#xff0c;又称部分求值&#xff0c;是一种函数转换技术。这种技术将一个接受多个参数的函数转换为一系列接受单一参数的函数。具体来说&#xff0c;一个柯里化的…

LWIP+TCP客户端

一、TCP API函数 其中tcp_poll()函数的第三个参数表示隔几秒调用一次这个周期性函数 二、修改服务器的IP 三、TCP客户端编程思路 申请套接字绑定服务器IP和端口号等待客户端连接 进入连接回调函数在连接回调函数中 配置一些回调函数&#xff0c;如接收回调函数&#xff0c;周期…

C语言 基本数据类型及大小

一、基本数据类型 1.整型int 整型的关键字是int&#xff0c;定义一个整型变量时&#xff0c;只需要用int来修饰即可。也分为短整型和长整型。 2.浮点型 浮点型又分单精度浮点型float和双精度浮点型double。 3.字符型char 前面的整型和浮点型都是用于存放数字。字符型&…

【python的魅力】:教你如何用几行代码实现文本语音识别

文章目录 引言一、运行效果二、文本转换为语音2.1 使用pyttsx32.2 使用SAPI实现文本转换语音2.3 使用 SpeechLib实现文本转换语音 三、语音转换为文本3.1 使用 PocketSphinx实现语音转换文本 引言 语音识别技术&#xff0c;也被称为自动语音识别&#xff0c;目标是以电脑自动将…

用LangChain打造一个可以管理日程的智能助手

存储设计定义工具创建llm提示词模板创建Agent执行总结 众所周知&#xff0c;GPT可以认为是一个离线的软件的&#xff0c;对于一些实时性有要求的功能是完全不行&#xff0c;比如实时信息检索&#xff0c;再比如我们今天要实现个一个日程管理的功能&#xff0c;这个功能你纯依赖…

Hdfs小文件治理策略以及治理经验

小文件是 Hadoop 集群运维中的常见挑战&#xff0c;尤其对于大规模运行的集群来说可谓至关重要。如果处理不好&#xff0c;可能会导致许多并发症。Hadoop集群本质是为了TB,PB规模的数据存储和计算因运而生的。为啥大数据开发都说小文件的治理重要&#xff0c;说HDFS 存储小文件…

Rust中的并发性:Sync 和 Send Traits

在并发的世界中&#xff0c;最常见的并发安全问题就是数据竞争&#xff0c;也就是两个线程同时对一个变量进行读写操作。但当你在 Safe Rust 中写出有数据竞争的代码时&#xff0c;编译器会直接拒绝编译。那么它是靠什么魔法做到的呢&#xff1f; 这就不得不谈 Send 和 Sync 这…

jupyter notebook 设置密码报错ModuleNotFoundError: No module named ‘notebook.auth‘

jupyter notebook 设置密码报错ModuleNotFoundError: No module named ‘notebook.auth‘ 原因是notebook新版本没有notebook.auth 直接输入以下命令即可设置密码 jupyter notebook password

ICode国际青少年编程竞赛- Python-1级训练场-基础训练1

ICode国际青少年编程竞赛- Python-1级训练场-基础训练1 1、 Dev.step(4)2、 Dev.step(-4) Dev.step(8)3、 Dev.turnLeft() Dev.step(4)4、 Dev.step(3) Dev.turnLeft() Dev.step(-1) Dev.step(4)5、 Dev.step(-1) Dev.step(3) Dev.step(-2) Dev.turnLeft() Dev.step(…