Você está na página 1de 9

Histogram Adjustments in MATLAB

Part I Stretching
Nick Loomis July 12, 2013 Image Enhancement, Recent Posts 6 Comments
Obtaining an image with the right contrast for your application is never easy. Whether it is for art,
detection, recognition, or improving your own photographs, contrast adjustment is a common
task. Industrial setups can adjust the lighting and camera parameters before image capturebut
what about after the image has been captured? Histogram processing is one answer for
computationally adjusting the contrast, and can also be applied to a number of problems
involving color balance or tone transfer.
This three-part post focuses on different methods for post-processing an image to modify its
underlying histogram, affecting contrast and color balance in some amazing ways. The first part
introduces basic pixel operations and how they affect histograms, and then shows several
schemes for how to stretch a histogram to cover the full range of available pixel values. The
second part discusses how to spread out the pixel values evenly over some range, a process
known as histogram equalization. The third partextends the idea of controllably modifying the
pixel values so that the resulting histogram matches any arbitrary distribution.
To begin, well look at the basics of working with image histograms using MATLAB and how to
perform histogram stretching to modify contrast and color.
Histogram math
A histogram is the probability distribution of pixel values in an image. (For RGB images, the
histogram is usually broken into three histograms of the three component channels.) Like any
other distribution, histograms have simple mathematical rules. Two operations that affect the
pixel values, and thus the histograms, will be used extensively through these posts:
1) Adding a value to all the pixels adds that amount to the histogram; visually, this shifts the
histogram
2) Multiplying all the pixel values by a certain amount scales where the histogram data appears;
visually, this stretches the histogram

To plot the histogram of an image, there are several options. If you have the Image Processing
Toolbox, the imhist function is the easiest to use, but only works on single color channels. For
example, to plot the green channel of an image,
img = imread('Flowers.jpg');
imhist(img(:,:,2))

If you dont have the Toolbox or want to have greater control over the histogram data, you can
compute the values directly using the hist function and plot using your desired methods:
img = imread('Flowers.jpg');
g = img(:,:,2);
bins = 0:1:255;
H = hist(g(:), bins);
plot(bins, H, 'linewidth',2, 'color', 'g');

This second example assumes that the image uses eight bits per color channel, or a maximum
value of 2^8-1 = 255. The remainder of the examples in these posts will use the assumption that
pixel values range from 0 to 255.
Histogram stretching
Contrast is a measure of how much the pixel brightness changes relative to the average
brightness. A technique known as histogram stretching can be used to shift the pixel values to fill
the entire brightness range, resulting in high contrast.
The first step is to find the pixel values that should get mapped to 0% and 100% brightness. Any
real-world image has noise, however. To keep the noise from unduly influencing the stretching,
an assumption is made: a small percentage of the brightest and darkest pixels are ignored, writing
them off to sensor noise. If you have the Image Processing Toolbox (well cover what you can do
if you dont have the toolbox in a moment), you can use
limits = stretchlim(img, tol);
to find the lower and upper limits of the pixels in image img so that a fraction, tol, will be
ignored. (See the stretchlim documentation for other ways to call the function.)
Next, the imadjust function can apply the scaling:
img_adjusted = imadjust(img, limits, []);
The function actually shifts and scales the pixel values so that the limits are scaled between 0 and
100% (the default when [] is used as the third input argument).
img = imread('treeArt.png');
limits = stretchlim(img, 0.01);
img_adjusted = imadjust(img, limits, []);
imagesc(img_adjusted, [0,255]);
axis image;

In order to understand and program some of the more advanced histogram-processing techniques,
it will help to take a moment to better understand what is happening behind the scenes of these
two functions. (Similarly: if you dont have the Image Processing Toolbox, this is how you could
perform the same operations.) The first step, finding the input pixel values which correspond to
some percentile of pixel values, is an inverse look-up. We can start by calculating the histogram,
then computing its cumulative distribution function (CDF):
bins = linspace(0,255,256);
H = hist(img(:), bins);
H(H==0) = eps(sum(H));
cdf = [0,cumsum(H)/sum(H)];

There are several assumptions here. The first is that the image, img, is assumed to have pixels
which could range in value from 0 to 255. The second is that the histogram, H, could have bins
with 0 counts. The problem is that, in the next step, well be doing an inverse look-up, which
means that we need an invertible function. If H has zeros, then cumsum(H) will have at least two
identical values and will not be invertible. Setting the zero-count values in H
to eps(sum(H)) ensures that H does not have non-zero values but only has a trivial effect on the
CDF. Using the CDF for the inverse look-up can be as simple as this:
h_low = interp1(cdf, [0,bins], pct);
h_high = interp1(cdf, [0,bins], 1-pct);
where pct is the percent of pixels to ignore. This will give similar limits as stretchlim.
Figure: Interpolating the CDF at pct = 0.05 to find the corresponding h_low and h_high pixel
values.
The next step is to adjust the image values so that the h_low and h_high values are remapped to
the 0% and 100% brightness levels. Using simple histogram math,
img_adjusted2 = uint8( (double(img) h_low)/(h_high-h_low) * 255);
Note that this includes both a shift, double(img) h_low, and a stretch, 1/(h_high-h_low) of the
original histogram.
In general, histogram stretching tends to work well on images that have a poor image contrast to
start with and which should have full contrast. You can limit the amount of stretching that occurs
by not mapping h_low and h_high to the 0% and 100% brightness levels, but instead by only
scaling them a certain amount.
Multi-channel Histogram Stretching
The histogram stretching that weve looked at so far has focused on single-channel images.
Whats useful is that the same functions can be applied to multi-channel images, like RGB
images, by applying the codes to each channel independently.
MATLABs stretchlim and imadjust functions are both written to be able to take in RGB images
in addition to single-channel images using the exact same code as we used earlier.
Again, if you dont have the Image Processing Toolbox or want to use multi-channel images that
dont have three channels (for example, some microscopy or satellite imagery), youll need to
take a few extra steps. For example, say you have a multi-channel image, img, and you plan to
adjust each channel. Then you could consider using something like this:
img_adjusted = zeros(size(img), 'uint8');
for ch = 1:size(img,3)
img_channel = img(:,:,ch);
limits = stretchlim(img_channel, 0.01);
img_adjusted(:,:,ch) = imadjust(img_channel, limits, []);
end
Variations on Histogram Stretching
The RGB color space is the de-facto standard for images. It doesnt always result in the best
processing, however, especially when the image is intended for human viewing. A color space
that better represents the human visual system, like L*a*b* or Luv can provide more natural
stretching in some cases. In both of these color spaces, the L channel represents the brightness,
while the (a*, b*) or (u, v) channels represent the color. (This is conceptually similar to YCbCr,
used in JPEG encoding.) The stretching is performed on the L channel after converting the colors.
Using the functions in the Image Processing Toolbox,
img = imread('lion.png');
c_rgb2lab = makecform('srgb2lab');
labimg = applycform(img, c_rgb2lab);
labimg(:,:,1) = imadjust(labimg(:,:,1), ...
stretchlim(labimg(:,:,1), 0.01) );
c_lab2rgb = makecform('lab2srgb');
img_adjusted = applycform(labimg, c_lab2rgb);
Figure: Histogram stretching within a L*a*b* color space and in an RGB color space. Note that
the RGB stretching changes the colors slightly, emphasizing reds and yellows in this particular
case.
Since only the L channel was adjusted, the colors remain the same, but the brightness is re-
mapped. This adjusts the contrast in a way that sometimes can be more visually pleasing without
significantly affecting the color balance.
If you dont have the Image Processing Toolbox, Mark Ruzon wrote simple functions to convert
between RGB and L*a*b*: Lab2RGB and RGB2Lab.
Another variation is to apply histogram stretching in the hue-saturation-value (HSV) color space.
The histogram stretching is only applied to the S and V channels, but not the hue channel. This
tends to result in reasonable contrast and fuller colors without significantly affecting the color
balance.
img = imread('bopFlower.png');
hsvimg = rgb2hsv(img);
for ch=2:3
hsvimg(:,:,ch) = imadjust(hsvimg(:,:,ch), ...
stretchlim(hsvimg(:,:,ch), 0.01));
end
img_adjusted = hsv2rgb(hsvimg);
Stretching on HSV is also sometimes done after stretching in the RGB color space to ensure full
color saturation. One example is this underwater image, which required significant stretching in
RGB, but then benefits from an additional HSV stretch to gain full contrast:


This is a good point to try out histogram stretching on your own images and get familiar with the
different color transforms. In the next post, well explore histogram equalization, primarily used
for modifying the contrast in an image, and some of its extensions.
Nick Loomis writes for loomsci.wordpress.com and uses the methods from this series of
posts for various projects: histogram stretching to correct the colors of underwater
photos and histogram matching to replicate the effect of color filters.

Você também pode gostar