Skip to content

fix: support for 1-channel and 16-bit images in Visualizer#616

Closed
tybulewicz wants to merge 1 commit into
masterfrom
ttybulew/fix_rendering_16bit
Closed

fix: support for 1-channel and 16-bit images in Visualizer#616
tybulewicz wants to merge 1 commit into
masterfrom
ttybulew/fix_rendering_16bit

Conversation

@tybulewicz

@tybulewicz tybulewicz commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds Visualizer support for additional input image formats (16-bit and single-channel/grayscale) by normalizing numpy inputs to PIL-compatible 8-bit RGB, and expands unit coverage for these cases.

Fixes #596

Before submitting

  • This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case).
  • Did you make sure to update the documentation with your changes?
  • Did you write any new necessary tests?

@tybulewicz
tybulewicz requested a review from Copilot June 30, 2026 08:48
@tybulewicz
tybulewicz requested a review from a team as a code owner June 30, 2026 08:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot couldn't run its full agentic review because no GitHub Actions runner was available. Make sure your repository has a runner available to run Copilot's review, or add a copilot-setup-steps.yml file specifying one with the runs-on attribute. See the docs for more details.

Adds Visualizer support for additional input image formats (16-bit and single-channel/grayscale) by normalizing numpy inputs to PIL-compatible 8-bit RGB, and expands unit coverage for these cases.

Changes:

  • Introduced a numpy→PIL conversion helper that downcasts uint16 and converts non-RGB modes to RGB.
  • Updated Visualizer.show/save/render to use the new conversion for numpy inputs.
  • Added unit tests covering 16-bit rendering/show/save and grayscale rendering.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 8 comments.

File Description
model_api/src/model_api/visualizer/visualizer.py Adds _to_pil conversion and uses it in show/save/render to handle uint16 + grayscale numpy inputs.
model_api/tests/unit/visualizer/test_visualizer.py Adds tests validating Visualizer behavior with 16-bit and 1-channel images.
Comments suppressed due to low confidence (1)

model_api/src/model_api/visualizer/visualizer.py:1

  • The new normalization is only applied for np.ndarray inputs, but the public API also accepts PIL.Image. If a caller passes a grayscale (mode='L') or 16-bit (mode='I;16') PIL image, it will bypass _to_pil, which makes behavior inconsistent with the stated goal (“handling 16-bit and grayscale images”). Consider ensuring PIL images are also converted to 8-bit RGB (e.g., if isinstance(image, Image.Image) and image.mode != 'RGB': image = image.convert('RGB'), or centralize both paths into a single helper that accepts Image.Image | np.ndarray).
"""Visualizer for modelAPI."""

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +67 to +68
if image.dtype == np.uint16:
image = (image / 256).astype(np.uint8)
Comment on lines 89 to +91
def show(self, image: Image.Image | np.ndarray, result: Result) -> None:
if isinstance(image, np.ndarray):
image = Image.fromarray(image)
image = self._to_pil(image)
Comment on lines 95 to +97
def save(self, image: Image.Image | np.ndarray, result: Result, path: Path) -> None:
if isinstance(image, np.ndarray):
image = Image.fromarray(image)
image = self._to_pil(image)
Comment on lines +172 to +175
def test_render_grayscale_image():
"""Test Visualizer.render() handles 8-bit grayscale (1 channel) images."""
image_gray = np.zeros((100, 100), dtype=np.uint8)
image_gray[25:75, 25:75] = 255
Comment on lines +185 to +189
visualizer = Visualizer()
rendered = visualizer.render(image_gray, anomaly_result)

assert isinstance(rendered, np.ndarray)
assert rendered.dtype == np.uint8

@staticmethod
def _to_pil(image: np.ndarray) -> Image.Image:
"""Convert numpy array to PIL Image, handling 16-bit and grayscale images.
Grayscale images are converted to RGB for compatibility with overlays.

Args:
image: Input numpy array (uint8 or uint16, grayscale or RGB).
self.layout = layout
self.auto_scale = auto_scale

@staticmethod
@github-actions github-actions Bot added tests Related to tests python python related changes labels Jun 30, 2026
@tybulewicz tybulewicz closed this Jun 30, 2026
@tybulewicz
tybulewicz deleted the ttybulew/fix_rendering_16bit branch June 30, 2026 12:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python python related changes tests Related to tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Visualizer().render fails for 16-bit images

2 participants