了解 OpenCV 的 undistort 功能

Posted

技术标签:

【中文标题】了解 OpenCV 的 undistort 功能【英文标题】:Understanding OpenCV's undistort function 【发布时间】:2013-11-11 01:16:40 【问题描述】:

我希望使用我为相机计算的失真系数来消除图像失真,而不更改相机矩阵。这正是undistort() 所做的,但我想将输出绘制到更大的画布图像上。

当我尝试这个时:

Mat drawtransform = getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, size, 1.0, size * 2);
undistort(inputimage, undistorted, cameraMatrix, distCoeffs, drawtransform);

它仍然写出相同大小的图像,但只是放大了 2 倍未失真结果的左上角四分之一。 Like the documentation says, undistort 写入相同大小的目标图像。

很明显,我可以复制出来并重新实现 undistort() 的稍微调整的版本,但我在理解它的作用时遇到了一些麻烦。以下是来源:

void cv::undistort( InputArray _src, OutputArray _dst, InputArray _cameraMatrix,
                    InputArray _distCoeffs, InputArray _newCameraMatrix )

    Mat src = _src.getMat(), cameraMatrix = _cameraMatrix.getMat();
    Mat distCoeffs = _distCoeffs.getMat(), newCameraMatrix = _newCameraMatrix.getMat();

    _dst.create( src.size(), src.type() );
    Mat dst = _dst.getMat();

    CV_Assert( dst.data != src.data );

    int stripe_size0 = std::min(std::max(1, (1 << 12) / std::max(src.cols, 1)), src.rows);
    Mat map1(stripe_size0, src.cols, CV_16SC2), map2(stripe_size0, src.cols, CV_16UC1);

    Mat_<double> A, Ar, I = Mat_<double>::eye(3,3);

    cameraMatrix.convertTo(A, CV_64F);
    if( distCoeffs.data )
        distCoeffs = Mat_<double>(distCoeffs);
    else
    
        distCoeffs.create(5, 1, CV_64F);
        distCoeffs = 0.;
    

    if( newCameraMatrix.data )
        newCameraMatrix.convertTo(Ar, CV_64F);
    else
        A.copyTo(Ar);

    double v0 = Ar(1, 2);
    for( int y = 0; y < src.rows; y += stripe_size0 )
    
        int stripe_size = std::min( stripe_size0, src.rows - y );
        Ar(1, 2) = v0 - y;
        Mat map1_part = map1.rowRange(0, stripe_size),
            map2_part = map2.rowRange(0, stripe_size),
            dst_part = dst.rowRange(y, y + stripe_size);

        initUndistortRectifyMap( A, distCoeffs, I, Ar, Size(src.cols, stripe_size),
                                 map1_part.type(), map1_part, map2_part );
        remap( src, dst_part, map1_part, map2_part, INTER_LINEAR, BORDER_CONSTANT );
    

这里大约一半的行用于完整性检查和初始化输入参数。我对map1map2 的情况感到困惑。遗憾的是,这些名称的描述性不如大多数名称。我一定错过了一些解释,也许它隐藏在一些介绍页面中,或者在文档下用于另一个功能。

map1 是一个双通道有符号短整数矩阵,map2 是一个无符号短整数矩阵,它们的维度都是(高度,最大值(4096/宽度,1))。问题是,为什么?这些地图将包含什么?这种条带化的意义和目的是什么?条纹奇异维度的意义和目的是什么?

【问题讨论】:

【参考方案1】:

使用initUndistortRectifyMap 获得所需比例的转换,然后将其输出(您提到的两个矩阵)应用于remap。

第一张图用于计算每个像素位置的 x 坐标变换,第二张图用于变换 y 坐标。

【讨论】:

【参考方案2】:

您可能需要阅读函数remap 的说明。该映射表示目标图像中每个像素在源图像中的像素 X、Y 位置。 Map1_part 是源中的每个 X 位置,而 Map2_part 是源中的每个 Y 位置。

无需过多阅读,条带化可能是加快转换过程的一种方法。

编辑:

此外,如果您只想将图像缩放到更大的尺寸,您可以重新调整输出图像的大小。

double scaleX = 2.0;
double scaleY = 2.0;
cv::Mat undistortedScaled;

cv::resize(undistorted, undistortedScaled, cv::Size(0,0), scaleX, scaleY);

【讨论】:

这不会比使用映射函数对更大的输出图像进行采样更准确吗?这只会将其放大 2 倍,不会提高分辨率...

以上是关于了解 OpenCV 的 undistort 功能的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV calibrateCamera 如何在 undistort 后将角点匹配到提供的 objectPoints

如何避免由于cv2.undistort引起的延迟?

如何在opencv中对鱼眼图像生成的去扭曲?

opencv 鱼眼矫正

opencv:检测棋盘角的最佳方法

写出opencv源代码啥水平