# How to separate text from the paper on a black and white page?

I tried to discretize an image into black and white and came into some difficult. The difference between the letters and paper is pretty clear to our eyes:

However a simple thresholding trick doesn't work. Here we move everything below 0.4 intensity to 0 and everything above 0.4 to 1:

Now let's try moving the threshhold to 0.5 and some nasty artifacts occur:

I wish I could take the "best of both worlds" of trial 1 or trial 2. Here is the Python code I used... basically the introductory tutorial with a few changes:

from skimage import io
from skimage import color
import numpy as np

import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['figure.figsize'] = 7,7

io.imshow(poem)
io.show()

poem_gray = color.rgb2gray(poem)

t = 0.5
poem_gray[poem_gray < t] = 0
poem_gray[poem_gray > t] = 1
io.imshow(poem_gray)
io.show()


Here is the histogram for black and white in my image, to justify my threshhold of about 0.45 My eyes are playing tricks on me!! Some of the "white section" is as dark as the text. Is there a more standard method for separating grayscale images?

• You could split the image into rectangular areas and apply different thresholds to each area. – Biswajit Banerjee Nov 21 '15 at 1:20

I believe the best approach here is to use a threshold based on the local average brightness of the image. Setting the threshold to be 90% of the mean value of the 11x11 grid surrounding each pixel gives results that are about as good as you can expect with such a low resolution image.

For each pixel you just need to compute the mean brightness of the pixels near it. Then, if the pixel's brightness is less than 0.9 (or some threshold of your choosing) times the mean set it to black, otherwise set it to white.

@DougLipinski's answer is absolutely correct.

You identified a key clue when you said the problem is "pretty clear to our eyes": Understanding human vision is a great path to learning computer vision. In this case, knowing that human vision is great at handling local contrast would imply that the computer may do better if "everything but" the local contrast is removed.

But let's take a moment to consider a general way to approach such problems by starting with a simple question: How can a good image processing algorithm be found for a specific situation when known methods are unsatisfactory?

One great approach is to find a tool that makes it easy to interactively try a bunch of algorithms. My favorite is ImageJ, which has tools that range from the elegantly simple to the bafflingly complex. Many are built-in, and many more are available as plugins.

In this specific case, a Mean Shift Filter with radius 5 should do nicely.

Once an algorithm is found that meets your specific needs, you can either look at its ImageJ source code, or do a search on the algorithm name to find an implementation in the language of your choice.

For me, that language is generally Python, either via OpenCV, or PIL (the Python Imaging Library), or SciKit-Image, or Pillow, or sometimes the Python bindings for ImageMagick, depending on which package best handles the specific workflow (what supplies the images, and what needs to happen next).