OpenCV开发笔记(七十五):相机标定矫正中使用remap重映射进行畸变矫正
前言
相机标定,重映射可以进行插值映射从而矫正图像,这是一种方法,也有矩阵映射方法,本篇使用重映射方式解说畸变矫正的计算原理。
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 0
for(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 0
for(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 1
for(int row = 0; row < srcMat.rows; row++)
{
for(int col = 0; col < srcMat.cols; col++)
{
// 比例系数 0~1.0(400~800)/400
mapX.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));
}
}
#endif
cv::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开发笔记(七十五):相机标定矫正中使用remap重映射进行畸变矫正的更多相关文章
- 树莓派开发笔记(十五):树莓派4B+从源码编译安装mysql数据库
前言 树莓派使用数据库时,优先选择sqlite数据库,但是sqlite是文件数据库同时仅针对于单用户的情况,考虑到多用户的情况,在树莓派上部署安装mysql服务,通过读写锁事务等使用,可以实现多进 ...
- .Net开发笔记(十五) 基于“泵”的TCP通讯(接上篇)
上一篇博客中说了基于“泵”的UDP通讯,附上了一个Demo,模拟飞鸽传书的功能,功能不太完善,主要是为了说明“泵”在编程中的应用.本篇文章我再附上一个关于TCP通讯的两个Demo,也都采用了“泵”模式 ...
- Java开发笔记(十五)短路逻辑运算的优势
前面提到逻辑运算只能操作布尔变量,这其实是不严谨的,因为经过Java编程实现,会发现“&”.“|”.“^”这几个逻辑符号竟然可以对数字进行运算.譬如下面的代码就直接对数字分别开展了“与”.“或 ...
- Android笔记(七十五) Android中的图片压缩
这几天在做图记的时候遇第一次遇到了OOM,好激动~~ 追究原因,是因为在ListView中加载的图片太大造成的,因为我使用的都是手机相机直接拍摄的照片,图片都比较大,所以在加载的时候会出现内存溢出,那 ...
- UWP开发入门(十五)——在FlipView中通过手势操作图片
本篇的最终目的,是模拟系统的照片APP可以左右滑动,缩放图片的操作.在实现的过程中,我们会逐步分析UWP编写UI的一些思路和技巧. 首先我们先实现一个横向的可以浏览图片的功能,也是大部分APP中的实现 ...
- OpenCV开发笔记(六十五):红胖子8分钟带你深入了解ORB特征点(图文并茂+浅显易懂+程序源码)
若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...
- OpenCV开发笔记(五十五):红胖子8分钟带你深入了解Haar、LBP特征以及级联分类器识别过程(图文并茂+浅显易懂+程序源码)
若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...
- OpenCV开发笔记(五十六):红胖子8分钟带你深入了解多种图形拟合逼近轮廓(图文并茂+浅显易懂+程序源码)
若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...
- OpenCV开发笔记(七十二):红胖子8分钟带你使用opencv+dnn+tensorFlow识别物体
前言 级联分类器的效果并不是很好,准确度相对深度学习较低,本章使用opencv通过tensorflow深度学习,检测已有模型的分类. Demo 可以猜测,1其实是人,18序号类是狗 ...
- OpenCV开发笔记(七十四):OpenCV3.4.1+ffmpeg3.4.8交叉编译移植到海思平台Hi35xx平台
前言 移植opencv到海思平台,opencv支持对视频进行解码,需要对应的ffmpeg支持. Ffmpeg的移植 Ffmpeg的移植请参考之前的文章:<FFmpeg开发笔记(十): ...
随机推荐
- [转帖]s3对象存储挂载到本地文件夹
https://www.zhangzhuo.ltd/articles/2021/10/22/1634888049032.html 一.s3fs工具 s3fs-fuse 是一个采用 c++ 开发的开源应 ...
- [转帖]VCSA6.7证书过期后的处置方法
0x00 环境说明 一台测试的ESXI主机,元旦之后已然发现证书已过期,具体现象:VCenter无法登录,一直提示输入用户名和密码,ESXI主机web页面无法登录.重启VC以后,报故障503错误. / ...
- [转帖]Full GC (Ergonomics) 产生的原因
发生Full GC,有很多种原因,不仅仅是只有Allocation Failure. 还有以下这么多: #include "precompiled.hpp" #include &q ...
- [转帖] Linux命令拾遗-剖析工具
https://www.cnblogs.com/codelogs/p/16060472.html 简介# 这是Linux命令拾遗系列的第五篇,本篇主要介绍Linux中常用的线程与内存剖析工具,以及更高 ...
- vue 路由守卫是否携带token
//整个实例出来 配置路由守卫 const router = new Router({ //这里面是路由配置哈 }) router.beforeEach((to, from, next) => ...
- [置顶] Gin--Begoo框架快速指南
Gin篇 第一篇: Gin-官方文档 第二篇: Gin-安装,修改启动端口,get/post 请求参数,模型绑定shouldbind,自定义验证器/表单验证 第三篇: Gin-上传文件/返回前端.中间 ...
- 靠谱:开源IM项目OpenIM压测程序介绍-自己动手测试性能和稳定性
压测前准备 (一)服务端配置调整 config/config.yaml 以8核16G为例 (1)openImMessagePort: [ 10130, 10131, 10132, 10133, 101 ...
- Unity的SpriteAtlas实践
我的环境 Unity引擎版本:Unity2019.3.7f1 AssetBundles-Browser 于2021-1-14拉取,github上最后提交日期是2019-12-14,在本文简称:ABBr ...
- 14.3 Socket 字符串分块传输
首先为什么要实行分块传输字符串,一般而言Socket套接字最长发送的字节数为8192字节,如果发送的字节超出了此范围则后续部分会被自动截断,此时将字符串进行分块传输将显得格外重要,分块传输的关键在于封 ...
- Python 实现进制转换与反汇编
通过利用反汇编库,并使用python编写工具,读取PE结构中的基地址偏移地址,找到OEP并计算成FOA文件偏移,使用反汇编库对其进行反汇编,并从反汇编代码里查找事先准备好的ROP绕过代码,让其自动完成 ...