OpenCV - 如何利用棋盘作为参考获取2D图像的实际距离

13

enter image description here

在检查了几个代码片段之后,我拍了几张照片,找到了棋盘的角点,并使用它们来获取相机矩阵、畸变系数、旋转和平移向量。现在,有人能告诉我需要哪个Python OpenCV函数来计算从2D图像到实际世界的距离吗?projectPoints?例如,使用棋盘作为参考(见图片),如果方块大小为5厘米,则4个方块的距离应为20厘米。我看到了一些函数,如projectPoints、findHomography、solvePnP,但我不确定我需要哪一个来解决我的问题并获取相机世界与棋盘世界之间的转换矩阵。 一个摄像头,相机位置在所有情况下都相同,但不完全位于棋盘上方,并且棋盘放置在一个平面物体(桌子)上。
# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
    objp = np.zeros((nx * ny, 3), np.float32)
    objp[:, :2] = np.mgrid[0:nx, 0:ny].T.reshape(-1, 2)

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

    # Make a list of calibration images
    images = glob.glob(path.join(calib_images_dir, 'calibration*.jpg'))
    print(images)
    # Step through the list and search for chessboard corners
    for filename in images:

        img = cv2.imread(filename)

        imgScale = 0.5
        newX,newY = img.shape[1]*imgScale, img.shape[0]*imgScale
        res = cv2.resize(img,(int(newX),int(newY)))

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

        # Find the chessboard corners
        pattern_found, corners = cv2.findChessboardCorners(gray, (nx,ny), None)

        # If found, add object points, image points (after refining them)
        if pattern_found is True:
            objpoints.append(objp)

            # Increase accuracy using subpixel corner refinement
            cv2.cornerSubPix(gray,corners,(5,5),(-1,-1),(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.1 ))
            imgpoints.append(corners)

            if verbose:
                # Draw and display the corners
                draw = cv2.drawChessboardCorners(res, (nx, ny), corners, pattern_found)
                cv2.imshow('img',draw)
                cv2.waitKey(500)

    if verbose:
        cv2.destroyAllWindows()

    #Now we have our object points and image points, we are ready to go for calibration
    # Get the camera matrix, distortion coefficients, rotation and translation vectors
    ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
    print(mtx)
    print(dist)
    print('rvecs:', type(rvecs),' ',len(rvecs),' ',rvecs)
    print('tvecs:', type(tvecs),' ',len(tvecs),' ',tvecs)

    mean_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)
        mean_error += error

    print("total error: ", mean_error/len(objpoints))


    imagePoints,jacobian = cv2.projectPoints(objpoints[0], rvecs[0], tvecs[0], mtx, dist)
    print('Image points: ',imagePoints)

请看这里 - Rick M.
所以,只是为了确保我理解了,您使用几张棋盘图像进行了校准,现在您想拍摄相同的棋盘图像并计算相机与棋盘之间的距离? - Milo
@Milo,我已经校准了相机以获取相机参数。有了相机矩阵和畸变系数,我的新想法是使用getPerspective方法来获得更好的棋盘视图,那么如果棋盘可以被视为相机正好在棋盘上方,我如何获得实际世界中的大小呢?是否有任何opencv方法可以做到这一点?我发现关于使用像素/毫米比率的信息,但我想知道是否有更好的方法。现在,有没有好的方法将像素转换为米单位?opencv内部有任何方法吗? - Pablo Gonzalez
@PabloGonzalez 为了帮助您,我真的需要了解您的最终目标。您在桌子上放置了一个经过校准的相机,上面会有一个棋盘(但不是直接在相机下方)。您想生成一个“平面”棋盘图像,就像您发布的那个一样,但所有这些都是为了什么目的?只是生成“平面”图像吗?计算新(未知)棋盘的大小?计算棋盘相对于相机的位置?也许您可以在此答案中找到一些信息:https://dev59.com/l7Lma4cB1Zd3GeqPdZuy#55284535 - Milo
@Milo 我想测量瓦片的大小,以便我可以知道毫米/像素比率,并最终使用相机计算真实世界中的长度和尺寸。类似于 https://dev59.com/-VUL5IYBdhLWcg3w-cHr - Pablo Gonzalez
2个回答

9

你说得没错,我认为你应该使用solvePnP来解决这个问题。(在这里阅读更多关于透视n点问题的内容:https://en.wikipedia.org/wiki/Perspective-n-Point。)

Python OpenCV的solvePnP函数接受以下参数,并返回一个输出旋转和输出平移向量,将模型坐标系转换为相机坐标系。

cv2.solvePnP(objectPoints, imagePoints, cameraMatrix, distCoeffs[, rvec[, tvec[, useExtrinsicGuess[, flags]]]]) → retval, rvec, tvec

在您的情况下,imagePoints 将是棋盘角落,因此它将类似于:
ret, rvec, tvec = cv2.solvePnP(objpoints, corners, mtx, dist)

通过返回的翻译向量,您可以计算相机到棋盘的距离。solvePnP 的输出翻译与 objectPoints 中指定的单位相同。

最后,您可以通过欧几里得距离从 tvec 计算实际距离:

d = math.sqrt(tx*tx + ty*ty + tz*tz).

感谢您的贡献。这个“d”结果会输出从相机到物体的距离,对吗?如果是的话,我的目标是确定瓷砖的实际大小。我的新想法是使用getPerspective方法来获得更好的棋盘视图,那么如果棋盘可以被视为相机正好在棋盘上方,我如何获得实际世界中的大小呢?是否有任何opencv方法可以做到这一点?我发现了关于使用px/mm比率的信息,但我想知道是否有更好的方法。 - Pablo Gonzalez
@PabloGonzalez 在使用 solvePnP 函数之前,您需要提前知道棋盘格子的大小。请查看以下示例:https://docs.opencv.org/3.4.2/d7/d53/tutorial_py_pose.html - Yonatan Simson

0

您的问题主要涉及相机校准,特别是在解决opencv中畸变时实现不佳。您必须通过在棋盘的不同坐标处进行少量距离探测来近似计算相机镜头的畸变函数。一个好的想法是首先在镜头中心取小距离,然后再取一个正方形远一点的距离,并重复操作到边缘。这将给出您畸变函数的系数。 Matlab有自己的库可以解决您的问题,精度很高,但价格相当昂贵。
根据:

现在,有人能告诉我需要哪个python opencv函数来计算2D图像中的实际距离吗?

我认为这篇文章很好地解释了Python OpenCV函数集生成实际测量的方法。通过解决系数,如我上面所说,您可以获得很好的准确性。无论如何,我不认为这是一个开源的函数实现。

cv2.GetRealDistance(...)

感谢您的评论。关于GetRealDistance,我刚刚发现了https://github.com/DIPlib/diplib,因此这将为任务提供一些提示。 - Pablo Gonzalez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接