点击上方“3D视觉工坊”,选择“星标”

干货第一时间送达

3D视觉工坊的第52篇文章

最近在运行如下一段代码时,生成的mapx和mapy有点异常。

代码片段如下:

#include

#include"opencv.hpp"

using namespace std;

using namespace cv;

int  main(int argc, char ** argv)

{

if (argc < 2)

{

cout << " if(argc < 2)" << endl;

return -1;

}

string img_name = argv[1];

cv::Mat img = imread(img_name, CV_LOAD_IMAGE_UNCHANGED);

if (img.cols != 640)

{

resize(img, img, cv::Size(640, 480));

}

存在bug ***有明显突变位置

cv::Mat mK = cv::Mat::eye(3, 3, CV_32F);

mK.at(0, 0) = 274.135594f;

mK.at(1, 1) = 274.733768f;

mK.at(0, 2) = 324.330264f;

mK.at(1, 2) = 231.508914f;

cv::Mat mDistCoef = (Mat_(8, 1) << 0.195393, -0.113167, -0.000077, -0.000027, -0.003565, 0.529943, -0.133162, -0.022701);

Rect validPixROI;

double alpha = 0;

Size image_size(int(img.cols), int(img.rows));

Size new_image_size(image_size);

cout << "image_size" << endl << image_size << endl;

Mat mK2 = getOptimalNewCameraMatrix(mK, mDistCoef, image_size, alpha, new_image_size, &validPixROI);

Mat mapx, mapy;

initUndistortRectifyMap(mK, mDistCoef, Mat(),

mK2,new_image_size, CV_32F, mapx, mapy);

return 0;

}

程序中用到的图片示例如下图所示:

运行程序之后,生成的mapx和mapy,存在的较为明显的异常点位置bug如下图所示。

如果我们对mapx和mapy更进一步分析,如果统计相邻两元素差值的绝对值对于10或者该位置处的像素值低于两边或者高于两边,得到的mapx和mapy的异常点位置处如下图:

mapx存在的异常位置分布(白色区域为异常)

mapy存在的异常位置分布如下图(白色区域为异常)

放大了细看,如下图:

上述中的27.786低于两边,上述的最后一行29.453低于左边同时也低于右边。

根据以上,想与大家探讨两个问题。

1)为什么mapx和mapy矩阵会发生突变?

2)  如何有效地消除上述产生的突变?

相信学习视觉的小伙伴们对于畸变矫正时用到的函数initUndistortRectifyMap并不陌生,此处翻开OpenCV Documentation官网,查询了一下对于该函数的解释,截图如下:

将上述的介绍简单翻译成中文,如下:

函数原型:

CV_EXPORTS_W void initUndistortRectifyMap( InputArray cameraMatrix, InputArray distCoeffs,

InputArray R, InputArray newCameraMatrix,

Size size, int m1type, OutputArray map1, OutputArray map2 );

函数功能:计算无畸变和修正转换映射。

函数说明:

这个函数主要用于计算无畸变和修正转换关系,为了重映射,将结果以映射的形式表达。无畸变的图像看起来就像原始的图像,就好比这个图像是用内参为newCameraMatrix且无畸变的相机采集得到的。

在单目相机的情况下,newCameraMatrix通常等于cameraMatrix,或者可以通过cv::getOptimalNewCameraMatrix来计算,以便更好地控制缩放。

在双目立体相机的情况下,newCameraMatrix通常设置为由cv::stereoRectify计算的P1或P2。

此外,根据矩阵R,新相机在坐标空间中的取向也是不同的。例如,它帮助配准双目相机的两个相机方向,从而使得图像的极线是水平的,且y坐标相同。

该函数实际上为反向映射算法构建映射,供反向映射使用。也就是说,对于在已经修正畸变的图像中的每个像素(u,v),该函数计算原来图像(从相机中获得的原始图像)中对应的坐标系。这个过程是这样的,见上述OpenCV Documentation中的计算公式。

在双目相机的例子中,这个函数被调用两次:一次是为了确定每个相机的朝向,经过stereoRectify之后,依次调用cv::stereoCalibrate。但是如果双目立体相机没有被标定,依然可以使用cv::stereoRectifyUncalibrated直接从单应性矩阵H中计算修正变换。对每个相机,函数计算像素域中的单应性矩阵H作为修正变换,而不是3D空间中的旋转矩阵R。R可以通过H 矩阵计算得来:

我们翻出OpenCV3.2.0中关于OpenCV中的initUndistortRectifyMap函数源码,重新命名为一个函数,代入原工程中,分析存在异常的原因。

首先,我们先看一下initUndistortRectifyMap函数在OpenCV3.2.0版本中的源码(稍作了修改,并添加了一点注释),如下:

void initUndistortRectifyMap(cv::Mat _cameraMatrix, cv::Mat _distCoeffs, cv::Mat _matR, cv::Mat _newCameraMatrix,Size size, int m1type, cv::Mat &_map1, cv::Mat &_map2)

{

Mat cameraMatrix = _cameraMatrix.clone(), distCoeffs = _distCoeffs.clone();

Mat matR = _matR.clone(), newCameraMatrix = _newCameraMatrix.clone();

if (m1type <= 0)

m1type = CV_16SC2;

CV_Assert(m1type == CV_16SC2 || m1type == CV_32FC1 || m1type == CV_32FC2);

_map1.create(size, m1type);

Mat map1 = _map1.clone(), map2;

if (m1type != CV_32FC2)

{

_map2.create(size, m1type == CV_16SC2 ? CV_16UC1 : CV_32FC1);

map2 = _map2.clone();

}

else

_map2.release();

Mat_ R = Mat_::eye(3, 3);

Mat_ A = Mat_(cameraMatrix), Ar;

if (!newCameraMatrix.empty())

Ar = Mat_(newCameraMatrix);

else

Ar = getDefaultNewCameraMatrix(A, size, true);

if (!matR.empty())

R = Mat_(matR);

if (!distCoeffs.empty())

distCoeffs = Mat_(distCoeffs);

else

{

distCoeffs.create(14, 1, CV_64F);

distCoeffs = 0.;

}

CV_Assert(A.size() == Size(3, 3) && A.size() == R.size());

CV_Assert(Ar.size() == Size(3, 3) || Ar.size() == Size(4, 3));

Mat_ iR = (Ar.colRange(0, 3)*R).inv(DECOMP_LU); //LU分解求逆,矩阵求逆共有「LU,cholesky,eig以及SVD」

const double* ir = &iR(0, 0);

double u0 = A(0, 2), v0 = A(1, 2);

double fx = A(0, 0), fy = A(1, 1);

CV_Assert(distCoeffs.size() == Size(1, 4) || distCoeffs.size() == Size(4, 1) ||

distCoeffs.size() == Size(1, 5) || distCoeffs.size() == Size(5, 1) ||

distCoeffs.size() == Size(1, 8) || distCoeffs.size() == Size(8, 1) ||

distCoeffs.size() == Size(1, 12) || distCoeffs.size() == Size(12, 1) ||

distCoeffs.size() == Size(1, 14) || distCoeffs.size() == Size(14, 1));

if (distCoeffs.rows != 1 && !distCoeffs.isContinuous())

distCoeffs = distCoeffs.t();

const double* const distPtr = distCoeffs.ptr();

double k1 = distPtr[0];

double k2 = distPtr[1];

double p1 = distPtr[2];

double p2 = distPtr[3];

double k3 = distCoeffs.cols + distCoeffs.rows - 1 >= 5 ? distPtr[4] : 0.;

double k4 = distCoeffs.cols + distCoeffs.rows - 1 >= 8 ? distPtr[5] : 0.;

double k5 = distCoeffs.cols + distCoeffs.rows - 1 >= 8 ? distPtr[6] : 0.;

double k6 = distCoeffs.cols + distCoeffs.rows - 1 >= 8 ? distPtr[7] : 0.;

double s1 = distCoeffs.cols + distCoeffs.rows - 1 >= 12 ? distPtr[8] : 0.;

double s2 = distCoeffs.cols + distCoeffs.rows - 1 >= 12 ? distPtr[9] : 0.;

double s3 = distCoeffs.cols + distCoeffs.rows - 1 >= 12 ? distPtr[10] : 0.;

double s4 = distCoeffs.cols + distCoeffs.rows - 1 >= 12 ? distPtr[11] : 0.;

double tauX = distCoeffs.cols + distCoeffs.rows - 1 >= 14 ? distPtr[12] : 0.;

double tauY = distCoeffs.cols + distCoeffs.rows - 1 >= 14 ? distPtr[13] : 0.;

// Matrix for trapezoidal distortion of tilted image sensor

//倾斜图像传感器的梯形畸变矩阵

cv::Matx33d matTilt = cv::Matx33d::eye();

cv::detail::computeTiltProjectionMatrix(tauX, tauY, &matTilt);

for (int i = 0; i < size.height; i++)

{

float* m1f = map1.ptr(i);//指向第i+1行第一个元素指针

float* m2f = map2.empty() ? 0 : map2.ptr(i);

short* m1 = (short*)m1f;

ushort* m2 = (ushort*)m2f;

double _x = i*ir[1] + ir[2], _y = i*ir[4] + ir[5], _w = i*ir[7] + ir[8];

for (int j = 0; j < size.width; j++, _x += ir[0], _y += ir[3], _w += ir[6])

{

double w = 1. / _w, x = _x*w, y = _y*w;

double x2 = x*x, y2 = y*y;

double r2 = x2 + y2, _2xy = 2 * x*y;

double kr = (1 + ((k3*r2 + k2)*r2 + k1)*r2) / (1 + ((k6*r2 + k5)*r2 + k4)*r2);

double xd = (x*kr + p1*_2xy + p2*(r2 + 2 * x2) + s1*r2 + s2*r2*r2);

double yd = (y*kr + p1*(r2 + 2 * y2) + p2*_2xy + s3*r2 + s4*r2*r2);

cv::Vec3d vecTilt = matTilt*cv::Vec3d(xd, yd, 1);

double invProj = vecTilt(2) ? 1. / vecTilt(2) : 1;

double u = fx*invProj*vecTilt(0) + u0;

double v = fy*invProj*vecTilt(1) + v0;

yong.qi added

//double A = ((k3*r2 + k2)*r2 + k1);

//double B = ((k6*r2 + k5)*r2 + k4);

//double r2_A = r2*A;

//double r2_B = r2*B;

//double kr_res = (1 + r2_A) / (1 + r2_B);

//fout << "A: " << A << endl

// << "B: " << B << endl

// << "r2_A: " << r2_A << endl

// << "r2_B: " << r2_B << endl

// << "kr_res: " << kr_res << endl

// << "w:" << w << endl

// << "x2:" << x2 << endl

// << "y2:" << y2 << endl

// << "p1:" << p1 << endl

// << "p2:" << p2 << endl

// << "s1:" << s1 << endl

// << "s2:" << s2 << endl

// << "s3:" << s3 << endl

// << "s4:" << s4 << endl

// << "_2xy:" << _2xy << endl

// << "r2:" << r2 << endl

// << "kr:" << kr << endl

// << "xd:" << xd << endl

// << "yd:" << yd << endl

// << "fx:" << fx << endl

// << "fy:" << fy << endl

// << "u0:" << u0 << endl

// << "v0:" << v0 << endl

// << "matTilt:" << matTilt << endl

// << "vecTilt:" << vecTilt << endl

// << "invProj:" << invProj << endl

// << "vecTilt(0):" << vecTilt(0) << endl

// << "vecTilt(1):" << vecTilt(1) << endl << endl

// << "u:" << u << endl

// << "v:" << v << endl;

//fout.close();

yong.qi end

if (m1type == CV_16SC2)

{

int iu = saturate_cast(u*INTER_TAB_SIZE);

int iv = saturate_cast(v*INTER_TAB_SIZE);

m1[j * 2] = (short)(iu >> INTER_BITS);

m1[j * 2 + 1] = (short)(iv >> INTER_BITS);

m2[j] = (ushort)((iv & (INTER_TAB_SIZE - 1))*INTER_TAB_SIZE + (iu & (INTER_TAB_SIZE - 1)));

}

else if (m1type == CV_32FC1)

{

m1f[j] = (float)u;

m2f[j] = (float)v;

}

else

{

m1f[j * 2] = (float)u;

m1f[j * 2 + 1] = (float)v;

}

}

}

_map1 = map1;

_map2 = map2;

}

将上述函数替换掉OpenCV中的函数,目的是分析A、B以及r2_A,r2_B,kr_res等变量为何会引起异常。其中,kr_res=(1+r2_A)/(1+r2_B),分析了四处明显产生异常的位置,数据如下:

经过上述分析,留给大家思考:

1)为何会产生跳变呢?

2)如何有效解决跳变呢?

3) 源代码如何优化便可以解决呢?

PS:经过测试,OpenCV最新版本4.1.0仍然会出现此bug。

欢迎大家留言积极讨论,同时我在【3D视觉工坊】星球里也发起了作业,感兴趣的小伙伴欢迎积极参与回答。

上述内容,如有侵犯版权,请联系作者,会自行删文。

荐读

2D、3D视觉技术干货之杂谈

再谈「相机标定」

计算机视觉基本原理——RANSAC

一分钟详解本质矩阵的推导过程

一分钟详解OpenCV之相机标定函数calibrateCamera()

藏在标定板背后的秘密

回复关键词——1,前往知识星球

opencv mat 修改_OpenCV中initUndistortRectifyMap函数存在bug原因探究相关推荐

  1. OpenCV中initUndistortRectifyMap函数存在bug原因探究

    原文首发于公众号「3D视觉工坊」:OpenCV中initUndistortRectifyMap函数存在bug原因探究. 最近在运行如下一段代码时,生成的mapx和mapy有点异常. 代码片段如下: # ...

  2. 修改MFC中AfxMessageBox()函数的对话框标题

    修改MFC中AfxMessageBox()函数的对话框标题 如何在MFC中修改AfxMessageBox()函数所弹出的对话框标题,步骤如下: 1.找到项目工程的资源视图,打开.rc资源文件下的Str ...

  3. JS中Promise函数then的奥秘探究

    JS中Promise函数then的奥秘探究 Promise概述 Promise对象是CommonJS工作组提出的一种规范,目的是为异步操作提供统一接口. 那么,什么是Promises? 首先,它是一个 ...

  4. c++imread 函数_OpenCV中C++函数imread读取图片的问题及解决方法

    今天在用OpenCV实验Image Pyramid的时候发现一个奇怪的问题,就是利用C++函数imread读取图片的时候返回的结果总是空,而利用C函数cvLoadImage时却能读取到图像.代码如下: ...

  5. opencv mat 修改_C++ opencv矩阵和pytorch tensor的互相转换

    矩阵和tensor相互转换 cvmat到tensor tips:这里主要要注意的就是在opencv和pytorch中存储顺序的差异 cv::cvtColor(frame, frame, CV_BGR2 ...

  6. TI 中 acos()函数 存在 bug

    编写 DSP 程序时(TMS320F28335),如果直接调用系统自带的 acos 函数,有时会出现莫名其妙的错误 (如同步坐标系中的电流在稳态情况下本来是直流,但可能出现尖峰).这是因为 acos( ...

  7. mat 释放_OpenCV中Mat总结

    一.数字图像存储概述 数字图像存储时,我们存储的是图像每个像素点的数值,对应的是一个数字矩阵. 二.Mat的存储 1.OpenCV1基于C接口定义的图像存储格式IplImage*,直接暴露内存,如果忘 ...

  8. SQL中Round函数没有四舍五入原因及处理方法

    为什么我们在写sql使用round函数四舍五入时,明明后面是5可以进位反而舍掉了那? 原因在于"四舍六入五成双"原则(来源于百度百科) 对于位数很多的近似数,当有效位数确定后,其后 ...

  9. 一分钟详解initUndistortRectifyMap函数bug修复方法

    本文首发于微信公众号「3D视觉工坊」--一分钟详解initUndistortRectifyMap函数bug修复方法 在上一篇文章OpenCV中initUndistortRectifyMap函数存在bu ...

最新文章

  1. 软件产品线工程方法:如何在OpenExpressApp做客户化工作
  2. Fiddler之Autoresponder替换(Web)
  3. 【算法精讲】集成分类与随机森林
  4. LeetCode 多线程 1116. 打印零与奇偶数
  5. mysql maxconnections 最大值,MySQL性能优化之max_connections配置参数浅析
  6. java线程池参数面试题,附赠复习资料
  7. 【UnityShader】使用Cubemap/Matcap制作玻璃
  8. mysql扩容方案_MySQL分库分表:扩容方案
  9. Linux文件属性的777权限
  10. JVM(四)--垃圾收集器
  11. html怎么打入文本框,html怎么在文本框里面输入文字
  12. 流密码(一)同步流密码、自同步流密码以及线性反馈移位寄存器
  13. 痛心!又一中产家庭倒下,为什么我建议你不要轻易买保险?
  14. 计算机专业选修课怎么选比较好,你知道怎么选AP课程吗?附AP不同专业方向的选课建议...
  15. 用PS做法线,高光贴图的最简图文教程
  16. MySQL最重要的日志-binlog详解
  17. Android 批量打包 基于Walle的多渠道快速打包自动脚本
  18. C#手机号码段生成 前7位补全后4位
  19. python下载批量图片
  20. osgEarth显示地球影像

热门文章

  1. 不同坐标系下角速度_技术 | 西安80坐标与地方坐标系的转换方法技巧
  2. pageinfo对合并list进行分页_Pagehelper不是特别好用。对list直接分页
  3. java 取出集合前两个数据库_【Java】获取两个List中不同的数据(效率非常不错)-Go语言中文社区...
  4. go语言能编android程序吗,用 Golang 开发 Android 应用(二)—— 简单 UI-Go语言中文社区...
  5. STM32 进阶教程 16 - ADC1与ADC2同步采样
  6. STM32 基础系列教程 12 – ADC 中断
  7. FPGA实现数字信号处理的定点运算
  8. 雅客EXCEL (3)-合并取消单元格、平均值、添加序号
  9. 【PC工具】更新在线流程图绘制工具bullmind,免费云存储流程图绘制,可直接粘贴图片...
  10. 【移动通信】移动通信基础