Exemplo n.º 1
0
def test_separate_lens_keystone_and_real_point_correction_calls_are_equivalent_to_a_single_combined_call(
):

    image = cv.imread("sample_images/002h.bmp")

    config = calib.Config()
    config.populate_lens_parameters_from_chessboard(image, 6, 8)
    config.populate_keystone_and_real_parameters_from_chessboard(
        image, 8, 6, 90.06, 64.45)

    _, points = cv.findChessboardCorners(image, (6, 8))

    camera_corrected = calib.correct_points(points, config,
                                            calib.Correction.lens_and_keystone)
    real_converted = calib.correct_points(camera_corrected, config,
                                          calib.Correction.real_coordinates)

    combo_corrected = calib.correct_points(
        points, config, calib.Correction.lens_keystone_and_real_coordinates)

    assert np.array_equal(real_converted, combo_corrected)
def test_homography_border_doesnt_affect_point_transforms():
    default_border_config = calib.Config()
    assert default_border_config.populate_lens_parameters_from_chessboard(
        "sample_images/002h.bmp", 6, 8
    ), "Unable to populate distortion parameters"
    assert default_border_config.populate_keystone_and_real_parameters_from_chessboard(
        "sample_images/002h.bmp", 8, 6, 90.06, 64.45
    ), "Unable to populate homography parameters"

    custom_border_config = calib.Config()
    assert custom_border_config.populate_lens_parameters_from_chessboard(
        "sample_images/002h.bmp", 6, 8
    ), "Unable to populate distortion parameters"
    assert custom_border_config.populate_keystone_and_real_parameters_from_chessboard(
        "sample_images/002h.bmp", 8, 6, 90.06, 64.45, border=200
    ), "Unable to populate homography parameters"

    found, corners = cv.findChessboardCorners(
        cv.imread("sample_images/mocked checkboard.png"), (15, 10)
    )
    targets = np.zeros((len(corners), 2), np.float32)
    for i in range(15):
        for j in range(10):
            targets[j * 15 + i, 0] = 70 * (14 - i) / 14.0
            targets[j * 15 + i, 1] = 45 * (9 - j) / 9.0

    default_border_transformed = calib.correct_points(
        corners,
        default_border_config,
        calib.Correction.lens_keystone_and_real_coordinates,
    )
    custom_border_transformed = calib.correct_points(
        corners,
        custom_border_config,
        calib.Correction.lens_keystone_and_real_coordinates,
    )

    assert np.allclose(
        default_border_transformed, custom_border_transformed
    ), "Real world coordinates not consistent with image borders"
def assess_points_transform_to_given_absolute_accuracy(
    config, points, expectations, accuracy
):
    # type: (calib.Config, np.ndarray, np.ndarray, float) -> None
    """
    Apply a camera correction to points, testing if the result is within the given accuracy of their expected corrected
    values
    :param config: The calibration configuration for a camera to test
    :param points: An (N x 1 x 2) numpy array of points in the camera's image space to correct
    :param expectations: An (N x 2) numpy array of points in real space that the points array is expected to correct to
    :param accuracy: To pass, the config should transform all input points to within this distance of their expectation
    """
    np.set_printoptions(suppress=True)

    corrected_points = calib.correct_points(
        points, config, calib.Correction.lens_keystone_and_real_coordinates
    )

    assert points.shape == corrected_points.shape

    distances = []
    distance_hist = {}
    highest = 0
    for i in range(len(corrected_points)):
        original = points[i, 0]
        point = corrected_points[i, 0]
        expectation = expectations[i]
        assert len(point) == 2
        distance = math.hypot(point[0] - expectation[0], point[1] - expectation[1])
        if distance > highest:
            highest = distance
            print(
                "new highest: px{} res{} exp{} dist{}".format(
                    original, point, expectation, distance
                )
            )
        distances.append(distance)
        microns = math.ceil(distance * 1000)
        if microns in distance_hist:
            distance_hist[microns] += 1
        else:
            distance_hist[microns] = 1

    print("average deviation:\n{}mm".format(sum(distances) / len(distances)))
    print("max deviation:\n{}mm".format(max(distances)))
    print("deviation spread:")
    pprint.pprint(distance_hist)

    assert (
        max(distances) <= accuracy
    ), "Expected and calculated points differed by more than the permitted accuracy"
def assess_config(image_path, chess_image, distorted_grid,
                  corner_only_homography):

    config = calib.Config()
    config.populate_lens_parameters_from_chessboard(chess_image, rows, cols)
    config.populate_keystone_and_real_parameters_from_chessboard(
        chess_image,
        cols,
        rows,
        grid_width,
        grid_height,
        corners_only=corner_only_homography,
        border=200,
    )

    cv.imshow(
        "{} {} lens".format(image_path, corner_only_homography),
        calib.correct_image(chess_image, config,
                            calib.Correction.lens_distortion),
    )
    cv.imshow(
        "{} {} lens and keystone".format(image_path, corner_only_homography),
        calib.correct_image(chess_image, config,
                            calib.Correction.lens_and_keystone),
    )
    # grid[0] -> bottom left
    # grid[10] -> top left
    # grid[-11] -> bottom right
    # grid[-1] -> top right
    expectations = np.zeros((len(distorted_grid), 2), np.float32)
    for i in range(rows):
        for j in range(cols):
            expectations[i + j * rows, 0] = square_edge_mm * j
            expectations[i + j * rows, 1] = grid_height - (square_edge_mm * i)

    corrected_points = calib.correct_points(
        distorted_grid, config,
        calib.Correction.lens_keystone_and_real_coordinates)

    print(config)
    print(config.to_dict())

    distances = []
    distance_hist = {}
    highest = 0
    for i in range(len(corrected_points)):
        original = distorted_grid[i, 0]
        point = corrected_points[i, 0]
        expectation = expectations[i]
        assert len(point) == 2
        dist = distance(point, expectation)
        if dist > highest:
            highest = dist
            print("new highest: px{} res{} exp{} dist{}".format(
                original, point, expectation, dist))
        distances.append(dist)
        mm = math.ceil(dist)
        if mm in distance_hist:
            distance_hist[mm] += 1
        else:
            distance_hist[mm] = 1

    print("image: {} corners_only_homography: {}".format(
        image_path, corner_only_homography))
    print("average deviation:\n{}mm".format(sum(distances) / len(distances)))
    print("max deviation:\n{}mm".format(max(distances)))
    print("deviation spread:")
    pprint(distance_hist)
    (dot_grid_cols, dot_grid_rows),
    cv.CALIB_CB_SYMMETRIC_GRID + cv.CALIB_CB_CLUSTERING,
    dot_detector,
    cv.CirclesGridFinderParameters(),
)

