Python 摄像机校准,将像素反向投影到方向

Python 摄像机校准,将像素反向投影到方向,python,image,opencv,matrix,camera-calibration,Python,Image,Opencv,Matrix,Camera Calibration,我使用OpenCV从一系列棋盘图像中估计网络摄像头的固有矩阵,如本文所述,并将像素反向投影到一个方向(根据方位角/仰角) 最终目标是让用户选择图像上的一个点,估计该点相对于网络摄像头中心的方向,并将其用作波束形成算法的DOA。 因此,一旦我估计了内在矩阵,我就反向投影用户选择的像素(见下面的代码),并将其显示为方位角/仰角 result = [0, 0, 0] # reverse projected point, in homogeneous coord. while 1: _, im

我使用OpenCV从一系列棋盘图像中估计网络摄像头的固有矩阵,如本文所述,并将像素反向投影到一个方向(根据方位角/仰角)

最终目标是让用户选择图像上的一个点,估计该点相对于网络摄像头中心的方向,并将其用作波束形成算法的DOA。

因此,一旦我估计了内在矩阵,我就反向投影用户选择的像素(见下面的代码),并将其显示为方位角/仰角

result = [0, 0, 0]  # reverse projected point, in homogeneous coord.
while 1:
    _, img = cap.read()
    if flag:  # If the user has clicked somewhere
        result = np.dot(np.linalg.inv(mtx), [mouse_x, mouse_y, 1])
        result = np.arctan(result)  # convert to angle
        flag = False

    cv2.putText(img, '({},{})'.format(mouse_x, mouse_y), (20, 440), cv2.FONT_HERSHEY_SIMPLEX,
                0.5, (0, 255, 0), 2, cv2.LINE_AA)
    cv2.putText(img, '({:.2f},{:.2f})'.format(180/np.pi*result[0], 180/np.pi*result[1]), (20, 460),
                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2, cv2.LINE_AA)

    cv2.imshow('image', img)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
我的问题是我不确定我的结果是否一致。主要的不一致之处在于,与{0,0}角度相对应的图像点明显偏离图像中心,如下所示(出于隐私原因,相机图像已被黑色背景替换):

我真的看不到一种简单而有效的测量精度的方法(我能想到的唯一方法是使用一个伺服电机,上面有一个激光器,就在相机下面,并将其指向计算出的方向)

以下是15幅图像校准后的固有矩阵:

我得到的误差约为0.44 RMS,这似乎令人满意

我的校准代码:

nCalFrames = 12  # number of frames for calibration
nFrames = 0
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)  # termination criteria

objp = np.zeros((9*7, 3), np.float32)
objp[:, :2] = np.mgrid[0:9, 0:7].T.reshape(-1, 2)
objpoints = []  # 3d point in real world space
imgpoints = []  # 2d points in image plane.

cap = cv2.VideoCapture(0)
previousTime = 0
gray = 0

while 1:
    # Capture frame-by-frame
    _, img = cap.read()

    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # Find the chess board corners
    ret, corners = cv2.findChessboardCorners(gray, (9, 7), None)

    # If found, add object points, image points (after refining them)
    if ret:

        corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)

        if time.time() - previousTime > 2:
            previousTime = time.time()
            imgpoints.append(corners2)
            objpoints.append(objp)
            img = cv2.bitwise_not(img)
            nFrames = nFrames + 1

        # Draw and display the corners
        img = cv2.drawChessboardCorners(img, (9, 7), corners, ret)

    cv2.putText(img, '{}/{}'.format(nFrames, nCalFrames), (20, 460), cv2.FONT_HERSHEY_SIMPLEX,
                2, (0, 255, 0), 2, cv2.LINE_AA)
    cv2.putText(img, 'press \'q\' to exit...', (255, 15), cv2.FONT_HERSHEY_SIMPLEX,
                0.5, (0, 0, 255), 1, cv2.LINE_AA)
    # Display the resulting frame
    cv2.imshow('Webcam Calibration', img)
    if nFrames == nCalFrames:
        break

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

RMS_error, mtx, disto_coef, _, _ = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)

编辑:另一种测试方法是使用具有已知角度点的白板,并通过与实验结果比较来估计误差,但我不知道如何设置这样的系统

关于您的第一个问题,将主点偏离图像中心是正常的。估计点,即零仰角和方位角的点,是使径向畸变系数最小的点,对于低值广角镜头(例如,典型网络摄像头的广角镜头),它可以很容易地以明显的数量关闭

在调用
calibrateCamera
之前,您的校准应该是正常的。然而,在代码片段中,似乎忽略了失真系数。缺少的是
initundistorticrectitymap
,如果重要的话,它还可以让您重新调整主要点的中心

h,  w = img.shape[:2]
# compute new camera matrix with central principal point
new_mtx,roi = cv2.getOptimalNewCameraMatrix(mtx,disto_coef,(w,h),1,(w,h))
print(new_mtx)
# compute undistort maps
mapx,mapy = cv2.initUndistortRectifyMap(mtx,disto_coef,None,new_mtx,(w,h),5)
它基本上使焦距在两个维度上相等,并以主点为中心(有关参数,请参阅OpenCV python文档)

然后,每次

_, img = cap.read()
在渲染之前,必须取消对图像的扭曲

# apply the remap
img = cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)
# crop the image
x,y,w,h = roi
img = img[y:y+h, x:x+w]
在这里,我将背景设置为绿色,以强调桶形失真。输出可能是这样的(出于隐私原因,相机图像被棋盘格取代):

如果你做了所有这些,你的校准目标是准确的,你的校准样本充满了整个图像区域,你应该对计算充满信心。然而,为了验证相对于未失真图像像素读数的测量方位角和仰角,我可能建议从镜头第一个主点开始测量,并在相机正前方以正常角度放置校准板。在那里,您可以计算预期角度并进行比较


希望这能有所帮助。

看看这些来检查摄像机校准的准确性。是的,我已经看到了,我的RMS误差看起来令人满意,但我想知道的是反向投影(az/el估计),我的代码是否正确……我可能在这里偏离了基准(已经有一段时间了),但是,在执行arctan函数之前,难道不需要将结果乘以[R | t]矩阵的逆吗?我想你的点在度量相机坐标系中,但不是世界坐标系中。你是对的,如果我想要世界坐标系中的点,我需要外部矩阵,但考虑到我只需要一个方向向量,我认为度量坐标系中的图像点就足够了:我的向量从相机原点到这一点,它的az/el角与拉伸参数无关(如果我错了,请纠正我)。移动相机,穿过像素的光线不会改变方向非常感谢这个答案!我确实没有应用失真校正,因为我认为失真系数是内在矩阵的一部分,因此反向投影是正确的。如果您允许,我将在稍后编辑您的答案,将代码更改回python,只是为了与问题更加一致!