限制对比度自适应直方图均衡(CLAHE算法),本算法与普通的自适应直方图均衡不同地方在于对比度限幅,即下面的直方图修剪过程,修剪后的直方图均衡图像时,图像对比度会更自然。这个算法还是挺需要编程技巧的,写程序水平也都是随着解决问题难度而无形中提高的。本程序主要改写自下面的C代码,经过二次加工后感觉可读性高了一些。很多时候摘录的代码一定要经过二次加工,这样才能和项目中的代码保持风格一致,以及逻辑调用一致。
但是原算法处理完后有些偏色,所以在处理完后我额外增加了图层滤色混合操作,可以很好的降低偏色,使增强后的图片看着更自然、舒服。
主要参考资料:
原C代码:
参考文章:
算法主要分以下三部:
1.图像分块,以块为单位,先计算直方图,然后修剪直方图,最后均衡;
// 修剪直方图
void ClipHistogram(int* pHistogram, int clipThreshold)
{
int binExcess = 0, totalExcess = 0, avgBinIncr = 0, upperLimit = 0;
// 累积超出阈值部分
for (int i = 0; i < 256; i++)
{
binExcess = pHistogram[i] - clipThreshold;
if (binExcess > 0)
{
totalExcess += binExcess;
}
}
avgBinIncr = totalExcess / 256;
upperLimit = clipThreshold - avgBinIncr;
// 修剪直方图并重新分配数值
for (int i = 0; i < 256; i++)
{
if (pHistogram[i] > clipThreshold)
{
pHistogram[i] = clipThreshold;
}
else
{
if (pHistogram[i] > upperLimit)
{
totalExcess -= (clipThreshold - pHistogram[i]);
pHistogram[i] = clipThreshold;
}
else
{
totalExcess -= avgBinIncr;
pHistogram[i] += avgBinIncr;
}
}
}
// 剩余部分再次分配
int *pCurBin = pHistogram;
int *pStartBin = pHistogram;
int *pEndBin = pHistogram + 255;
while (totalExcess > 0 && pStartBin < pEndBin)
{
int stepSize = 256 / totalExcess;
if (stepSize < 1)
{
stepSize = 1;
}
for (pCurBin = pStartBin; pCurBin < pEndBin && totalExcess > 0; pCurBin += stepSize)
{
if (*pCurBin < clipThreshold)
{
(*pCurBin)++;
totalExcess--;
}
}
pStartBin++;
}
}
2.块间线性插值,这里需要遍历、操作各个图像块,处理起来复杂一些;
// 线性插值
void BlocksBilinearInter(uchar *pBlockData, int width, int blockWidth, int blockHeight, int *histogramLU, int *histogramRU, int *histogramLB, int *histogramRB)
{
int p_offet = (width - blockWidth)*4;
int blockSize = blockWidth * blockHeight;
int x_wgt = 0, y_wgt = 0, xinv_wgt = 0, yinv_wgt = 0;
for (y_wgt = 0, yinv_wgt = blockHeight; y_wgt < blockHeight; y_wgt++, yinv_wgt--, pBlockData += p_offet)
{
for (x_wgt = 0, xinv_wgt = blockWidth; x_wgt < blockWidth; x_wgt++, xinv_wgt--, pBlockData += 4)
{
pBlockData[AXJ_BLUE] = (uchar)((yinv_wgt*(xinv_wgt*histogramLU[pBlockData[AXJ_BLUE]] + x_wgt*histogramRU[pBlockData[AXJ_BLUE]]) + y_wgt*(xinv_wgt*histogramLB[pBlockData[AXJ_BLUE]] + x_wgt*histogramRB[pBlockData[AXJ_BLUE]])) / blockSize);
pBlockData[AXJ_GREEN] = (uchar)((yinv_wgt*(xinv_wgt*histogramLU[pBlockData[AXJ_GREEN]] + x_wgt*histogramRU[pBlockData[AXJ_GREEN]]) + y_wgt*(xinv_wgt*histogramLB[pBlockData[AXJ_GREEN]] + x_wgt*histogramRB[pBlockData[AXJ_GREEN]])) / blockSize);
pBlockData[AXJ_RED] = (uchar)((yinv_wgt*(xinv_wgt*histogramLU[pBlockData[AXJ_RED]] + x_wgt*histogramRU[pBlockData[AXJ_RED]]) + y_wgt*(xinv_wgt*histogramLB[pBlockData[AXJ_RED]] + x_wgt*histogramRB[pBlockData[AXJ_RED]])) / blockSize);
}
}
}
3.与原图做图层滤色混合操作:
f(a, b) = 1 - (1 - a)*(1 - b)
。
下面是一些增强效果图: