diff --git a/convolve2d_cython.pyx b/convolve2d_cython.pyx new file mode 100644 index 0000000..b31d92e --- /dev/null +++ b/convolve2d_cython.pyx @@ -0,0 +1,42 @@ +# Import necessary packages +import cython +import numpy as np +cimport numpy as np + + +# Declare types for function arguments and variables +cpdef np.ndarray[np.float64_t, ndim=2] convolve2d(np.ndarray[np.float64_t, ndim=2] image, + np.ndarray[np.float64_t, ndim=2] kernel): + + cdef int image_height, image_width, kernel_height, kernel_width, pad_height, pad_width, row, col + cdef np.ndarray[np.float64_t, ndim=2] padded_image, convolved_image, patch, product + + # Get the dimensions of the input image and kernel + # Get the dimensions of the input image and kernel + image_height, image_width = int(image.shape[0]), int(image.shape[1]) + kernel_height, kernel_width = int(kernel.shape[0]), int(kernel.shape[1]) + + # Compute the padding needed to handle boundary effects + pad_height = (kernel_height - 1) // 2 + pad_width = (kernel_width - 1) // 2 + padded_image = np.pad(image, ((pad_height, pad_height), (pad_width, pad_width)), mode='constant') + + # Initialize the output image + convolved_image = np.zeros((image_height, image_width), dtype=np.float64) + + # Loop over each pixel in the output image and compute the convolved value + for row in range(image_height): + for col in range(image_width): + # Extract the patch centered at the current pixel + patch = padded_image[row : row + kernel_height, col : col + kernel_width] + + # Compute the element-wise product of the patch and the flipped kernel + product = patch * np.flip(kernel, axis=(0, 1)) + + # Compute the sum of the element-wise products + convolved_value = np.sum(product) + + # Store the convolved value in the output image + convolved_image[row, col] = convolved_value + + return convolved_image \ No newline at end of file diff --git a/makehdr.py b/makehdr.py index 0db378c..4e079a2 100644 --- a/makehdr.py +++ b/makehdr.py @@ -1,11 +1,13 @@ #!//usr/bin/python3 import file_utility as file import numpyHDR as hdr +import os '''CLI application for HDR experiments''' + stack = [] -select = input("Select Image Source: 1 - Raspicam, 2 - From File, 3 - Image sequence, 4 debug: ") +select = input("Select Image Source: 1 - Raspicam, 2 - From File, 3 - Image sequence, 4 debug, 5 - compile Cython: ") if int(select) == 1: import picamburst as pcb @@ -36,7 +38,15 @@ if int(select) == 4: path_list = ['webcam25_3_2023_ev0.jpg','webcam25_3_2023_ev1.jpg','webcam25_3_2023_ev2.jpg'] stack = file.openImageList(path_list, True) -print(path_list) +if int(select) == 5: + try: + os.system('python3 setup.py build_ext --inplace') + except Exception as e: + print("Error while compiling cython function", e) + print("Please restart") + exit() + +#print(path_list) #Process HDR with mertens fusion and post effects, blur #Set last value to false for double the speed but lesser blancaed hdr effect. diff --git a/numpyHDR.py b/numpyHDR.py index 2a8ca76..64d6cb2 100644 --- a/numpyHDR.py +++ b/numpyHDR.py @@ -1,5 +1,15 @@ import numpy as np +try: + import convolve2d_cython + available = True + print("Using compiled Cython Convolve") +except ImportError: + available = False + print("Using normal Numpy Convolve") + + + '''Numpy and PIL implementation of a Mertens Fusion alghoritm Usage: Instantiate then set attributes: input_image = List containing path strings including .jpg Extension @@ -103,8 +113,13 @@ def blur(image, amount=1): [0, -1, 0]]) # Apply the kernel to each channel of the image using convolution - blurred = convolve2d(image, kernel) - + #blurred = convolve2d(image, kernel) + kernel = kernel.astype(np.float64) + #image= image.astype(np.float64) + if available: + blurred = convolve2d_cython.convolve2d(image, kernel) + else: + blurred = convolve2d(image, kernel) # Add the original image to the sharpened image with a weight of the sharpening amount sharpened = image + amount * (image - blurred) @@ -133,12 +148,13 @@ def mertens_fusion(stack, gamma:float =1, contrast_weight:float =1 ,blurred: boo images = [] for array in stack: #Incoming arrays in 255 er range - img = np.array(array).astype(np.float32) / 255.0 + img = np.array(array).astype(np.float64) / 255.0 img = np.power(img, gamma) images.append(img) # Compute the weight maps for each input image based on the local contrast. weight_maps = [] + kernel = np.array([[1, 2, 1], [2, -11, 2], [1, 2, 1]]) for img in images: threshold_h = .99 @@ -149,8 +165,12 @@ def mertens_fusion(stack, gamma:float =1, contrast_weight:float =1 ,blurred: boo if blurred: gray = blur(gray, 1) #kernel = np.array([[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]) - kernel = np.array([[1, 2, 1], [2, -11, 2], [1, 2, 1]]) - laplacian = np.abs(convolve2d(gray, kernel)) + + kernel = kernel.astype(np.float64) + if available: + laplacian = np.abs(convolve2d_cython.convolve2d(gray, kernel)) + else: + laplacian = np.abs(convolve2d(gray, kernel)) weight = np.power(laplacian, contrast_weight) weight_maps.append(weight) @@ -159,7 +179,7 @@ def mertens_fusion(stack, gamma:float =1, contrast_weight:float =1 ,blurred: boo weight_maps = [w / total_weight for w in weight_maps] # Compute the fused HDR image by computing a weighted sum of the input images. - fused = np.zeros(images[0].shape, dtype=np.float32) + fused = np.zeros(images[0].shape, dtype=np.float64) for i, img in enumerate(images): fused += weight_maps[i][:, :, np.newaxis] * img diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..c5772b7 --- /dev/null +++ b/setup.py @@ -0,0 +1,9 @@ +from setuptools import setup +from Cython.Build import cythonize +import numpy + +setup( + name='convolve2d_cython', + ext_modules=cythonize("convolve2d_cython.pyx"), + include_dirs=[numpy.get_include()] +) \ No newline at end of file