### Histogram Equalization And Magic Behind It

C# tutorial on how to achieve histogram equalization on an image.

Filter by Category

- C# Tutorial(85)
- C# Image Processing(69)
- Morphological Processes(20)
- Image Processing(16)
- Image Restoration and Reconstruction(16)
- Image Segmentation(13)
- C# Data Structures And Algorithms(11)
- Color Image Processing(8)
- Frequency Domain Filtering(8)
- Image Noise(6)
- Grayscale Morphology(5)
- Thresholding(4)
- Order-Statistic Filters(4)
- Mean Filters(4)
- Sorting Algorithms(4)
- Morphological Reconstruction(3)
- Edge Detection(3)
- Simple Lists(2)
- RGB to HSI Color Model(2)
- Adaptive Filters(2)
- Tone and Color Corrections(2)
- Linked Lists(2)
- Stacks(1)
- Queues(1)
- Point Detection(1)
- Line Detection(1)
- C# Arrays(1)
- Region Segmentation Using Superpixels(1)
- Region Segmentation With K Means Clustering(1)
- Region Splitting And Merging(1)
- Sorted Lists(1)
- Region Growing Segmentation(1)
- Digital Image Watermarking(1)
- Using Color In Image Segmentation(1)
- Social Games(1)
- Bandreject Filters(1)
- Bandpass filters(1)
- Notch Filters(1)
- Landing Pages(1)
- Intensity Slicing and Color Coding(1)
- Color Slicing(1)
- Histogram Processing Color Images(1)
- Color Image Smoothing And Sharpening(1)
- C# Basics(1)

Back to Latest Articles
###
Histogram Equalization And Magic Behind It

###
C# Tutorial: How To Create Image Zero Padding

###
C# Tutorial: How To Apply Contrast Stretch To An Image

C# Tutorial

C# tutorial on how to achieve histogram equalization on an image.

C# Tutorial

Zero padding an image is useful when we’re convolving it with a filter of certain size. How much padding should we use depends on how big our filter is. What is the purpose...

C# Tutorial

Contrast stretch or otherwise known as normalization is a process where your image’s intensity is changed in such a way, that dark pixels become darker and light pixels...

Histogram Equalization And Magic Behind It

This tutorial is meant to demonstrate how histogram equalization works. This is a continuation on contrast stretch articles I’ve made in the past. An article on histogram equalization already exists on here, but it is made in “unsafe” code and that just kinda bothers me a little bit.

This article is accompanied by code, that manipulates bytes of an image with a “lockbits” method. This method basically stores image into a one dimensional byte array.

Histograms are charts to visualize data in columns.We can create a histogram from an image, where vertical axis shows us number of pixels and horizontal axis shows color intensities that these pixels have.

Images that need to be processed here will have a washed out look. This is because the range of intensities in this image doesn’t span the whole range of possible intensities. Histogram equalization basically stretches this range to cover complete spectrum of possible intensities.

Sounds familiar doesn’t it? We talked about the same effect in article for normalization, but the key difference here is that we use a different equation to achieve this effect. As it turns out normalization is not as effective in expanding the range as histogram equalization is.

**pn = number of pixels with intensity n / total number of pixels**

pn represents the probability of each intensity in an image.

And finally the equation that does all the heavy lifting,

where k represents intensity value and L all possible intensities.

For example, if we have a pixel with intensity value of 100, we need to sum all probabilities pn up to the probability for pixels with intensity value of 100. Then we need to multiply this summation by 255 and round it down to the closest integer value. Since we are working with images of 32 bit pixel format, the max intensity value for each channel is 255, hence the multiplication with this number.

```
public static Bitmap HistEq(this Bitmap img)
{
int w = img.Width;
int h = img.Height;
BitmapData sd = img.LockBits(new Rectangle(0, 0, w, h),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int bytes = sd.Stride * sd.Height;
byte[] buffer = new byte[bytes];
byte[] result = new byte[bytes];
Marshal.Copy(sd.Scan0, buffer, 0, bytes);
img.UnlockBits(sd);
int current = 0;
double[] pn = new double[256];
for (int p = 0; p < bytes; p += 4)
{
pn[buffer[p]]++;
}
for (int prob = 0; prob < pn.Length; prob++)
{
pn[prob] /= (w * h);
}
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
current = y * sd.Stride + x * 4;
double sum = 0;
for (int i = 0; i < buffer[current]; i++)
{
sum += pn[i];
}
for (int c = 0; c < 3; c++)
{
result[current + c] = (byte)Math.Floor(255 * sum);
}
result[current + 3] = 255;
}
}
Bitmap res = new Bitmap(w, h);
BitmapData rd = res.LockBits(new Rectangle(0, 0, w, h),
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
Marshal.Copy(result, 0, rd.Scan0, bytes);
res.UnlockBits(rd);
return res;
}
```