Python: OpenCV: How to get an undistorted image without the cropping?

Viewed 5830

This is similar to nkint's question from September 11, 2013. Link is here: how to get all undistorted image with opencv I'm a new user, so I didn't have enough reputation/clout to comment on the OP.

I have tried to emulate the code andrewmkeller posted, using Python instead of C++, with some minor changes based on Josh Bosch's response. The result is the following:

  #!/usr/bin/env python

  import cv2
  import numpy as np

  def loadUndistortedImage(fileName):
      # load image
      image = cv2.imread(fileName)
      #print(image)

      # set distortion coeff and intrinsic camera matrix (focal length, centerpoint offset, x-y skew)
      cameraMatrix = np.array([[894.96803896,0,470.38713516],[0,901.32629374,922.41232898], [0,0,1]])
      distCoeffs = np.array([[-0.340671222,0.110426603,-.000867987573,0.000189669273,-0.0160049526]])

      # setup enlargement and offset for new image
      y_shift = 60    #experiment with
      x_shift = 70    #experiment with    
      imageShape = image.shape  #image.size
      print(imageShape)
      imageSize = (int(imageShape[0])+2*y_shift, int(imageShape[1])+2*x_shift, 3)
      print(imageSize)
    
      # create a new camera matrix with the principal point offest according to the offset above
      newCameraMatrix, validPixROI = cv2.getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, imageSize,
        1)
      #newCameraMatrix = cv2.getDefaultNewCameraMatrix(cameraMatrix, imageSize, True) # imageSize, True

      # create undistortion maps
      R = np.array([[1,0,0],[0,1,0],[0,0,1]])
      map1, map2 = cv2.initUndistortRectifyMap(cameraMatrix, distCoeffs, R, newCameraMatrix, imageSize,
        cv2.CV_16SC2)

      # remap
      outputImage = cv2.remap(image, map1, map2, INTER_LINEAR)
      #save output image as file with "FIX" appened to name - only works with .jpg files at the moment
      index = filename.find('.jpg')
      fixed_filename = filename[:index] +'_undistorted'+fileName[index:]
      cv2.imwrite(fixed_filename, outputImage)
      cv2.imshow('fix_img',outputImage)
      cv2.waitKey(0)
      return
    
  #Undistort the images, then save the restored images
  loadUndistortedImage('./calib/WIN_20200626_11_29_16_Pro.jpg')

This seemed good to me, but then problems came up when trying to use cv2.getOptimalNewCameraMatrix or cv2.getDefaultNewCameraMatrix and cv2.initUndistortRectifyMap. I kept getting told that 'the argument takes exactly 2 arguments (3 given)' even though I am putting the parameters as specified in their documentation here: https://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html https://docs.opencv.org/2.4/modules/imgproc/doc/geometric_transformations.html

I can remove the error from "...getDefault..." if I remove the optional params, but I'd rather not do that.

Stacktrace:

Traceback (most recent call last):
  File ".\main.py", line 46, in <module>
    loadUndistortedImage('./<image file name>.jpg')
  File ".\main.py", line 27, in loadUndistortedImage
    newCameraMatrix, validPixROI = cv2.getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, imageSize, 1)
TypeError: function takes exactly 2 arguments (3 given)
2 Answers

I don't have enough reputation to comment, but you could try:

newcameramatrix, _ = cv2.getOptimalCameraMatrix(
    camera_matrix, dist_coeffs, (width, height), 1, (width, height)
)

According to this, that's how the function should be called.

Now, instead of getting the undistorted image with cv2.initUndistortRectifyMap, you could just do:

undistorted_image = cv2.undistort(
    image, camera_matrix, dist_coeffs, None, newcameramatrix
)
cv2.imshow("undistorted", undistorted_image)

Following up to my comment on Sebastian Liendo's answer, and also thanks to a Finnish responder on Github (whose Issues are not for these sort of general questions, I learned), here is 1) the updated documentation for the python functions, and 2) the heart of my revised code which does a decent job of getting around the cropping. (Don't do what I did in the question and post the ENTIRE code, just the part essential to your question.)

  1. https://docs.opencv.org/4.3.0/d9/d0c/group_calib3d.html#ga7a6c4e032c97f03ba747966e6ad862b1

  2. #load image image = cv2.imread(fileName) #images = glob.glob(pathName + '*.jpg') #loop within a specified directory #for fileName in images: #image = cv2.imread(fileName)

    #set camera parameters height, width = image.shape[:2] cameraMatrix = np.array([[894.96803896,0,470.38713516],[0,901.32629374,922.41232898], [0,0,1]]) distCoeffs = np.array([[-0.340671222,0.110426603,-.000867987573,0.000189669273,-0.0160049526]])

    #create new camera matrix newCameraMatrix, validPixROI = cv2.getOptimalNewCameraMatrix(cameraMatrix, distCoeffs,(width, height), 1, (width, height))

    #undistort outputImage = cv2.undistort(image, cameraMatrix, distCoeffs, None, newCameraMatrix)

    #crop, modified x, y, w, h = validPixROI #(211, 991, 547, 755) outputImage = outputImage[y-200:y+h+200, x-40:x+w+80] #fudge factor to minimize cropping

THE ONE CAVEAT: this code still crops a bit of the outer-trim of the original capture, but not by much. Minimizing that cropping is the reason for the fudge factor I put in the ouputImage = outputImage[...] line.

Related