19

I get images from a camera where it is not possible to take a chessboard picture and calculate the correction matrix using OpenCV. Up to now I corrected the images using imagemagick convert with the option '-distort Barrel "0.0 0.0 -0.035 1.1"' where I got the parameters with trial and error.

Now I want to do this inside OpenCV but all I find in the web is the automatic correction using the chessboard image. Is there any chance to apply some simple manual trial and error lens distortion correction as I did with imagemagick?

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
Michael Hecht
  • 2,093
  • 6
  • 25
  • 37
  • In the samples using chessboard they should compute the distortion parameters anywhere. Just skip the computation and provide those parameters explicitly. If that part is encapsulated you can look at the oprnCV sourcecode and use the internally used functions – Micka Oct 28 '14 at 07:47
  • I tried to define the chessboard corners myself (e.g. 4x4 points), but didn't get what the structure of the corners array should be. Has anyone any idea? – Michael Hecht Oct 28 '14 at 08:00
  • Ok, I think the smallest possible array should be configured like this: corners = np.zeros((4*4,1,2),dtype="float32"). With 3x3 it seems not to work. Nevertheless I would prefer something like convert -distort Barrel instead of defining now the distorted points. – Michael Hecht Oct 28 '14 at 08:22
  • Did you try initUndistortRectifyMap(InputArray cameraMatrix, InputArray distCoeffs, InputArray R, InputArray newCameraMatrix, Size size, int m1type, OutputArray map1, OutputArray map2) ?? It doesnt use any chessboard but just the distortion coefficients... – Micka Oct 28 '14 at 08:43
  • To avoid manual definition of chessboard points I got a non-distorted chessboard from web and added some barrel distortion via imagemagick, e.g.: convert chessboard_0_0.png -distort Barrel "0.0 0.0 0.005 1.0" chessoard_05_1.jpg. Now I have a set of distorted reference chessboards which I can use to define the most appropriate correction matrix. – Michael Hecht Oct 28 '14 at 08:44
  • 1
    Hmmm ... now I see that it isn't done with one distorted chessboard since one needs about 10 different distorted images for application of cv2.calibrateCamera :-( so I try cv2.initUndistortRectifyMap. – Michael Hecht Oct 28 '14 at 09:11

3 Answers3

29

Ok, I think I got it. In the matrices cam1, cam2 the image centers were missing (see documentation). I added it and changed the focal length to avoid a too strong change of image size. Here is the code:

  import numpy as np
  import cv2

  src    = cv2.imread("distortedImage.jpg")
  width  = src.shape[1]
  height = src.shape[0]

  distCoeff = np.zeros((4,1),np.float64)

  # TODO: add your coefficients here!
  k1 = -1.0e-5; # negative to remove barrel distortion
  k2 = 0.0;
  p1 = 0.0;
  p2 = 0.0;

  distCoeff[0,0] = k1;
  distCoeff[1,0] = k2;
  distCoeff[2,0] = p1;
  distCoeff[3,0] = p2;

  # assume unit matrix for camera
  cam = np.eye(3,dtype=np.float32)

  cam[0,2] = width/2.0  # define center x
  cam[1,2] = height/2.0 # define center y
  cam[0,0] = 10.        # define focal length x
  cam[1,1] = 10.        # define focal length y

  # here the undistortion will be computed
  dst = cv2.undistort(src,cam,distCoeff)

  cv2.imshow('dst',dst)
  cv2.waitKey(0)
  cv2.destroyAllWindows()

Thank you very much for your assistence.

Michael Hecht
  • 2,093
  • 6
  • 25
  • 37
  • 1
    Nice to hear. If youve tested it and it works, afaik you can accept your own answer to help others searching for a solution to the same question – Micka Oct 28 '14 at 21:14
  • where does p1 and p2 comes from, and do we have to define the focal length seperately ? thanks in advance – Profstyle Apr 06 '22 at 10:27
3

Here is a method that will undistort an image if you have no chessboard pattern but you know the distortion coefficients.

