限制对比度自适应直方图均衡化算法(CLAHE)实现

1. 概述

本篇文章是基于这篇博文写的,然后经过粗略查看之后,将其运用到课题中,这里将此记录下来作为记录,文章中若有错误的地方敬请谅解。

2. 实现代码

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);
			}
		}
	}
}

3. 实现效果

限制对比度自适应直方图均衡化算法(CLAHE)实现_第1张图片

你可能感兴趣的:([4],图像处理相关)