Source code for picturepyfect.compression_pyfect

import numpy as np
from picturepyfect.rotate_pyfect import rotate_pyfect


[docs]class DimensionError(Exception): """ Raised when when a numpy array has the wrong shape. """
[docs]def compression_pyfect(image, kernel_size=2, pooling_function="max"): """ This function uses a lossy pooling algorithm to compress an image. The function can be applied to single channel or 3-channel images. The user passes an image which is to be compressed and the resulting compressed numpy array is returned. The user can also specify the pooling algorithm to be used and the size of the kernel to apply over the image. Parameters ---------- image : numpy.ndarray A n*n or n*n*3 numpy array representing a single channel or a 3-channel image. kernel_size : int The size of the kernel to be passed over the image. The resulting filter moving across the image will be a 2D array with dimensions kernel_size x kernel_size. Default: 2 pooling_function : str The pooling algorithm to be used within a kernel. There are three options: "max", "min", and "mean". Default: "max" Returns: --------- numpy.ndarray A numpy array representing the compressed image. Examples -------- >>> compression_pyfect(image, kernel_size=3, pooling_function="max") array([[0.04737957, 0.04648845, 0.04256656, 0.04519495], [0.04657273, 0.04489012, 0.04031093, 0.04047667], [0.04641026, 0.04106843, 0.04560866, 0.04732271], [0.0511907 , 0.04518351, 0.04946411, 0.04030291]]) """ # Check if the image and kernel_size are valid inputs check_values(image, kernel_size) # Check for a valid pooling_function input if pooling_function == "max": pool_func = np.max elif pooling_function == "min": pool_func = np.min elif pooling_function == "mean": pool_func = np.mean else: raise ValueError( """ The pooling_function argument only takes a value of 'max', 'min', or 'mean'. """ ) # If image is not divisible by the kernel_size # crop off the right side columns and the bottom rows divisible_row = image.shape[0] // kernel_size * kernel_size divisible_col = image.shape[1] // kernel_size * kernel_size # If image is greyscale, compress just one colour band if len(image.shape) == 2: image = image[:divisible_row, :divisible_col] b1 = pool_band(image, kernel_size, pool_func) return b1 # If image is colour, compress all 3 colour bands else: image = image[:divisible_row, :divisible_col, :] # Pool the 3 colour bands b1 = pool_band(image[:, :, 0], kernel_size, pool_func) b2 = pool_band(image[:, :, 1], kernel_size, pool_func) b3 = pool_band(image[:, :, 2], kernel_size, pool_func) # Combine the 3 colour bands return np.dstack((b1, b2, b3))
[docs]def pool_band(band, kernel_size, pool_func): """ This function is to be used in conjunction with compression_pyfect and compresses a single colour band of an image. The function applies a lossy pooling algorithm to compress the specified colour band and the resulting compressed numpy array is returned. Parameters ---------- band : numpy.ndarray A n*n numpy array representing a single colour band of an image. kernel_size : int The size of the kernel to be passed over the image. The resulting filter moving across the image will be a 2D array with dimensions kernel_size x kernel_size. pooling_function : str The pooling algorithm to be used within a kernel. There are three options: "max", "min", and "mean". Returns: --------- numpy.ndarray An n*n numpy array representing the compressed image. Examples -------- >>> pool_band(image, kernel_size=3, pooling_function="max") array([[0.04737957, 0.04648845, 0.04256656, 0.04519495], [0.04657273, 0.04489012, 0.04031093, 0.04047667], [0.04641026, 0.04106843, 0.04560866, 0.04732271], [0.0511907 , 0.04518351, 0.04946411, 0.04030291]]) """ # Using this forum post as a guide and reference for the below code # https://tinyurl.com/yhnbexcs col = band.shape[1] // kernel_size # The colour band to pool pool_colour_band = band # pool along the rows pool_colour_band = pool_colour_band.reshape(-1, kernel_size) pool_colour_band = pool_func(pool_colour_band, axis=1) pool_colour_band = pool_colour_band.reshape(-1, col) # rotate and pool along the rows again # old pool_colour_band = np.rot90(pool_colour_band) pool_colour_band = rotate_pyfect(pool_colour_band, n_rot=3) pool_colour_band = pool_colour_band.reshape(-1, kernel_size) pool_colour_band = pool_func(pool_colour_band, axis=1) pool_colour_band = pool_colour_band.reshape(col, -1) # rotate back to proper layout # old pool_colour_band = np.rot90(pool_colour_band, 3) pool_colour_band = rotate_pyfect(pool_colour_band, n_rot=1) return pool_colour_band
[docs]def check_values(image, kernel_size): """ This function checks that the image and kernel size are valid inputs and raises an error if not. Parameters ---------- image : numpy.ndarray A n*n or n*n*3 numpy array representing a single channel or 3-channel image. kernel_size : int The size of the kernel to be passed over the image. The resulting filter moving across the image will be a 2D array with dimensions kernel_size x kernel_size. Examples -------- >>> check_values(image, kernel_size=3) """ if not isinstance(image, np.ndarray): raise ValueError("Image must be a numpy array.") if not isinstance(kernel_size, int): raise ValueError( "kernel_size must be a positive integer greater than 0." ) if kernel_size < 1: raise ValueError( "kernel_size must be a positive integer greater than 0." ) # Check if the image is of the correct shape. # Greyscale and colour images both accepted if len(image.shape) != 2 and len(image.shape) != 3: raise DimensionError( """ The input image array needs to be of shape n x n, or n x n x 3. """ ) # If the image is of size n x n x n, # ensure that the third dimension equals 3. if len(image.shape) == 3: if image.shape[2] != 3: raise DimensionError( """ The input image array needs to be of shape n x n, or n x n x 3. """ ) # Check that the kernel_size is smaller than the image height and width if image.shape[0] < kernel_size or image.shape[1] < kernel_size: raise ValueError( """ The kernel size must not be larger than the height or width of the input image array. """ )