以像素为单位的焦距是线性测量吗

Is focal length in pixel unit a linear measurment

我有一个云台变焦相机(随时间改变焦距)。不知道它的基本焦距(例如时间点 0 的焦距)。但是,可以根据一些已知的约束和假设(做 SLAM)来跟踪帧与另一帧之间的焦距变化。

如果我假设一个随机焦距(以像素为单位),例如1000像素。然后,逐帧跟踪新的焦距。我会得到相对正确的结果吗?每帧中的结果(焦距)是否正确以符合地面实况焦距?

对于摇摄和倾斜,假设开始时为 0 是有效的。尽管它不正确,但新 tili-pan 的估计值将正确到一个偏移量。但是,我怀疑估计的焦距甚至不符合比例或偏移量。它是否正确?

快速简短回答 - 如果 pan-tilt-zoom 相机近似为薄镜头,则这是距离 (z) 和焦距 (f) 之间的关系:

这只是一个近似值。不完全正确。有关更精确的计算,请参阅 camera matrix. Focal length is an intrinsic parameter in the camera matrix. Even if not known, it can be calculated using some camera calibration method such as DLT, Zhang's Method and RANSAC。一旦你有了相机矩阵,焦距只是其中的一小部分。你会得到更多有用的东西。

OpenCV 内置了 Zhang 方法的实现。 (查看此 documentation 以获得解释,但代码陈旧且无法使用。下面是新的 up-to-date 代码。)您需要通过相机拍摄一些棋盘的照片。这是一些帮助代码:

import cv2
from matplotlib import pyplot as plt
import numpy as np
from glob import glob
from scipy import linalg

x,y = np.meshgrid(range(6),range(8))

world_points=np.hstack((x.reshape(48,1),y.reshape(48,1),np.zeros((48,1)))).astype(np.float32)

_3d_points=[]
_2d_points=[]

img_paths=glob('./*.JPG') #get paths of all checkerboard images

for path in img_paths:
    im=cv2.imread(path)
    
    ret, corners = cv2.findChessboardCorners(im, (6,8))
    
    if ret: #add points only if checkerboard was correctly detected:
        _2d_points.append(corners) #append current 2D points
        _3d_points.append(world_points) #3D points are always the same


ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(_3d_points, _2d_points, (im.shape[1],im.shape[0]), None, None)

print ("Ret:\n",ret)
print ("Mtx:\n",mtx)
print ("Dist:\n",dist)

您可能需要不失真:校正径向失真

# termination criteria
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)

# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((6*8,3), np.float32)
objp[:,:2] = np.mgrid[0:6,0:8].T.reshape(-1,2)

# Arrays to store object points and image points from all the images.
objpoints = [] # 3d point in real world space
imgpoints = [] # 2d points in image plane.

for fname in img_paths:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

    # Find the chess board corners
    ret, corners = cv2.findChessboardCorners(gray, (6,8),None)    
    
    # If found, add object points, image points (after refining them)
    if ret == True:
        objpoints.append(objp)

        cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
        imgpoints.append(corners)

    if 'IMG_5456.JPG' in fname:
        plt.figure(figsize=(20,10))
        img_vis=img.copy()
        cv2.drawChessboardCorners(img_vis, (6,8), corners, ret) 
        plt.imshow(img_vis)
        plt.show() 

#Calibration
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1],None,None)

# Reprojection Error
tot_error = 0
for i in range(len(objpoints)):
    imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
    error = cv2.norm(imgpoints[i],imgpoints2, cv2.NORM_L2)/len(imgpoints2)
    tot_error += error

print ("Mean Reprojection error: ", tot_error/len(objpoints))

# undistort
mapx,mapy = cv2.initUndistortRectifyMap(mtx,dist,None,newcameramtx,(w,h),5)
dst = cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)
# crop the image
x,y,w,h = roi
dst = dst[y:y+h, x:x+w]
plt.figure(figsize=(20,10))
#cv2.drawChessboardCorners(dst, (6,8), corners, ret) 
plt.imshow(dst)
plt.show() 

# Reprojection Error
tot_error = 0
for i in range(len(objpoints)):
    imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
    error = cv2.norm(imgpoints[i],imgpoints2, cv2.NORM_L2)/len(imgpoints2)
    tot_error += error

print ("Mean Reprojection error: ", tot_error/len(objpoints))