How does OpenCV remove shadows from images

One, foreword

If you’ve ever printed anything yourself, you’ve probably experienced this. If you take your own pictures, they are still clear to see on the phone, but when printed out, they are dark. Take these two pictures:

All prints are bad because of the large shadows on the left (it costs 30 cents to print, so the second image is just a simulation I used with the program).

Is there any way to solve this? The answer is yes, and today we’ll look at a few ways to remove shadows.

How to remove shadows?

First, for ease of processing, we usually grayscale the image (that is, convert the image to a gray image with only one layer).

Then let’s analyze. In the picture above, there are three main colors, namely the font color (black), the paper color (white) and the shadow color (gray). Now that we know that, it’ll be easy for us. All we need to do is make the gray and the white part white.

How am I supposed to know about the white and gray areas? For an 8-bit gray-scale image, the black part of the image is approximately 0-30 pixels. White and gray should be around 31-255 (this range is a rough estimate, you need to see the picture). As shown in figure:

On the left is the original image and on the right is the processed image. We made the gray and nearly white parts white.

So let’s get started.

Ndarray array for numpy

For those of you who have not been exposed to Numpy, here is a brief introduction.

Numpy is a third party module that makes it easy to manipulate multi-dimensional arrays (ndarray arrays). And the way images are stored in OpenCV is ndarray, so what we do with arrays is we do with images.

We need to install the OpenCV module before using it:

pip install opencv-python
Copy the code

Numpy is automatically installed when OpenCV is installed.

Boolean index: Boolean index: Boolean index: Boolean index

import numpy as np
# Create an array with elements 1, 0, 1, 1
arr = np.array([1.0.1.1])
# Determine if there are 0's in the array
res = arr == 0
# Assign a value of 10 to a 0 element in an array
arr[res] = 10
Copy the code

If you haven’t worked with Numpy, you may not understand the syntax above. Let’s break it down:

  1. Creating an Ndarray array: We use np.array to replace an existing list with an Ndarray object. This makes sense

  2. For example, arr == 0, which returns an Ndarray object with the same structure and number of elements. But the object returned is of primitive type bool, so let’s look at the output of res:

    [False  True False False]
    Copy the code

    As you can see, we compare arr==0 by comparing each element in the array and returning a Boolean value for the comparison.

  3. Assign a 0 element in an array to a value of 10: and the hardest to understand arr[res] operation. What it does is it takes the view that is True in res, so for example, if the second result is True, it only returns the second element. We execute the following code:

    arr[res] = 10
    Copy the code

    We assign 10 to the part where res is True, and 10 to the part where arR is 0.

Here are the final results of the ARR:

[ 1 10  1  1]
Copy the code

You can see that the original zero was treated as a one.

4. Remove shadows

Now that we know the Boolean index, we can do something with the image. All we need to do is read the image and make the pixels greater than 30 white. Here is our code:

import cv2
# Read image
img = cv2.imread('page.jpg'.0)
# Change pixel values greater than 30 to 255 (white)
img[img > 30] = 255
# Save the modified image
cv2.imwrite('res.jpg', img)
Copy the code

The above code is very simple. We use the cv2.imread function to read the image. The first parameter is the image path and the second parameter means to read the image as grayscale. Take a look at the renderings:

You can see how well the shadows have been removed. Some words are fuzzy, we can adjust the range of gray and white. Such as:

img[img > 40] = 255
Copy the code

The exact value depends on the image to be processed.

Fifth, improve the

There is a small improvement that can be made to the above processing. We can make the paper color less white. Let’s look at the improved code:

import cv2
import numpy as np
img = cv2.imread('page.jpg'.0)
Calculate the average value of the gray and white pixels
pixel = int(np.mean(img[img > 140]))
# Change the gray and white to a color close to the background
img[img > 30] = pixel
cv2.imwrite('res.jpg', img)
Copy the code

Instead of setting the gray part to 255 in the above code, we calculated a number beforehand.

pixel = int(np.mean(img[img > 140]))
Copy the code

Guess that the color value of the shaded part is less than 140, so first index the parts of the image greater than 140. Then take the average, so that we calculate the background color is roughly the original image, and then the image is not text part of the background color, is the final result. Here are our renderings:

And you can see it’s a little bit better this time. But since the background is the same color, it still looks a little different.

However, it should be noted that the above operation only applies to relatively simple pictures, such as test papers.

That’s all for today. Thanks for reading. Interested readers can follow my personal public account “new folder X”.