Source code for IQM_Vis.transforms.effects

'''
Sample image transformations to get the user started with
'''
# Author: Matt Clifford <matt.clifford@bristol.ac.uk>
# License: BSD 3-Clause License

from skimage.transform import resize
from skimage.util import img_as_ubyte
import cv2
import numpy as np


[docs]def brightness(image, value=0): '''Adjust image brightness. Image is clipped to the range (0, 1) Args: image (np.array): image to have its brightness adjusted value (float): value to adjust all pixels by in the range (-1, 1). (Defaults to 0) Returns: image (np.array): adjusted image ''' if value == 0: return image return np.clip(image + value, 0, 1)
[docs]def binary_threshold(image, threshold=100): '''conver image to binary at a given threshold Args: image (np.array): image to be thresholded threshold (int): threshold value in the range (1, 255) (Defaults to 100) Returns: image (np.array): thresholded image (float32 in range 0, 1) ''' image = img_as_ubyte(image) if len(image.shape) > 2: if image.shape[2] == 3: image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) image = cv2.adaptiveThreshold(image, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, int(threshold)) if len(image.shape) == 2: image = np.stack((image,)*3, axis=-1) return np.clip(image.astype(np.float32) / 255.0, 0, 1)
[docs]def jpeg_compression(image, compression=101): '''encode image using jpeg then decode Args: image (np.array): image to be compressed compression (int): amount of jpeg compression, higher is better quality. 101 returns identity image. (Defaults to 101) Returns: image (np.array): jpeg compressed image ''' if compression == 101: return image encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), int(compression)] encoder = '.jpg' return _encode_compression(image, encoder, encode_param)
def _encode_compression(image, encoder, encode_param, uint=True): ''' generic image encoder for jpeg, png etc using https://dev_resources/docs.opencv.org/3.4/d4/da8/group__imgcodecs.html to encode/decode for encode types: https://dev_resources/docs.opencv.org/3.4/d6/d87/imgcodecs_8hpp.html ''' original_size = image.shape if uint == True: # jpeg/png encoder needs uint images not float image = img_as_ubyte(image) # encode image result, encoded_image = cv2.imencode(encoder, image, encode_param) # decode image decoded_image = cv2.imdecode(encoded_image, 1) if uint == True: # return to float type image = decoded_image.astype(np.float32) / 255.0 # have to resize as jpeg can sometimes change the size of an image if image.shape != original_size: image = resize(image, original_size) return np.clip(image, 0, 1)
[docs]def contrast(image, contrast=1.0): """adjust the contrast of an image Args: image (np.array): image to have contrast adjusted contrast (float): amount of contrast to adjust by. Values less that 1 will decrease contrast, Values higher than 1 will increase the contrast. (Defaults to 1.0). Returns: image (np.array): image with contrast adjusted """ if contrast == 1: return image image = img_as_ubyte(image) # convert to 255 max value # need to zero center the data so we only adjust for contrast brightness = int(round(255*(1-contrast)/2)) image = cv2.addWeighted(image, contrast, image, 0, brightness) return np.clip(image/255, 0, 1)
[docs]def hue(image, h=0): """adjust the hue of an image Args: image (np.array): image to have hue adjusted h (float): amount of hue to adjust by. (Defaults to 0). Returns: image (np.array): image with hue adjusted """ return _adjust_HSV(image, h, channel=0)
[docs]def saturation(image, sat=0): """adjust the saturation of an image Args: image (np.array): image to have saturation adjusted sat (float): amount of saturation to adjust by. (Defaults to 0). Returns: image (np.array): image with saturation adjusted """ return _adjust_HSV(image, sat, channel=1)
[docs]def brightness_hsv(image, b=0): """adjust the brightness of an image Args: image (np.array): image to have brightness adjusted b (float): amount of brightness to adjust by. (Defaults to 0). Returns: image (np.array): image with brightness adjusted """ return _adjust_HSV(image, b, channel=2)
def _adjust_HSV(image, value, channel): ''' adjust hue, saturation or brightness of an image, image is float between 0, 1 and value is between -1 and 1 channels: 0 = hue 1 = saturation 2 = brightness ''' if value == 0: return image image = img_as_ubyte(image) # convert to HSV hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV) # normalise between 0, 1 hsv = hsv.astype(np.float32) hsv[:, :, 0] = hsv[:, :, 0]/179 hsv[:, :, 1:] = hsv[:, :, 1:]/255 # add the adjustment to the required channel hsv[:, :, channel] += value # account for hue colours being radial if channel == 0: hsv[:, :, channel] = np.mod( hsv[:, :, channel], 1) # reset to 0, 1 bounds hsv = np.clip(hsv, 0, 1) # convert back to RGB hsv[:, :, 0] = hsv[:, :, 0]*179 hsv[:, :, 1:] = hsv[:, :, 1:]*255 hsv = hsv.astype(np.uint8) return np.clip(cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB)/255, 0, 1)
[docs]def blur(image, kernel_size=1): '''Gaussian Blur on an image Args: image (np.array): image to be rotated kernel_size (int): size of the convolutional kernel, will be converted to an odd number. 1 is no blur. (Defaults to 1) Returns: image (np.array): rotated image ''' if kernel_size == 1: return image elif kernel_size > 0: blur_odd = (int(kernel_size/2)*2) + 1 # need to make kernel size odd image = cv2.GaussianBlur( image, (blur_odd, blur_odd), cv2.BORDER_DEFAULT) if len(image.shape) == 2: image = np.expand_dims(image, axis=2) return np.clip(image, 0, 1)