What I wanna do here is trying to match the brightness of my source and template images. I found a very good coding example from here. This was done on the grayscale. I tried this on my images in greyscale form and it really give me good result.
I am now trying to repeat it by using colour images, I first converted both my source and template images to HSV colour format as I am only interested in histogram matching the Value(brightness) channel.
I was able to get the matched value, but I am having an error trying to merge the matched value to the source images.
After printing out the value for "source" "template" and "matched". I figure out it might be because my matched value were in float number? I intend to keep the float number because I am afraid rounding off the number to integer might gave me inaccurate matching. Will it be any different? Is there any another way of doing this?
RESTART: C:\Users\310293649\AppData\Local\Programs\Python\Python36\ImageAnalysisCODING\HistogramMatching.py
[[119 119 120 ..., 100 100 100]
[120 120 119 ..., 98 98 99]
[120 120 119 ..., 97 97 98]
...,
[ 74 73 73 ..., 61 61 61]
[ 73 73 73 ..., 61 61 61]
[ 72 72 73 ..., 61 61 61]]
[[140 140 141 ..., 130 129 130]
[140 140 140 ..., 129 129 130]
[139 139 140 ..., 128 128 129]
...,
[ 85 84 84 ..., 76 76 77]
[ 84 84 85 ..., 76 75 76]
[ 82 81 85 ..., 76 75 75]]
[[ 135.50896712 135.50896712 136.35596888 ..., 118.31246023
118.31246023 118.31246023]
[ 136.35596888 136.35596888 135.50896712 ..., 116.73808586
116.73808586 117.51789135]
[ 136.35596888 136.35596888 135.50896712 ..., 115.96259322
115.96259322 116.73808586]
...,
[ 90.25379483 89.27703193 89.27703193 ..., 79.3186822 79.3186822
79.3186822 ]
[ 89.27703193 89.27703193 89.27703193 ..., 79.3186822 79.3186822
79.3186822 ]
[ 88.30202317 88.30202317 89.27703193 ..., 79.3186822 79.3186822
79.3186822 ]]
Traceback (most recent call last):
File "C:\Users\310293649\AppData\Local\Programs\Python\Python36\ImageAnalysisCODING\HistogramMatching.py", line 65, in <module>
img2 = cv2.merge((img2[:,:,0], img2[:,:,1],matched))
cv2.error: C:\projects\opencv-python\opencv\modules\core\src\convert.cpp:222: error: (-215) mv[i].size == mv[0].size && mv[i].depth() == depth in function cv::merge
Here is the coding which I edited based on the example which I got from another post.
import numpy as np
from matplotlib import pyplot as plt
import cv2
def hist_match(source, template):
"""
Adjust the pixel values of images such that its histogram
matches that of a target image
Arguments:
-----------
source: np.ndarray
Image to transform; the histogram is computed over the flattened
array
template: np.ndarray
Template image; can have different dimensions to source
Returns:
-----------
matched: np.ndarray
The transformed output image
"""
oldshape = source.shape
source = source.ravel()
template = template.ravel()
# get the set of unique pixel values and their corresponding indices and
# counts
s_values, bin_idx, s_counts = np.unique(source, return_inverse=True,
return_counts=True)
t_values, t_counts = np.unique(template, return_counts=True)
# take the cumsum of the counts and normalize by the number of pixels to
# get the empirical cumulative distribution functions for the source and
# template images (maps pixel value --> quantile)
s_quantiles = np.cumsum(s_counts).astype(np.float64)
s_quantiles /= s_quantiles[-1]
t_quantiles = np.cumsum(t_counts).astype(np.float64)
t_quantiles /= t_quantiles[-1]
# interpolate linearly to find the pixel values in the template image
# that correspond most closely to the quantiles in the source image
interp_t_values = np.interp(s_quantiles, t_quantiles, t_values)
return interp_t_values[bin_idx].reshape(oldshape)
img1 = cv2.imread('IMG_1.png')
img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2HSV)
img1[:,:,0], img1[:,:,1], img1[:,:,2] = cv2.split(img1)
source = img1[:,:,2]
print(source)
img2 = cv2.imread('IMG_2.png')
img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2HSV)
img2[:,:,0], img2[:,:,1], img2[:,:,2] = cv2.split(img2)
template = img2[:,:,2]
print(template)
matched = hist_match(source, template)
print(matched)
img2 = cv2.merge((img2[:,:,0], img2[:,:,1],matched))
cv2.imwrite('Matched_1770.png', img2)
def ecdf(x):
"""convenience function for computing the empirical CDF"""
vals, counts = np.unique(x, return_counts=True)
ecdf = np.cumsum(counts).astype(np.float64)
ecdf /= ecdf[-1]
return vals, ecdf
x1, y1 = ecdf(source.ravel())
x2, y2 = ecdf(template.ravel())
x3, y3 = ecdf(matched.ravel())
fig = plt.figure()
gs = plt.GridSpec(2, 3)
ax1 = fig.add_subplot(gs[0, 0])
ax2 = fig.add_subplot(gs[0, 1], sharex=ax1, sharey=ax1)
ax3 = fig.add_subplot(gs[0, 2], sharex=ax1, sharey=ax1)
ax4 = fig.add_subplot(gs[1, :])
for aa in (ax1, ax2, ax3):
aa.set_axis_off()
ax1.imshow(source)
ax1.set_title('Source')
ax2.imshow(template)
ax2.set_title('Template')
ax3.imshow(matched)
ax3.set_title('Matched')
ax4.plot(x1, y1 * 100, '-r', lw=3, label='Source')
ax4.plot(x2, y2 * 100, '-k', lw=3, label='Template')
ax4.plot(x3, y3 * 100, '--r', lw=3, label='Matched')
ax4.set_xlim(x1[0], x1[-1])
ax4.set_xlabel('Pixel value')
ax4.set_ylabel('Cumulative %')
ax4.legend(loc=5)
plt.show()