直方圖匹配本質上是讓兩幅圖像的累積直方圖盡量相似,累積直方圖相似了,直方圖也就相似了。
把原圖像img的直方圖匹配到參考圖像ref的直方圖,包括以下幾個步驟:
1. 求出原圖像img的累積直方圖img_accu;
2. 求出參考圖像ref的累積直方圖ref_accu;
3. 灰度級g在img_accu中對應的值記為img_accu_g,找出ref_accu中與ref_accu_g最接近的值,記為ref_accu_G,記該值對應的灰度級為G;
4. 根據g和G的對應關系,得到img經過匹配之后的直方圖。
為了說明該過程,我們舉一個簡單的例子,並把計算過程列在表格中。該例子中圖像只有10個灰度級,總共3289個像素,如下圖所示。

(a) 原圖像img的直方圖 (b) 參考圖像ref的直方圖

(c) 原圖像img的累積直方圖 (d) 參考圖像ref的累積直方圖
| 灰度級 | ref直方圖 | ref累積直方圖 | img直方圖 |
img累積直方圖 | 匹配之后的灰度級 | 匹配之后的img累積直方圖 |
匹配之后的img直方圖 |
| 1 | 0 | 0 | 927 | 927(匹配第三列第七行的1137) | 7 | 0 | 0 |
| 2 | 0 | 0 | 690 | 1617(匹配第三列第八行的1672) | 8 | 0 | 0 |
| 3 | 20 | 20 | 535 | 2152(匹配第三列第九行的2362) | 9 | 0 | 0 |
| 4 | 112 | 132 | 450 | 2602(匹配第三列第九行的2362) | 9 | 0 | 0 |
| 5 | 221 | 353 | 334 | 2936(匹配第三列第十行的3289) | 10 | 0 | 0 |
| 6 | 334 | 687 | 221 | 3157(匹配第三列第十行的3289) | 10 | 0 | 0 |
| 7 | 450 | 1137 | 112 | 3269(匹配第三列第十行的3289) | 10 | 927 | 927 |
| 8 | 535 | 1672 | 20 | 3289(匹配第三列第十行的3289) | 10 | 1617 | 690 |
| 9 | 690 | 2362 | 0 | 3289(匹配第三列第十行的3289) | 10 | 2152 | 985 |
| 10 | 927 | 3289 | 0 | 3289(匹配第三列第十行的3289) | 10 | 3289 | 687 |
img在匹配之后的效果如下:

(a) img的經過匹配之后的累積直方圖 (b) img的經過匹配之后的直方圖
1. OpenCV-Python實現直方圖匹配
代碼如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('C:\\Users\\admin\\Desktop\\original_img3\\testimg\\lena_300_500.jpg')
ref = cv2.imread('C:\\Users\\admin\\Desktop\\original_img3\\testimg\\messi_300_500.jpg')
out = np.zeros_like(img)
_, _, colorChannel = img.shape
for i in range(colorChannel):
print(i)
hist_img, _ = np.histogram(img[:, :, i], 256) # get the histogram
hist_ref, _ = np.histogram(ref[:, :, i], 256)
cdf_img = np.cumsum(hist_img) # get the accumulative histogram
cdf_ref = np.cumsum(hist_ref)
for j in range(256):
tmp = abs(cdf_img[j] - cdf_ref)
tmp = tmp.tolist()
idx = tmp.index(min(tmp)) # find the smallest number in tmp, get the index of this number
out[:, :, i][img[:, :, i] == j] = idx
cv2.imwrite('C:\\Users\\admin\\Desktop\\lena.jpg', out)
print('Done')
效果如下:

2. matlab實現直方圖匹配
程序如下:
clear;
% matching img's histogram to ref's histogram.
path = 'C:\\Users\\admin\\Desktop\\original_img3\\yijia0923_9\\';
ref = imread([path, '18.jpg']);
img = imread([path, '21.jpg']);
[H, W, colorChannel] = size(ref);
out = zeros(H, W, colorChannel);
for k = 1:colorChannel
disp(k);
hist_ref = imhist(ref(:, :, k)); % ref的直方圖
cumHist_ref = []; %ref的累積直方圖
for i=1:256
cumHist_ref = [cumHist_ref sum(hist_ref(1:i))];
end
img1 = img(:, :, k);
hist_img = imhist(img1); %img的直方圖
cumHist_img = []; %img的累積直方圖
for i=1:256
cumHist_img = [cumHist_img sum(hist_img(1:i))];
end
for i=1:256
tmp{i} = cumHist_ref - cumHist_img(i);
tmp{i} = abs(tmp{i}); % 找到兩個累積直方圖距離最近的點
[a, index(i)] = min(tmp{i}); % a是tmp{i}中最小的值,index是該值對應的下標
end
imgn = zeros(H,W);
for i = 1:H
for j = 1:W
imgn(i,j) = index(img1(i,j)+1)-1; % 由原圖的灰度通過索引映射到新的灰度
end
end
out(:, :, k) = imgn;
end
out=uint8(out);
% imwrite(out, [path, 'new3.jpg']);
figure; imshow(out); title('out')
disp('Done');
