OpenCV开发笔记(七十五):相机标定矫正中使用remap重映射进行畸变矫正

本文主要是介绍OpenCV开发笔记(七十五):相机标定矫正中使用remap重映射进行畸变矫正,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

若该文为原创文章,转载请注明原文出处
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/136293833
各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究
红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中…(点击传送门)

OpenCV开发专栏(点击传送门)

上一篇:《OpenCV开发笔记(七十四):OpenCV3.4.1+ffmpeg3.4.8交叉编译移植到海思平台Hi35xx平台》
下一篇:持续补充中…


前言

  相机标定,重映射可以进行插值映射从而矫正图像,这是一种方法,也有矩阵映射方法,本篇使用重映射方式解说畸变矫正的计算原理。


Demo

  横向纵向区域固定拉伸:
  在这里插入图片描述

  横向纵向拉伸:
  在这里插入图片描述

  右下角拉伸:
  在这里插入图片描述


相机畸变矫正

  标定相机需要做两件事:

  • 纠正畸变的影响
  • 根据图像重构三位场景

纠正畸变的影响

  Opencv提供了可以直接使用的矫正算法,即通过输入原始图像和由函数cv::calibrateCamera()得到的畸变系数,生成校正后的图像。(注意:这里可使用用cv::undistort()使用该算法直接完成所需任务,也可以使用函数cv::iniitUndistorRectifyMap()和cv::remap()来更有效的处理。


矫正映射remap(畸变映射)

  当进行图像矫正时,必须指定输入图像的每个像素在输出图像中移动到的位置,成为“矫正映射”(畸变映射)。

双通道浮点数表示方式

  N x M的矩阵A中,重映射由双通道浮点数的N x M的矩阵B表示,对于图像A中的任意一点aPoint(i, j),映射为b1Point(i’, j’)和b2Point(i’, j’),在A中假设i=2,j=3,那么(假设重映射之后4.5,5.5)在B1中b1Point(i’, j’)值为4.5,b2Point(i’, j’)值为5.5,由于坐标是浮点数,那么需要插值得到整数位置以及中间过渡的区域颜色(平滑处理)。
  在这里插入图片描述

双矩阵浮点数表示方式

  双矩阵浮点数表示,N x M的矩阵A中,重映射由一对N x M的矩阵B和C描述,这里所有的N x M矩阵都是单通道浮点矩阵,在A中的点aPoint(i, j),重映射矩阵B中的点bPoint(i,j)存储了重映射后的i’ (映射后的i坐标), 重映射矩阵C中的点cPoint(i,j)存储了重映射后的j’(映射后的j坐标)。
  在这里插入图片描述

定点表示方式

  映射由双通道有符号整数矩阵(即CV_16SC2类型)表示。该方式与双通道浮点数表示方式相同,但使用此格式要快得多(笔者理解:由浮点数插值改为整数插值,会要快一些,但是肯定双通道浮点数的表示方式图像效果会稍微好一些)。
  在这里插入图片描述


remap核心关键

  在于得到插值的坐标系来映射新位置的x和y位置,要渐近等,所以本方法的核心关键在于得到标定后的矩阵,得到映射矩阵的方式可以自己写算法,也可以使用其他方式,后续文章继续深入这块。


函数原型

void remap( InputArray src,OutputArray dst,InputArray map1,InputArray map2,int interpolation,int borderMode = BORDER_CONSTANT,const Scalar& borderValue = Scalar());
  • 参数一:InputArray类型的src,一般为cv::Mat;
  • 参数二:OutputArray类型的dst,目标图像。它的大小与map1相同,类型与src相同。
  • 参数三:InputArray类型的map1,它有两种可能的表示对象:表示点(x,y)的第一个映射或者表示CV_16SC2 , CV_32FC1 或CV_32FC2类型的x值。
  • 参数四:InputArray类型的map2,它也有两种可能的表示对象,而且他是根据map1来确定表示哪种对象。若map1表示点(x,y)时,这个参数不代表任何值,否则,表示CV_16UC1 , rCV_32FC1类型的y值(第二个值)。
  • 参数五:int类型的interpolation,使用的插值方法;
  • 参数六:int类型的borderMode,边界处理方式;
  • 参数七:Scalar类型的borderValue,重映射后,离群点的背景,需要broderMode设置为BORDER_CONSTRANT时才有效。(离群点:当图片大小为400x300,那么对应的map1和map2范围为0399、0299,小于0或者大于299的则为离散点,使用该颜色填充);

Demo源码

void OpenCVManager::testRemap2()
{std::string srcFilePath = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/25.jpg";// 步骤一:读取文件cv::Mat srcMat = cv::imread(srcFilePath);// 缩放一下int width = 400;int height = 400;cv::resize(srcMat, srcMat, cv::Size(width, height));// 步骤二:映射矩阵cv::Mat mapX;cv::Mat mapY;mapX.create(srcMat.size(), CV_32FC1);mapY.create(srcMat.size(), CV_32FC1);// 算法:这里400x400,将0~100放大至0~200,将100~400映射为200~400// 算法:这里400x400,将0~100放大至0~200,将100~400映射为200~400
#if 0for(int row = 0; row < srcMat.rows; row++){for(int col = 0; col < srcMat.cols; col++){
//            if(true)if(col < 200){mapX.at<float>(row, col) = static_cast<float>(col * 1.0f / 2);}else{mapX.at<float>(row, col) = static_cast<float>(100 + (col - 200) * 1.0f / 2 * 3);}
//            if(true)if(row < 200){mapY.at<float>(row, col) = static_cast<float>(row * 1.0f / 2);}else{mapY.at<float>(row, col) = static_cast<float>(100 + (row - 200) * 1.0f / 2 * 3);}}}
#endif
#if 0for(int row = 0; row < srcMat.rows; row++){for(int col = 0; col < srcMat.cols; col++){// 这里是 0~200 缩放为 0~100 缩小                               // 比例系数if(col == 0){mapX.at<float>(row, col) = static_cast<float>(col);}else if(col < 200){mapX.at<float>(row, col) = static_cast<float>(col * 1.0f / 2 * (col * 1.0f / 199));}else{mapX.at<float>(row, col) = static_cast<float>(col * 1.0f / 2 * (col * 1.0f / 199));}if(row == 0){mapY.at<float>(row, col) = static_cast<float>(row);}else if(row < 200){mapY.at<float>(row, col) = static_cast<float>(row * 1.0f / 2 * (row * 1.0f / 199));}else{mapY.at<float>(row, col) = static_cast<float>(row * 1.0f / 2 * (row * 1.0f / 199));}}}
#endif
#if 1for(int row = 0; row < srcMat.rows; row++){for(int col = 0; col < srcMat.cols; col++){//                                                             比例系数 0~1.0(400~800)/400mapX.at<float>(row, col) = static_cast<float>(col * ((col + 1 + 400) * 1.0f / 800));mapY.at<float>(row, col) = static_cast<float>(row * ((row + 1 + 400) * 1.0f / 800));}}
#endifcv::Mat dstMat;cv::remap(srcMat,dstMat,mapX,mapY,CV_INTER_LINEAR,cv::BORDER_CONSTANT,cv::Scalar(255, 0, 0));cv::imshow("1", srcMat);cv::imshow(_windowTitle.toStdString(), dstMat);cv::waitKey(0);
}

对应工程模板v1.66.0

  在这里插入图片描述


上一篇:《OpenCV开发笔记(七十四):OpenCV3.4.1+ffmpeg3.4.8交叉编译移植到海思平台Hi35xx平台》
下一篇:持续补充中…


本文章博客地址:https://blog.csdn.net/qq21497936/article/details/136293833

这篇关于OpenCV开发笔记(七十五):相机标定矫正中使用remap重映射进行畸变矫正的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Linux join命令的使用及说明

《Linuxjoin命令的使用及说明》`join`命令用于在Linux中按字段将两个文件进行连接,类似于SQL的JOIN,它需要两个文件按用于匹配的字段排序,并且第一个文件的换行符必须是LF,`jo... 目录一. 基本语法二. 数据准备三. 指定文件的连接key四.-a输出指定文件的所有行五.-o指定输出

Linux jq命令的使用解读

《Linuxjq命令的使用解读》jq是一个强大的命令行工具,用于处理JSON数据,它可以用来查看、过滤、修改、格式化JSON数据,通过使用各种选项和过滤器,可以实现复杂的JSON处理任务... 目录一. 简介二. 选项2.1.2.2-c2.3-r2.4-R三. 字段提取3.1 普通字段3.2 数组字段四.

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

k8s按需创建PV和使用PVC详解

《k8s按需创建PV和使用PVC详解》Kubernetes中,PV和PVC用于管理持久存储,StorageClass实现动态PV分配,PVC声明存储需求并绑定PV,通过kubectl验证状态,注意回收... 目录1.按需创建 PV(使用 StorageClass)创建 StorageClass2.创建 PV

一文详解Python如何开发游戏

《一文详解Python如何开发游戏》Python是一种非常流行的编程语言,也可以用来开发游戏模组,:本文主要介绍Python如何开发游戏的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、python简介二、Python 开发 2D 游戏的优劣势优势缺点三、Python 开发 3D

Redis 基本数据类型和使用详解

《Redis基本数据类型和使用详解》String是Redis最基本的数据类型,一个键对应一个值,它的功能十分强大,可以存储字符串、整数、浮点数等多种数据格式,本文给大家介绍Redis基本数据类型和... 目录一、Redis 入门介绍二、Redis 的五大基本数据类型2.1 String 类型2.2 Hash

Redis中Hash从使用过程到原理说明

《Redis中Hash从使用过程到原理说明》RedisHash结构用于存储字段-值对,适合对象数据,支持HSET、HGET等命令,采用ziplist或hashtable编码,通过渐进式rehash优化... 目录一、开篇:Hash就像超市的货架二、Hash的基本使用1. 常用命令示例2. Java操作示例三