Take the 2-minute tour ×
Signal Processing Stack Exchange is a question and answer site for practitioners of the art and science of signal, image and video processing. It's 100% free, no registration required.

I'm trying to implement various binarization algorithms to the image shown: enter image description here

Here's the code: clc; clear; x=imread('n2.jpg'); %load original image

% Now we resize the images so that computational work becomes easier later onwards for us.

x=imresize(x,[500 800]);
title('original image');

z=rgb2hsv(x);       %extract the value part of hsv plane

%now we find the mean and standard deviation required for niblack and %sauvola algorithms

m = mean(v(:))
value=m+ k*s;

% implementing niblack thresholding algorithm:

for p=1:1:500
    for q=1:1:800
title('result by niblack');
title('result by kittlerMet');

% implementing sauvola thresholding algorithm:

for p=1:1:500
for q=1:1:800


title('result by sauvola');

The results I obtained are as shown: enter image description here enter image description here enter image description here

As you can see the resultant images are degraded at the darker spots.Could someone please suggest how to optimize my result??

share|improve this question
Can you use color information to throw away the background instead of brightness only? –  endolith Apr 5 '12 at 16:02
add comment

5 Answers

Your image doesn't have uniform brightness,so you shouldn't work with a uniform threshold. You need an adaptive threshold. This can be implemented by preprocessing the image to make the brightness more uniform across the image (code written in Mathematica, you'll have to implement the Matlab version for yourself):

A simple way to make the brightness uniform is to remove the actual text from the image using a closing filter:

white = Closing[src, DiskMatrix[5]]

enter image description here

The filter size should be chosen larger than the font stroke width and smaller than the size of the stains you're trying to remove.

EDIT: I was asked in the comments to explain what a closing operation does. It's a morphological dilation followed by a morphological erosion. The dilation essentially moves the structuring element at every position in the image, and picks the brightest pixel under the mask, thus :

  • removing dark structures smaller than the structuring element
  • shrinking larger dark structures by the size of the structuring element
  • enlarging bright structures

The erosion operation does the opposite (it picks the darkest pixel under inside the structuring element), so if you apply it on the dilated image:

  • the dark structures that were removed because they're smaller than the structuring element are still gone
  • the darker structures that were shrunk are enlarged again to their original size (though their shape will be smoother)
  • the bright structures are reduced to their original size

So the closing operation removes small dark objects with only minor changes to larger dark objects and bright objects.

Here's an example with different structuring element sizes:

enter image description here

As the size of the structuring element increases, more and more of the characters is removed. At radius=5, all of the characters are removed. If the radius is increased further, the smaller stains are removed, too:

enter image description here

Now you just divide the original image by this "white image" to get an image of (nearly) uniform brightness:

whiteAdjusted = Image[ImageData[src]/ImageData[white]*0.85]

enter image description here

This image can now be binarized with a constant threshold:

Binarize[whiteAdjusted , 0.6]

enter image description here

share|improve this answer
Wow! This is really cool! Huge +1! –  Phonon Apr 4 '12 at 14:03
@nikie +1 Very nice - what do you mean exactly by closing filter must be "chosen larger than the font stroke"? Width or length of any letter? Furthermore, what is a closing filter 'really' doing? Thanks! –  Mohammad Apr 4 '12 at 16:02
@Mohammad: I've added a little explanation to my answer. And yes, these are non-linear operations. The common heading is morphological image processing. –  nikie Apr 4 '12 at 20:15
@nikie Never mind, the white is the max, not the black. :-) –  Mohammad Apr 4 '12 at 20:33
@gdelfino: I usually try to avoid it by using a large enough mask, and use Clip[ImageData[white],{eps,Infinity}] where eps is a small number, to be safe. –  nikie Nov 22 '12 at 19:53
show 9 more comments

Nikie's answer seems best and also seems to be working and producing results. So it's a clear winner.

However, just to documentation i adding one more reference, that could just be very fast.

This technique is called Adaptive thresholding which doesn't require to learn the background explicitly.

Essentially, instead of finding the most suitable global threshold - we can partition the image in a local window (say about 7x7 or appropriate) and find thresholds that changes as the window traverses.

The reference below details the exact method. http://homepages.inf.ed.ac.uk/rbf/HIPR2/adpthrsh.htm

This method would be relatively computationally faster.

share|improve this answer
Aren't those two things essentially the same? Namely estimating the local mean of the signal prior to thresholding? –  Maurits Apr 9 '12 at 11:12
@Maurits It looks like the main differences are the ordering and the statistics used. For example, in the opening/closing operators, (which are made up of dilation and erosion but in a different order), a window is raster scanned and max is taken. (Among other things). However in the adaptive threshold the mean/median can be taken instead of the max. –  Mohammad Apr 9 '12 at 23:17
OP asked it on SO as well, which I answered. But in principle, I don't think there is any difference between the answers, one is always estimating local statistics. If you do adapative thresholding, you also learn the background in the process. –  Maurits Apr 10 '12 at 7:54
add comment

Another way using a bandpass filter (in MATLAB). Playing around with the difference of Gaussian parameters may give better results. The process is basically bandpass filter the image to remove the low frequency background blobs, normalise to [0,1] required for 'graythresh' command, threshold image.

Load image and convert to grayscale double:

I = imread('hw.jpg');
I = rgb2gray(I);
I = double(I);

enter image description here

Filter using difference of Gaussian kernel and normalise:

J = imgaussian(I,1.5) - imgaussian(I,0.5);
J = J - min(J(:));
J = J / max(J(:));

enter image description here

Calculate threshold and make 010101:

T = J > graythresh(J);

enter image description here

share|improve this answer
add comment

This is a good Matlab code for adaptive thresholding: http://www.mathworks.com/matlabcentral/fileexchange/8647-local-adaptive-thresholding

share|improve this answer
Argh! Requires the image processing toolbox though. :-/ –  Mohammad Apr 9 '12 at 23:00
Indeed. Sorry if you do not have it. But DIPImage is a free Image Tolbox for Matlab. diplib.org/documentation It has a few methods for thresholding (check segmentation section) and you can also do all the morphological operations like closing. The developer also has a blog cb.uu.se/~cris/blog/index.php/archives/tag/matlab –  MyCarta Apr 10 '12 at 14:15
add comment

It is perhaps easy just binaryzing the Red Chanel only:

In Mathematica:

First@(Binarize /@ ColorSeparate@Import@"http://i.stack.imgur.com/jUElt.jpg")

Mathematica graphics

share|improve this answer
add comment

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.