Histogram manipulation


We are going to consider three actions on the histograms of a couple of images to enhance them.

Our first test image is the following photo. It was taken by night. The lights in the background saturates the white and the details in the foreground are hidden in similar gray tones.

ballc
Original image 1

The second test image is out of focus (but we do no not treat this problem here) and shows no contrast between the paper and the surface.

cookie
Original image 2

These are the histograms of the images. In both cases they are essentially concentrated in a part of the interval [0,1]. In the first case there are tiny bars near 1, representing the white spots.

hist1 hist2
Histogram image 1 Histogram image 2

We first apply histogram equalization as implemented in octave (see the code below). In practice it does not flatten completely the histogram because the gray levels are discrete but it distributes the values more evenly. In our case, the result is:

hist1e hist2e
Equalized histogram 1 Equalized histogram 2

This equalization acts quite well in the first case to show a couple of trees difficult to see in the original.

hist_eq1
Equalized image 1

On the other hand the result is bad for the second image. Some noise due to the low quality of the photo is considered as "details" and acquires the same black tone as the text.

hist_eq1
Equalized image 2

As in both cases the histograms are rather localized, a second natural procedure is to crop the levels and stretch the histogram.

For the first image, in the code below the levels are cropped to [0, 0.6] that contains almost all the mass and the result is stretched to the whole range [0,1]. Doing this, we obtain the following image. It improves the original but we do not distinguish the hidden details.

hist_cs1
Cropping-Stretching 1

For the second image the natural interval is [0.2,0.8]. The result improves the original and is much better than the equalization.

hist_cs1
Cropping-Stretching 2

Finally, we are going to apply a logarithmic model. Basically it correspond to apply cropping and stretching to the logarithm of the values of the pixels divided by the values of a blurred version of the image (my signal processing course). In both cases we use [-1.8, 1.2] and the blurred image comes from applying a Gaussian filter with standard deviation 60.

For the first image the log model gives a result with a slightly more natural aspect than the equalization and still reveals the hidden results.

hist_lm1
Log model for image 1

For the second image the result is, to my taste, a little better than the previous direct cropping and stretching.

hist_lm2
Log model for image 2

The code

This is the octave code to produce the images.

pkg load image

clear all

name = '../images/happy_r_bw.jpg'
name = '../images/cookie.jpg'



ima1 = imread( '../images/happy_r_bw.jpg' );
ima1 = im2double(ima1);

ima2 = imread( '../images/cookie.jpg' );
ima2 = im2double(ima2);

figure(1)
imhist( ima1, 256 );

figure(2)
imhist( ima2, 256 );


% Equalization
res = hist_mod( ima1, 256, 0, 0);
imwrite( res, './his_eq1.jpg' );


figure(3)
imhist( res, 256 );


res = hist_mod( ima2, 256, 0, 0);
imwrite( res, './his_eq2.jpg' );


figure(4)
imhist( res, 256 );



% Crop - Stretch
res = hist_mod( ima1, 0.0, 0.6, 1);
imwrite( res, './his_cs1.jpg' );

res = hist_mod( ima2, 0.2, 0.8, 1);
imwrite( res, './his_cs2.jpg' );

% Log model
res = hist_mod( ima1, -1.8, 1.0, 2);
imwrite( res, './his_lm1.jpg' );

res = hist_mod( ima2, -1.8, 1.0, 2);
imwrite( res, './his_lm2.jpg' );

It calls to the function hist_mod:

% Three histogram modifications in images
% ima image
% a,b parameters
% t = 0 ->equalization, a=nbins, b=arbitrary
% t = 1 ->stretching, a=min, b=max
% t = 2 ->log model, a=min, b=max (g=60, eps=1/256)

function res = hist_mod( ima, a, b, t )
  
  if t==0
    % Equalization
    res = histeq( ima, a );
    
  elseif t==1
    % Cropping-stretching
    res = max(ima, a);
    res = min(res, b);

  elseif t==2
    % log model
    res = imsmooth(ima, "Gaussian", 60);
    res = log((ima+1/256)./res);
    res = max(res, a);
    res = min(res, b);

  end
  
  
  % normalize
  res = im2double(mat2gray(res));
  

end