Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Descriptors not consistent across cycled contour indices #14

Open
geloescht opened this issue Mar 21, 2021 · 2 comments
Open

Descriptors not consistent across cycled contour indices #14

geloescht opened this issue Mar 21, 2021 · 2 comments

Comments

@geloescht
Copy link
Contributor

Description

I am trying to create invariant descriptors for the same silhouettes at different rotation angles.

What I Did

Created rotated copies of the same picture. Ran skimage.measure.find_contours() on it to extract a contour and pyefd.elliptic_fourier_descriptors(normalize=True) on the result.
Expected output: Equal with some margin of error for differently rotated copies.
Actual output: Result is only sometimes equal.

Unfortunately my code is spread over several source files and depends on data, so I cannot easily share an example of what I am actually doing. But here is a function that, when inserted into tests.py will result in a failed test:

def test_normalizing_4():
    contour_2 = np.roll(contour_1[:-1,:], 40, axis=0)
    contour_2 = np.append(contour_2, [contour_2[0]], axis=0)
    c1 = pyefd.elliptic_fourier_descriptors(contour_1, normalize=True)
    c2 = pyefd.elliptic_fourier_descriptors(contour_2, normalize=True)
    np.testing.assert_almost_equal(c1, c2, decimal=12)

The reason for this behaviour is actually mentioned in the original paper in chapter 5.1 and figure 8: For every shape there are two possible classifications, each rotated along one of the two semi-major axes (rotated 180 degrees from each other). It seems like pyefd chooses one of them based on the location of the first point in the contour.

There might be two solutions to this, firstly to return both classifications or to choose one of them (more) consistently by examining higher harmonic content of the descriptor. Note that the (near-)circular case also exists as outlined in the paper in chapter 5.2, so returning multiple descriptors and normalisation parametres might be required anyway for contours with rotational symmetry.

@hbldh
Copy link
Owner

hbldh commented Mar 24, 2021

Thank you for reporting this. I admit, this was something that I missed when reading the paper, and it does hinder the use of pyefd if one expects such identical output.

I guess I never noticed it because I calculated contours for a lot of MNIST images and trained a ML classifier to distinguish between them. Given a large enough dataset, there will be shapes of both solution configurations present for each class in the training set and a ML classifier will be tolerant to this flaw in pyefd in such a case.

But for comparing like this, it does not work.

I am reluctant to change the output of the elliptic_fourier_descriptors to return a tuple of solutions instead, given that it will slow down the method considerably. However, a new method for returning both would be ok given that a consistent and efficient way of finding both can be found.

Personally, I have no time to invest in this package (and very little incentive to do so as well, since I do not use pyefd currently) in the foreseeable future, but I will accept a PR.

@geloescht
Copy link
Contributor Author

I might give it a go, but my math skills are sometimes hitting a brick wall with this project. The fact that my dataset is essentially split in two halves is not a huge problem, but it does give me potentially less interesting results. In case you are wondering what I am using pyefd for, I am basically using it to find similar images to form an animation in a half-automated fashion. This is my latest test: https://vimeo.com/528553087

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants