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:
Here’s a basic overview of how histogram matching works:
Compute histograms: We calculate the histograms of both the original image and the reference image as shown below:
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.
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:
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 |
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:
Here is the code example of all the steps mentioned above.
import numpy as npimport matplotlib.pyplot as pltimport numpy as npdef histogram_matching(original_image, reference_image):# Compute histograms for original and reference imagesoriginal_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 valueoriginal_cdf_normalized = original_cdf / original_cdf.max()reference_cdf_normalized = reference_cdf / reference_cdf.max()# Multiply CDF by 3 and round offoriginal_mapping = np.round(original_cdf_normalized * 3).astype(int)reference_mapping = np.round(reference_cdf_normalized * 3).astype(int)# Perform histogram matchingmatched_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_mappingdef plot_image_and_histogram(ax, image, title):# Plot the imageax[0].imshow(image, cmap='gray')ax[0].set_title(title)# Plot the histogramax[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 gridfig, axs = plt.subplots(3, 2, figsize=(15, 15))# Plot the original image and histogramplot_image_and_histogram(axs[0], original_image, "Original Image")# Plot the reference image and histogramplot_image_and_histogram(axs[1], reference_image, "Reference Image")# Plot the matched image and histogramplot_image_and_histogram(axs[2], matched_image, "Matched Image")# Remove empty subplotsfor i in range(3):axs[i, 0].axis('off')plt.tight_layout()plt.show()
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).
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