Source code for aitlas.transforms.spacenet6

"""Classes and methods for image transformations specific for the Spacenet6 dataset."""

import random

import cv2
import numpy as np

from aitlas.base import BaseTransforms


def _blend(img1, img2, alpha):
    """
    Blend two images together using the specified alpha.

    :param img1: First input image
    :type img1: numpy.ndarray
    :param img2: Second input image
    :type img2: numpy.ndarray
    :param alpha: blending factor
    :type alpha: float
    :return: Blended image
    :rtype: numpy.ndarray
    """
    return img1 * alpha + (1 - alpha) * img2


_alpha = np.asarray([0.25, 0.25, 0.25, 0.25]).reshape((1, 1, 4))


def _grayscale(img):
    """
    Transform an image to grayscale.

    :param img: Input image
    :type img: numpy.ndarray
    :return: grayscale image
    :rtype: numpy.ndarray
    """
    return np.sum(_alpha * img, axis=2, keepdims=True)


[docs]def saturation(img, alpha): """ Adjust the saturation of an image. :param img: input image :type img: numpy.ndarray :param alpha: saturation factor :type alpha: float :return: image with adjusted saturation :rtype: numpy.ndarray """ gs = _grayscale(img) return _blend(img, gs, alpha)
[docs]def brightness(img, alpha): """ Adjust the brightness of an image. :param img: input image :type img: numpy.ndarray :param alpha: brightness factor :type alpha: float :return: image with adjusted brightness :rtype: numpy.ndarray """ gs = np.zeros_like(img) return _blend(img, gs, alpha)
[docs]def contrast(img, alpha): """ Adjust the contrast of an image. :param img: input image :type img: numpy.ndarray :param alpha: contrast factor :type alpha: float :return: image with adjusted contrast :rtype: numpy.ndarray """ gs = _grayscale(img) gs = np.repeat(gs.mean(), 4) return _blend(img, gs, alpha)
[docs]class SpaceNet6Transforms(BaseTransforms): """ SpaceNet6 specific image transformations. """ def __call__(self, sample): """ Apply transformations to the sample. The transformations include: - random crop to 512x512 - random rotation with probability 0.7 - random scale between 0.5 and 2.0 - random left-right flip with probability 0.5 - random color augmentation (saturation, brightness, contrast) :param sample: a dictionary containing image and mask data :type sample: dict :return: image and mask after applying transformations :rtype: tuple """ # Unpack sample # Data image = sample.get("image", None) mask = sample.get("mask", None) # Crop size crop_size = 512 # Transform probabilities rot_prob = 0.7 flip_lr_prob = 0.5 ################################### # Start transforms pad = max(0, crop_size - image.shape[0]) image = cv2.copyMakeBorder( src=image, top=0, bottom=pad, left=0, right=0, borderType=cv2.BORDER_CONSTANT, value=0.0, ) mask = cv2.copyMakeBorder( src=mask, top=0, bottom=pad, left=0, right=0, borderType=cv2.BORDER_CONSTANT, value=0.0, ) # Rotate image if random.random() < rot_prob: rotation_matrix = cv2.getRotationMatrix2D( center=(image.shape[0] // 2, image.shape[1] // 2), angle=random.randint(0, 10) - 5, scale=1.0, ) image = cv2.warpAffine( src=image, M=rotation_matrix, dsize=image.shape[:2], flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101, ) mask = cv2.warpAffine( src=mask, M=rotation_matrix, dsize=mask.shape[:2], flags=cv2.INTER_NEAREST, borderMode=cv2.BORDER_REFLECT_101, ) # Scale image (because scale_prob = 1) rotation_matrix = cv2.getRotationMatrix2D( center=(image.shape[0] // 2, image.shape[1] // 2), angle=0, scale=random.uniform(0.5, 2.0), ) image = cv2.warpAffine( src=image, M=rotation_matrix, dsize=image.shape[:2], flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101, ) mask = cv2.warpAffine( src=mask, M=rotation_matrix, dsize=mask.shape[:2], flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101, ) # Random crop x0 = random.randint(0, image.shape[1] - crop_size) y0 = random.randint(0, image.shape[0] - crop_size) image = image[y0 : y0 + crop_size, x0 : x0 + crop_size] mask = mask[y0 : y0 + crop_size, x0 : x0 + crop_size] # Apply these functions (because color_aug_prob = 1) image = saturation(image, 0.8 + random.random() * 0.4) image = brightness(image, 0.8 + random.random() * 0.4) image = contrast(image, 0.8 + random.random() * 0.4) # Left-right flip if random.random() < flip_lr_prob: image = np.fliplr(image) mask = np.fliplr(mask) # Return results return image, mask