bool CLAHE_Algorithm::CLAHE_Process(cv::Mat& src_img)
{
if (!src_img.data)
{
std::cout << "无图像数据" << endl;
return false;
}
int rows(src_img.rows);
int cols(src_img.cols);
int img_type = src_img.type(); //图像的类型
if (CV_8UC1 != img_type && CV_8UC3 != img_type)
{
//cout << "输入8位的图像" << endl;
return false;
}
int img_channels = src_img.channels();
unsigned char* data_array = nullptr;
unsigned char* data = nullptr;
if (3 == img_channels)
{
std::vector splited_img;
cv::Mat src_img_temp = src_img.clone();
cv::resize(src_img_temp, src_img_temp, cv::Size(512,512));
rows=src_img_temp.rows;
cols=src_img_temp.cols;
cv::split(src_img_temp, splited_img);
for (int i = 0; i < 3; i++)
{
delete[] data_array;
data_array = nullptr;
data_array = new unsigned char[rows*cols];
cv::Mat& temp = splited_img[i];
for (int i = 0; i < rows; i++)
{
data = temp.ptr(i);
for (int j = 0; j < cols; j++)
{
data_array[i*rows + j] = *data++;
}
}
this->CLAHE(data_array, cols, rows, 0, 255, 8, 8, 128, 10.0);
//cv::Mat result(temp.rows, temp.cols, temp.type(), cv::Scalar::all(0));
for (int i = 0; i < rows; i++)
{
data = temp.ptr(i);
for (int j = 0; j < cols; j++)
{
*data++ = data_array[i*rows + j];
}
}
}
cv::Mat& blue_img = splited_img[0]; //blue channel
cv::Mat& green_img = splited_img[1]; //green channel
cv::Mat& red_img = splited_img[2]; //red channel
cv::Mat result;
cv::merge(splited_img, result);
result.copyTo(src_img);
}
else if (1== img_channels)
{
data_array = new unsigned char[rows*cols];
for (int i = 0; i < rows; i++)
{
data = src_img.ptr(i);
for (int j=0; jCLAHE(data_array, cols, rows, 0, 255, 8, 8, 128, 10.0);
for (int i = 0; i < rows; i++)
{
data = src_img.ptr(i);
for (int j = 0; j < cols; j++)
{
*data++ = data_array[i*rows + j];
}
}
}
if (data_array)
{
delete[] data_array;
data_array = nullptr;
}
return true;
}
//************************************************************************
// 函数名称: CLAHE
// 访问权限: private
// 创建日期: 2016/11/27
// 创 建 人:
// 函数说明: CLAHE算法的主函数
// 函数参数: kz_pixel_t * pImage 输入的图像数据
// 函数参数: unsigned int uiXRes 图像X轴上的分辨率
// 函数参数: unsigned int uiYRes 图像Y轴上的分辨率
// 函数参数: kz_pixel_t Min 输入图像也是输出图像的最小像素值
// 函数参数: kz_pixel_t Max 输入图像也是输出图像的最大像素值
// 函数参数: unsigned int uiNrX 输入图像在X轴方向上进行分块的数目(min 2, max uiMAX_REG_X)
// 函数参数: unsigned int uiNrY 输入图像在Y轴方向上进行分块的数目(min 2, max uiMAX_REG_X)
// 函数参数: unsigned int uiNrBins 输出图像的有效灰度级数,输出图像同样拥有之前设定好的最大和最小灰度值,通常选择128既能加快处理速度并且效果不错
// 函数参数: float fCliplimit 图像规范化区间限制,数值越大图像对比度越高(当为1时直接返回输入图像)
// 返 回 值: int $
//************************************************************************
int CLAHE_Algorithm::CLAHE (kz_pixel_t* pImage, unsigned int uiXRes, unsigned int uiYRes, kz_pixel_t Min, kz_pixel_t Max, unsigned int uiNrX,
unsigned int uiNrY, unsigned int uiNrBins, float fCliplimit)
{
unsigned int uiX, uiY; /* counters */
unsigned int uiXSize, uiYSize, uiSubX, uiSubY; /* size of context. reg. and subimages */
unsigned int uiXL, uiXR, uiYU, uiYB; /* auxiliary variables interpolation routine */
unsigned long ulClipLimit, ulNrPixels; /* clip limit and region pixel count */
kz_pixel_t* pImPointer; /* pointer to image */
kz_pixel_t aLUT[uiNR_OF_GREY]; /* lookup table used for scaling of input image */
unsigned long* pulHist, *pulMapArray; /* pointer to histogram and mappings*/
unsigned long* pulLU, *pulLB, *pulRU, *pulRB; /* auxiliary pointers interpolation */
if (uiNrX > uiMAX_REG_X) return -1; /* # of regions x-direction too large */
if (uiNrY > uiMAX_REG_Y) return -2; /* # of regions y-direction too large */
if (uiXRes % uiNrX) return -3; /* x-resolution no multiple of uiNrX */
if (uiYRes & uiNrY) return -4; /* y-resolution no multiple of uiNrY */
if (Max >= uiNR_OF_GREY) return -5; /* maximum too large */
if (Min >= Max) return -6; /* minimum equal or larger than maximum */
if (uiNrX < 2 || uiNrY < 2) return -7; /* at least 4 contextual regions required */
if (fCliplimit == 1.0) return 0; /* is OK, immediately returns original image. */
if (uiNrBins == 0) uiNrBins = 128; /* default value when not specified */
pulMapArray=(unsigned long *)malloc(sizeof(unsigned long)*uiNrX*uiNrY*uiNrBins); //全部块的直方图查询表
if (pulMapArray == 0) return -8; /* Not enough memory! (try reducing uiNrBins) */
uiXSize = uiXRes/uiNrX; uiYSize = uiYRes/uiNrY; /* Actual size of contextual regions */
ulNrPixels = (unsigned long)uiXSize * (unsigned long)uiYSize;
if(fCliplimit > 0.0) { /* Calculate actual cliplimit */
ulClipLimit = (unsigned long) (fCliplimit * (uiXSize * uiYSize) / uiNrBins);
ulClipLimit = (ulClipLimit < 1UL) ? 1UL : ulClipLimit;
} //计算直方图限制值
else ulClipLimit = 1UL<<14; /* Large value, do not clip (AHE) */
MakeLut(aLUT, Min, Max, uiNrBins); /* Make lookup table for mapping of greyvalues */
/* Calculate greylevel mappings for each contextual region */
for (uiY = 0, pImPointer = pImage; uiY < uiNrY; uiY++)
{
for (uiX = 0; uiX < uiNrX; uiX++, pImPointer += uiXSize)
{
pulHist = &pulMapArray[uiNrBins * (uiY * uiNrX + uiX)];
MakeHistogram(pImPointer, uiXRes, uiXSize, uiYSize, pulHist, uiNrBins, aLUT); //计算块直方图
ClipHistogram(pulHist, uiNrBins, ulClipLimit); //修剪直方图,去高填低
MapHistogram(pulHist, Min, Max, uiNrBins, ulNrPixels); //计算最后的直方图的映射lookup表
}
pImPointer += (uiYSize - 1) * uiXRes; /* skip lines, set pointer */
}
/* Interpolate greylevel mappings to get CLAHE image */
for (pImPointer = pImage, uiY = 0; uiY <= uiNrY; uiY++)
{
if (uiY == 0) { /* special case: top row */
uiSubY = uiYSize >> 1; uiYU = 0; uiYB = 0;
}
else
{
if (uiY == uiNrY) { /* special case: bottom row */
uiSubY = uiYSize >> 1; uiYU = uiNrY - 1; uiYB = uiYU;
}
else { /* default values */
uiSubY = uiYSize; uiYU = uiY - 1; uiYB = uiYU + 1;
}
}
for (uiX = 0; uiX <= uiNrX; uiX++)
{
if (uiX == 0)
{ /* special case: left column */
uiSubX = uiXSize >> 1; uiXL = 0; uiXR = 0;
}
else
{
if (uiX == uiNrX)
{ /* special case: right column */
uiSubX = uiXSize >> 1; uiXL = uiNrX - 1; uiXR = uiXL;
}
else
{ /* default values */
uiSubX = uiXSize; uiXL = uiX - 1; uiXR = uiXL + 1;
}
}
pulLU = &pulMapArray[uiNrBins * (uiYU * uiNrX + uiXL)];
pulRU = &pulMapArray[uiNrBins * (uiYU * uiNrX + uiXR)];
pulLB = &pulMapArray[uiNrBins * (uiYB * uiNrX + uiXL)];
pulRB = &pulMapArray[uiNrBins * (uiYB * uiNrX + uiXR)];
Interpolate(pImPointer, uiXRes, pulLU, pulRU, pulLB, pulRB, uiSubX, uiSubY, aLUT);
pImPointer += uiSubX; /* set pointer on next matrix */
}
pImPointer += (uiSubY - 1) * uiXRes; //跳转到下一个行块的起始位置
}
free(pulMapArray); /* free space for histograms */
return 0; /* return status OK */
}
//************************************************************************
// 函数名称: ClipHistogram
// 访问权限: private static
// 创建日期: 2016/11/27
// 创 建 人:
// 函数说明: 直方图修剪This function performs clipping of the histogram and redistribution of bins.
// The histogram is clipped and the number of excess pixels is counted.Afterwards
// the excess pixels are equally redistributed across the whole histogram(providing
// the bin count is smaller than the cliplimit
// 函数参数: unsigned long * pulHistogram 统计直方图数据
// 函数参数: unsigned int uiNrGreylevels 输出灰度等级
// 函数参数: unsigned long ulClipLimit 限制值
// 返 回 值: void $
//************************************************************************
void CLAHE_Algorithm::ClipHistogram (unsigned long* pulHistogram, unsigned int uiNrGreylevels, unsigned long ulClipLimit)
{
unsigned long* pulBinPointer, *pulEndPointer, *pulHisto;
unsigned long ulNrExcess, ulUpper, ulBinIncr, ulStepSize, i;
long lBinExcess;
ulNrExcess = 0; pulBinPointer = pulHistogram;
for (i = 0; i < uiNrGreylevels; i++)
{ /* calculate total number of excess pixels */
lBinExcess = (long)pulBinPointer[i] - (long)ulClipLimit;
if (lBinExcess > 0)
ulNrExcess += lBinExcess; /* excess in current bin */
};
/* Second part: clip histogram and redistribute excess pixels in each bin */
ulBinIncr = ulNrExcess / uiNrGreylevels; /* average binincrement */
ulUpper = ulClipLimit - ulBinIncr; /* Bins larger than ulUpper set to cliplimit */
for (i = 0; i < uiNrGreylevels; i++)
{
if (pulHistogram[i] > ulClipLimit)
pulHistogram[i] = ulClipLimit; /* clip bin */
else
{
//if (pulHistogram[i] > ulUpper)
//{ /* high bin count */
// ulNrExcess -= pulHistogram[i] - ulUpper;
// pulHistogram[i] = ulClipLimit;
//}
//应修正为
if (pulHistogram[i] > ulUpper)
{ /* high bin count */
ulNrExcess -= (ulClipLimit - pulHistogram[i]); pulHistogram[i] = ulClipLimit;
}
else
{ /* low bin count */
ulNrExcess -= ulBinIncr;
pulHistogram[i] += ulBinIncr;
}
}
}
while (ulNrExcess)
{ /* Redistribute remaining excess */
pulEndPointer = &pulHistogram[uiNrGreylevels]; pulHisto = pulHistogram;
while (ulNrExcess && pulHisto < pulEndPointer)
{
ulStepSize = uiNrGreylevels / ulNrExcess;
if (ulStepSize < 1)
ulStepSize = 1; /* stepsize at least 1 */
for (pulBinPointer = pulHisto; pulBinPointer < pulEndPointer && ulNrExcess;
pulBinPointer += ulStepSize)
{
if (*pulBinPointer < ulClipLimit)
{
(*pulBinPointer)++;
ulNrExcess--; /* reduce excess */
}
}
pulHisto++; /* restart redistributing on other bin location */
}
}
}
//************************************************************************
// 函数名称: MakeHistogram
// 访问权限: private static
// 创建日期: 2016/11/27
// 创 建 人:
// 函数说明:
// 函数参数: kz_pixel_t * pImage 输入的图像数据
// 函数参数: unsigned int uiXRes 输入图像X轴方向的分辨率
// 函数参数: unsigned int uiSizeX 输入图像经过分割之后拆分得到的单个块X轴的分辨率
// 函数参数: unsigned int uiSizeY 输入图像经过分割之后拆分得到的单个块Y轴的分辨率
// 函数参数: unsigned long * pulHistogram 输出统计直方图
// 函数参数: unsigned int uiNrGreylevels 输出图像的灰度级数
// 函数参数: kz_pixel_t * pLookupTable 查询表
// 返 回 值: void $
//************************************************************************
void CLAHE_Algorithm::MakeHistogram (kz_pixel_t* pImage, unsigned int uiXRes, unsigned int uiSizeX, unsigned int uiSizeY,
unsigned long* pulHistogram, unsigned int uiNrGreylevels, kz_pixel_t* pLookupTable)
/* This function classifies the greylevels present in the array image into
* a greylevel histogram. The pLookupTable specifies the relationship
* between the greyvalue of the pixel (typically between 0 and 4095) and
* the corresponding bin in the histogram (usually containing only 128 bins).
*/
{
kz_pixel_t* pImagePointer;
unsigned int i;
for (i = 0; i < uiNrGreylevels; i++)
pulHistogram[i] = 0L; /* clear histogram */
for (i = 0; i < uiSizeY; i++)
{
pImagePointer = &pImage[uiSizeX];
while (pImage < pImagePointer)
pulHistogram[pLookupTable[*pImage++]]++; //统计图像的灰度直方图
pImagePointer += uiXRes; //将图像的指针转换到下一行像素数据
//pImage = &pImagePointer[-uiSizeX];
pImage = &(pImagePointer-uiSizeX)[0];
}
}
//************************************************************************
// 函数名称: MapHistogram
// 访问权限: private static
// 创建日期: 2016/11/27
// 创 建 人:
// 函数说明: 计算映射的直方图This function calculates the equalized lookup table (mapping) by
// cumulating the input histogram.Note: lookup table is rescaled in range[Min..Max].
// 函数参数: unsigned long * pulHistogram 直方图
// 函数参数: kz_pixel_t Min 图像的最小灰度值
// 函数参数: kz_pixel_t Max 图像的最大灰度值
// 函数参数: unsigned int uiNrGreylevels 输出图像的灰度级数
// 函数参数: unsigned long ulNrOfPixels 单个块的总像素点个数
// 返 回 值: void $
//************************************************************************
void CLAHE_Algorithm::MapHistogram (unsigned long* pulHistogram, kz_pixel_t Min, kz_pixel_t Max, unsigned int uiNrGreylevels,
unsigned long ulNrOfPixels)
{
unsigned int i; unsigned long ulSum = 0;
const float fScale = ((float)(Max - Min)) / ulNrOfPixels;
const unsigned long ulMin = (unsigned long) Min;
for (i = 0; i < uiNrGreylevels; i++)
{
ulSum += pulHistogram[i];
pulHistogram[i]=(unsigned long)(ulMin+ulSum*fScale);
if (pulHistogram[i] > Max)
pulHistogram[i] = Max;
}
}
//************************************************************************
// 函数名称: MakeLut
// 访问权限: private static
// 创建日期: 2016/11/27
// 创 建 人:
// 函数说明: To speed up histogram clipping, the input image [Min,Max] is scaled down to*[0, uiNrBins - 1].
// This function calculates the LUT.
// 函数参数: kz_pixel_t * pLUT 查询表
// 函数参数: kz_pixel_t Min 输入图像的最小值
// 函数参数: kz_pixel_t Max 输出图像的最大值
// 函数参数: unsigned int uiNrBins 输出图像指定的灰度级数
// 返 回 值: void $
//************************************************************************
void CLAHE_Algorithm::MakeLut (kz_pixel_t * pLUT, kz_pixel_t Min, kz_pixel_t Max, unsigned int uiNrBins)
{
int i;
const kz_pixel_t BinSize = (kz_pixel_t) (1 + (Max - Min) / uiNrBins);
for (i = Min; i <= Max; i++)
pLUT[i] = (i - Min) / BinSize;
}
void CLAHE_Algorithm::Interpolate (kz_pixel_t * pImage, int uiXRes, unsigned long * pulMapLU, unsigned long * pulMapRU, unsigned long * pulMapLB,
unsigned long * pulMapRB, unsigned int uiXSize, unsigned int uiYSize, kz_pixel_t * pLUT)
/* pImage - pointer to input/output image
* uiXRes - resolution of image in x-direction
* pulMap* - mappings of greylevels from histograms
* uiXSize - uiXSize of image submatrix
* uiYSize - uiYSize of image submatrix
* pLUT - lookup table containing mapping greyvalues to bins
* This function calculates the new greylevel assignments of pixels within a submatrix
* of the image with size uiXSize and uiYSize. This is done by a bilinear interpolation
* between four different mappings in order to eliminate boundary artifacts.
* It uses a division; since division is often an expensive operation, I added code to
* perform a logical shift instead when feasible.
*/
{
const unsigned int uiIncr = uiXRes-uiXSize; /* Pointer increment after processing row */
kz_pixel_t GreyValue; unsigned int uiNum = uiXSize*uiYSize; /* Normalization factor */
unsigned int uiXCoef, uiYCoef, uiXInvCoef, uiYInvCoef, uiShift = 0;
if (uiNum & (uiNum - 1)) /* If uiNum is not a power of two, use division */
{
for (uiYCoef = 0, uiYInvCoef = uiYSize; uiYCoef < uiYSize; uiYCoef++, uiYInvCoef--, pImage += uiIncr)
{
for (uiXCoef = 0, uiXInvCoef = uiXSize; uiXCoef < uiXSize; uiXCoef++, uiXInvCoef--)
{
GreyValue = pLUT[*pImage]; /* get histogram bin value */
*pImage++ = (kz_pixel_t)((uiYInvCoef * (uiXInvCoef*pulMapLU[GreyValue]
+ uiXCoef * pulMapRU[GreyValue])
+ uiYCoef * (uiXInvCoef * pulMapLB[GreyValue]
+ uiXCoef * pulMapRB[GreyValue])) / uiNum);
}
}
}
else
{ /* avoid the division and use a right shift instead */
while (uiNum >>= 1) uiShift++; /* Calculate 2log of uiNum */
for (uiYCoef = 0, uiYInvCoef = uiYSize; uiYCoef < uiYSize; uiYCoef++, uiYInvCoef--, pImage += uiIncr)
{
for (uiXCoef = 0, uiXInvCoef = uiXSize; uiXCoef < uiXSize; uiXCoef++, uiXInvCoef--)
{
GreyValue = pLUT[*pImage]; /* get histogram bin value */
*pImage++ = (kz_pixel_t)((uiYInvCoef* (uiXInvCoef * pulMapLU[GreyValue]
+ uiXCoef * pulMapRU[GreyValue])
+ uiYCoef * (uiXInvCoef * pulMapLB[GreyValue]
+ uiXCoef * pulMapRB[GreyValue])) >> uiShift);
}
}
}
}