10000 Deprecate function `plot_matches` in favor of `plot_matched_features` by mkcor · Pull Request #7255 · scikit-image/scikit-image · GitHub
[go: up one dir, main page]

Skip to content

Deprecate function plot_matches in favor of plot_matched_features #7255

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

Merged
merged 16 commits into from
Mar 10, 2024

Conversation

mkcor
Copy link
Member
@mkcor mkcor commented Nov 29, 2023

Description

Edit: The overall goal is to use image0, image1 in function signatures which have image1, image2 inputs; this PR focuses on updating function plot_matches.

Tackling an item from the to-do list which lays the ground for skimage2, i.e., "Where function signatures still use im1, im2, finally replace them with either image0, image1 or reference_image, target_image. #4187."

In some cases, e.g., where im_true is used, it might make sense to go with reference_image rather than image0. But there's clearly no reason to have image0, image1 in some places and image1, image2 in others.

Checklist

Release note

Summarize the introduced changes in the code block below in one or a few sentences. The
summary will be included in the next release notes automatically:

Deprecate function `skimage.feature.plot_matches` in favor of
`skimage.feature.plot_matched_features`.

@mkcor mkcor added the 🔧 type: Maintenance Refactoring and maintenance of internals label Nov 29, 2023
@mkcor
Copy link
Member Author
mkcor commented Nov 29, 2023

Before I tackle the im_true and image_true instances, I wanted to check with reviewers whether I should take 5bdeed8 further:

  • I searched for all the user-facing instances of image2;
  • I replaced the (user-facing) x1, x2 pairs with x0, x1;
  • should I update the source code so it's not only functional but more readable/maintainable, e.g.,
    replacing
    image1 = img_as_float(image0)                                              
    image2 = img_as_float(image1)

with

    image0 = img_as_float(image0)                                              
    image1 = img_as_float(image1)

and all the rest accordingly?

@stefanv
Copy link
Member
stefanv commented Nov 30, 2023

Thanks @mkcor! The inconsistencies should definitely go (0 -> 1). The x1, x2 pairs don't bother me as much, but I think it's customary in our community to count from zero.

Pretty sure there's a Monty Python joke in there somewhere, but my references fail me now.

@hmaarrfk
Copy link
Member

Are these all internal functions? I feel like we are going to get into trouble with users using kwargs.

I for one, like to limit positional arguments to 2.

from wabisabi import kwarg_name_change
kwarg_name_change = partial(kwarg_name_change,
                            current_library_version=skimage.__version__,
                            library_name='skimage')


@kwarg_name_change('0.24.0', {'image1': 'image0', 'image2': 'image1', 'keypoints1': 'keypoints0', 'keypoints2': 'keypoints1'})
def foo():
...

i forget if an other decorator was done for keyword argument name changes, but I believe the one I wrote for wabisabi meets a slew of usecases

@soupault
Copy link
Member

I for one, like to limit positional arguments to 2.

Very good suggestion! :) Having deprecation issues like this one in mind, and to help static code analysis in IDEs, I have personally switched to using kw-only signatures in my R&D code.

@mkcor
Copy link
Member Author
mkcor commented Nov 30, 2023

I for one, like to limit positional arguments to 2.

👍 as well and I believe many of us are leaning towards this preference (see #7225).

To carry out a proper deprecation cycle, I guess I'll wait for an update from @lagru on #7225 (comment).

@mkcor mkcor marked this pull request as draft December 2, 2023 19:16
@lagru
Copy link
Member
lagru commented Dec 4, 2023

@mkcor, see the WIP at #7256.

@lagru
Copy link
Member
lagru commented Dec 4, 2023

+1 of paying close attention to how many parameters we want to make reachable via position. Not a fan of hard-coding a number. I'd rather recommend to use positional args only for parameters whose contribution to the function is obvious. I'm optimistic about our instincts on that. :)

@@ -4,15 +4,15 @@
from itertools import product


def compare_images(image1, image2, method='diff', *, n_tiles=(8, 8)):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case we could get away with making the arguments position-only, but I'm hesitant to really use that feature of Python. I feel like it's a lesser known aspect of Python and might confuse unnecessarily.

But I think, if we use a wrapper here that warns for a deprecation period, we can get away with eventually settling on image0, image1, cause regardless of how you used the old API and how long a user slept on the warning, the new API will always raise an error:

compare_images(a, b)  # fine for old and new
compare_images(image1=a, image2=b)  # will raise
compare_images(a, image2=b) # will raise

Thinking about this, that might go for the plot_matches above as well, but renaming might still be easier.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case we could get away with making the arguments position-only, but I'm hesitant to really use that feature of Python. I feel like it's a lesser known aspect of Python and might confuse unnecessarily.

