dwehr's picture
Migrate action viewer to local Cosmos generation
9f818c5
Raw
History Blame Contribute Delete
5.73 kB
# 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)