2017-08-02 109 views
2

假设我从具有185ºFoV的鱼眼摄像头拍摄这张扭曲的图像。使用视场模型的反向鱼眼径向畸变

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

distorted

我想用的FoV模型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解释undistort它,具体的公式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); 
} 
+0

我怀疑错误在FoV模型输入值中。例如,左上方像素'(-575,-543)'有'rd = 790,87',FoV模型返回'ru = 0,012'。 –

+0

检查它可能会帮助你: –

+1

https://github.com/ethz-asl/ethzasl_ptam/blob/e761e802c6382bfe7c09f5f2db342c7a89209348/ptam/src/ATANCamera.cc#L52 –

回答

2

我只能通过你链接的纸脱脂,所以我不知道我这样做是正确的,但它看起来像三个东西是错的实现:

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

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

  3. 您忘了标准化径向坐标,必须分别用Cx, Cy进行分割,然后进行变换,然后通过乘以返回进行去归一化。

也有可能存在的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

+0

这听起来真的很好,这三个更正有道理。我会使用第一个实现,它更聪明,肯定速度更快。关于信息的损失,是的...'重新映射'返回与原始大小相同的图像。这对我来说不是问题,我想为图像中的一组功能使用未失真功能。未失真图像的可视化是检查失真失效的一种方法。 –

+0

我认为在第一个实现中有一些bug,for(double y = -1.0; y <1.0; y + = 1.0/Cx)应该是1.0/Cy,当然是'rd/ru * y * Cx + Cy'应该是'rd/ru * y * Cy + Cy'。稍后检查并修复我的答案。 – Headcrab