直方圖規定化
1 原理
直方圖均衡可以使圖像的灰度分佈產生均分分佈的特性,是一種較爲方便的圖像增強的方法。但在某些應用中,尤其是希望輸出圖像的直方圖具備特定的直方圖形狀時,直方圖均衡的效果就欠佳了。此時需要用到直方圖匹配或直方圖規定化。
先在連續空間中討論,將連續隨機變量和分別看成輸入圖像和輸出圖像的灰度,那麼和分別表示對應的概率密度函數,即表示輸入圖像的概率密度函數,表示希望輸出圖像具有的概率密度函數。
令爲一個具有以下特性的隨機變量:
其中爲積分假變量。
定義隨機變量具有如下的特性:
其中爲積分假變量。由此可以得出,因此,必須滿足下列條件:
由以上的推理過程,可以得出直方圖規定化的執行過程,對一幅給定圖像得到一幅其灰度級具有指定概率密度函數的圖像過程如下:
- 1 由輸入圖像得到,並求得的值;
- 2 使用上式中指定的PDF求;
- 3 求的反變換函數;因爲是由得到的,因此,該處理是到的映射,而後者正是我們期望的值。
- 4 首先,對輸入圖像進行直方圖均衡得到數數圖像,該圖像的像素值時值,對均衡後圖像中具有值得每個像素進行反映射,得到輸出圖像中的相應像素當所有的像素都處理完成之後,輸出圖像的PDF等於指定的PDF。
示例:
對一幅大小爲64×64像素(MN=4096)的3比特圖像(L=8)的灰度分佈如下表所示:
790 | 0.19 | |
1023 | 0.25 | |
850 | 0.21 | |
656 | 0.16 | |
329 | 0.08 | |
245 | 0.06 | |
122 | 0.03 | |
81 | 0.02 |
確定映射關係執行直方圖規定化的過程如下:
在實際開發過程中,可以按如下思路執行:
- 1 計算輸入圖像的灰度直方圖,並求得該直方圖對應的累積概率分佈圖;
- 2 計算規定目標圖像的灰度直方圖,並求得該直方圖對應的累積概率分佈圖;
- 3 將輸入圖像的累積概率分佈圖中的各個值,在規定目標圖像的累積概率分佈圖上查找與之最接近的值,並將該值對應的灰度級與輸入圖像的各個值行成查找表。
- 4 對輸入圖像執行查表操作。
個人認爲直方圖規定化可以這麼理解:輸入圖A具有直方圖ZA,輸入圖B具有直方圖ZB,爲了讓圖A具有類似ZB的直方圖,選擇使用直方圖均衡進行過渡,對A進行直方圖均衡得到結果JA,JA具有1(L-1)的分佈,對B進行直方圖均衡得到結果JB,JB具有1(L-1)的分佈,此時,將JA的像素與JB的像素一一對應,就能得到結果圖像。
2 Matlab實現
2.1 Matlab函數實現
Matlab的直方圖規定化的函數還是histeq
,該函數使用如下調用方式時將執行直方圖規定化過程:
% 讀取源圖像
srcimg = imread('../images/18.jpg');
srcimg = rgb2gray(srcimg); % 源圖像灰度化
[srcHist,srcX] = imhist(srcimg,256);% 計算源圖像的直方圖
dstimg = imread('../images/3.jpg'); % 讀取直方圖規定化的目標圖像
dstimg = rgb2gray(dstimg);% 目標圖像灰度化
[dstHist,dstX] = imhist(dstimg,256); % 計算目標圖像的灰度直方圖
g2 = histeq(srcimg,dstHist);
[g2Hist,g2X] = imhist(g2);
2.2 自己造輪子
clc;
clear;
close all;
% 讀取源圖像
srcimg = imread('../images/18.jpg');
srcimg = rgb2gray(srcimg); % 源圖像灰度化
[srcHist,srcX] = imhist(srcimg,256);% 計算源圖像的直方圖
srcHist = srcHist/numel(srcimg);% 直方圖歸一化
cps = zeros(256,1,'double');% 計算源圖像的灰度累積概率
for i=1:1:256
cps(i) = sum(srcHist(1:i));
end
cps = 255*cps; % 構建源圖像灰度均衡化的變換函數
cps = uint8(cps);
dstimg = imread('../images/3.jpg'); % 讀取直方圖規定化的目標圖像
dstimg = rgb2gray(dstimg);% 目標圖像灰度化
[dstHist,dstX] = imhist(dstimg,256); % 計算目標圖像的灰度直方圖
dstHist = dstHist/sum(dstHist); % 灰度直方圖歸一化
cpd = zeros(256,1,'double');% 計算目標圖像的灰度分佈累積概率
for i=1:1:256
cpd(i) = sum(dstHist(1:i));
end
cpd = cpd*255; % 構建目標圖像的灰度均衡化變換函數
cpd = uint8(cpd);
srcl=zeros(256,1,'uint8');
minv = 256;
for i = 1:1:256
minv =256;
for j = 1:1:256
if minv > abs(cps(i)-cpd(j))
minv = abs(cps(i)-cpd(j));
srcl(i) =j;
end
end
end
[width,height] = size( srcimg);
gray1 = srcimg;
for i=1:1:width
for j = 1:1:height
gray1(i,j)=srcl(srcimg(i,j)+1);
end
end
[g1Hist,g1X] = imhist(gray1);
% g1Hist = g1Hist/sum(g1Hist);
g2 = histeq(srcimg,dstHist);
[g2Hist,g2X] = imhist(g2);
% g2Hist = g2Hist/sum(g2Hist);
figure(1),
subplot(2,4,1),imshow(srcimg),title('原圖');
subplot(2,4,2),imshow(dstimg),title('匹配圖');
subplot(2,4,3),imshow(gray1),title('結果1');
subplot(2,4,4),imshow(g2),title('結果2');
subplot(2,4,5),stem(srcX,srcHist),title('原圖直方圖');
subplot(2,4,6),stem(dstX,dstHist),title('匹配圖直方圖');
subplot(2,4,7),stem(g1X,g1Hist),title('結果1直方圖');
subplot(2,4,8),stem(g2X,g2Hist),title('結果2直方圖');
3 C++實現
自己造輪子的快樂。。。。
3.1 自己造輪子
void CalcNormalizedHist1D(cv::Mat& img, cv::Mat& hist)
{
int channels[] = { 0 };
int histsize[] = { 256 };
float grayRnage[] = { 0,256 };
const float* ranges[] = { grayRnage };
cv::calcHist(&img, 1, channels, cv::Mat(), hist, 1, histsize, ranges);
hist = hist*1.0 / (img.rows*img.cols);
}
void CalcCSum(cv::Mat& normalizedHist, cv::Mat& matCSum)
{
matCSum = cv::Mat(normalizedHist.rows, normalizedHist.cols, CV_32FC1);
for (int i = 0; i < normalizedHist.rows; i++)
{
if (i == 0)
{
matCSum.at<float>(i, 0) = normalizedHist.at<float>(i, 0);
}
else
{
matCSum.at<float>(i, 0) = matCSum.at<float>(i-1, 0)+normalizedHist.at<float>(i, 0);
}
}
}
void CreateHSLT(cv::Mat& CSumSrc, cv::Mat& CSumDst, cv::Mat& lut)
{
lut = cv::Mat(1, 256, CV_8UC1);
float fMinV = 2;
int nMinLoc = 0;
for (int i = 0; i < CSumSrc.rows; i++)
{
fMinV = 2;
float f1 = CSumSrc.at<float>(i, 0);
for (int j = nMinLoc; j < CSumDst.rows; j++)
{
float f2 = CSumDst.at<float>(j, 0);
if (fMinV > fabs(f2-f1))
{
fMinV = fabs(f2 - f1);
nMinLoc = j;
}
}
lut.at<uchar>(i) = nMinLoc;
}
}
#include "../include/baseOps.h"
#include <iostream>
#include <string>
#include "../include/opencv400/opencv2/opencv.hpp"
#include "../include/opencv400/opencv2/core/core.hpp"
#include "../include/opencv400/opencv/highgui.h"
#include "windows.h"
int main()
{
SetCurrentDirectoryToExePath();
cv::Mat srcimg = cv::imread("../images/6.jpg");
cv::cvtColor(srcimg, srcimg, cv::COLOR_BGR2GRAY);
cv::Mat srcHist;
CalcNormalizedHist1D(srcimg, srcHist);
cv::Mat csumSrc;
CalcCSum(srcHist, csumSrc);
cv::Mat dstimg = cv::imread("../images/7.jpg");
cv::cvtColor(dstimg, dstimg, cv::COLOR_BGR2GRAY);
cv::Mat dstHist;
CalcNormalizedHist1D(dstimg, dstHist);
cv::Mat csumDst;
CalcCSum(dstHist, csumDst);
cv::Mat lut;
CreateHSLT(csumSrc, csumDst, lut);
cv::Mat res;
cv::LUT(srcimg, lut, res);
cv::Mat resHist;
CalcNormalizedHist1D(res, resHist);
ShowHist("Result Hist", resHist);
cv::waitKey();
return 0;
}