From f04cda018999f8a7e00c1f1454eedd3d9b95624f Mon Sep 17 00:00:00 2001 From: Nada Kouddane <nada.kouddane@grenoble-inp.org> Date: Wed, 31 Jan 2024 20:47:10 +0100 Subject: [PATCH] Upload New File --- src/methods/nada_kouddane/functions.py | 308 +++++++++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100644 src/methods/nada_kouddane/functions.py diff --git a/src/methods/nada_kouddane/functions.py b/src/methods/nada_kouddane/functions.py new file mode 100644 index 0000000..e455298 --- /dev/null +++ b/src/methods/nada_kouddane/functions.py @@ -0,0 +1,308 @@ +"""A file containing the main function for the chosen interpolation (Adams-Hamilton Algorithm). +""" + +### BE CAREFUL TO ADD THE MODULE TORCH TO THE FILE requirements.txt + +import numpy as np +from scipy.signal import convolve2d +import torch +import torch.nn.functional as F + +from src.forward_model import CFA + + +# Initial kernels for the bilinear interpolation +ker_bayer_red_blue = np.array([[1, 2, 1], + [2, 4, 2], + [1, 2, 1]]) / 4 +ker_bayer_green = np.array([[0, 1, 0], + [1, 4, 1], + [0, 1, 0]]) / 4 + +# Kernels for the quad_bayer filter +ker_quad_red_blue = np.array([[1, 1, 2, 2, 0, 1], + [1, 1, 2, 2, 1, 1], + [2, 2, 4, 4, 2, 2], + [2, 2, 4, 4, 2, 2], + [1, 1, 2, 2, 1, 1], + [1, 1, 2, 2, 1, 1]])/16 +ker_quad_green = np.array([[0, 0, 1, 1, 0, 0], + [0, 0, 1, 1, 0, 0], + [1, 1, 4, 4, 1, 1], + [1, 1, 4, 4, 1, 1], + [0, 0, 1, 1, 0, 0], + [0, 0, 1, 1, 0, 0]])/16 + + +def second_naive_interpolation(op: CFA, y: np.ndarray) -> np.ndarray: + """Performs a naive interpolation of the lost pixels. for the quad_bayer, performs a convolution + with kernels (above: ker_quad_red_blue and ker_quad_green) inspired from the classic bilnear interpolation + + Args: + op (CFA): CFA operator. + y (np.ndarray): Mosaicked image. + + Returns: + np.ndarray: Demosaicked image. + """ + z = op.adjoint(y) + + if op.cfa == 'bayer': + res = np.empty(op.input_shape) + + res[:, :, 0] = convolve2d(z[:, :, 0], ker_bayer_red_blue, mode='same') + res[:, :, 1] = convolve2d(z[:, :, 1], ker_bayer_green, mode='same') + res[:, :, 2] = convolve2d(z[:, :, 2], ker_bayer_red_blue, mode='same') + + else: + res = np.empty(op.input_shape) + + res[:, :, 0] = convolve2d(z[:, :, 0], ker_quad_red_blue, mode='same') + res[:, :, 1] = convolve2d(z[:, :, 1], ker_quad_green, mode='same') + res[:, :, 2] = convolve2d(z[:, :, 2], ker_quad_red_blue, mode='same') + #res[:, :, 0] = convolution_pad_stride(z[:, :, 0], ker_quad_red_blue) + #res[:, :, 1] = convolution_pad_stride(z[:, :, 1], ker_quad_green) + #res[:, :, 2] = convolution_pad_stride(z[:, :, 2], ker_quad_red_blue) + + + return res + + +import torch +import torch.nn.functional as F + +def convolution_pad_stride(input, kernel): + """An attempt of convolution with stride and padding. + + Args: + input(np.ndarray): input (mosaicked) image. + kernel (np.ndarray): convolution kernel. + + Returns: + np.ndarray: Convolution of the image with the kernel with stride of 2 and padding of 512. + """ + input_tensor = torch.tensor(np.expand_dims(input, axis=(0,1))) + kernel_tensor = torch.tensor(np.expand_dims(kernel, axis=(0,1))) + output = F.conv2d(input_tensor, kernel_tensor, stride=2, padding=514) + output = output.numpy().squeeze() + + return output + + +import numpy as np + + + + + +def take(array2d, i, j): + """ + Helper function that returns the indices of an array, prevents index outbound + + Args: + array2d (np.ndarray): input array + i: row index + j: column index + + Returns: + np.float64(0): array value at position[i,j] + """ + if 0 <= i < array2d.shape[0] and 0 <= j < array2d.shape[1]: + return array2d[i, j] + return np.float64(0) + + +def red_blue_positions(img): + """ + Helper function that yields the red and blue positions in the mosaicked image + + Args: + img (np.ndarray): mosaicked image + + Returns: + None + """ + first_non_green = 1 + for i in range(img.shape[0]): + for j in range(first_non_green, img.shape[1], 2): + yield i, j + first_non_green = 1 - first_non_green + + +def directional_green_interpolation(img): + """ + Function that performs green interpolation in horizontal and vertical directions. + + Args: + img (np.ndarray): input (mosaicked) image + + Returns: + green_h, green_v (np.ndarray): horizontally and vertically interpolated green components + """ + + green_h = img.copy() # green positions are copied + green_v = img.copy() # other values will be replaced + + for i, j in red_blue_positions(img): + r = lambda k: take(img, i, j + k) # r - relative indexing + green_h[i, j] = (r(1) + r(-1) + r(0)) / 2 - (r(2) + r(-2)) / 4 + r = lambda k: take(img, i + k, j) + green_v[i, j] = (r(1) + r(-1) + r(0)) / 2 - (r(2) + r(-2)) / 4 + + return green_h, green_v + + +def green_decision(img, green_h, green_v, cardinal_directions_improvement = True): + + """ + Function that performs the green decision between the chrominance components based on the color difference uniformity + by calculating the horizontal and the vertical gradients. + + Args: + img (np.ndarray): input (mosaicked) image + green_h (np.ndarray): horizontally interpolated green component + green_v (np.ndarray): vertically interpolated green component + cardinal_directions_improvement (bool) = True (default) : parameter that allows to adjust a given window weight in order + to improve the gradients calculation + + Returns: + green (np.ndarray): interpolated green image + delta_h (np.ndarray): horizontal gradient image + delta_v (np.ndarray): vertical gradient image + """ + + height, width = img.shape + + # "chrominance" is R - G in red locations, B - G in blue locations + # and 0 in green locations + chrominance_h = img - green_h + chrominance_v = img - green_v + + # also 0 in green locations, this will be useful + gradient_h = chrominance_h.copy() + gradient_v = chrominance_v.copy() + + for i, j in red_blue_positions(img): + gradient_h[i, j] -= take(chrominance_h, i, j + 2) + gradient_v[i, j] -= take(chrominance_v, i + 2, j) + gradient_h = np.abs(gradient_h) + gradient_v = np.abs(gradient_v) + # could be easily rewritten without loops + + window = np.ones(shape=(5, 5), dtype=np.float64) + if cardinal_directions_improvement: + window[2, :] = 3 + window[:, 2] = 3 + + delta_h = np.zeros(shape=(img.shape), dtype=np.float64) + delta_v = delta_h.copy() + padded_grad_h = np.zeros(shape=(img.shape[0] + 4, img.shape[1] + 4), dtype=np.float64) + padded_grad_v = padded_grad_h.copy() + padded_grad_h[2 : img.shape[0] + 2, 2 : img.shape[1] + 2] = gradient_h + padded_grad_v[2 : img.shape[0] + 2, 2 : img.shape[1] + 2] = gradient_v + green = green_h.copy() + for i, j in red_blue_positions(img): + delta_h[i, j] = np.sum(window * padded_grad_h[i : i + 5, j : j + 5]) + delta_v[i, j] = np.sum(window * padded_grad_v[i : i + 5, j : j + 5]) + if delta_v[i, j] < delta_h[i, j]: + green[i, j] = green_v[i, j] + + return green, delta_h, delta_v + + +def red_blue_interpolation(img, green, delta_h, delta_v): + + """ + Function that performs the red and blue components interpolation. + + Args: + img (np.ndarray): input (mosaicked) image + green (np.ndarray): interpolated green image + delta_h (np.ndarray): horizontal gradient image + delta_v (np.ndarray): vertical gradient image + + Returns: + red, blue (np.ndarray): interpolated red and blue image components + """ + + height, width = img.shape + red = img.copy() + blue = img.copy() + + # green positions first + for i in range(0, height, 2): # green-red rows + for j in range(0, width, 2): + red[i, j] = (take(img, i, j - 1) + + take(img, i, j + 1)) / 2 + blue[i, j] = (take(img, i - 1, j) + + take(img, i + 1, j)) / 2 + for i in range(1, height, 2): # green-blue rows + for j in range(1, width, 2): + blue[i, j] = (take(img, i, j - 1) + + take(img, i, j + 1)) / 2 + red[i, j] = (take(img, i - 1, j) + + take(img, i + 1, j)) / 2 + + # now red in blue positions, blue in red positions + red_minus_blue = red - blue + for i in range(1, height, 2): + for j in range(0, width, 2): + if delta_v[i, j] < delta_h[i, j]: + red[i, j] = blue[i, j] + (take(red_minus_blue, i - 1, j) + + take(red_minus_blue, i + 1, j)) / 2 + else: + red[i, j] = blue[i, j] + (take(red_minus_blue, i, j - 1) + + take(red_minus_blue, i, j + 1)) / 2 + for i in range(0, height, 2): + for j in range(1, width, 2): + if delta_v[i, j] < delta_h[i, j]: + blue[i, j] = red[i, j] - (take(red_minus_blue, i - 1, j) + + take(red_minus_blue, i + 1, j)) / 2 + else: + blue[i, j] = red[i, j] - (take(red_minus_blue, i, j - 1) + + take(red_minus_blue, i, j + 1)) / 2 + + return red, blue + + +def demosaicking_algorithm(img): + """ + Main function of the Daniele Menon demosaicking algorithm. + + Args: + img (np.ndarray): input (mosaicked image) + + Returns: + np.ndarray: reconstructed image + + """ + green_h, green_v = directional_green_interpolation(img) + green, delta_h, delta_v = green_decision(img, green_h, green_v) + red, blue = red_blue_interpolation(img, green, delta_h, delta_v) + return np.clip(np.dstack((red, green, blue)), 0, 1) + + +#### +#### +#### + +#### #### #### ############# +#### ###### #### ################## +#### ######## #### #################### +#### ########## #### #### ######## +#### ############ #### #### #### +#### #### ######## #### #### #### +#### #### ######## #### #### #### +#### #### ######## #### #### #### +#### #### ## ###### #### #### ###### +#### #### #### ## #### #### ############ +#### #### ###### #### #### ########## +#### #### ########## #### #### ######## +#### #### ######## #### #### +#### #### ############ #### +#### #### ########## #### +#### #### ######## #### +#### #### ###### #### + +# 2023 +# Authors: Mauro Dalla Mura and Matthieu Muller \ No newline at end of file -- GitLab