Skip to content

Undo (double) mirroring in ski2.morphology.dilation#8060

Merged
lagru merged 18 commits intoscikit-image:mainfrom
lagru:sk2-gray-morph-followup
Mar 15, 2026
Merged

Undo (double) mirroring in ski2.morphology.dilation#8060
lagru merged 18 commits intoscikit-image:mainfrom
lagru:sk2-gray-morph-followup

Conversation

@lagru
Copy link
Copy Markdown
Member

@lagru lagru commented Feb 19, 2026

Description

Follows up on #8046. Among smaller outstanding tweaks, this undoes the accidental mirroring in skimage2.morphology.dilation.

Inside scipy.ndimage.grey_dilation the footprint is inverted. This inversion is intentional so that the composition of erosion and dilation lead to a correct closing and opening. However, skimage.morphology.dilation accidentally undoes this by inverting again. So in skimage2.morphology.dilation we drop the mirroring and align with SciPy (see also #6676).

Todo:

  • Investigate test failures
  • De-duplicate test suite in test_grayscale_operators.py
    • Due to the significant changes with regards to mirroring, I chose to keep the test suites duplicated. This allows me to update the tests for skimage2 now while I have the context and also be more sure of correctness.
  • Consider dropping the usage of gray_morph_output.npz in tests (very opaque and hard to modify)
    • A problem for another time.
  • Update migration guide

Checklist

Release note

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

...

@lagru lagru added 🔧 type: Maintenance Refactoring and maintenance of internals 🥾 Path to skimage2 A step towards the new "API 2.0" labels Feb 19, 2026
@lagru lagru changed the title Sk2 gray morph followup Undo mirroring in ski2.morphology.dilation Feb 19, 2026
@lagru lagru changed the title Undo mirroring in ski2.morphology.dilation Undo (double) mirroring in ski2.morphology.dilation Feb 19, 2026
Inside `scipy.ndimage.grey_dilation` the footprint is inverted/mirrored.
This inversion is intentional so that the composition of erosion and
 dilation lead to a correct closing and opening.

`skimage.morphology.dilation` accidentally undoes this by mirroring
the footprint, before passing it to `scipy.ndimage.grey_dilation`.

`skimage.morphology.closing` and `skimage.morphology.black_tophat` then
mirror/invert again to correct for this.

In `skimage2` we drop all mirroring and align with SciPy. This function
documents this procedure and ensures the wrappers around `skimage2` keep the
old behavior for `skimage.morphology.dilation`, `skimage.morphology.closing`
and `skimage.morphology.black_tophat`.

`skimage.morphology.erosion`, `skimage.morphology.opening` and
`skimage.morphology.white_tophat` _aren't_ affected.
@lagru lagru requested a review from stefanv February 25, 2026 22:27
@lagru lagru marked this pull request as ready for review February 26, 2026 10:19
@lagru lagru requested a review from mkcor February 26, 2026 10:19
@coderabbitai coderabbitai bot added 📄 type: Documentation Updates, fixes and additions to documentation 🩹 type: Bug fix Fixes unexpected or incorrect behavior and removed 🔧 type: Maintenance Refactoring and maintenance of internals labels Feb 26, 2026
@coderabbitai

This comment was marked as outdated.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (2)
src/skimage/morphology/footprints.py (1)

9-9: Use relative import instead of absolute import.

This line uses an absolute import, but the coding guidelines specify using relative module imports within src/skimage/**/*.py.

♻️ Proposed fix
-from skimage import morphology
+from . import _top_level_morphology as morphology

Alternatively, if the import is only used in footprint_from_sequence at line 100, consider importing directly where needed or restructuring to avoid the circular dependency that may have motivated this pattern.

As per coding guidelines: "Use relative module imports (e.g., from .._shared import xyz instead of from skimage._shared import xyz)".

tests/skimage2/morphology/test_grayscale_operators.py (1)

52-66: Consider extracting the test data loading to a fixture.

The NPZ file is loaded on every parameterized test execution (36 times for this test alone). Using a module-scoped fixture would reduce redundant I/O.

Additionally, there's a minor inconsistency: np.testing.assert_equal is used here while assert_array_equal (which is already imported) is used elsewhere in the file.

♻️ Proposed fixture to cache test data
`@pytest.fixture`(scope="module")
def gray_morph_expected_data():
    return dict(np.load(fetch('data/gray_morph_output.npz')))

Then use gray_morph_expected_data in both test methods:

-    def test_reproduce_skimage_data_not_mirrored(self, footprint_args, size, func):
+    def test_reproduce_skimage_data_not_mirrored(self, footprint_args, size, func, gray_morph_expected_data):
         ...
         footprint_name, footprint_func = footprint_args
         key = f'{footprint_name}_{size}_{func.__name__}'
-        data = dict(np.load(fetch('data/gray_morph_output.npz')))
-        expected = data[key]
+        expected = gray_morph_expected_data[key]
         footprint = footprint_func(size)

         result = func(image, footprint, mode="reflect")
-        np.testing.assert_equal(result, expected)
+        assert_array_equal(result, expected)

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b235935 and 01dd02c.

📒 Files selected for processing (9)
  • doc/source/conf.py
  • doc/source/user_guide/skimage2_migration.md
  • environment.yml
  • pyproject.toml
  • requirements/docs.txt
  • src/skimage/morphology/footprints.py
  • src/skimage/morphology/gray.py
  • src/skimage2/morphology/_grayscale_operators.py
  • tests/skimage2/morphology/test_grayscale_operators.py

@lagru lagru removed the 📄 type: Documentation Updates, fixes and additions to documentation label Feb 27, 2026
@coderabbitai coderabbitai bot removed the 🩹 type: Bug fix Fixes unexpected or incorrect behavior label Feb 27, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
doc/source/user_guide/skimage2_migration.md (1)

134-134: ⚠️ Potential issue | 🟡 Minor

Fix grammar in the migration note sentence.

Line 134 should read “This only impacts behavior...” (current text has incorrect grammar: “does only impacts”).

✏️ Proposed fix
-  This does only impacts behavior for asymmetric/eccentric footprints.
+  This only impacts behavior for asymmetric/eccentric footprints.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 01dd02c and 4f8f853.

📒 Files selected for processing (3)
  • doc/source/user_guide/skimage2_migration.md
  • src/skimage/morphology/footprints.py
  • src/skimage/morphology/gray.py

Copy link
Copy Markdown
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.

Nice clean-up! 🙏

are deprecated in favor of counterparts in `skimage2.morphology` with new behavior:

- All functions now default to `mode='ignore'` (was `mode='reflect'`).
- Additionally, `skimage.morphology.dilation`, `skimage.morphology.closing`, and `skimage.morphology.black_tophat` now also mirror the footprint (invert its order in each dimension).
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
- Additionally, `skimage.morphology.dilation`, `skimage.morphology.closing`, and `skimage.morphology.black_tophat` now also mirror the footprint (invert its order in each dimension).
- Additionally, `skimage2.morphology.dilation`, `skimage2.morphology.closing`, and `skimage2.morphology.black_tophat` now mirror the footprint (i.e., invert its order in each dimension).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch!

I don't think i.e. is well known. I'll expand it to that is. Same logic as for e.g. to for example.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Or do you think

now mirror the footprint (that is, invert its order in each dimension).

reads weirdly?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Using "that is" is correct, for sure. It's just that "i.e." is particularly fitting in a technical context and even more specifically in {parentheses, footnotes, references} (and it is 'well-known').

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Well, I had to look up the exact meaning of "i.e.". I don't think that knowledge gap would have hindered me in practice, but I've been thinking about avoiding latin-derived acronyms in technical writing.

But I grant you that many in our academic audience are familiar with this. I did some digging and came up with this somewhat opinionated article. I'm curious what you think, and interested in finding common ground, since I suspect that we might encounter this again otherwise.

Feel welcome to push yourself or let me know if I should do so.

@coderabbitai coderabbitai bot added the 🩹 type: Bug fix Fixes unexpected or incorrect behavior label Feb 27, 2026
coderabbitai[bot]

This comment was marked as outdated.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/skimage/morphology/gray.py (1)

46-54: Make the warning snippet self-contained.

This message tells users to call ski2.morphology.*, but ski2 is never introduced in the warning itself. Using fully qualified skimage2.morphology.pad_footprint / skimage2.morphology.mirror_footprint, or adding the import line, would make the migration hint copy-pasteable as-is.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e6ef00f9-7db0-4b87-a4cd-ddc461403bd6

📥 Commits

Reviewing files that changed from the base of the PR and between 4ce6a08 and df6c04a.

📒 Files selected for processing (4)
  • doc/source/user_guide/skimage2_migration.md
  • src/skimage/morphology/gray.py
  • src/skimage2/morphology/_grayscale_operators.py
  • tests/skimage2/morphology/test_grayscale_operators.py

@lagru lagru requested a review from mkcor March 11, 2026 11:41
@lagru lagru moved this to In progress in Work pitches Mar 12, 2026
@lagru lagru self-assigned this Mar 12, 2026
@lagru lagru moved this from Doing to Needs review in Work pitches Mar 12, 2026
Copy link
Copy Markdown
Member

@stefanv stefanv left a comment

Choose a reason for hiding this comment

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

Also close!

# `dilation`, `closing`, `black_tophat` need a more specific warning.
# See `_patch_footprint_mirroring`
_PENDING_SKIMAGE2_TEMPLATE_MIRROR = """\
`skimage.morphology.{name}` is deprecated in favor of
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Point to documentation?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Do you mean a hyperlink to the HTML version of the migration guide?

@stefanv stefanv moved this from Review to Doing in Work pitches Mar 13, 2026
@lagru lagru moved this from Doing to Review in Work pitches Mar 14, 2026
Copy link
Copy Markdown
Member

@stefanv stefanv 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 to me! Ready to merge?

@lagru lagru merged commit ed0f3e9 into scikit-image:main Mar 15, 2026
21 of 23 checks passed
@github-project-automation github-project-automation bot moved this from Review to Done in Work pitches Mar 15, 2026
@stefanv stefanv added this to the 0.27 milestone Mar 15, 2026
@lagru lagru deleted the sk2-gray-morph-followup branch March 15, 2026 16:29
matthew-brett added a commit that referenced this pull request Apr 10, 2026
* origin/main: (27 commits)
  Move `_shared` to `_skimage2` (#8102)
  Port `pad_footprint` & `mirror_footprint` to `skimage2` (#8094)
  Add rescaling API to skimage2 (#8075)
  Move `skimage2` implementation into `_skimage2` (#8093)
  Undo (double) mirroring in `ski2.morphology.dilation` (#8060)
  Grammar (#8091)
  Move `ensure_spacing` into `skimage2` (#8067)
  Ensure that `skimage2` does not eagerly import `skimage` (#8087)
  Use lazy-loader 0.5 (#8080)
  MAINT: make ellipse fitting forward compatible  (#8054)
  Avoid deprecated assign to ndarray.shape (#8020)
  Avoid circular import in `feature/corner.py` (#8077)
  Turn off coderabbit auto-labeling & label checks (#8070)
  Fix GIL being re-enabled by C++ extensions (#8059)
  Fix conventions following docstub and misc. (#8055)
  Port grayscale morphology operators to skimage2 (#8046)
  Add revised `peak_local_max` to `skimage2` (#8039)
  Allow read-only arrays as input to remap (#7535)
  Add `prescale`  parameter to "blob functions" (#7858)
  Try to make coderabbit respect exisiting type labels (#8042)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🩹 type: Bug fix Fixes unexpected or incorrect behavior 🥾 Path to skimage2 A step towards the new "API 2.0"

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants