相机校准,焦距值似乎太大

Posted

技术标签:

【中文标题】相机校准,焦距值似乎太大【英文标题】:Camera calibration, focal length value seems too large 【发布时间】:2021-11-08 13:02:48 【问题描述】:

我尝试使用 python 和 opencv 进行相机校准以找到相机矩阵。我使用了此链接中的以下代码

https://automaticaddison.com/how-to-perform-camera-calibration-using-opencv/

import cv2 # Import the OpenCV library to enable computer vision
import numpy as np # Import the NumPy scientific computing library
import glob # Used to get retrieve files that have a specified pattern
 
# Path to the image that you want to undistort
distorted_img_filename = r'C:\Users\uid20832\3.jpg'
 
# Chessboard dimensions
number_of_squares_X = 10 # Number of chessboard squares along the x-axis
number_of_squares_Y = 7  # Number of chessboard squares along the y-axis
nX = number_of_squares_X - 1 # Number of interior corners along x-axis
nY = number_of_squares_Y - 1 # Number of interior corners along y-axis
 
# Store vectors of 3D points for all chessboard images (world coordinate frame)
object_points = []
 
# Store vectors of 2D points for all chessboard images (camera coordinate frame)
image_points = []
 
# Set termination criteria. We stop either when an accuracy is reached or when
# we have finished a certain number of iterations.
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
 
# Define real world coordinates for points in the 3D coordinate frame
# Object points are (0,0,0), (1,0,0), (2,0,0) ...., (5,8,0)
object_points_3D = np.zeros((nX * nY, 3), np.float32)       
 
# These are the x and y coordinates                                              
object_points_3D[:,:2] = np.mgrid[0:nY, 0:nX].T.reshape(-1, 2) 
 
def main():
     
  # Get the file path for images in the current directory
  images = glob.glob(r'C:\Users\Kalibrierung\*.jpg')
     
  # Go through each chessboard image, one by one
  for image_file in images:
  
    # Load the image
    image = cv2.imread(image_file)  
 
    # Convert the image to grayscale
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)  
 
    # Find the corners on the chessboard
    success, corners = cv2.findChessboardCorners(gray, (nY, nX), None)
     
    # If the corners are found by the algorithm, draw them
    if success == True:
 
      # Append object points
      object_points.append(object_points_3D)
 
      # Find more exact corner pixels       
      corners_2 = cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), criteria)       
       
            # Append image points
      image_points.append(corners)
 
      # Draw the corners
      cv2.drawChessboardCorners(image, (nY, nX), corners_2, success)
 
      # Display the image. Used for testing.
      #cv2.imshow("Image", image) 
     
      # Display the window for a short period. Used for testing.
      #cv2.waitKey(200) 
                                                                                                                     
  # Now take a distorted image and undistort it 
  distorted_image = cv2.imread(distorted_img_filename)
 
  # Perform camera calibration to return the camera matrix, distortion coefficients, rotation and translation vectors etc 
  ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(object_points, 
                                                    image_points, 
                                                    gray.shape[::-1], 
                                                    None, 
                                                    None)

但我认为我总是得到错误的参数。从校准开始,我在 x 和 y 方向上的焦距约为 1750。我认为这不可能是正确的,它几乎是。相机文档说焦距在 4-7 毫米之间。但我不确定,为什么从校准来看它如此之高。这是我的一些校准照片。也许他们有什么问题。我将摄像机下方的棋盘朝不同的方向、角度和高度移动。

我也想知道,为什么我不需要代码中正方形的大小。有人可以向我解释一下吗?还是我在某处忘记了这个输入?

【问题讨论】:

为什么你认为校准结果是错误的? 我假设 cameramatrix 的输出以 mm 为单位。但正如答案中所解释的,现在我知道它是以像素为单位的。 【参考方案1】:

您的误解是关于“焦距”。这是一个超载的术语。

光学部分的“焦距”(单位mm):描述镜头平面和图像/传感器平面之间的距离 相机矩阵中的“焦距”(单位像素):它描述了将现实世界映射到一定分辨率的图片的比例因子

1750 很可能是正确的,如果您有高分辨率图片(全高清或其他)。

计算如下:

f [像素] = (焦距 [mm]) / (像素间距 [µm / 像素])

(注意单位和前缀,1 mm = 1000 µm)

示例:Pixel 4a 手机,像素间距为 1.40 µm,焦距为 4.38 mm,f = ~3128.57 (= fx = fy)。

另一个示例:像素 4a 的对角视场角约为 77.7 度,分辨率为 4032 x 3024 像素,因此对角线为 5040 像素。你可以计算:

f = (5040 / 2) / tan(~77.7° / 2)

f = ~3128.6 [像素]

您可以将该计算应用于您知道其视野和图片大小的任意相机。如果对角线分辨率不明确,则使用 horizo​​ntal FoV 和水平分辨率。如果传感器不是 16:9,但您从中拍摄的视频被 裁剪 到 16:9,则可能会发生这种情况...假设裁剪仅垂直裁剪,而仅保留水平。 p>


为什么在这段代码中不需要棋盘格的大小?因为它只校准内在参数(相机矩阵和失真系数)。这些不取决于与棋盘或场景中任何其他对象的距离。

如果您要校准外部参数,即立体设置中相机的距离,那么您需要给出正方形的大小。

【讨论】:

非常感谢。您的解释有助于我了解以毫米为单位的焦距和像素之间的区别。我的想法是用 px[cm]= z/fx (u - cx) 和 py[cm]= z/fy (v - cy) 的相机矩阵计算对象大小。 Z 是传感器测量的到我的物体的距离。所以我用像素的焦距,对吧? 看起来正确,但我猜是因为您没有解释变量。取大小 x_p [像素] 和到对象 z [米] 的距离,并计算:x [m] = x_p [像素] / fx [像素] * z [m] 对不起,我忘了解释其他变量。 fx/fy 是焦距。 U/V 是以像素为单位的图片坐标。而 cx/cy 是校准矩阵中的相机主要点。你能解释一下你对x_p的意思吗?这是我的像素点吗?我认为 c_x/c_y 是从图片坐标转换为相机坐标?!但这对于获取对象大小是不必要的,对吧? 您可以将长度测量为 差异(以像素为单位)。在这种情况下,绝对像素坐标无关紧要。你可以和他们一起工作,但是你有以像素或米为单位的“绝对”位置,任何你想知道大小的东西,你都会计算这些绝对位置的差异,以获得长度......玩数字。在方格纸上绘制 2D 草图并探索数学。

以上是关于相机校准,焦距值似乎太大的主要内容,如果未能解决你的问题,请参考以下文章

来自 Unity 的相机内在矩阵

确定适合相机校准的校准板尺寸

相同相机的内在参数相同?

使用适用于 Matlab 与 OpenCV 的相机校准工具箱进行校准

OpenCV 2.3 相机校准

来自形成立体系统的两个校准相机的 openCV 深度图