直方图均衡是一种将图像中的灰度分布转换成均匀分布,从而增强图像的对比度的图像处理方法。直方图均衡可以将原本偏白或者偏黑的图像转换成对比度符合人眼视觉的图像。
1 原理
连续空间
连续空间内的图像灰度r∈[0,L−1],L表示灰度级r\in[0,L-1],L表示灰度级r∈[0,L−1],L表示灰度级,期望的均衡化转换函数为
s=T(r),0≤r≤L−1s=T(r),0\le r\le L-1 s=T(r),0≤r≤L−1
其中TTT为灰度变换函数,sss是经过变换后的灰度值,该变化函数满足两个条件:
- T(r)T(r)T(r)在区间[0,L−1][0, L-1][0,L−1]内是严格单调递增函数。保证转换是一一对应的避免出现二义性;
- 当0≤r≤L−10 \le r\le L - 10≤r≤L−1时,0≤s≤L−10 \le s\le L - 10≤s≤L−1。保证输入输出的灰度范围相同。
我们期望的是将输入的灰度概率分布p(r)p(r)p(r)转换成输出的灰度概率分布p(s)=1L−1p(s)=\frac{1}{L - 1}p(s)=L−11。这里选择下面的变换函数来证明这个函数是可行的:
s=T(r)=(L−1)∫0rpr(w)dw,w为积分的假变量s=T(r)=(L-1)\int_{0}^{r}p_r(w)dw,w为积分的假变量 s=T(r)=(L−1)∫0rpr(w)dw,w为积分的假变量
首先,∫0rpr(w)dw\int_{0}^{r}p_r(w)dw∫0rpr(w)dw的值域为[0,1][0,1][0,1]因此sss的值域为[0,L−1][0,L-1][0,L−1];另外该函数也是严格单调递增的。基本条件满足。下面我们证明p(s)=1L−1p(s)=\frac{1}{L-1}p(s)=L−11。因为
p(s)=pr(r)∣drds∣=pr(r)1dsdr=pr(r)1(L−1)ddr[∫0rpr(w)dw]=pr(r)1(L−1)pr(r)=1L−1\begin{equation} \begin{aligned} p(s)&=p_r(r)|\frac{dr}{ds}|\\ &=p_r(r)\frac{1}{\frac{ds}{dr}}\\ &=p_r(r)\frac{1}{(L-1)\frac{d}{dr}[\int_{0}^{r}p_r(w)dw]}\\ &=p_r(r)\frac{1}{(L-1)p_r(r)}\\ &=\frac{1}{L-1} \end{aligned} \end{equation} p(s)=pr(r)∣dsdr∣=pr(r)drds1=pr(r)(L−1)drd[∫0rpr(w)dw]1=pr(r)(L−1)pr(r)1=L−11
因此s=T(r)=(L−1)∫0rpr(w)dws=T(r)=(L-1)\int_{0}^{r}p_r(w)dws=T(r)=(L−1)∫0rpr(w)dw可以作为均衡化变换函数。
离散空间
而对于图像处理始终是离散空间,离散空间的灰度rk,k为灰度级,k∈[0,L−1]r_k,k为灰度级,k\in [0,L-1]rk,k为灰度级,k∈[0,L−1]的概率近似为
pr(rk)=nkMNp_r(r_k)=\frac{n_k}{MN} pr(rk)=MNnk
其中k为灰度级,k∈[0,L−1]k为灰度级,k\in [0,L-1]k为灰度级,k∈[0,L−1];MMM和NNN分别为图像的宽高,nkn_knk为当前灰度级像素点的数量。则利用上面的变换函数得到的sks_ksk为
sk=T(rk)=(L−1)∑j=0kpr(rj)=L−1MN∑j=0knjs_k=T(r_k)=(L-1)\sum_{j=0}^{k}p_r(r_j)=\frac{L-1}{MN}\sum_{j=0}^{k}{n_j} sk=T(rk)=(L−1)j=0∑kpr(rj)=MNL−1j=0∑knj
另外利用上面的变化函数计算出来的灰度值可能是小数,因此需要最近取整。以及对于RGB图像分别对R、G、B三个通道的图像进行变换的话会出现色相问题,因此需要将图像转换成其他颜色空间比如YUV,只对Y做灰度变换。
2 实现
实现比较简单,首先逐像素计算每个灰度级的数量,根据改数量计算出每个灰度级的概率,最后利用上面的公式计算出最终的颜色映射关系,最终将根据映射关系将原图中颜色进行映射。
static vector<float> countGrayProp(const Mat &img) {assert(img.channels() == 1);double pixelProp = 1.0 / (img.rows * img.cols);vector<float> ret(GRAPH_GRAY_LAYER_NUM, 0.0);for (int i = 0; i < img.rows; i++) {for (int j = 0; j < img.cols; j++) {ret[static_cast<int>(img.at<uchar>(i, j))] += pixelProp;}}return ret;
}
/** @brief 均衡化灰度图*/
static Mat avgGrayHistogram(const Mat &img) {assert(img.channels() == 1);vector<float> props = countGrayProp(img);vector<float> propSum(GRAPH_GRAY_LAYER_NUM, 0.0);for (int i = 0; i < GRAPH_GRAY_LAYER_NUM;i ++) {if (i == 0) {propSum[i] = props[i];}else {propSum[i] = (props[i] + propSum[i - 1]);}}Mat ret(img.rows, img.cols, CV_8UC1);for (int i = 0; i < img.rows; i++) {for (int j = 0; j < img.cols; j++) {int value = static_cast<int>(img.at<uchar>(i, j));ret.at<uchar>(i, j) = int((GRAPH_GRAY_LAYER_NUM - 1) * propSum[value]);}}return ret;
}Mat avgHistogram(const Mat &img) {if (img.channels() == 1) {return avgGrayHistogram(img);}else if (img.channels() == 3) {Mat yuvImg;cvtColor(img, yuvImg, COLOR_BGR2YUV);std::vector<Mat> yuvImgs;split(yuvImg, yuvImgs);yuvImgs[0] = avgGrayHistogram(yuvImgs[0]);Mat proYUV, ret;merge(yuvImgs, proYUV);cv::cvtColor(proYUV, ret, COLOR_YUV2BGR);return ret;}return img;
}
对于单通道的灰度图效果如下,能够看到均衡化后的灰度直方图中灰度更加均匀:
如果直接对rgb三个通道做均衡化会出现下面的情况(1,2,3,4四张图中1和3为处理前的图,34为处理后的)
首先将图像转换到其他颜色空间比如YUV,只对Y分量进行均衡化就不会有明显的的色差和奇异点。