Enhancing Gray-Scale Images Using Numpy, Open CV

Kavya Musty
Sep 6, 2020 · 4 min read
Image for post
Image for post

We often scan papers to convert them into images. We have various tools available online to enhance those images to make them brighter and remove any shading in those images. What if we can do that shading removal manually? We could just load any image as a gray-scale image into our code and obtain a output within seconds without the help of any app.

This can be achieved using basic Numpy manipulations and a few open CV functions. To explain the process the following image is used which is clicked from a phone.

Image for post
Image for post
Test_image

There is a definite shading which needs to be removed. Let’s get started.

  1. Import the necessary packages into your environment. For easy display of images Jupyter Notebook is being used.
import cv2
import numpy as np
import matplotlib.pyplot as plt

2. There are 2 things to note while performing the shading removal. As the image is a gray-scale image, if the image has a light background and darker objects, we have to perform max-filtering first, followed by min-filtering. If the image has a dark background and light objects, we can perform min-filtering first and then proceed with max-filtering.

So what exactly is max-filtering and min-filtering?

3. Max-filtering: Let us assume we have an image I of a certain size. The algorithm that we write should go through the pixels of I one by one, and for each pixel (x,y) it must find the maximum gray value in a neighborhood (a window of size N x N) around that pixel, and write that maximum gray value in the corresponding pixel location (x,y) in A. The resulting image A is called a max-filtered image of input image I.

Lets implement this concept in code.

  • max_filtering() function takes in the input image and the window size N.
  • It initially creates a ‘wall’ (pads with -1) around the input array which would help us when we iterate through edge pixels.
  • We then create a ‘temp’ variable to copy our calculated max values into it.
  • We then iterate through the array and create a window around the current pixel of size N x N.
  • We then calculate the max value in that window using ‘amax()’ function and write that value in the temp array.
  • We the copy this temp array into the main array A and return this as the output.
  • A is the max-filtered image of input I.
def max_filtering(N, I_temp):
wall = np.full((I_temp.shape[0]+(N//2)*2, I_temp.shape[1]+(N//2)*2), -1)
wall[(N//2):wall.shape[0]-(N//2), (N//2):wall.shape[1]-(N//2)] = I_temp.copy()
temp = np.full((I_temp.shape[0]+(N//2)*2, I_temp.shape[1]+(N//2)*2), -1)
for y in range(0,wall.shape[0]):
for x in range(0,wall.shape[1]):
if wall[y,x]!=-1:
window = wall[y-(N//2):y+(N//2)+1,x-(N//2):x+(N//2)+1]
num = np.amax(window)
temp[y,x] = num
A = temp[(N//2):wall.shape[0]-(N//2), (N//2):wall.shape[1]-(N//2)].copy()
return A

4. Min-filtering: This algorithm is exactly the same as max-filtering but instead of finding the maximum gray values in the neighborhood, we find the minimum values in the N x N neighborhood around that pixel, and write that minimum gray value in (x,y) in B. The resulting image B is called a min-filtered image of the image I.

Lets code this function.

def min_filtering(N, A):
wall_min = np.full((A.shape[0]+(N//2)*2, A.shape[1]+(N//2)*2), 300)
wall_min[(N//2):wall_min.shape[0]-(N//2), (N//2):wall_min.shape[1]-(N//2)] = A.copy()
temp_min = np.full((A.shape[0]+(N//2)*2, A.shape[1]+(N//2)*2), 300)
for y in range(0,wall_min.shape[0]):
for x in range(0,wall_min.shape[1]):
if wall_min[y,x]!=300:
window_min = wall_min[y-(N//2):y+(N//2)+1,x-(N//2):x+(N//2)+1]
num_min = np.amin(window_min)
temp_min[y,x] = num_min
B = temp_min[(N//2):wall_min.shape[0]-(N//2), (N//2):wall_min.shape[1]-(N//2)].copy()
return B

5. So, if a image has a lighter background, we want to perform max-filtering first which will give us a enhanced background and pass that max-filtered image into the min-filtering function which will take care of the actual content enhancement.

6. So, after performing the min-max filtering the values we obtain are not in the range of 0–255. So, we have to normalize the final array obtained using the background subtraction method which is subtracting the min-max filtered image with the original image to obtain the final image with the shading removed.

#B is the filtered image and I is the original image
def background_subtraction(I, B):
O = I - B
norm_img = cv2.normalize(O, None, 0,255, norm_type=cv2.NORM_MINMAX)
return norm_img

7. The variable N which is the window size for filtering is to be altered for the size of the particles or content in your image. For the test image the size N=20 was chosen. The final output image after being enhanced looks like:

Image for post
Image for post
Test_image_output

The output image is more enhanced than the original image. The code implemented is a humble attempt to manually implement some of the pre-existing functions in open CV to enhance images. The entire notebook with the images can be found in the Github link.

Kavya Musty

Written by

I am a data enthusiast who loves to solve problems. https://www.linkedin.com/in/kavya-musty/

The Startup

Medium's largest active publication, followed by +771K people. Follow to join our community.

Kavya Musty

Written by

I am a data enthusiast who loves to solve problems. https://www.linkedin.com/in/kavya-musty/

The Startup

Medium's largest active publication, followed by +771K people. Follow to join our community.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store