# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# +
# The Code is originally from towardsdatascience.com

# +
import skimage.data

# Reading the image

img = skimage.data.chelsea()

# Converting the image into gray. (It means, we don't want to use 3 Channels-R,G,B- for make problem easier)

img = skimage.color.rgb2gray(img)

# +
import numpy as np

l1_filter = np.zeros((2,3,3)) # 2 : num of filters, 3x3 kernel size
# -

l1_filter[0, :, :] = np.array([[[-1, 0, 1],
                                   [-1, 0, 1],
                                   [-1, 0, 1]]])
l1_filter[1, :, :] = np.array([[[1,   1,  1],
                                   [0,   0,  0],
                                   [-1, -1, -1]]])

# +
# Testing Code
print(img.shape)
print(len(img.shape))
print(img.shape[-1])

print(l1_filter.shape) # (num of filters, width of filter, height of filter)


# -

def conv(img, conv_filter):
    if len(img.shape) > 2 or len(conv_filter.shape) > 3: # Check if number of image channels matches the filter depth.
        if image.shape[-1] != conv_filter.shape[-1]:
            print("Error : Number of channels in both image and filter must match.")
            sys.exit()
    if conv_filter.shape[-1] != conv_filter.shape[2]: # Check if filter dimensions are equal.
        print("Error : Filter must be a square matrix. i.e. number of rows and columns must match.")
        sys.exit()
    if conv_filter.shape[1] % 2 == 0: # Check if filter dimensions are odd.
        print("Error : Filter must have an odd size. i.e. number of rows and columns must be odd.")
        sys.exit()
    
    # An empty feature map to hold the output of convolving the filter(s) with the image.
    feature_maps = numpy.zeros((img.shape[0]-conv_filter.shape[1] + 1, img.shape[1]-conv_filter.shape[1] + 1, conv_filter.shape[0]))
    # Dimension of feature_maps : (Image Width(img.shape[0]) - Filter Width(conv_filter.shape[1]) + 1(Bias), Image Height(img.shape[1]) - Filter Height(conv_filter.shape[1] + 1(Bias), Num of channels(conv_filter.shape[0]))
    # conv_filter.shape[1] == conv_filter.shape[2] (3 == 3)
    
    # Convolving the image by the filter(s).
    for filter_num in range(conv_filter.shape[0]): # for loop within filters(shape[0] is 2 for now)
        print("Filter ", filter_num + 1) # since idx starts from 0, we add 1
        curr_filter = conv_filter[filter_num, :] # getting a filter from the bank.
        """
        Checking if there are multiple channels for the single filter.
        If so, then each channel will convolve the image.
        The result of all convolutions are summed to return a single feature map.
        """
        if len(curr_filter.shape) > 2: # shape of curr_filter should be (3,3) so len will be 2(no depth)
            conv_map = conv_(img[:,:,0], curr_filter[:,:,0]) # Array holding the sum of all feature maps.
            for ch_num in range(1, curr_filter.shape[-1]): # Convolving each channel with the image and summing the results.
                conv_map = conv_map + conv_(img[:,:,ch_num], curr_filter[:,:,ch_num])
        else: # There is just a single channel in the filter
            conv_map = conv_(img, curr_filter)
        feature_maps[:,:, filter_num] = conv_map # Holding feature map with the current filter
    return feature_maps # Returning all feature maps


def conv_(img, conv_filter): # function for convolution operation
    filter_size = conv_filter.shape[1] # conv_filter : (num of filter, width, height)
    result = np.zeros((img.shape)) # make tuple size of img
    # Looping through the image to apply the convolution operation.
    for r in np.uint16(np.arange(filter_size/2.0, img.shape[0]-filter_size/2.0 + 1)):
        for c in np.uint16(np.arange(filter_size/2.0, img.shape[1]-filter_size/2.0 + 1)):
            """
            Getting the current region to get multiplied with the filter.
            How to loop through the image and get the region based on the image and filter sizes is the most tricky part of convolution.
            """
            curr_region = img[r - np.uint16(np.floor(filter_size/2.0)):r + np.uint16(np.ceil(filter_size/2.0)),
                              c - np.uint16(np.floor(filter_size/2.0)):c + np.uint16(np.ceil(filter_size/2.0))]
            # Element-wise multiplication between the current region and the filter.
            curr_result = curr_region * conv_filter
            conv_sum = np.sum(curr_result) # Summing the result of multiplication.
            result[r, c] = conv_sum # Saving the summation in the convolution layer feature map.
        
        # Clipping the outliers of the result matrix.
        final_result = result[np.uint16(filter_size/2.0):result.shape[0] - np.uint16(filter_size/2.0),
                              np.uint16(filter_size/2.0):result.shape[1] - np.uint16(filter_size/2.0)]
    return final_result



StarcoderdataJupyterScriptsDedupFiltered