Skip to content

Incorrect test for degenerate transform #7770

@matthew-brett

Description

@matthew-brett

Description:

While going through the tests for the estimate functionality of Transforms, I noticed a problem with the test for the degenerate transforms.

The test (at https://github.com/scikit-image/scikit-image/blob/main/skimage/transform/tests/test_geometric.py#L943)) has this:

    tform = AffineTransform()
    assert not tform.estimate(src, dst)
    # Prior to gh-6207, the above would set the parameters as the identity.
    assert np.all(np.isnan(tform.params))

However, this is accidentally using a bug in the scikit-image code, whereby tform = AffineTransform() (a 2D identity transform) gets estimated with 3D points. Because this is a 2D transform, and the tform has a 3x3 identity matrix, the code at https://github.com/scikit-image/scikit-image/blob/main/skimage/transform/_geometric.py#L1097 tells the estimation routine to use the wrong subset of the 4x4 matrix parameters that estimate is working on (at https://github.com/scikit-image/scikit-image/blob/main/skimage/transform/_geometric.py#L878). For this incorrect subset, the generated A matrix does lead to the test passing (V[-1, -1] is very close to zero). However, if you prevent this incorrect subset by starting with a 3D identity transform (tform = AffineTransform.identity(3)), with, therefore, a 4x4 identity transformation matrix, the test does not generate V[-1, -1] close to 0 (it's in the region of 0.5), and the test fails.

I think the test is incorrect. I'm afraid I didn't understand the explanation of the test:

        # if the last element of the vector corresponding to the smallest
        # singular value is close to zero, this implies a degenerate case
        # because it is a rank-defective transform, which would map points
        # to a line rather than a plane.

because I did not know why that was the correct test for the rank-defective transform.

V comes from:

        _, _, V = np.linalg.svd(A)

Failing to understand that logic, and to make the fixed (AffineTransform.identity(3)) version of the test pass, I also check the generated H matrix for rank deficiency:

        if np.isclose(np.linalg.det(H), 0):
            self.params = np.full((d + 1, d + 1), np.nan)
            return False

but I'm not confident that is the right test.

@cv3d - I think this is your code, from #3926? Could you explain more? What is the correct test for the correct initial (3D) transform?

Way to reproduce:

No response

Version information:

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions