The other day someone on the board had asked about histogram equalization and it had sparked some thoughts about how it is applied to picture correction and enhancement. Then of course it hit me like a ton of bricks (ouch!), why not make this into a blog post? So here I am telling all you fine people about using PHP to equalize the histogram information in pictures. Now we all know that PHP isn’t exactly the “ideal” technology to use for such a task, but that is where the fun comes in. Why not give it a try? Now we will make PHP dance like our sick little puppet and do our bidding on this entry of the Programming Underground!
To understand this idea of histogram equalization we have to first understand what a histogram is. You may have created one in school and not even have known it. Essentially it is a bar chart of counts. The standard X and Y axis with each bar representing the count, or total number of, a particular value listed on the X axis. So if you add up the counts of each bar, you will reach the total number of elements in the set. That’s all it is.
We know that images have pixels and the total number of pixels in an image is the width multiplied by the height. Straight forward right? Each pixel is a solid color but the fact that many of them sit next together with different colors and shades create things like shadows or other colors when viewed from afar. Typically a pixel’s color is made up of three colors known as RGB or the amount of Red, Green and Blue in the color. Each of these values are between 0 (for black) to 255 (white). Of course this could all be elementary for you, so let me go ahead and skip to the good part.
Since each pixel essentially contains different amounts of red, green and blue we can take each of those colors and build a histogram of them… along with other factors such as hue or saturation (which I may cover in another entry later when I cover HSV values). To help illustrate this point, I have a standard picture I downloaded off the net and have shown it below. As you can see this picture is very much washed out in colors of brown, tan, beige and the colors in between. So if we break down each pixel into its basic colors, we would expect to see our histograms have a large peak in their charts. This peak represents little color variation and if we look, sure enough…
As you can see from the picture and its various histograms (one for luminosity and the others for the RGB) the counts appear to peak in the same area and around the same values.
What histogram equalization does is take that peak and flattens it out across the entire spectrum. It amplifies the lower values and weakens the higher values. We go through the picture pixel by pixel, build a histogram of the colors (here we put the values in arrays) and then run them through another algorithm called “cumulative frequency.” As the name implies, we are essentially finding out how “frequent” a particular color value (and all those values below it on the scale) is in the histogram… their cumulative value. This means we sum up all the counts for each value, starting at 0, and sum up all values up to and including the Nth value. So if we are on value 5, we add up all values for 0, 1, 2, 3, 4 and 5. The result is a chart which starts off low and quickly climbs up and to the right and eventually flattens out.
So what good is this cumulative frequency chart? Well, it just so happens if we take a frequency of the color and multiply it by an alpha value (which is simply the scale of x, in our case 255, divided by the total number of pixels in the picture) we can formulate a new value for each of the red, green and blue of that pixel. But this time it will be evenly distributed across the spectrum. Here is the same picture above, but run through our script. Below it is the new histograms. Notice how they are flattened out and more equal?
Looking at the picture we still see that it is not crystal clear. It’s not suppose to be. However, it does reveal new details and provides more contrast. Notice how this new picture is no longer bogged down in all sorts of beige and tan colors, but features colors like blues, whites, greens, purples, yellows etc. Also take notice how the picture now appears to be closer to something taken during a clear day. The histograms now show how each of the colors, and luminosity, are evened out quite a bit. This allows us to see things like more detail in the road and almost make out individual pebbles. The green in the trees in the background are also a bit cleaner and more vivid. The sky is cleared up and even appears to be in the morning.
So how is this witchcraft performed? PHP’s image functions helps us out here. A word of warning, PHP is not exactly the fastest of languages for manipulating graphics. It is interpreted, so it is a bit slower than something like C++ or Java which are compiled and thus closer to being native to the computer making them run faster. Because of this, you will have to keep the images to reasonable sizes unless you want to wait forever to render the picture or until the script times out. Some hosts may even kill the script if it takes too long.
<?php // Main output function which reads the file and runs it through histogram equalization for the various color channels. function outputPicture($filename) { $reds = array(); $blues = array(); $greens = array(); $freqr = array(); $freqb = array(); $freqg = array(); $info = getimagesize($filename); $width = $info[0]; $height = $info[1]; $totalpixels = $width * $height; $img = imagecreatefromjpeg($filename); if ($img) { buildHistograms($img, $width, $height, $reds, $greens, $blues); buildFrequencies($reds, $greens, $blues, $freqr, $freqg, $freqb); } $alpha = (float)(255.0 / (float)$totalpixels); $newimg = @imagecreatetruecolor($width, $height); $color = imagecolorallocate($newimg, 255, 255, 255); for ($i = 0; $i < $height; $i++) { for ($j = 0; $j < $width; $j++) { $rgb = imagecolorat($img, $j, $i); $r = ($rgb >> 16) & 0xFF; $g = ($rgb >> 8) & 0xFF; $b = $rgb & 0xFF; $adjR = (int)((float)$freqr[$r] * $alpha); $adjG = (int)((float)$freqg[$g] * $alpha); $adjB = (int)((float)$freqb[$b] * $alpha); $color = imagecolorallocate($newimg, $adjR, $adjG, $adjB); imagesetpixel($newimg, $j, $i, $color); } } header('Content-Type: image/jpg'); imagejpeg($newimg, NULL, 100); imagedestroy($newimg); } // Fills $reds, $greens, $blues arrays with counts (histograms) based on picture $img. function buildHistograms($img, $width, $height, &$reds, &$greens, &$blues) { for ($i = 0; $i < $height; $i++) { for ($j = 0; $j < $width; $j++) { $rgb = imagecolorat($img, $j, $i); $r = ($rgb >> 16) & 0xFF; $g = ($rgb >> 8) & 0xFF; $b = $rgb & 0xFF; // Add counts to our histogram arrays for each color. $reds[$r]++; $greens[$g]++; $blues[$b]++; } } // Sort them by keys into order ksort($reds); ksort($greens); ksort($blues); } // Build frequency charts for all colors function buildFrequencies(&$arR, &$arG, &$arB, &$freqR, &$freqG, &$freqB) { // Loop through all values and sum counts from 0 to current column $i for ($i = 0; $i <= 255; $i++) { $sumR = 0; $sumG = 0; $sumB = 0; for ($j = 0; $j <= $i; $j++) { // Sums for Red, Green, Blue if (isset($arR[$j])) { $sumR += $arR[$j]; } if (isset($arG[$j])) { $sumG += $arG[$j]; } if (isset($arB[$j])) { $sumB += $arB[$j]; } } // Stash sums into frequency charts for each color $freqR[$i] = $sumR; $freqG[$i] = $sumG; $freqB[$i] = $sumB; } } outputPicture("stdSLR.jpg"); ?>
This PHP script is setup to output a raw image file. This means you can’t simply place it into the middle of a page or show any other kind of HTML/PHP output. It just shows a picture. We provide it a picture name (as seen down at the bottom of the script) and it creates several arrays to hold our histograms for RGB and each color’s frequencies. It also features a function for making the histogram counts and a buildFrequencies() function which calculates the color frequencies. It uses these histograms and the frequencies to then adjust the RGB colors into new variants which are then redrawn to a new image pixel by pixel. The result is that the picture is displayed to the user altered and cleaned up.
This type of process isn’t going to make all pictures instantly better. Its specialty is taking out those single high peaked histograms and equal them out. That means it works well on pictures which are bogged down in a particular color range (various oranges or various shades of red or purple etc). You can experiment with various pictures and see what kind of effects you get. Night time pictures may also reveal things in the shadows you hadn’t seen before by adjusting their colors to be lighter. Lights may get more a halo effect. You might be wondering, is this what filters in Photoshop or other Adobe products do? Yup!
I have another script available where we take this process a step further in which we adjust each RGB into a HSV value (hue/saturation/value) system and thus can adjust things like brightness and some color corrections. I may share that shortly in a separate entry since it will deal with two new functions for converting RGB to HSV and back again. So until then, enjoy the script and tinker with it to you heart’s content.
Thanks again for reading my blog! 🙂