Skip to content

Analysis

Analysis functions.

compute_reprojection_arrows(camera, correspondence, pose)

Compute reprojection arrows for a single correspondence.

Parameters:

Name Type Description Default
camera CameraBase

Camera model.

required
correspondence Correspondence

Correspondence.

required
pose Pose3D

Camera-relative pose of the correspondence object point frame.

required

Returns:

Type Description
ndarray

Reprojection arrows as an Nx4 numpy array. Each row represents (x1, y1, x2, y2) for a single arrow, where (x1, y1) is the image point and (x2, y2) is the reprojected point.

Source code in src/compas_camcal/util/analysis.py
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
def compute_reprojection_arrows(
    camera: CameraBase, correspondence: Correspondence, pose: Pose3D
) -> np.ndarray:
    """
    Compute reprojection arrows for a single correspondence.

    Args:
        camera: Camera model.
        correspondence: Correspondence.
        pose: Camera-relative pose of the correspondence object point frame.

    Returns:
        Reprojection arrows as an Nx4 numpy array. Each row represents (x1, y1, x2, y2) for a single arrow, where (x1, y1) is the image point and (x2, y2) is the reprojected point.
    """
    # Project the object points into the image plane
    object_points = correspondence.object_points.T
    reprojected_points = camera.project(object_points, pose)

    image_points = correspondence.image_points[0].reshape(-1, 2)

    # Pack the points into an Nx4 array
    reprojection_arrows = np.hstack((image_points, reprojected_points))

    return reprojection_arrows

compute_reprojection_error(camera, correspondence, pose)

Compute reprojection error for a single correspondence.

Parameters:

Name Type Description Default
camera CameraBase

Camera model.

required
correspondence Correspondence

Correspondence.

required
pose Pose3D

Camera-relative pose of the correspondence object point frame.

required

Returns:

Type Description
float

Reprojection error.

Source code in src/compas_camcal/util/analysis.py
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
def compute_reprojection_error(
    camera: CameraBase, correspondence: Correspondence, pose: Pose3D
) -> float:
    """
    Compute reprojection error for a single correspondence.

    Args:
        camera: Camera model.
        correspondence: Correspondence.
        pose: Camera-relative pose of the correspondence object point frame.

    Returns:
        Reprojection error.
    """
    # Project the object points into the image plane
    object_points = correspondence.object_points.T
    reprojected_points = camera.project(object_points, pose)

    image_points = correspondence.image_points.reshape(-1, 2)

    # Compute the reprojected point errors
    reprojected_point_errors = np.linalg.norm(reprojected_points - image_points, axis=1)

    # Return the reprojection error as the mean of the reprojected point errors
    return np.mean(reprojected_point_errors)

compute_reprojection_errors_opencv(camera_matrix, distortion_coefficients, rvecs, tvecs, object_points, image_points)

Compute reprojection errors from OpenCV calibration results.

M is the number of images/views.

Parameters:

Name Type Description Default
camera_matrix ndarray

3x3 camera matrix.

required
distortion_coefficients ndarray

1xN distortion coefficients.

required
rvecs Iterable[ndarray]

M Rodrigues rotation vectors.

required
tvecs Iterable[ndarray]

M Translation vectors.

required
object_points ndarray

Object points as an MxNx3 numpy array.

required
image_points ndarray

Image points as an MxNx2 numpy array.

required

Returns:

Type Description
List[float]

Mean reprojection error for each image.

Source code in src/compas_camcal/util/analysis.py
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
def compute_reprojection_errors_opencv(
    camera_matrix: np.ndarray,
    distortion_coefficients: np.ndarray,
    rvecs: Iterable[np.ndarray],
    tvecs: Iterable[np.ndarray],
    object_points: np.ndarray,
    image_points: np.ndarray,
) -> List[float]:
    """
    Compute reprojection errors from OpenCV calibration results.

    M is the number of images/views.

    Args:
        camera_matrix: 3x3 camera matrix.
        distortion_coefficients: 1xN distortion coefficients.
        rvecs: M Rodrigues rotation vectors.
        tvecs: M Translation vectors.
        object_points: Object points as an MxNx3 numpy array.
        image_points: Image points as an MxNx2 numpy array.

    Returns:
        Mean reprojection error for each image.
    """
    # Reshape camera matrix and distortion coefficients
    camera_matrix = camera_matrix.reshape(3, 3)
    distortion_coefficients = distortion_coefficients.reshape(1, -1)

    # Compute reprojection error per image
    reprojection_errors = []
    for object_points_im, image_points_im, rvec, tvec in zip(
        object_points, image_points, rvecs, tvecs
    ):
        # Project object points into the image plane
        reprojected_points, _ = cv2.projectPoints(
            object_points_im, rvec, tvec, camera_matrix, distortion_coefficients
        )

        reprojected_points = reprojected_points.reshape(-1, 2)  # Nx2
        image_points_im = image_points_im.reshape(-1, 2)  # Nx2

        # Compute point-to-point errors as L2 norm
        reprojected_point_errors = np.linalg.norm(reprojected_points - image_points_im, axis=1)

        # Compute mean error
        reprojection_error = np.mean(reprojected_point_errors)
        reprojection_errors.append(reprojection_error)

    return reprojection_errors

