code: def log_local(
      self,
      save_dir,
      split,
      images,
      global_step,
      current_epoch,
      batch_idx,
  ):
      root = os.path.join(save_dir, "image_log", split)
      for k in images:
          grid = torchvision.utils.make_grid(images[k], nrow=4)
          if self.rescale:
              grid = (grid + 1.0) / 2.0  # -1,1 -> 0,1; c,h,w
          grid = grid.transpose(0, 1).transpose(1, 2).squeeze(-1)
          grid = grid.numpy()
          grid = (grid * 255).astype(np.uint8)
          filename = "{}_gs-{:06}_e-{:06}_b-{:06}.png".format(
              k, global_step, current_epoch, batch_idx
          )
          path = os.path.join(root, filename)
          os.makedirs(os.path.split(path)[0], exist_ok=True)
          Image.fromarray(grid).save(path)
  
file_path: NeMo/nemo/collections/multimodal/models/text_to_image/controlnet/util.py
imports: import os
  import numpy
  import torch
  import torchvision
  from PIL import Image
  from pytorch_lightning import Callback
  from pytorch_lightning.utilities.rank_zero import rank_zero_only
label_desc: Use image node_id 2 for calling the ImageLogger obj's underlying member method code with example usage: obj.log_local(save_dir, split, images, global_step, current_epoch, batch_idx) without return types