Source code for swtloc.swtlocalizer

# Author : Achintya Gupta
# Purpose : Serves as an entry point

from .configs import config
from .abstractions import SWTImage
from .utils import SWTLocalizerValueError
from .configs import CONFIG__SWTLOCALIZER
from .utils import perform_type_sanity_checks
from .configs import TRANSFORM_INPUT__1C_IMAGE
from .configs import TRANSFORM_INPUT__3C_IMAGE
from .configs import CONFIG__SWTIMAGE__TRANSFORM
from .utils import generate_random_swtimage_names
from .configs import CONFIG__SWTLOCALIZER__MULTIPROCESSING
from .configs import CONFIG__SWTLOCALIZER__TRANSFORM_IMAGES
from .configs import CONFIG__SWTLOCALIZER__TRANSFORM_IMAGE_PATHS

import os
import numpy as np
from cv2 import cv2
from typing import List
from typing import Union
from copy import deepcopy
from typing import Optional


[docs]class SWTLocalizer: """ ``SWTLocalizer`` acts as an entry point for performing Transformations and Localizations. It creates and houses a list of ``SWTImage`` objects in `swtimages` attribute, after sanity checks have been performed on the input given. The inputs can be a path (string) to an image file or an numpy array of the image. Inputs can also be just a single image filepath (string) or a single pre-loaded image (np.ndarray) or it could be a list of either image filepath or list of np.ndarray. But both the parameters i.e `image_paths` and `images` cannot be provided. Once the inputs provided to the ``SWTLocalizer`` class, sanity checks are performed on the input, and in case of `images` being provided, random numerical names are assigned to each image(s). Example: :: >>> # Import the SWTLocalizer class >>> from swtloc import SWTLocalizer >>> from cv2 import cv2 >>> >>> root_path = 'examples/images/' >>> >>> # Single Image Path (NOTE : Use your own image paths) >>> single_image_path = root_path+'test_image_1/test_img1.jpg' >>> swtl = SWTLocalizer(image_paths=single_image_path) >>> >>> # Multiple Image Paths (NOTE : Use your own image paths) >>> multiple_image_paths = [root_path+'test_image_2/test_img2.jpg', >>> root_path+'test_image_3/test_img3.jpg', >>> root_path+'test_image_4/test_img4.jpeg' ] >>> swtl = SWTLocalizer(image_paths=multiple_image_paths) >>> # Single Pre-Loaded Image - Agnostic to image channels >>> single_image = cv2.imread(root_path+'test_image_1/test_img1.jpg') >>> swtl = SWTLocalizer(images=single_image) >>> # Multiple Pre-Loaded Image >>> multiple_images = [cv2.imread(each_path) for each_path in [root_path+'test_image_2/test_img2.jpg', >>> root_path+'test_image_3/test_img3.jpg', >>> root_path+'test_image_4/test_img4.jpeg' ]] >>> swtl = SWTLocalizer(images=multiple_images) >>> # Accessing `SWTImage` objects from the `SWTLocalizer` >>> multiple_images = [cv2.imread(each_path) for each_path in [root_path+'test_image_2/test_img2.jpg', >>> root_path+'test_image_3/test_img3.jpg', >>> root_path+'test_image_4/test_img4.jpeg' ]] >>> swtl = SWTLocalizer(images=multiple_images) >>> print(swtl.swtimages, type(swtl.swtimages[0])) [Image-SWTImage_982112, Image-SWTImage_571388, Image-SWTImage_866821] <class 'swtloc.abstractions.SWTImage'> >>> # Empty Initialisation -> Raises SWTLocalizerValueError (from v2.1.0) >>> swtl = SWTLocalizer() SWTLocalizerValueError: Either `images` or `image_paths` parameters should be provided. >>> >>> >>> # Mixed input given -> Raises SWTLocalizerValueError >>> mixed_input = [root_path+'test_image_1/test_img1.jpg' , cv2.imread(root_path+'test_image_1/test_img1.jpg')] >>> swtl = SWTLocalizer(images=mixed_input) SWTLocalizerValueError: If a list is provided to `images`, each element should be an np.ndarray >>> >>> # Wrong input type given -> Raises SWTLocalizerValueError >>> wrong_input = [True, 1, 'abc', root_path+'test_image_1/test_img1.jpg'] >>> swtl = SWTLocalizer(image_paths=wrong_input) SWTLocalizerValueError: `image_paths` should be a `list` of `str` >>> >>> >>> # If the file is not present at the location (NOTE : Use your own image paths) -> Raises FileNotFoundError >>> multiple_image_paths = [root_path+'test_image_2/test_img2.jpg', >>> root_path+'test_image_/image_not_there.jpg', >>> root_path+'test_image_4/test_img4.jpeg' ] >>> swtl = SWTLocalizer(image_paths=multiple_image_paths) FileNotFoundError: No image present at ../swtloc/examples/test_images/image_not_there.jpg >>> # Random Names being assigned to each image when `images` parameter is provided >>> multiple_images = [cv2.imread(each_path) for each_path in [root_path+'test_image_2/test_img2.jpg', >>> root_path+'test_image_3/test_img3.jpg', >>> root_path+'test_image_4/test_img4.jpeg' ]] >>> swtl = SWTLocalizer(images=multiple_images) >>> print([each_image.image_name for each_image in swtl.swtimages]) ['SWTImage_982112', 'SWTImage_571388', 'SWTImage_866821'] """ def __init__(self, multiprocessing: Optional[bool] = False, images: Optional[Union[np.ndarray, List[np.ndarray]]] = None, image_paths: Optional[Union[str, List[str]]] = None): """ Create a ``SWTLocalizer`` object which will house a list of ``SWTImage`` objects in `swtimage` attribute. Args: multiprocessing (Optional[bool]) : Whether to use multiprocessing or not. [default = False] images (Optional[Union[np.ndarray, List[np.ndarray]]]) : An individual image or a list of 3dimensional (RGB) or 1 dimensional (gray-scale)numpy array. [default = None] image_paths (Optional[Union[str, List[str]]]) : A single image path or a list of image paths. [default = None] Raises: - SWTLocalizerValueError - FileNotFoundError """ # Variable Initialisation config[CONFIG__SWTLOCALIZER__MULTIPROCESSING] = multiprocessing config[CONFIG__SWTLOCALIZER__TRANSFORM_IMAGES] = images config[CONFIG__SWTLOCALIZER__TRANSFORM_IMAGE_PATHS] = image_paths self.swtimages: List[SWTImage] = [] # Sanity Checks res_pack = self._sanityChecks(images=images, image_paths=image_paths) transform_inputs, transform_input_flags, transform_input_image_names = res_pack # Instantiate each transform_input as SWTImage for each_image, each_input_flag, each_image_name in zip(*[transform_inputs, transform_input_flags, transform_input_image_names]): swt_img_cfg = {k: v for k, v in config.items() if CONFIG__SWTIMAGE__TRANSFORM} swt_img_obj = SWTImage(image=each_image, image_name=each_image_name, input_flag=each_input_flag, cfg=deepcopy(swt_img_cfg)) # Append the SWTImage object to swtimages list self.swtimages.append(swt_img_obj) @staticmethod def _sanityChecks(images: Optional[Union[np.ndarray, List[np.ndarray]]] = None, image_paths: Optional[Union[str, List[str]]] = None): """ Perform sanity checks on `SWTLocalizer``'s input. Args: images (Optional[Union[np.ndarray, List[np.ndarray]]]) : An individual image or a list of 3 dimensional (RGB) or 1 dimensional (gray-scale) numpy array. image_paths (Optional[Union[str, List[str]]]) : A single image path or a list of image paths. Returns: (List[np.ndarray]) : A list of images (numpy array). [default = None]. (List[ByteString]) : A list of flags associated with each input image, one of [`TRANSFORM_INPUT__1C_IMAGE`, `TRANSFORM_INPUT__3C_IMAGE`], representing a single channel and a RGB channel image. [default = None]. (List[str]) : A list of image names. In case of `image_paths` parameter being provided the names are taken from path of the image. In case of `images` parameter being provided, random image names are assigned to each image. [default = None]. Raises: - SWTLocalizerValueError - FileNotFoundError """ # Type Sanity checks perform_type_sanity_checks(cfg=config, cfg_of=CONFIG__SWTLOCALIZER) # Return Variables transform_inputs: List[np.ndarray] = [] transform_input_flags: List[bytes] = [] transform_input_image_names: List[str] = [] # Either one of imgpaths & images should be given as input to the function if all([val is None for val in [images, image_paths]]) or all( [val is not None for val in [images, image_paths]]): raise SWTLocalizerValueError("Either `images` or `image_paths` parameters should be provided.") # If `image_paths` parameter is given if image_paths is not None: # If only a single file path is provided as a string. # Convert it into a list of images paths if isinstance(image_paths, str): image_paths = [image_paths] elif isinstance(image_paths, list): if not all([isinstance(each_path, str) for each_path in image_paths]): raise SWTLocalizerValueError("`image_paths` should be a `list` of `str`") for each_path in image_paths: if not os.path.isfile(each_path): raise FileNotFoundError(f"No image present at {each_path}") else: _img_name = " ".join(each_path.split('/')[-1].split('.')[:-1]) transform_input_image_names.append(_img_name) transform_inputs.append(cv2.imread(each_path)) # If transform_inputs have been populated from image_paths if transform_inputs: images = transform_inputs transform_inputs = [] # If `images` parameter is given - np.ndarray, [np.ndarray] if images is not None: if isinstance(images, np.ndarray): images = [images] for each_image in images: # Check if all the elements in the list are images if not isinstance(each_image, np.ndarray): raise SWTLocalizerValueError( "If a list is provided to `images`, each element should be an np.ndarray") # Check if its whether 3d or 1d image if not (len(each_image.shape) in [3, 2]): raise SWTLocalizerValueError( "Every image in `images` parameter can only be 3D(RGB Image) or 2D(Grayscale Image)") if len(each_image.shape) == 3: if each_image.shape[-1] != 3: raise SWTLocalizerValueError( "Every image in `images` parameter must be 3 Channels (RGB) image for a 3D image") else: transform_inputs.append(each_image) transform_input_flags.append(TRANSFORM_INPUT__3C_IMAGE) elif len(each_image.shape) == 2: transform_inputs.append(each_image) transform_input_flags.append(TRANSFORM_INPUT__1C_IMAGE) # If the case was of `image_paths` then the image names have already been extracted # if not, then the case was that of the `images` if not transform_input_image_names: transform_input_image_names = generate_random_swtimage_names(n=len(transform_inputs)) return transform_inputs, transform_input_flags, transform_input_image_names