Spaces:
Running on L40S
Running on L40S
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| # SPDX-License-Identifier: OpenMDW-1.1 | |
| """image visualization utilities. | |
| based on https://gitlab.com/qsh.zh/jam/-/blob/master/jamviz/img.py MIT License | |
| """ | |
| import os | |
| from typing import Union | |
| import numpy as np | |
| import torch | |
| from einops import rearrange | |
| from PIL import Image | |
| from torchvision.utils import make_grid | |
| __all__ = [ | |
| "show_batch_img", | |
| "save_batch_img", | |
| ] | |
| def _reshape_viz_batch_img(img_data: torch.Tensor | np.ndarray, shape: int | str = 7) -> tuple: | |
| """ | |
| Reshapes a batch of images for visualization, organizing them into a grid format. | |
| Args: | |
| img_data (torch.Tensor | np.ndarray): The image data to be reshaped, can be either a PyTorch tensor or a NumPy array. | |
| shape (int | str, optional): Defines the layout of the grid. If an integer is provided, it specifies both the number of rows and columns. If a string is provided in the format 'nrowxncol', it parses to individual row and column numbers. Defaults to 7. | |
| Returns: | |
| tuple: A tuple containing: | |
| img (np.ndarray | torch.Tensor): The image data arranged in grid format. | |
| nrow (int): Number of rows in the grid. | |
| ncol (int): Number of columns in the grid. | |
| Raises: | |
| RuntimeError: If the shape parameter is neither an int nor a string, or if it's a string that doesn't contain 'x'. | |
| Example: | |
| >>> tensor_images = torch.rand(64, 3, 28, 28) # Example tensor of 64 images | |
| >>> img_grid, rows, cols = _reshape_viz_batch_img(tensor_images, '8x8') | |
| >>> img_grid.shape | |
| (224, 224, 3) | |
| """ | |
| if isinstance(shape, int): | |
| nrow, ncol = shape, shape | |
| elif isinstance(shape, str): | |
| if "x" not in shape: | |
| nrow, ncol = int(shape), int(shape) | |
| else: | |
| shape = shape.split("x") | |
| nrow, ncol = int(shape[0]), int(shape[1]) | |
| else: | |
| raise RuntimeError(f"shape {shape} not support") | |
| if isinstance(img_data, torch.Tensor): | |
| assert img_data.shape[1] in [1, 3] | |
| grid_img = make_grid(img_data[: nrow * ncol].detach().cpu(), ncol) | |
| img = grid_img.permute(1, 2, 0) | |
| elif isinstance(img_data, np.ndarray): | |
| if img_data.shape[1] in [1, 3]: | |
| img = rearrange(img_data[: nrow * ncol], "(b t) c h w -> (b h) (t w) c", b=nrow) | |
| else: | |
| img = rearrange(img_data[: nrow * ncol], "(b t) h w c -> (b h) (t w) c", b=nrow) | |
| return img, nrow, ncol | |
| def show_batch_img( | |
| img_data: torch.Tensor | np.ndarray, | |
| shape: int | str = 7, | |
| grid: int = 3, | |
| is_n1p1: bool = False, | |
| auto_n1p1: bool = True, | |
| ) -> None: | |
| """ | |
| Displays a batch of images using matplotlib after arranging them into a specified grid layout. | |
| Args: | |
| img_data (torch.Tensor | np.ndarray): The image data to be displayed. | |
| shape (int | str, optional): The grid shape to organize the images. Defaults to 7. | |
| grid (int, optional): Scaling factor for each image in the grid, affecting the overall size of the displayed figure. Defaults to 3. | |
| is_n1p1 (bool, optional): Whether to normalize the images from [-1, 1] to [0, 1] for visualization. Defaults to False. | |
| auto_n1p1 (bool, optional): If true, automatically adjusts images from [-1, 1] to [0, 1] based on minimum pixel value detection. Defaults to True. | |
| Returns: | |
| None: This function does not return anything but displays the image grid using matplotlib. | |
| Example: | |
| >>> tensor_images = torch.rand(64, 3, 28, 28) # Example tensor of 64 images | |
| >>> show_batch_img(tensor_images, '8x8') | |
| """ | |
| import matplotlib.pyplot as plt | |
| if is_n1p1: | |
| img_data = (img_data + 1) / 2 | |
| else: | |
| if auto_n1p1: | |
| if isinstance(img_data, torch.Tensor): | |
| if img_data.min().item() < -0.5: | |
| img_data = (img_data + 1) / 2 | |
| elif isinstance(img_data, np.ndarray): | |
| if np.min(img_data) < -0.5: | |
| img_data = (img_data + 1) / 2 | |
| img, nrow, ncol = _reshape_viz_batch_img(img_data, shape) | |
| plt.figure(figsize=(ncol * grid, nrow * grid)) | |
| plt.axis("off") | |
| plt.imshow(img) | |
| def save_batch_img(fpath: str, img_data: Union[torch.Tensor, np.ndarray], shape: Union[int, str] = 7) -> None: | |
| """ | |
| Saves a batch of images to a file after arranging them into a grid format. Handles both PyTorch tensors and NumPy arrays as input. | |
| Args: | |
| fpath (str): File path where the image will be saved. | |
| img_data (Union[torch.Tensor, np.ndarray]): The image data to be saved. Can be a PyTorch tensor or a NumPy array. | |
| shape (Union[int, str], optional): The grid shape to organize the images. Can be an integer specifying equal number of rows and columns, or a string specifying 'nrowxncol'. Defaults to 7. | |
| Returns: | |
| None: This function does not return anything but saves the image to the specified file path. | |
| Raises: | |
| RuntimeError: If the input shape is neither an integer nor a string, or it does not include 'x' when provided as a string. | |
| Example: | |
| >>> tensor_images = torch.rand(64, 3, 28, 28) # Example tensor of 64 images | |
| >>> save_batch_img('path/to/save/image.png', tensor_images, '8x8') | |
| # This saves the image grid to 'path/to/save/image.png' | |
| """ | |
| img, _, _ = _reshape_viz_batch_img(img_data, shape) | |
| if isinstance(img, np.ndarray): | |
| img = torch.from_numpy(img) | |
| ndarr = img.mul(255).add_(0.5).clamp_(0, 255).to("cpu", torch.uint8).numpy() | |
| im = Image.fromarray(ndarr) | |
| os.makedirs(os.path.dirname(fpath), exist_ok=True) | |
| im.save(fpath) | |