Opencv 利用视场模型反演鱼眼径向畸变

Opencv 利用视场模型反演鱼眼径向畸变,opencv,camera,computer-vision,distortion,fisheye,Opencv,Camera,Computer Vision,Distortion,Fisheye,让我们假设我有一张从鱼眼相机拍摄的185ºFoV的扭曲图像 这张照片是从中国拍摄的 我想使用中解释的FoV模型,具体地说,在等式13和14中,使其不失真 rd = 1 / ω * arctan (2 * ru * tan(ω / 2)) // Equation 13 ru = tan(rd * ω) / (2 * tan(ω / 2)) // Equation 14 我已经在OpenCV中实现了它,但我无法实现它。我将rd解释为点到光学中心的扭曲距离,ru解释为新的未扭曲

让我们假设我有一张从鱼眼相机拍摄的185ºFoV的扭曲图像

这张照片是从中国拍摄的

我想使用中解释的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);
}
#包括
#定义W(185*CV_PI/180)
cv::Mat不失真鱼眼(常数cv::Mat&失真,常数浮动w)
{
cv::Mat map_x,map_y;
映射x.create(扭曲的.size(),CV_32FC1);
映射y.create(扭曲的.size(),CV_32FC1);
int Cx=1.cols/2;
int Cy=扭曲的行数/2;
对于(int x=-Cx;x
我只是浏览了一下您链接的论文,所以我不确定是否正确,但您的实现似乎有三个问题:

  • 您应该只使用FOV角度的一半作为
    W
    参数(该算法在一些径向坐标中运行,计算距离中心的距离,因此角度也应该是距离中心的,这就给出了一半的角度)

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

  • 您忘记了对径向坐标进行规格化,必须将它们分别除以
    Cx
    Cy
    ,然后进行变换,然后通过相乘反规格化

  • 也可能有一些
    double
    int
    的隐式转换,但我不确定-我永远记不住这方面的规则,我只是尽量不将
    int
    double
    混合在同一个等式中,如果对您有效,请随意将那些
    Cx,Cy
    转换回
    int
    。无论如何,这似乎是可行的(两个版本的
    undistortFishEye
    函数都给出了相同的结果,所以使用您更喜欢的方法):

    #包括
    #定义W(185/2*CV_PI/180)
    cv::Mat不失真鱼眼(常数cv::Mat&失真,常数浮动w)
    {
    cv::Mat map_x,map_y;
    映射x.create(扭曲的.size(),CV_32FC1);
    映射y.create(扭曲的.size(),CV_32FC1);
    双Cx=0.cols/2.0;
    双Cy=2.0行/2.0;
    对于(双x=-1.0;x<1.0;x+=1.0/Cx){
    对于(双y=-1.0;y<1.0;y+=1.0/Cy){
    双ru=sqrt(x*x+y*y);
    双rd=(1.0/w)*阿坦(2.0*ru*tan(w/2.0));
    映射x.at(y*Cy+Cy,x*Cx+Cx)=rd/ru*x*Cx+Cx;
    映射y.at(y*Cy+Cy,x*Cx+Cx)=rd/ru*y*Cy+Cy;
    }
    }
    cv::未变形的垫;
    重新映射(失真、未失真、映射x、映射y、CV\U内部线性);
    返回不失真;
    }
    cv::Mat不失真鱼眼2(常数cv::Mat&失真,常数浮动w)
    {
    cv::Mat map_x,map_y;
    映射x.create(扭曲的.size(),CV_32FC1);
    映射y.create(扭曲的.size(),CV_32FC1);
    双cx=0.cols/2.0;
    双cy=2.0行/2.0;
    对于(int x=0;x

    在转换过程中,原始图像的大部分都丢失了——应该是这样吗?或者算法应该仍然将它们映射到某个地方吗?我试着把它转换成一个更大的目标图像,它的边缘被拉伸了:

    我怀疑错误在FoV模型输入值中。例如,左上像素
    (-575,-543)
    具有
    rd=790,87
    ,FoV模型返回我
    ru=0012
    。检查此选项可能会帮助您:@Ja_cpp是的,可能会有帮助。我已经检查了它并返回等式14。稍后,我的代码也是这样:
    [xu,yu]=ru/rd*[xd,yd]
    。在靠近光学中心的位置,它假设没有失真,所以它只显示undis
    #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);
    }