Since I dont know to which coefficients your barrel distortion parameters correspond (maybe have a look at http://docs.opencv.org/doc/tutorials/calib3d/camera_calibration/camera_calibration.html and http://docs.opencv.org/modules/imgproc/doc/geometric_transformations.html#initundistortrectifymap you will have to try it out or maybe someone else can help here.

Another point is that I'm not sure whether openCV will handle both, float and double automatically. If that's not the case there might be a bug in this code (I don't know whether double or single precision is assumed):

cv::Mat distCoeff;
distCoeff = cv::Mat::zeros(8,1,CV_64FC1);

// indices: k1, k2, p1, p2, k3, k4, k5, k6 
// TODO: add your coefficients here!
double k1 = 0;
double k2 = 0;
double p1 = 0;
double p2 = 0;
double k3 = 0;
double k4 = 0;
double k5 = 0;
double k6 = 0;

distCoeff.at<double>(0,0) = k1;
distCoeff.at<double>(1,0) = k2;
distCoeff.at<double>(2,0) = p1;
distCoeff.at<double>(3,0) = p2;
distCoeff.at<double>(4,0) = k3;
distCoeff.at<double>(5,0) = k4;
distCoeff.at<double>(6,0) = k5;
distCoeff.at<double>(7,0) = k6;




// assume unit matrix for camera, so no movement
cv::Mat cam1,cam2;
cam1 = cv::Mat::eye(3,3,CV_32FC1);
cam2 = cv::Mat::eye(3,3,CV_32FC1);
//cam2.at<float>(0,2) = 100;    // for testing a translation

// here the undistortion will be computed
cv::Mat map1, map2;
cv::initUndistortRectifyMap(cam1, distCoeff, cv::Mat(), cam2,  input.size(), CV_32FC1, map1, map2);

cv::Mat distCorrected;
cv::remap(input, distCorrected, map1, map2, cv::INTER_LINEAR);
Micka
  • 19,585
  • 4
  • 56
  • 74
  • Where does the k4,k5 and k6 come from? It is not part of the documentation, is it? Is there also a Python code snippet? – Michael Hecht Oct 28 '14 at 15:09
  • If I follow your link, I see something like this: xcor=x*(1+k1*r^2+k2*r^4+k3*r^6); This differs from the description at imagemagick(http://www.imagemagick.org/Usage/distorts/#barrel) where following formula is applied: Rsrc = r * ( A*r^3 + B*r^2 + C*r + D ); So setting e.g. k1 to 0.001 results in a really strange image but doesn't correct the barrel distortion?! Nevertheless, I think we come closer to the solution. – Michael Hecht Oct 28 '14 at 15:31
  • sorry, no python snippet from my side, but its just about the initUndistortRectifyMap call and setting up the coefficients. Wikipedia gives some coefficients similar to OpenCV (didnt check the terms though) http://en.wikipedia.org/wiki/Distortion_%28optics%29#Software_correction so maybe imageMagick uses a different distortion model, or they assume some kind of inverse parameters?!? Don't know, sorry =) – Micka Oct 28 '14 at 15:52
  • according to http://www.imagemagick.org/Usage/lens/correcting_lens_distortions.pdf imagemagick uses PTLens undistortion model, which itself `uses a third-order po- lynomial approach to describe radial distortion`. Have a look at that PDF, maybe you can find out the differences between those models and write an answer to your question which could help others :) – Micka Oct 28 '14 at 15:59
  • another thing I have to mention: `cam1` and `cam2` are unit matrix in my example. If you adjust the focal length to your needs (read about camera matrix if necessary), the distortion coefficients might change too? maybe that's another fault here?!? – Micka Oct 28 '14 at 16:04
1

This is a complementary function to undistort, may be faster or better ways to do it but it works:

void distort(const cv::Mat& src, cv::Mat& dst, const cv::Mat& cameraMatrix, const cv::Mat& distCoeffs)
{

  cv::Mat distort_x = cv::Mat(src.size(), CV_32F);
  cv::Mat distort_y = cv::Mat(src.size(), CV_32F);

  cv::Mat pixel_locations_src = cv::Mat(src.size(), CV_32FC2);

  for (int i = 0; i < src.size().height; i++) {
    for (int j = 0; j < src.size().width; j++) {
      pixel_locations_src.at<cv::Point2f>(i,j) = cv::Point2f(j,i);
    }
  }

  cv::Mat fractional_locations_dst = cv::Mat(src.size(), CV_32FC2);

  cv::undistortPoints(pixel_locations_src, pixel_locations_dst, cameraMatrix, distCoeffs);

  cv::Mat pixel_locations_dst = cv::Mat(src.size(), CV_32FC2);

  const float fx = cameraMatrix.at<double>(0,0);
  const float fy = cameraMatrix.at<double>(1,1);
  const float cx = cameraMatrix.at<double>(0,2);
  const float cy = cameraMatrix.at<double>(1,2);

  // is there a faster way to do this?
  for (int i = 0; i < fractional_locations_dst.size().height; i++) {
    for (int j = 0; j < fractional_locations_dst.size().width; j++) {
      const float x = fractional_locations_dst.at<cv::Point2f>(i,j).x*fx + cx;
      const float y = fractional_locations_dst.at<cv::Point2f>(i,j).y*fy + cy;
      pixel_locations_dst.at<cv::Point2f>(i,j) = cv::Point2f(x,y);
    }
  }

  cv::remap(src, dst, pixel_locations_dst, cv::Mat(), CV_INTER_LINEAR);
}
Varun Patel
  • 79
  • 10