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

Replace square, rectangle, cube with footprint_rectangular #7566

Merged
merged 14 commits into from
Dec 5, 2024

Conversation

lagru
Copy link
Member

@lagru lagru commented Oct 3, 2024

Description

We have a rework of our footprint generating function on our skimage2 agenda. This WIP starts that work by combining "rectangular" footprint generators (square, rectangle, cube) into a single skimage.morphology.footprint_rectangular with ND support.

This is also an opportunity to change to decomposition="sequence" as the default as suggested in the agenda. Though, I'm not sure if we really want to do this. It's arguably a more unexpected form to represent the footprint for the sake of performance. Usually, we decide that trade-off in favor of simpler behavior.

I plan to do the same for "elliptical" footprint generators in a separate PR to keep reviewing reasonable.

I'm also playing around with a small wrapper class to help with the more complicated representation of decomposed footprints. I might move that into another PR depending on how this approach turns out.

Checklist

Release note

For maintainers and optionally contributors, please refer to the instructions on how to document this PR for the release notes.

Add the new `skimage.morphology.footprint_rectangle`
supporting generation of rectangular or hyper-rectangular footprints
in one function. {label="New feature"}
Deprecate `skimage.morphology.square` in favor of the new function
`skimage.morphology.footprint_rectangle`. {label="API"}
Deprecate `skimage.morphology.rectangle` in favor of the new function
`skimage.morphology.footprint_rectangle`. {label="API"}
Deprecate `skimage.morphology.cube` in favor of the new function
`skimage.morphology.footprint_rectangle`. {label="API"}

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
@lagru lagru added 🔽 Deprecation Involves deprecation 📜 type: API Involves API change(s) 🥾 Path to skimage2 A step towards the new "API 2.0" labels Oct 3, 2024
@lagru
Copy link
Member Author

lagru commented Oct 4, 2024

I'm thinking how to best go about updating the test suite considering review experience and long-term maintenance.

The test's are somewhat strewn about over at least 2 files. So my idea is to consolidate them in a new Test_footprint_rectangle class but keep the old ones around during the deprecation. That way we make sure that the replacement really fully implements the old API. When we finish the deprecation, we just remove them and are left with a clearer grouping of tests.

@hmaarrfk
Copy link
Member

hmaarrfk commented Oct 4, 2024

Have "nd extensions" been using in your "actual work"? I have mostly stayed on "2D" + broadcasting.

I find "square rectangle and cube" relatable, and "footprint_rectangle" just too abstract....

@stefanv
Copy link
Member

stefanv commented Oct 4, 2024

We are planning on renaming all structuring elements to footprint_, so that part at least will be more obvious.

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
@lagru
Copy link
Member Author

lagru commented Oct 10, 2024

My personal gut feeling is that rectangle isn't too abstract for our users to understand that it also entails squares. I could live with keeping footprint_cube as a 3d-specific alias and common use case of the general algorithm in footprint_rectangle around.

But thinking about it more, I'd actually find footprint_rectangular (and footprint_elliptical) to be perfect names. They aren't specific to a number of dimensions and I think communicate the core concept of the footprint class clearly.

Meta: I think the original aim of the discussion was to make our API clearer and reduce the maintenance aspect of it. While working on this, I discovered that the "rectangular" approach can actually be covered by one ND-enabled algorithm. I feel like that one generalized version is actually simpler to maintain long-term.

@hmaarrfk, what do you think?

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
The new name is not specific to a number of dimensions and I think
communicates the core concept of the footprint class clearly.
@lagru lagru changed the title Replace square, rectangle, cube with footprint_rectangle Replace square, rectangle, cube with footprint_rectangular Oct 10, 2024
@hmaarrfk
Copy link
Member

@lagru all green with me.

lagru and others added 3 commits October 25, 2024 17:49

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
@jarrodmillman
Copy link
Contributor

pre-commit.ci autofix

@jarrodmillman jarrodmillman added this to the 0.25 milestone Nov 20, 2024
@lagru lagru modified the milestones: 0.25, 0.26 Nov 21, 2024
While it may be more performant, I'm very unsure if enabling
decomposition by default is the better option. The returned decomposed
format is not as straightforward to understand.

I also don't like that this function may return different types.
Maybe we want a second function that decomposes footprints by default.
E.g. `footprint_rectangular` and additionally
`decomposed_footprint_rectangular`?
of `square`, `rectangle` and `cube`.
Comment on lines +159 to +165
if decomposition == "sequence" and has_even_width:
warnings.warn(
"decomposition='sequence' is only supported for uneven footprints, "
"falling back to decomposition='separable'",
stacklevel=2,
)
decomposition = "sequence_fallback"
Copy link
Member Author

