Source code for cilissa.transformations

from typing import Optional, Tuple, Union

import cv2
import numpy as np

from cilissa.images import Image
from cilissa.operations import Transformation


[docs]class Blur(Transformation): """ Blurs an image. Args: gaussian (bool): If True, uses a Gaussian filter to blur the image. If False, uses normalized box filter. kernel_size (Tuple[int, int]): Gaussian/blurring kernel size. Elements in tuple can differ but they both must be positive and odd. If using Gaussian filter they can be zero's and then they are computed from sigma. sigma (float): Gaussian kernel standard deviation in X direction. Used only with Gaussian filter. References: - https://docs.opencv.org/4.5.2/d4/d86/group__imgproc__filter.html#ga8c45db9afe636703801b0b2e440fce37 - https://docs.opencv.org/4.5.2/d4/d86/group__imgproc__filter.html#gaabe8c836e97159a9193fb0b11ac52cf1 """ def __init__(self, gaussian: bool = True, kernel_size: Tuple[int, int] = (0, 0), sigma: float = 1.0) -> None: self.gaussian = gaussian self.kernel_size = kernel_size self.sigma = sigma def transform(self, image: Image) -> Image: im = image.as_int() if self.gaussian: new_im = cv2.GaussianBlur(im, self.kernel_size, self.sigma) else: new_im = cv2.blur(im, self.kernel_size) return Image(new_im)
[docs]class Sharpen(Transformation): """ Sharpens an image using an unsharp mask. Args: amount (float): Amount of sharpening applied. threshold (int): Threshold for the low-constrast mask. Pixels for which the difference between the input and blurred images is less than threshold will remain unchanged. kwargs (Any): Arguments passed as kwargs will be passed to the blur transformation. References: - https://en.wikipedia.org/wiki/Unsharp_masking """ def __init__(self, amount: float = 1.5, kernel_size: Tuple[int, int] = (0, 0), sigma: float = 1.0) -> None: # Parameters for blur self.kernel_size = kernel_size self.sigma = sigma # Parameters for unsharp mask self.amount = amount def transform(self, image: Image) -> Image: im = image.as_int() new_im = cv2.GaussianBlur(im, self.kernel_size, self.sigma) new_im = cv2.addWeighted(im, self.amount, new_im, -self.amount + 1, 0) return Image(new_im)
[docs]class Linear(Transformation): """ Changes brightness of the image with a simple linear transformation. g(x) = a * f(x) + b, where `a` controls contrast and `b` controls brightness Brightness refers to the overall lightness or darkness of the image. Increasing the brightness every pixel in the frame gets lighter. Contrast is the difference in brightness between objects in the image. Increasing the contrast makes light areas lighter and dark area in the frame becomes much darker. Args: contrast (int/float/None): Value by which to change the contrast. 1 and None is the original image. A float from interval (0, 1) reduces the contrast. Values above 1 increase the contrast. brightness (int/float/None): Value by which to change the brightness. 0 and None is the original image. Negative values reduce the brightness. Positive values increase the brightness. References: - https://docs.opencv.org/3.4/d3/dc1/tutorial_basic_linear_transform.html """ def __init__( self, contrast: Optional[Union[float, int]] = None, brightness: Optional[Union[float, int]] = None ) -> None: self.contrast = contrast self.brightness = brightness def transform(self, image: Image) -> Image: im = image.as_int() # Apply linear transformation new_im = cv2.convertScaleAbs(im, alpha=self.contrast, beta=self.brightness) return Image(new_im)
[docs]class Translation(Transformation): """ Shifts an image by given amount in pixels along X and/or Y axis. Uses an affine transformation to perform image translation. Args: x (int): Value (in px) to move the image along X-axis. Positive - right, negative - left. y (int): Value (in px) to move the image along Y-axis. Positive - down, negative - up. References: - https://en.wikipedia.org/wiki/Affine_transformation """ def __init__(self, x: int = 0, y: int = 0) -> None: self.x = x self.y = y def transform(self, image: Image) -> Image: im = image.as_int() # Transformation matrix M = np.array([[1, 0, self.x], [0, 1, self.y]], dtype=np.float32) new_im = cv2.warpAffine(im, M, (im.shape[1], im.shape[0])) return Image(new_im)
[docs]class Equalization(Transformation): """ Constrast adjustment using histogram equalization. CV2 equalization is limited to 8-bit images so we use the NumPy CDF function. References: - https://en.wikipedia.org/wiki/Histogram_equalization - https://web.archive.org/web/20151219221513/http://www.janeriksolem.net/2009/06/histogram-equalization-with-python-and.html # noqa """ def __init__(self, nbins: int = 256) -> None: # Number of bins for image histogram self.number_of_bins = nbins def transform(self, image: Image) -> Image: im = image.as_int() # Get normalized histogram - probability density function of each gray level image_histogram, bins = np.histogram(im.flatten(), self.number_of_bins, density=True) cdf = image_histogram.cumsum() cdf = 255 * cdf / cdf[-1] # Use linear interpolation of cdf to find new pixel values image_equalized = np.interp(im.flatten(), bins[:-1], cdf) new_im = image_equalized.reshape(im.shape).astype(np.uint8, casting="unsafe") return Image(new_im)