The operation that you are interested in is known as linear contrast stretching. Basically, you want to multiply each of the intensities by some gain and then shift the intensities by some number so you can manipulate the dynamic range of the histogram. This is not the same as histeq
or using the probability density function of the image (a precursor to histogram equalization) to enhance the image. Histogram equalization seeks to flatten the image histogram so that the intensities are more or less equiprobable in being encountered in the image. If you'd like a more authoritative explanation on the topic, please see my answer on how histogram equalization works:
Explanation of the Histogram Equalization function in MATLAB
In any case, choosing the values of a
and b
is highly image dependent. However, one thing I can suggest if you want this to be adaptive is to use min-max normalization. Basically, you take your histogram and linearly map the intensities so that the lowest input intensity gets mapped to 0, and the highest input intensity gets mapped to the highest value of the associated data type for the image. For example, if your image was uint8
, this means that the highest value gets mapped to 255.
Performing min/max normalization is very easy. It is simply the following transformation:
out(x,y) = max_type*(in(x,y) - min(in)) / (max(in) - min(in));
in
and out
are the input and output images. min(in)
and max(in)
are the overall minimum and maximum of the input image. max_type
is the maximum value associated for the input image type.
For each location (x,y)
of the input image, you substitute an input image intensity and run through the above equation to get your output. You can convince yourself that substituting min(in)
and max(in)
for in(x,y)
will give you 0 and max_type
respectively, and everything else is linearly scaled in between.
Now, with some algebraic manipulation, you can get this to be of the form out(x,y) = a*in(x,y) + b
as mentioned in your problem statement. Concretely:
out(x,y) = max_type*(in(x,y) - min(in)) / (max(in) - min(in));
out(x,y) = (max_type*in(x,y)/(max(in) - min(in)) - (max_type*min(in)) / (max(in) - min(in))
out(x,y) = (max_type/(max(in) - min(in)))*in(x,y) - (max_type*min(in))/(max(in) - min(in))
out(x,y) = a*in(x,y) + b
a
is simply (max_type/(max(in) - min(in))
and b
is -(max_type*min(in))/(max(in) - min(in))
.
You would make these a
and b
and run through your code. BTW, if I may suggest something, please consider vectorizing your code. You can very easily use the equation and operate on the entire image data at once, rather than looping over each value in the image.
Simply put, your code would now look like this:
image = imread('image.png');
image_of_doubles = 0.95*double(image) + 5; %
[n_elements,centers] = hist(image_of_doubles(:),20);
bar(centers,n_elements);
.... isn't it much simpler?
Now, modify your code so that the new constants a
and b
are calculated in the way we discussed:
image = imread('image.png');
%
max_type = double(intmax(class(image)));
%
min_val = double(min(image(:)));
max_val = double(max(image(:)));
a = max_type / (max_val - min_val);
b = -(max_type*min_val) / (max_val - min_val);
%
image_of_doubles = a*double(image) + b;
%
figure;
subplot(2,1,1);
[n_elements1,centers1] = hist(double(image(:)),20);
bar(centers1,n_elements1);
%
xlim([0 max_type]);
subplot(2,1,2);
[n_elements2,centers2] = hist(image_of_doubles(:),20);
bar(centers2,n_elements2);
%
xlim([0 max_type]);
%
figure; subplot(1,2,1);
imshow(image);
subplot(1,2,2);
imshow(image_of_doubles,[]);
It's very important that you cast the maximum integer to double
because what is returned is not only the integer, but the integer cast to that data type. I've also taken the liberty to change your code so that we can display the histogram before and after the transformation, as well as what the image looks like before and after you transform it.
As an example of seeing this work, let's use the pout.tif
image that's part of the image processing toolbox. It looks like this:

You can definitely see that this requires some image contrast enhancement operation because the dynamic range of intensities is quite limited.
This had the appearance of looking washed out.
Using this as the image, this is what the histogram looks like before and after.

We can certainly see the histogram being stretched. Now this is what the images look like:

You can definitely see more detail, even though it's more dark. This tells you that a simple linear scaling isn't enough. You may want to actually use histogram equalization, or use a gamma-law or power law transformation.
Hopefully this will be enough to get you started though. Good luck!