Choose a reason for hiding this comment

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

Note this new warning which is raised when a sequence-decomposition is requested for footprints that have a dimension with an uneven length. This doesn't change previous behavior but makes it more visible to users what's going on.

@lagru lagru requested a review from mkcor November 28, 2024 23:26
@lagru lagru marked this pull request as ready for review November 28, 2024 23:26
@stefanv
Copy link
Member

stefanv commented Nov 28, 2024

This LGTM. I think the name footprint_rectangle would be clearer, and aligns with previously used names cube, square, etc. This will also help us name the others footprint_diamond, footprint_sphere, etc.

@lagru
Copy link
Member Author

lagru commented Nov 29, 2024

Hmm, I'm rather fond of "rectangular" which works for any number of dimensions, e.g. rectangular cuboid, square, hyper-cube. footprint_rectangle makes it less clear that this function may also work for more than 2D.

Along the same line of thought, I'd suggest footprint_elliptical for ellipsis, sphere, ball, etc.

I think we only support 2D for diamond, octagon, etc. So going with a more precise name rather than a property makes sense here in my book.

What do you think?

@stefanv
Copy link
Member

stefanv commented Nov 29, 2024

I think that's the problem: rectangular means rectangular something, and the name doesn't say what. footprint_hyperrect may have been most correct, and perhaps hyper is a way to distinguish from 2D footprints. Not sure what the correct form of "diamond" would be if we went with "rectangular". Also just extra letters and not the most direct form.

We could also consider footprint_rect_nd, footprint_diamond etc.

Copy link
Member

@mkcor mkcor left a comment

Choose a reason for hiding this comment

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

The generalization makes sense to me. Nice work, @lagru!

Comment on lines +111 to +112
The length of the footprint in each dimension. The length of the
sequence determines the number of dimensions of the footprint.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
The length of the footprint in each dimension. The length of the
sequence determines the number of dimensions of the footprint.
The size of the footprint in each dimension. The length of `shape`
determines the number of dimensions of the footprint.

Copy link
Member

Choose a reason for hiding this comment

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

No big deal, I just find it 'visually' confusing to read/see "The length ..." and then again, next sentence, "The length ..." when really they don't refer to the same thing at all.

Comment on lines +117 to +118
footprints is returned. Applying this series of smaller footprints will
give an identical result to a single, larger footprint, but often with
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
footprints is returned. Applying this series of smaller footprints will
give an identical result to a single, larger footprint, but often with
footprints is returned. Applying this sequence of smaller footprints will
give a result identical to applying a single, larger, equivalent footprint, but often with

dtype : data-type, optional
The data type of the footprint.
decomposition : {None, 'separable', 'sequence'}, optional
If None, a single array is returned. For 'sequence', a tuple of smaller
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
If None, a single array is returned. For 'sequence', a tuple of smaller
If None, a single array is returned. With 'sequence', a tuple of smaller

Returns
-------
footprint : array or tuple[tuple[ndarray, int], ...]
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
footprint : array or tuple[tuple[ndarray, int], ...]
footprint : array or tuple[tuple[array, int], ...]

or ndarray or tuple[tuple[array, int], ...]?

Returns
-------
footprint : array or tuple[tuple[ndarray, int], ...]
A footprint consisting only of ones, i.e. every pixel belongs to the
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
A footprint consisting only of ones, i.e. every pixel belongs to the
A footprint consisting only of ones, i.e., every pixel belongs to the

A footprint consisting only of ones, i.e. every pixel belongs to the
neighborhood. When `decomposition` is None, this is just an array.
Otherwise, this will be a tuple whose length is equal to the number of
unique structuring elements to apply (see Examples for more detail).
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
unique structuring elements to apply (see Examples for more detail).
unique footprints to apply (see Examples for more detail).

Or the fact that we speak of 'structuring elements' and 'footprints' interchangeably should be introduced earlier/clearly.

@lagru
Copy link
Member Author

lagru commented Dec 1, 2024

Thanks @mkcor! Do you have a preference or thoughts with regard to footprint_rectangular and footprint_rectangle?

--

I think that's the problem: rectangular means rectangular something, and the name doesn't say what.

@stefanv, I can follow that argument. It may be somewhat alleviated by the fact, that footprint is included in the function name, which identifies the "something".

