fix: support for 1-channel and 16-bit images in Visualizer#616
fix: support for 1-channel and 16-bit images in Visualizer#616tybulewicz wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
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/renderto 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.ndarrayinputs, but the public API also acceptsPIL.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 acceptsImage.Image | np.ndarray).
"""Visualizer for modelAPI."""
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if image.dtype == np.uint16: | ||
| image = (image / 256).astype(np.uint8) |
| 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) |
| 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) |
| 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 |
| 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 |
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