code: def plot_2d_or_3d_image(
      data: NdarrayTensor | list[NdarrayTensor],
      step: int,
      writer: SummaryWriter | SummaryWriterX,
      index: int = 0,
      max_channels: int = 1,
      frame_dim: int = -3,
      max_frames: int = 24,
      tag: str = "output",
  ) -> None:
      """Plot 2D or 3D image on the TensorBoard, 3D image will be converted to GIF image.
  
      Note:
          Plot 3D or 2D image(with more than 3 channels) as separate images.
          And if writer is from TensorBoardX, data has 3 channels and `max_channels=3`, will plot as RGB video.
  
      Args:
          data: target data to be plotted as image on the TensorBoard.
              The data is expected to have 'NCHW[D]' dimensions or a list of data with `CHW[D]` dimensions,
              and only plot the first in the batch.
          step: current step to plot in a chart.
          writer: specify TensorBoard or TensorBoardX SummaryWriter to plot the image.
          index: plot which element in the input data batch, default is the first element.
          max_channels: number of channels to plot.
          frame_dim: if plotting 3D image as GIF, specify the dimension used as frames,
              expect input data shape as `NCHWD`, default to `-3` (the first spatial dim)
          max_frames: if plot 3D RGB image as video in TensorBoardX, set the FPS to `max_frames`.
          tag: tag of the plotted image on TensorBoard.
      """
      data_index = data[index]
      # as the `d` data has no batch dim, reduce the spatial dim index if positive
      frame_dim = frame_dim - 1 if frame_dim > 0 else frame_dim
  
      d: np.ndarray = (
          data_index.detach().cpu().numpy()
          if isinstance(data_index, torch.Tensor)
          else data_index
      )
  
      if d.ndim == 2:
          d = rescale_array(d, 0, 1)  # type: ignore
          dataformats = "HW"
          writer.add_image(
              f"{tag}_{dataformats}", d, step, dataformats=dataformats
          )
          return
  
      if d.ndim == 3:
          if d.shape[0] == 3 and max_channels == 3:  # RGB
              dataformats = "CHW"
              writer.add_image(
                  f"{tag}_{dataformats}",
                  d,
                  step,
                  dataformats=dataformats,
              )
              return
          dataformats = "HW"
          for j, d2 in enumerate(d[:max_channels]):
              d2 = rescale_array(d2, 0, 1)
              writer.add_image(
                  f"{tag}_{dataformats}_{j}",
                  d2,
                  step,
                  dataformats=dataformats,
              )
          return
  
      if d.ndim >= 4:
          spatial = d.shape[-3:]
          d = d.reshape([-1] + list(spatial))
          if (
              d.shape[0] == 3
              and max_channels == 3
              and has_tensorboardx
              and isinstance(writer, SummaryWriterX)
          ):  # RGB
              # move the expected frame dim to the end as `T` dim for video
              d = np.moveaxis(d, frame_dim, -1)
              writer.add_video(
                  tag,
                  d[None],
                  step,
                  fps=max_frames,
                  dataformats="NCHWT",
              )
              return
          # scale data to 0 - 255 for visualization
          max_channels = min(max_channels, d.shape[0])
          d = np.stack(
              [rescale_array(i, 0, 255) for i in d[:max_channels]],
              axis=0,
          )
          # will plot every channel as a separate GIF image
          add_animated_gif(
              writer,
              f"{tag}_HWD",
              d,
              max_out=max_channels,
              frame_dim=frame_dim,
              global_step=step,
          )
          return
  
file_path: MONAI/monai/visualize/img2tensorboard.py
imports: from __future__ import annotations
  from typing import TYPE_CHECKING, Any
  import numpy
  import torch
  from monai.config import NdarrayTensor
  from monai.transforms import rescale_array
  from monai.utils import convert_data_type, optional_import
label_desc: Use image node_id 4 for calling a global function with example usage: plot_2d_or_3d_image(data, step, writer, index, max_channels, frame_dim, max_frames, tag) without return types