Right; I had to look it up to learn that all arguments before / in the function signature means they are positional-only. I don't think we have it anywhere in the codebase (as of now).

@mkcor mkcor changed the title Use image0, image1 in function signatures Deprecate function plot_matches in favor of plot_matched_features Feb 16, 2024
@mkcor
Copy link
Member Author
mkcor commented Feb 16, 2024

46eca3b needs to pass, ensuring that I didn't miss any name change when copying-pasting plot_matches into plot_matched_features (7f02925).

471d79d doesn't change any functionality, it's just a formal update so the variable names read naturally now that we've moved from (1, 2) to (0, 1) naming.

@mkcor
Copy link
Member Author
mkcor commented Feb 16, 2024

It looks like Python 3.12 treats deprecation warnings as errors: I have kept the test for (now deprecated function) plot_matches). Should I:

  • remove this test (now useless, since we have the same test for the new function) or
  • decorate this test in a specific way? @lagru do you know of a good practice for this?

@mkcor mkcor marked this pull request as ready for review February 16, 2024 12:48
@mkcor
Copy link
Member Author
mkcor commented Feb 16, 2024

"Keyword-only arguments are not required to have a default value." PEP 3102 😌

Copy link
Member
@lagru lagru left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I:
remove this test (now useless, since we have the same test for the new function) or
decorate this test in a specific way? @lagru do you know of a good practice for this?

I think the usual practice has been to repurpose the existing tests to the new API. So with the current state, I'd just remove the old test triggering the deprecation warning.

You could use @pytest.mark.filterwarnings as well, but why bother.

Comment on lines 134 to 151
plot_matched_features(
img0,
img1,
ax=ax,
keypoints0=keypoints0,
keypoints1=keypoints1,
matches=matches,
keypoints_color='r',
)
plot_matched_features(
img0,
img1,
ax=ax,
keypoints0=keypoints0,
keypoints1=keypoints1,
matches=matches,
matches_color='r',
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's some repetition here? In my book it would be okay to shorten this even further and use pytest.mark.parametrize for the optional parameters.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's some repetition here?

The difference is that one call tests for keypoints_color='r' and the other matches_color='r'.

In my book it would be okay to shorten this even further and use pytest.mark.parametrize for the optional parameters.

Hmm, that would only increase the number of tests, since pytest.mark.parametrize lets you test different values for a given parameter. Here, only one value (per optional parameter) is being tested...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh, I should check my eyes (or maybe take a nap). 😅 Any reason not to increase the number of tests? They don't seem slow on my machine.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I'm not opposed to it, I just didn't think it was necessary and I thought (I guess I misunderstood) you were saying there were too many already!

@mkcor
Copy link
Member Author
mkcor commented Feb 19, 2024

I think the usual practice has been to repurpose the existing tests to the new API. So with the current state, I'd just remove the old test triggering the deprecation warning.

Thanks for your reply, @lagru. In the end, I thought that removing the 'old' test before v0.25 would artificially lower our test coverage... So I thought we could keep it until then.

Copy link
Member
@lagru lagru left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Let's leave it open for a bit to give other devs a chance to chime in about the API change.

matches=matches,
alignment='vertical',
)
plt.close()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious why you added these as there isn't a call to show() anywhere?

F438
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of this: RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface (`matplotlib.pyplot.figure`) are retained until explicitly closed and may consume too much memory. (To control this warning, see the rcParam `figure.max_open_warning`). Consider using `matplotlib.pyplot.close()`.
The number of figures opened increased after I started using pytest.mark.parametrize 😉

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that seems like a very sensible change then. :D

@lagru lagru added 📜 type: API Involves API change(s) and removed 🔧 type: Maintenance Refactoring and maintenance of internals labels Feb 22, 2024
@lagru
Copy link
Member
lagru commented Feb 22, 2024

Btw I relabeled this as an API change as it involves a deprecation. We've used "type: Maintenance" mostly to categorize internal stuff that's of no interest to users.

@lagru lagru merged commit 78c32ec into scikit-image:main Mar 10, 2024
@stefanv stefanv added this to the 0.23 milestone Mar 10, 2024
@lagru
Copy link
Member
lagru commented Mar 10, 2024

Merging. Thanks @mkcor! One more item of the list. ✔️

@mkcor mkcor deleted the standardize-signature branch March 11, 2024 08:54
@lagru lagru mentioned this pull request Apr 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🥾 Path to skimage2 A step towards the new "API 2.0" 📜 type: API Involves API change(s)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants
0