使用视场模型反转鱼眼径向畸变

时间:2017-08-02 11:04:39

标签: opencv camera computer-vision distortion fisheye

假设我从带有185ºFoV的鱼眼相机拍摄的这张失真图像。

图片取自Proc. IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP), pp. 1541-1545, Shanghai, China, March 2016

distorted

我想使用Frederic Devernay, Olivier Faugeras. Straight lines have to be straight: automatic calibration and removal of distortion from scenes of structured enviroments. Machine Vision and Applications, Springer Verlag, 2001, 13 (1), pp.14-24中解释的FoV模型,特别是在方程13和14中解除它。

rd = 1 / ω * arctan (2 * ru * tan(ω / 2))   // Equation 13
ru = tan(rd * ω) / (2 * tan(ω / 2))         // Equation 14

我在OpenCV中实现了它,我无法实现它。我将rd解释为距离光学中心的点的扭曲距离,并将ru解释为新的未失真距离。

我告诉你一个完整的小项目。

#include <opencv2/opencv.hpp>

#define W (185*CV_PI/180)

cv::Mat undistortFishEye(const cv::Mat &distorted, const float w)
{
    cv::Mat map_x, map_y;
    map_x.create(distorted.size(), CV_32FC1);
    map_y.create(distorted.size(), CV_32FC1);

    int Cx = distorted.cols/2;
    int Cy = distorted.rows/2;

    for (int x = -Cx; x < Cx; ++x) {
        for (int y = -Cy; y < Cy; ++y) {
            double rd = sqrt(x*x+ y*y);
            double ru = tan(rd*w) / (2*tan(w/2));
            map_x.at<float>(y+Cy,x+Cx) = ru/rd * x + Cx;
            map_y.at<float>(y+Cy,x+Cx) = ru/rd * y + Cy;
        }
    }

    cv::Mat undistorted;
    remap(distorted, undistorted, map_x, map_y, CV_INTER_LINEAR);
    return undistorted;
}

int main(int argc, char **argv)
{
    cv::Mat im_d = cv::imread(<your_image_path>, CV_LOAD_IMAGE_GRAYSCALE);
    cv::imshow("Image distorted", im_d);

    cv::Mat im_u = undistortFishEye(im_d, W);
    cv::imshow("Image undistorted", im_u);

    cv::waitKey(0);
}

1 个答案:

答案 0 :(得分:2)

我只浏览了您链接的论文,所以我不确定我是否正确,但看起来您的实施有三个问题:

  1. 你应该只使用你的FOV角度的一半作为W参数(算法在一些径向坐标中运行,计算距离中心的距离,所以角度也应该来自中心,这给出了角度的一半)。

  2. 您计算rurd错误:ru应该是距离,然后rd应该根据公式(13)。这是因为你做了逆映射:你创建一个空图像,然后对于每个(x, y) - 点,你必须从扭曲的图像中选择一种颜色 - 你通过扭曲(x, y)并看看它指向扭曲的图像,然后将该颜色映射到原始的非扭曲(x, y)坐标上。进行直接映射(例如,对于失真图像的每(x, y)个移动到非失真图像上的计算位置)会产生视觉伪像,因为并非所有目标像素都必须被覆盖。

  3. 您忘记规范化径向坐标,必须分别将它们除以CxCy,进行转换,然后通过相乘来反规范化。

    < / LI>

    也可能会有doubleint的隐式转换,但我不确定 - 永远不会记住有关的规则,我只是尽量不要混合int的和double在同一个等式中,如果它适合你,可以将Cx, Cy转换回int。无论如何,这似乎有效(undistortFishEye函数的两个版本都给出了相同的结果,所以使用你想要的更好的一个):

    #include <opencv2/opencv.hpp>
    
    #define W (185/2*CV_PI/180)
    
    cv::Mat undistortFishEye(const cv::Mat &distorted, const float w)
    {
        cv::Mat map_x, map_y;
        map_x.create(distorted.size(), CV_32FC1);
        map_y.create(distorted.size(), CV_32FC1);
    
        double Cx = distorted.cols / 2.0;
        double Cy = distorted.rows / 2.0;
    
        for (double x = -1.0; x < 1.0; x += 1.0/Cx) {
            for (double y = -1.0; y < 1.0; y += 1.0/Cy) {
                double ru = sqrt(x*x + y*y);
                double rd = (1.0 / w)*atan(2.0*ru*tan(w / 2.0));
    
                map_x.at<float>(y*Cy + Cy, x*Cx + Cx) = rd/ru * x*Cx + Cx;
                map_y.at<float>(y*Cy + Cy, x*Cx + Cx) = rd/ru * y*Cy + Cy;
            }
        }
    
        cv::Mat undistorted;
        remap(distorted, undistorted, map_x, map_y, CV_INTER_LINEAR);
        return undistorted;
    }
    
    cv::Mat undistortFishEye2(const cv::Mat &distorted, const float w)
    {
        cv::Mat map_x, map_y;
        map_x.create(distorted.size(), CV_32FC1);
        map_y.create(distorted.size(), CV_32FC1);
    
        double cx = distorted.cols / 2.0;
        double cy = distorted.rows / 2.0;
    
        for (int x = 0; x < distorted.cols; ++x)
        {
            for (int y = 0; y < distorted.rows; ++y)
            {
                double rx = (x - cx) / cx;
                double ry = (y - cy) / cy;
                double ru = sqrt(rx*rx + ry*ry);
                //TODO: check for ru == 0.0
    
                double rd = (1.0 / w)*atan(2.0*ru*tan(w/2.0));
                double coeff = rd / ru;
                rx *= coeff;
                ry *= coeff;
                map_x.at<float>(y, x) = rx*cx + cx;
                map_y.at<float>(y, x) = ry*cy + cy;
            }
        }
    
        cv::Mat undistorted;
        remap(distorted, undistorted, map_x, map_y, CV_INTER_LINEAR);
        return undistorted;
    }
    
    int main(int argc, char **argv)
    {
        cv::Mat im_d = cv::imread("C:/projects/test_images/fisheye/library.jpg", CV_LOAD_IMAGE_GRAYSCALE);
        cv::imshow("Image distorted", im_d);
    
        cv::Mat im_u = undistortFishEye(im_d, W);
        cv::imshow("Image undistorted", im_u);
    
        cv::waitKey(0);
    }
    

    enter image description here

    原始图像的大部分在转换过程中丢失了 - 应该是这样吗?或者算法是否仍然将它们映射到某个地方?我尝试将它转换为更大的目标图像,它在边缘处得到了真正的拉伸: enter image description here