Skip to content

fix(filters): sharpen integer images in float32 in unsharp_mask - #5946

Merged
ducha-aiki merged 2 commits into
kornia:mainfrom
RavSinghChandan:unsharp-mask-integer-images
Oct 15, 2026
Merged

ducha-aiki merged 2 commits into
kornia:mainfrom
RavSinghChandan:unsharp-mask-integer-images

Conversation

@RavSinghChandan

@RavSinghChandan RavSinghChandan commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

What does this pull request do?

unsharp_mask and UnsharpMask raised RuntimeError: expected dtype float for end but got dtype unsigned char for a uint8 image, because torch.lerp mixes the float32 blur from gaussian_blur2d with the integer input. The image is now promoted with the existing _to_floating helper first, like guided_blur (#5890), so an integer or bool image is sharpened in float32 and returned as float32 on its own scale, matching gaussian_blur2d. Floating images are unchanged. The conventions page now lists unsharp_mask with the filters that follow this rule.

Related issue or discussion

Relates to #5155 (unsharp_mask was listed as not following the integer convention yet).

What did you check?

New test_integer_input_matches_float32_5155 (uint8, int16, bool) fails before the change and passes after.

$ pytest tests/filters
2879 passed, 12 skipped
$ ruff check / ruff format --check: clean


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **Bug Fixes**
  * Unsharp masking now supports integer and boolean images, returning float32 results on the input image’s scale. Floating-point inputs retain their dtype.
  * The `UnsharpMask` module provides the same behavior as the unsharp masking function.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

@RavSinghChandan
RavSinghChandan requested review from a team, edgarriba, johnnv1 and sidd-27 October 14, 2026 13:10
@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 8e5b9f2c-3a3f-4d27-98e6-79bc134cff84
📥 Commits

Reviewing files that changed from the base of the PR and between 8764833 and c5f1073.

📒 Files selected for processing: 4
  • changelog.d/5946.fixed.md
  • docs/source/get-started/conventions.rst
  • kornia/filters/unsharp.py
  • tests/filters/test_unsharp_mask.py

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

📝 Walkthrough

unsharp_mask and UnsharpMask now convert integer and boolean inputs to float32 before filtering. Their outputs use float32 on the input scale. Documentation and tests describe and verify this behavior.

Changes: Unsharp mask input support

Layer / File(s) Summary
Input conversion and validation
kornia/filters/unsharp.py, docs/source/get-started/conventions.rst, tests/filters/test_unsharp_mask.py, changelog.d/5946.fixed.md
The implementation converts inputs to floating point before filtering. Documentation states that integer and boolean inputs produce float32 output on their input scale, while floating inputs retain their dtype. Tests compare uint8, int16, and bool inputs with float32 references and check output dtype, device, and module results. The changelog records the behavior.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to c5f10

unsharp_mask and UnsharpMask now accept integer and bool images and return float32 on the original scale, instead of raising a dtype error. Floating inputs are unchanged. No merge-blocking risk was found.

Security architecture risk: ⚪ Minimal · up to c5f10

The reviewed change accepts integer and boolean images through the existing floating-point filtering path. It preserves floating-point behavior and introduces no material security risk in the inspected path.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The demonstrated expansion is successful processing of additional tensor dtypes within the invoking process. Integer inputs already reached Gaussian filtering before this change; no new privileged operation, external destination or cross-tenant access is introduced by the inspected wrapper.

Trust Boundaries and Controls

  • observed — Caller-supplied image and kernel parameters continue through the existing Gaussian path, including tensor-shape, kernel-size and sigma-batch checks. The test range marked as a public entrypoint is a pytest method invoking the library APIs, not a newly exposed production endpoint.
Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 2 files. (2 skipped: 2… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: unsharp_mask now processes integer images in float32.
Description check Passed The description explains the problem, implementation, related discussion, tests, and validation results. It covers the required template sections and is sufficiently complete.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 2 files. (2 skipped: 2 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@ducha-aiki ducha-aiki left a comment

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.

Thanks @RavSinghChandan, this is the right fix for the unsharp_mask entry of #5155. gaussian_blur2d already promotes an integer image, so torch.lerp got a float32 blur and the original integer tensor and raised. Promoting with _to_floating first follows the same rule as the other filters: integer and bool images give float32 on their own scale, and floating images keep their dtype.

On this head merged with current main: uint8, int8, int16, int32, int64 and bool inputs return float32 equal to the float32 call, and float16, bfloat16 and float64 keep their dtype. A fullgraph aot_eager compile of the uint8 call matches eager. tests/filters/test_unsharp_mask.py passes on CPU in float32, float64 and float16 and on MPS in float32. With main's unsharp.py, the new test fails for all three dtypes. The fragment is right as fixed.

One request: the description has no "How did AI help?" section from the pull request template. Please add one, even if the answer is that no AI was used. Heads-up: #5974 moves spatial_gradient and sobel within the same conventions.rst sentence. Whichever merges second gets a conflict, which we will resolve. Approving.

Posted on behalf of @ducha-aiki by Claude (Opus 5.5).

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants