Opencv中的两种去畸变函数
前言
参考 https://www.cnblogs.com/riddick/p/6711263.html 我们知道目前opencv在标定完后得到相机内参及畸变系数,要么通过cv::undistort()直接得到去畸变的图像,要么通过cv::getOptimalNewCameraMatrix()得到新的矩阵,再通过cv::initUndistortRectifyMap()得到x轴与y轴映射,最后通过cv::remap()将原图映射到新图。
两者的不同
在说两者不同之前,我们先介绍下getOptimalNewCameraMatrix()函数。
getOptimalNewCameraMatrix(cv::InputArray _cameraMatrix,cv::InputArray _distCoeffs,cv::Size imgSize, double alpha, cv::Size newImgSize,cv::Rect* validPixROI, bool centerPrincipalPoint)
alpha之前的参数自不必说,alpha之后的参数都可以视为默认。关键就在alpha的含义。
icvGetRectangles(cameraMatrix, distCoeffs, 0, 0, imgSize, inner, outer);
// Projection mapping inner rectangle to viewport
double fx0 = (newImgSize.width) / inner.width;
double fy0 = (newImgSize.height) / inner.height;
double cx0 = -fx0 * inner.x;
double cy0 = -fy0 * inner.y;
// Projection mapping outer rectangle to viewport
double fx1 = (newImgSize.width) / outer.width;
double fy1 = (newImgSize.height) / outer.height;
double cx1 = -fx1 * outer.x;
double cy1 = -fy1 * outer.y;
// Interpolate between the two optimal projections
M[0][0] = fx0*(1 - alpha) + fx1*alpha;
M[1][1] = fy0*(1 - alpha) + fy1*alpha;
M[0][2] = cx0*(1 - alpha) + cx1*alpha;
M[1][2] = cy0*(1 - alpha) + cy1*alpha;
在getOptimalNewCameraMatrix()函数中,有上述一段代码,意思是从内参/畸变系数中得到两个inner和outer矩阵,当alpha为0时,取inner即内矩阵,用内矩阵大小作为新的图像大小,重新得到fx,fy,cx,cy,因此新的内参矩阵诞生了. 当alpha为1时,取outer即外矩阵。当alpha介于0~1时,则按照比例重新计算fx,fy,cx,cy。
事实上,内矩阵等同于不含任何黑色边框的图幅大小,而外矩阵等同于原图大小。
继续深究,那么它的inner和outer又是怎么来的呢,
inner和outer的来源
inner和outer又是怎么来的呢,和原来的内参与畸变又有什么关系呢?
在icvGetRectangles()函数中有这样一段代码
for (y = k = 0; y < N; y++) //opencv 默认为 9
for (x = 0; x < N; x++)
pts[k++] = cvPoint2D32f((float)x*imgSize.width / (N - 1),
(float)y*imgSize.height / (N - 1));
cvUndistortPoints2(_pts, _pts, cameraMatrix, distCoeffs, R, newCameraMatrix);
float iX0 = -FLT_MAX, iX1 = FLT_MAX, iY0 = -FLT_MAX, iY1 = FLT_MAX;
float oX0 = FLT_MAX, oX1 = -FLT_MAX, oY0 = FLT_MAX, oY1 = -FLT_MAX;
// find the inscribed rectangle.
// the code will likely not work with extreme rotation matrices (R) (>45%)
for (y = k = 0; y < N; y++)
for (x = 0; x < N; x++)
{
CvPoint2D32f p = pts[k++];
oX0 = MIN(oX0, p.x);
oX1 = MAX(oX1, p.x);
oY0 = MIN(oY0, p.y);
oY1 = MAX(oY1, p.y);
if (x == 0)
iX0 = MAX(iX0, p.x);
if (x == N - 1)
iX1 = MIN(iX1, p.x);
if (y == 0)
iY0 = MAX(iY0, p.y);
if (y == N - 1)
iY1 = MIN(iY1, p.y);
}
inner = cv::Rect_<float>(iX0, iY0, iX1 - iX0, iY1 - iY0);
outer = cv::Rect_<float>(oX0, oY0, oX1 - oX0, oY1 - oY0);
这段代码的含义是指,按照输入图像的大小,生成长宽分别按照 imgSize.width / 8的间隔和 imgSize.height / 8 间隔的9*9个点。
再对这81个点,进行去畸变转换得到新的点。
再从去畸变后的点中相互比较,得到外围矩阵oX0,oY0,oX1,oY1,及由原四个角点得到的内围矩阵,也就是outer与inner。
结束语
之所以会研究这部分代码,是因为在处理Ladybug相机时,它可以自动提供去畸变后的图片,例如
但是黑色边框却没有去掉,因为想偷懒尝试通过getOptimalNewCameraMatrix()将alpha设为0得到去黑边图像,但由于ladybug自身只提供相机内参而不提供畸变系数,当我设畸变系数为空时,内参并没有变化,出于好奇就稍深入研究了下源码。现在明白了当畸变系数为空时,inner与outer由于undistortpoint()大小相同,fx,fy,cx,cy当然就没有变化。
最后还是自己手动截取可视范围,保持fx,fy不变,根据新左上角重新计算cx,cy。得到新的去黑边后的图像以及对应的新相机内参。
[1]: https://www.cnblogs.com/riddick/p/6711263.html
[2]: https://docs.opencv.org/3.4.1/