Skip to content

Instantly share code, notes, and snippets.

@vikashg
Created January 19, 2022 17:23
Show Gist options
  • Save vikashg/9b2f8f028b8472d599c871920ab4b4dc to your computer and use it in GitHub Desktop.
Save vikashg/9b2f8f028b8472d599c871920ab4b4dc to your computer and use it in GitHub Desktop.
This is a code for creating a custom transform where I am reading a list of images and converting to a single image
import monai
import inspect
from monai.transforms import LoadImage, AddChannel, Compose
import json
from typing import Dict, List, Optional, Sequence, Union
from monai.transforms import Transform
from monai.data.image_reader import ImageReader, ITKReader, NibabelReader, NumpyReader, PILReader
import logging
import warnings
from monai.config import DtypeLike, PathLike
import os
from PIL import Image
import numpy as np
from pathlib import Path
from monai.utils import InterpolateMode, OptionalImportError, ensure_tuple, look_up_option, optional_import
SUPPORTED_READERS = {
"itkreader": ITKReader,
"numpyreader": NumpyReader,
"pilreader": PILReader,
"nibabelreader": NibabelReader,
}
class LoadImageList(Transform):
"""
In this transform we are reading a list of images and converting them to a single image by tiling.
I will ideally like to overload the LoadImage Transform. But for the moment I reused the reader functionality from
LoadImage.
"""
def __init__(self, reader=None, image_only: bool = False,
dtype: DtypeLike = np.float32, *args, **kwargs):
self.auto_select = reader is None
self.image_only = image_only
self.dtype = dtype
self.readers: List[ImageReader] = []
for r in SUPPORTED_READERS: # set predefined readers as default
try:
self.register(SUPPORTED_READERS[r](*args, **kwargs))
except OptionalImportError:
logging.getLogger(self.__class__.__name__).debug(
f"required package for reader {r} is not installed, or the version doesn't match requirement."
)
except TypeError: # the reader doesn't have the corresponding args/kwargs
logging.getLogger(self.__class__.__name__).debug(
f"{r} is not supported with the given parameters {args} {kwargs}."
)
self.register(SUPPORTED_READERS[r]())
if reader is None:
return # no user-specified reader, no need to register
for _r in ensure_tuple(reader):
if isinstance(_r, str):
the_reader = look_up_option(_r.lower(), SUPPORTED_READERS)
try:
self.register(the_reader(*args, **kwargs))
except OptionalImportError:
warnings.warn(
f"required package for reader {r} is not installed, or the version doesn't match requirement."
)
except TypeError: # the reader doesn't have the corresponding args/kwargs
warnings.warn(f"{r} is not supported with the given parameters {args} {kwargs}.")
self.register(the_reader())
elif inspect.isclass(_r):
self.register(_r(*args, **kwargs))
else:
self.register(_r) # reader instance, ignoring the constructor args/kwargs
return
def register(self, reader: ImageReader):
"""
Register image reader to load image file and meta data.
Args:
reader: reader instance to be registered with this loader.
"""
if not isinstance(reader, ImageReader):
warnings.warn(f"Preferably the reader should inherit ImageReader, but got {type(reader)}.")
self.readers.append(reader)
def __call__(self, filename_list = None,
reader: Optional[ImageReader] = None):
self.imageList = []
for filename in filename_list:
filename = tuple(f"{Path(s).expanduser()}" for s in ensure_tuple(filename)) # allow Path objects
img = None
if reader is not None:
img = reader.read(filename) # runtime specified reader
self.imageList.append(img)
else:
for reader in self.readers[::-1]:
if self.auto_select: # rely on the filename extension to choose the reader
if reader.verify_suffix(filename):
img = reader.read(filename)
self.imageList.append(img)
break
else: # try the user designated readers
try:
img = reader.read(filename)
self.imageList.append(img)
except Exception as e:
logging.getLogger(self.__class__.__name__).debug(
f"{reader.__class__.__name__}: unable to load {filename}.\n" f"Error: {e}"
)
else:
break
image_np = self.createtile(reader)
return image_np
def createtile(self, reader):
"""
In this function we are taking a list of 4 numpy arrays (images), and converting them to a single image where
the images will be tiled in 2X2 fashion. This is achieved through a concatenation process. We have hard coaded it at the moment
where there are 4 images of size (224, 224) leading to an image of size (448, 448)
"""
# imagesize is (224, 224) and in
image_np = np.empty((448, 448))
_img = self.imageList[0]
img_array, meta_data = reader.get_data(_img)
img_array = img_array.astype(self.dtype, copy=False)
image_np[0:224, 0:224] = img_array
_img = self.imageList[1]
img_array, meta_data = reader.get_data(_img)
img_array = img_array.astype(self.dtype, copy=False)
image_np[0:224, 224:448] = img_array
_img = self.imageList[2]
img_array, meta_data = reader.get_data(_img)
img_array = img_array.astype(self.dtype, copy=False)
image_np[224:448, 0:224] = img_array
_img = self.imageList[3]
img_array, meta_data = reader.get_data(_img)
img_array = img_array.astype(self.dtype, copy=False)
image_np[224:448, 224:448] = img_array
return image_np
def main():
listOfImages = ['filename1', 'filename2', 'filename3', 'filename4']
#This works
image_array = LoadImageList()(listOfImages)
#This does not work
transform = Compose([LoadImageList()])
img_array = transform(listOfImages)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment