One interesting functionality offered by OpenCV in connection with histograms is the cv2.compareHist() function, which can be used to get a numerical parameter expressing how well two histograms match each other. In this sense, as histograms reflect the intensity distributions of the pixel values in the image, this function can be used to compare images. As previously commented, the histograms show only statistical information and not the location of pixels. Therefore, a common approach for image comparison is to divide the image into a certain number of regions (commonly with the same size), calculate the histogram for each region, and, finally, concatenate all the histograms to create the feature representation of the image. In this example, for simplicity, we are not going to divide the image into a certain number of regions, so only one region (the full image) will be used.
The signature for the cv2.compareHist() function is as follows:
cv2.compareHist(H1, H2, method)
Here, H1 and H2 are the histograms being compared, and method establishes the comparison method.
OpenCV offers four different metrics (methods) to compute the matching:
- cv2.HISTCMP_CORREL: This metric computes the correlation between the two histograms. This metric returns values in the range [-1, 1], where 1 means a perfect match and -1 is no match at all.
- cv2.HISTCMP_CHISQR: This metric computes the chi-squared distance between the two histograms. This metric returns values in the range [0, unbounded], where 0 means a perfect match and a mismatch is unbounded.
- cv2.HISTCMP_INTERSECT: This metric computes the intersection between the two histograms. This metric returns values in the range [0,1] if the histograms are normalized, where 1 means a perfect match and 0 no match at all.
- cv2.HISTCMP_BHATTACHARYYA: This metric computes the Bhattacharyya distance between the two histograms. This metric returns values in the range [0,1], where 0 is a perfect match and 1 no match at all.
In the compare_histograms.py script, we first load four images, and then we calculate the similarity among all these images and a test image using all the metrics commented previously.
The four images that we are using are as follows:
- gray_image.png: This image corresponds to a grayscale image.
- gray_added_image.png: This image corresponds to the original image, but modified in the sense that we have added 35 to every pixel of the image.
- gray_subtracted_image.png: This image corresponds to the original image, but modified in the sense that we have subtracted 35 to every pixel of the image.
- gray_blurred.png: This image corresponds to the original image but modified with a blur filter (cv2.blur(gray_image, (10, 10)).
The test (or query) image is also gray_image.png. The output for this example can be seen in the next screenshot:
As you can see, img 1 gives the best results (a perfect match in all the metrics) because it is the same image. Additionally, img 2 also gives very good performance metrics. It makes sense because img 2 is a smoothed version of the query image. Finally, img 3 and img 4 give poor performance metrics because the histogram is shifted.