Search code examples
python-3.xnumpyconvolutionopencvpython

Converting a color image into grayscale using a 3x3 convolution kernel


I am writing a python script that would use a 3x3 kernel to convert an image from color to grayscale.

I created a function that takes an 'image' and a 'kernel' as parameters, and returns the grayscale version of the image. Inside of the function I split the image into 3 individual channels: redChannel, greenChannel, and blueChannel. Then I take an average of these three channels as such: image = (red + green + blue) / 3.

I stored the values for Image height and width as follows: (Hi, Wi) = image.shape[:2] and I did the same for storing the height and width of the kernel, (Hk, Wk) = kernel.shape[:2]. I also included the padding for the image so that the kernel would not run out of bounds pad = (Wk - 1) // 2. Then I created two for loops that would iterate across height and width of the image using Hi and Wi. Inside of the for loops, I reshaped the image into so that I could multiply it with the kernel. Then I store the computed result in an output array.

This is the full code:

from skimage.exposure import rescale_intensity
import numpy as np
import cv2

def convolve(image, kernel):
    (Hi, Wi) = image.shape[:2]
    (Hk, Wk) = kernel.shape[:2]

    red, green, blue = cv2.split(image)
    image = (red + green + blue) / 3

    pad = (Wk - 1) // 2
    image = cv2.copyMakeBorder(image, pad, pad, pad, pad, cv2.BORDER_REPLICATE)
    output = np.zeros((Hi, Wi), dtype="float32")

    for y in range(Hi, Hk + pad):
        for x in range(Wi, Wk + pad):
            roi = image[y - pad:y + pad + 1, x - pad:x + pad + 1]
            k = (roi * kernel).sum()
            output[y - pad, x - pad] = k
        output = rescale_intensity(output, in_range=(0, 255))
        output = (output * 255).astype("uint8")
    return output


image = cv2.imread("mandrill.png")

kernel = np.ones((3, 3)) * (1/3)

cv2.imshow("Output", convolve(image, kernel))
cv2.waitKey(0)
cv2.destroyAllWindows()

I cannot seem to find any issues with the code, but the result is a black screen. Any help will be greatly appreciated))


Solution

  • I found the answer using a slightly different approach. This methods gets the pixel values of the image and stores it in 3 color channels (R,G,B). The res = np.dot(kernel, v) multiplies the image with a 3x3 grayscale kernel. The three if statements rescale the intensity of the pixel values.

    import matplotlib.pyplot as plt
    from PIL import Image
    import numpy as np
    
    def convolve(img, kernel):
        width, height = img.size
        pixels = img.load()
    
        for py in range(height):
            for px in range(width):
                r, g, b = img.getpixel((px, py))
    
                v = np.array([[r], [g], [b]])
                res = np.dot(kernel, v)
    
                tr, tg, tb = int(res[0, 0]), int(res[1, 0]), int(res[2, 0])
    
                if tr > 255:
                    tr = 255
    
                if tg > 255:
                    tg = 255
    
                if tb > 255:
                    tb = 255
    
                pixels[px, py] = (tr, tg, tb)
    
        return img
    
    img = Image.open('mandrill.jpg')
    grayscale = np.ones((3, 3)) * (1/3)
    
    convolve(img, grayscale)
    plt.imshow(img)