What is histogram matching?

In image processing, a histogram is a graphical representation or a plot that shows the distribution of pixel intensities in an image. It represents the frequency of occurrence of each intensity level within the entire image.

Histogram matching aims to transform the intensity distribution of an input image to resemble the intensity distribution of a reference or target image. This process helps to adjust an image’s contrast and brightness or match its appearance to a desired standard.

Let’s understand the histogram matching process with the following images: original imageThe original image refers to the image whose histogram we want to modify or adjust to match that of a reference image. and reference imageThe reference image is the image whose histogram we want the original image's histogram to match..

Steps of histogram matching

Here’s a basic overview of how histogram matching works:

  1. Compute histograms: We calculate the histograms of both the original image and the reference image as shown below:

  1. Calculate probability density function (PDF): We divide the count of occurrences for each intensity level by the total number of pixels in the image. This gives the probability of each intensity level.

  1. Calculate cumulative density function (CDF): The cumulative distribution function (CDF) for an image represents the cumulative probability of pixel intensities up to a certain level. Here’s how we can calculate the CDF for an image:

  1. Map intensities: We map the original pixel intensities of the input image to their corresponding values in the normalized CDF. This is typically done by multiplying the normalized CDF by the maximum intensity value and rounding to the nearest integer.

  Here is the complete table for histogram equalization of the original image:

Pixels (Gray Levels)

Histogram

Probability Density Function (PDF)

Commutative Density Function (CDF)

CDF x 3

Equalized Intensity

0

4

4 / 25 = 0.16

0.16

0.48

0

1

6

6 / 25 = 0.24

0.16 + 0.24 = 0.4

1.2

1

2

3

3 / 25 = 0.12

0.4 + 0.12 = 0.52

1.56

2

3

6

6 / 25 = 0.24

0.52 + 0.24 = 0.76

2.28

2

4

6

6 / 25 = 0.24

0.76 + 0.24 = 1

3

3

  Here is the complete table for histogram equalization of the reference image:

Pixels (Gray Levels)

Histogram

Probability Density Function (PDF)

Commutative Density Function (CDF)

CDF x 3

Equalized Intensity

0

0

0 / 25 = 0

0

0

0

1

3

3 / 25 = 0.12

0.12 + 0 = 0.12

0.36

0

2

8

8 / 25 = 0.32

0.32 + 0.12 = 0.44

1.32

1

3

8

8 / 25 = 0.32

0.44 + 0.32 = 0.76

2.28

2

4

6

6 / 25 = 0.24

0.76 + 0.24 = 1

3

3

  1. Create transformed image: We create a transformed image by mapping the equalized values of the reference image to the original image pixels as shown below:

Here is the transformed image:

Code example

Here is the code example of all the steps mentioned above.

import numpy as np
import matplotlib.pyplot as plt
import numpy as np
def histogram_matching(original_image, reference_image):
# Compute histograms for original and reference images
original_hist, _ = np.histogram(original_image, bins=np.arange(6), density=True)
reference_hist, _ = np.histogram(reference_image, bins=np.arange(6), density=True)
# Compute cumulative distribution functions (CDF)
original_cdf = np.cumsum(original_hist)
reference_cdf = np.cumsum(reference_hist)
# Normalize CDF by dividing by the maximum value
original_cdf_normalized = original_cdf / original_cdf.max()
reference_cdf_normalized = reference_cdf / reference_cdf.max()
# Multiply CDF by 3 and round off
original_mapping = np.round(original_cdf_normalized * 3).astype(int)
reference_mapping = np.round(reference_cdf_normalized * 3).astype(int)
# Perform histogram matching
matched_image = np.zeros_like(original_image)
for i in range(5):
matched_image[original_image == i] = reference_mapping[i]
return matched_image, original_mapping, reference_mapping
def plot_image_and_histogram(ax, image, title):
# Plot the image
ax[0].imshow(image, cmap='gray')
ax[0].set_title(title)
# Plot the histogram
ax[1].hist(image.flatten(), bins=np.arange(6) - 0.5, edgecolor='black', density=True)
ax[1].set_title("Histogram")
ax[1].set_xlabel("Pixel Value")
ax[1].set_ylabel("Frequency")
original_image = np.array([
[0, 2, 1, 3, 4],
[1, 3, 4, 3, 3],
[0, 1, 3, 1, 1],
[3, 1, 4, 2, 0],
[0, 4, 2, 4, 4]
])
reference_image = np.array([
[4, 3, 2, 3, 2],
[2, 2, 3, 4, 4],
[1, 2, 3, 2, 2],
[3, 3, 3, 1, 4],
[1, 2, 3, 4, 4]
])
matched_image, _, _ = histogram_matching(original_image, reference_image)
# Create a 2x3 subplot grid
fig, axs = plt.subplots(3, 2, figsize=(15, 15))
# Plot the original image and histogram
plot_image_and_histogram(axs[0], original_image, "Original Image")
# Plot the reference image and histogram
plot_image_and_histogram(axs[1], reference_image, "Reference Image")
# Plot the matched image and histogram
plot_image_and_histogram(axs[2], matched_image, "Matched Image")
# Remove empty subplots
for i in range(3):
axs[i, 0].axis('off')
plt.tight_layout()
plt.show()

Code explanation

  • Lines 1–3: We import the necessary libraries, NumPy, for numerical operations and matplotlib.pyplot for plotting.

  • Line 5: We define a function named histogram_matching that takes two parameters: original_image and reference_image.

  • Lines 7–8: We compute histograms for the original and reference images using np.histogram. The bins parameter specifies the bin edges, and density=True normalizes the histograms.

  • Lines 11–12: We compute the cumulative distribution functions (CDF) for the original and reference histograms using np.cumsum.

  • Lines 15–16: We normalize the CDFs by dividing them by their maximum values.

  • Lines 19–20: We multiply the normalized CDFs by 3, round off the values, and convert them to integers to create mapping functions.

  • Lines 23–25: We perform histogram matching by assigning values from the reference mapping to the corresponding pixel values in the original image.

  • Line 27: We return the matched image along with the original and reference mappings.

  • Line 29: We define a function named plot_image_and_histogram that takes three parameters: ax (a list of subplots), image, and title.

  • Line 57: We call the histogram_matching function with the original and reference images, storing the result in matched_image. The other two returned values (original and reference mappings) are not used (_ indicates they are ignored).

Conclusion

Histogram matching is a fundamental technique in image processing for adjusting the contrast and brightness of images by aligning their intensity distributions. The provided Python code offers a clear implementation of this process, making it accessible for practical use. By understanding and applying histogram matching, one can enhance the visual quality of images and ensure consistency across different sources or standards.

Free Resources

Copyright ©2025 Educative, Inc. All rights reserved