I still feel like rectangular is the more precise term, but I could live with using _rectangle. In that case, I'd advocate adding footprint_cuboid so that users looking for a 3D option can quickly find it. Then the two most common use cases should be covered.

@mkcor
Copy link
Member

mkcor commented Dec 2, 2024

@lagru oh sorry, I'm seeing this conversation only now! I just checked and the second meaning of adjective 'rectangular' is: placed or having parts placed at right angles (so the 'cuboid' case would be covered, in the broad sense).

My personal preference would go to footprint_rect because it's 'safe' and 'inclusive' (may stand for rectangular, rectangle, hyperrect, rect_nd... knowing that our functions are typically nD by default).

@ivanov
Copy link
Contributor

ivanov commented Dec 5, 2024

drive by peanut gallery comment (came here from the Scientific Python discord that solicited input here, because who doesn't love a good bike shed?):

I think that's the problem: rectangular means rectangular something, and the name doesn't say what.

In one interpretation, it does: rectangular footprint, where a user can assume you're using a noun_adjective convention to keep all of the nouns together for tab-completion usage and discovery convenience. This then raises the point of what the other footprints will be renamed to. Will it be footprint_sphere or footprint_spherical?

footprint_rect is a nice compromise, practically speaking. You could have a convention of using the 2d projection names for n-dimensional version, but if you stuck to that, would you want to change footprint_sphere to footprint_circle? An the annulus that looks like currently gets called disk - should you keep calling it that in an n-dimensional case, where it should perhaps be better described as a spherical shell?

I really don't have a dog in this race, or a say, but I would leave the "legacy" specialized names, or at least their footprint_ prefixed version to help those that have been using those all along, and cross reference the relevant generalization in their docstrings

Note: footprint_square is a special case of footprint_rect for 2 dimensions having the same size.

This way current users of those functions can continue to use them and think about their problems in the same way, only having to adjust to the footprint_ prefix. You could sprinkle in an optional n_dim or something like that where the hyper-shape makes sense. footprint_cube(3, n_dim=10) is pretty unambiguous, and a more pleasant API than the technically the same footprint_rectangular ((3, 3, 3, 3, 3, 3, 3, 3, 3, 3)), even if you shorten it to footprint_rectangular([3]*10), it's still iffy. I think it would be unergonomic to be forced to use footprint_elliptical to specify hyperspheres, even if that's how they'd be implemented internally.

@lagru
Copy link
Member Author

lagru commented Dec 5, 2024

leave the "legacy" specialized names, or at least their footprint_ prefixed version to help those that have been using those all along, and cross reference the relevant generalization in their docstrings

That's a good point I hadn't really considered as a deciding factor. Thanks for pointing it out @ivanov. 🫶

Then let's go with footprint_rectangle and the implicit convention of targeting the 2D case when naming footprints. And point out the 1D or ND support in the docstring. Shouldn't be too unexpected for an "image processing lib". 😄

footprint_cube(3, n_dim=10) is pretty unambiguous, and a more pleasant API than the technically the same footprint_rectangular((3, 3, 3, 3, 3, 3, 3, 3, 3, 3))

We explicitly chose to use an API that works for all shapes consistently, e.g., rectangles or ellipses that can't be represented with the API that you suggest. Another nice perk is, that it's consistent with NumPy's ones, zeros, etc. and rather explicit.

@lagru
Copy link
Member Author

lagru commented Dec 5, 2024

@mkcor, footprint_rect would be a way to sidestep this but how would we name the other cases, e.g., ellipsis, circle, sphere, ball? Also, I'm usually not a fan of contractions that may not be as obvious to other people that aren't already familiar with the convention.

@lagru
Copy link
Member Author

lagru commented Dec 5, 2024

I'll leave pressing the merge button to one of you, in case we have a consensus. 😉

@stefanv stefanv merged commit 4fb2464 into scikit-image:main Dec 5, 2024
24 of 26 checks passed
@stefanv stefanv modified the milestones: 0.26, 0.25 Dec 5, 2024
@stefanv
Copy link
Member

stefanv commented Dec 5, 2024

Thanks all for your inputs!

@lagru lagru deleted the replace-with-footprint-rectangle branch December 6, 2024 20:28
@lagru
Copy link
Member Author

lagru commented Dec 8, 2024

By the way, DIPlib uses "rectangular".

@lagru lagru linked an issue Mar 4, 2025 that may be closed by this pull request
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🔽 Deprecation Involves deprecation 🥾 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.

Unify API for footprint-generating functions
6 participants