compute_reprojection_errors_opencv_fisheye(camera_matrix, distortion_coefficients, rvecs, tvecs, object_points, image_points)

Compute reprojection errors from OpenCV fisheye calibration results.

M is the number of images/views.

Parameters:

Name Type Description Default
camera_matrix ndarray

3x3 camera matrix.

required
distortion_coefficients ndarray

1x4 distortion coefficients.

required
rvecs Iterable[ndarray]

M Rodrigues rotation vectors.

required
tvecs Iterable[ndarray]

M Translation vectors.

required
object_points ndarray

Object points as an MxNx3 numpy array.

required
image_points ndarray

Image points as an MxNx2 numpy array.

required

Returns:

Type Description
List[float]

Mean reprojection error for each image.

Source code in src/compas_camcal/util/analysis.py
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
def compute_reprojection_errors_opencv_fisheye(
    camera_matrix: np.ndarray,
    distortion_coefficients: np.ndarray,
    rvecs: Iterable[np.ndarray],
    tvecs: Iterable[np.ndarray],
    object_points: np.ndarray,
    image_points: np.ndarray,
) -> List[float]:
    """
    Compute reprojection errors from OpenCV fisheye calibration results.

    M is the number of images/views.

    Args:
        camera_matrix: 3x3 camera matrix.
        distortion_coefficients: 1x4 distortion coefficients.
        rvecs: M Rodrigues rotation vectors.
        tvecs: M Translation vectors.
        object_points: Object points as an MxNx3 numpy array.
        image_points: Image points as an MxNx2 numpy array.

    Returns:
        Mean reprojection error for each image.
    """
    # Reshape camera matrix and distortion coefficients
    camera_matrix = camera_matrix.reshape(3, 3)
    distortion_coefficients = distortion_coefficients.reshape(1, 4)

    # Compute reprojection error per image
    reprojection_errors = []
    for object_points_im, image_points_im, rvec, tvec in zip(
        object_points, image_points, rvecs, tvecs
    ):
        # Project object points into the image plane
        reprojected_points, _ = cv2.fisheye.projectPoints(
            object_points_im, rvec, tvec, camera_matrix, distortion_coefficients
        )

        reprojected_points = reprojected_points.reshape(-1, 2)  # Nx2
        image_points_im = image_points_im.reshape(-1, 2)  # Nx2

        # Compute point-to-point errors as L2 norm
        reprojected_point_errors = np.linalg.norm(reprojected_points - image_points_im, axis=1)

        # Compute mean error
        reprojection_error = np.mean(reprojected_point_errors)
        reprojection_errors.append(reprojection_error)

    return reprojection_errors

estimate_pose(camera, correspondence)

Estimate the pose of a correspondence using cv::solvePnP.

Parameters:

Name Type Description Default
camera Camera

Camera model.

required
correspondence Correspondence

Correspondence.

required

Returns:

Type Description
Pose3D

Estimated pose.

Source code in src/compas_camcal/util/analysis.py
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
def estimate_pose(camera: Camera, correspondence: Correspondence) -> Pose3D:
    """
    Estimate the pose of a correspondence using cv::solvePnP.

    Args:
        camera: Camera model.
        correspondence: Correspondence.

    Returns:
        Estimated pose.
    """
    # Solve PnP
    retval, rvec, tvec = cv2.solvePnP(
        correspondence.object_points,
        correspondence.image_points[0],
        camera.intrinsics.to_matrix(),
        camera.distortion.to_vector(),
    )

    # Convert to pose
    pose = Pose3D(rotation=Rotation3D_Rodrigues(rvec), translation=Translation3D.from_vector(tvec))

    return pose