if not found:
    print("Could not find dot grid in {}".format(dot_grid_image_path))
    exit()

distorted_points = np.array(
    [[point] for point in cv.KeyPoint_convert(dot_detector.detect(dot_grid_image))],
    np.float32,
)
transformed_points = calib.correct_points(
    distorted_points, chess_config, calib.Correction.lens_distortion
)

# Use the lens-corrected grid and lens corrected points from the original image to determine the grid in the distorted image
# The lens distortion in the original image means that OpenCV cannot detect the grid itself
distorted_grid = np.zeros(undistorted_grid.shape, undistorted_grid.dtype)
for i in range(len(undistorted_grid)):
    if i % 100 == 0:
        print("progress: {}/{}".format(i, dot_grid_cols * dot_grid_rows), end="\r")
    # get the point at i in the grid
    grid_member = undistorted_grid[i]
    # find the nearest member of transformed_points, and its undistorted original
    nearest_distance = sys.float_info.max
    original_point = None
    for j in range(len(transformed_points)):
        transformed_point = transformed_points[j]
Exemplo n.º 6
0
def assess_config(image_path, dot_image, distorted_grid,
                  corner_only_homography):
    sparse_rows = rows // 2
    sparse_cols = cols // 2
    sparse_grid = np.zeros((sparse_rows * sparse_cols, 1, 2), np.float32)
    for i in range(sparse_rows):
        for j in range(sparse_cols):
            sparse_grid[i * sparse_cols + j, 0,
                        0] = distorted_grid[(i * 2) * cols + (j * 2), 0, 0]
            sparse_grid[i * sparse_cols + j, 0,
                        1] = distorted_grid[(i * 2) * cols + (j * 2), 0, 1]

    h, w = dot_image.shape[:2]
    dot_config = calib.Config()
    dot_config.populate_lens_parameters_from_grid(sparse_grid, sparse_cols,
                                                  sparse_rows, w, h)

    undistorted_distorted_grid = cv.undistortPoints(
        distorted_grid,
        dot_config.distorted_camera_matrix,
        dot_config.distortion_coefficients,
        P=dot_config.undistorted_camera_matrix,
    )

    dot_config.populate_keystone_and_real_parameters_from_grid(
        undistorted_distorted_grid,
        cols,
        rows,
        84.5,
        57.5,
        corners_only=corner_only_homography,
    )

    expectations = np.zeros((len(distorted_grid), 2), np.float32)
    for i in range(rows):
        for j in range(cols):
            expectations[i * cols + j, 0] = 84.5 - (0.5 * j)
            expectations[i * cols + j, 1] = 57.5 - (0.5 * i)

    corrected_points = calib.correct_points(
        distorted_grid, dot_config,
        calib.Correction.lens_keystone_and_real_coordinates)

    distances = []
    distance_hist = {}
    highest = 0
    for i in range(len(corrected_points)):
        original = distorted_grid[i, 0]
        point = corrected_points[i, 0]
        expectation = expectations[i]
        assert len(point) == 2
        dist = distance(point, expectation)
        if dist > highest:
            highest = dist
            print("new highest: px{} res{} exp{} dist{}".format(
                original, point, expectation, dist))
        distances.append(dist)
        microns = math.ceil(dist * 1000)
        if microns in distance_hist:
            distance_hist[microns] += 1
        else:
            distance_hist[microns] = 1

    print("image: {} corners_only_homography: {}".format(
        image_path, corner_only_homography))
    print("average deviation:\n{}mm".format(sum(distances) / len(distances)))
    print("max deviation:\n{}mm".format(max(distances)))
    print("deviation spread:")
    pprint(distance_hist)
params.maxArea = 1000
params.filterByArea = True
params.minCircularity = 0.2
params.filterByCircularity = True
params.blobColor = 0
params.filterByColor = True
dot_detector = cv.SimpleBlobDetector_create(params)

dot_image = cv.imread(dot_grid_image_path)
distorted_points = np.array(
    [[point] for point in cv.KeyPoint_convert(dot_detector.detect(dot_image))],
    np.float32,
)

corrected_points = calib.correct_points(
    distorted_points, dot_config,
    calib.Correction.lens_keystone_and_real_coordinates)

distances = []
distance_hist = {}
deviation_map = np.zeros((dot_grid_cols, dot_grid_rows), np.float32)

# Determine each corrected points distance from the nearest point in a 0.5mm square grid
for lone_point in corrected_points:
    point = lone_point[0]
    x = int(round(point[0] * 2))
    y = int(round(point[1] * 2))
    expectation = (x / 2.0, y / 2.0)
    deviation = distance(point, expectation)
    distances.append(deviation)
    deviation_map[x, y] = deviation