精华内容
下载资源
问答
  • CLAHE

    千次阅读 2019-06-12 10:18:37
    CLAHE同普通的自适应直方图均衡不同的地方主要是其对比度限幅。这个特性也可以应用到全局直方图均衡化中,即构成所谓的限制对比度直方图均衡(CLHE),但这在实际中很少使用。在CLAHE中,对于每个小区域都必须使用...

    1.简述

         CLAHE同普通的自适应直方图均衡不同的地方主要是其对比度限幅。这个特性也可以应用到全局直方图均衡化中,即构成所谓的限制对比度直方图均衡(CLHE),但这在实际中很少使用。在CLAHE中,对于每个小区域都必须使用对比度限幅。CLAHE主要是用来克服AHE的过度放大噪音的问题。 

         这主要是通过限制AHE算法的对比提高程度来达到的。在指定的像素值周边的对比度放大主要是由变换函数的斜度决定的。这个斜度和领域的累积直方图的斜度成比例。CLAHE通过在计算CDF前用预先定义的阈值来裁剪直方图以达到限制放大幅度的目的。这限制了CDF的斜度因此,也限制了变换函数的斜度。直方图被裁剪的值,也就是所谓的裁剪限幅,取决于直方图的分布因此也取决于领域大小的取值。

         通常,直接忽略掉那些超出直方图裁剪限幅的部分是不好的,而应该将这些裁剪掉的部分均匀的分布到直方图的其他部分。如下图所示。

     

           Clahe-redist.svg

     

        这个重分布的过程可能会导致那些倍裁剪掉的部分由重新超过了裁剪值(如上图的绿色部分所示)。如果这不是所希望的,可以不带使用重复不的过程指导这个超出的部分已经变得微不足道了。

         2. 通过插值加快计算速度

            如上所述的直接的自适应直方图,不管是否带有对比度限制,都需要对图像中的每个像素计算器领域直方图以及对应的变换函数,这使得算法及其耗时。

            而插值使得上述算法效率上有极大的提升,并且质量上没有下降。首先,将图像均匀分成等份矩形大小,如下图的右侧部分所示(8行8列64个块是常用的选择)。然后计算个块的直方图、CDF以及对应的变换函数。这个变换函数对于块的中心像素(下图左侧部分的黑色小方块)是完全符合原始定义的。而其他的像素通过哪些于其临近的四个块的变换函数插值获取。位于图中蓝色阴影部分的像素采用双线性查插值,而位于便于边缘的(绿色阴影)部分采用线性插值,角点处(红色阴影处)直接使用块所在的变换函数。

           Clahe-tileinterpol.svg

     

         这样的过程极大的降低了变换函数需要计算的次数,只是增加了一些双线性插值的计算量。

    展开全文
  • CLAHE 代码

    热门讨论 2015-11-02 13:45:59
    Matlab编写的CLAHE,非常值得借鉴。
  • CLAHE算法

    万次阅读 2019-04-08 18:28:52
    限制对比度自适应直方图均衡(Contrast Limited Adaptive Histogram Equalization,CLAHE) 自适应直方图均衡(AHE)算法,对于图像中存在明显比其他区域亮或者暗的地方时,普通的直方图均衡算法就不能将该处的细节信息...

    限制对比度自适应直方图均衡(Contrast Limited Adaptive Histogram Equalization,CLAHE)

    自适应直方图均衡(AHE)算法,对于图像中存在明显比其他区域亮或者暗的地方时,普通的直方图均衡算法就不能将该处的细节信息描述出来。AHE算法通过在当前处理像素周边的一个矩形区域内进行直方图均衡,来达到扩大局部对比度,显示平滑区域细节的作用。
       AHE算法的2个属性:1、AHE算法处理的局部领域,矩形领域小,局部对比度强,矩形领域大,局部对比度弱。2、如果矩形区域内的图像块信息比较平坦,灰度接近,其灰度直方图呈尖状,在直方图均衡的过程中就可能会出现过度放大噪声的情况。

       CLAHE,对比度受限的自适应直方图均衡算法就能够有效的限制噪声放大的情形。下图表示的就是局部矩形领域内的灰度直方图,由于对比度放大的程度与像素点的概率分布直方图的曲线斜度成比例,所以为了限制对比度,将大于一定阈值的部分平均分配到直方图的其他地方,如下图所示,这样的话,通过限制CDF(累积分布函数)的斜率来一定程度限制对比度。
     

    插值过程,得到了CDF函数,也就获得了对应的亮度变换函数,在计算变换函数的时候可以通过插值过程来降低计算量。其中红色块(图像角点处)的变换函数是完全按照定义获得的,绿色块(图像边缘)的变换函数是通过旁边两个图像块的变换函数线性插值得到的,蓝色部分图像块的变换函数则是通过双线性插值得到。
     

    Matlab和OpenCV中都已经集成了CLAHE函数,在Matlab中,就是函数J = adapthisteq(I);

    全局映射算子
    每一个像素点将会根据它的全图特征和亮度信息进行映射,不管其空间位置几何。全局算子一个比较典型的例子就是色调曲线。
    导致图像色调映射过后看起来很平坦,失去了其局部的细节信息。

    局部映射算子
    像素点所在的空间位置会被考虑,具有相同亮度值的两个像素点会被映射成不同的值,因为它们的空间位置周边的亮度信息可能不一样。
    局部色调映射会很好的保护高亮和阴影部分的局部对比度和细节信息。
    https://www.cnblogs.com/whw19818/p/5766005.html

     算法主要分以下三部:
     1.图像分块,以块为单位,先计算直方图,然后修剪直方图,最后均衡;
     

     2.块间线性插值,这里需要遍历、操作各个图像块,处理起来复杂一些;
     

    3.与原图做图层滤色混合操作:f(a, b) = 1 - (1 - a)*(1 - b)。
    https://blog.csdn.net/grafx/article/details/53311915
     

    展开全文
  • CLAHE代码

    千次阅读 2016-01-14 18:59:19
    为了提高运行速度,把CLAHE的代码进行了修改,方便运行。 CLAHE.h class CLAHE { public: CLAHE(void); ~CLAHE(void); int m_nGridX; /* CLAHE 宽网格的个数 */ int m_nGridY; /* CLAHE 高网格的个数 ...

    为了提高运行速度,把CLAHE的代码进行了修改,方便运行。

    CLAHE.h

    class CLAHE
    {
    public:
    	CLAHE(void);
    	~CLAHE(void);
    	int m_nGridX;				 /* CLAHE 宽网格的个数 */
    	int m_nGridY;				 /* CLAHE 高网格的个数 */
    	unsigned char m_aLUT[256];	/* CLAHE lookup table used for scaling of input image */	   
    	int Initial(int nMaxPixel, int nMinPixel, int uiNrBins, int nX, int nY, int nWidth, int nHeight,float fCliplimit);
    	int m_nWidth;
    	int m_nHeight;
    	int m_nCliplimit;
    	int m_nHistBins;
    	int m_nGridSize_X;
    	int m_nGridSize_Y;
    	int *m_pMapArray;
    	int m_nGridSize;			/* Actual size of contextual regions */
    	int ProcessCLAHE(unsigned char* pImage);
    	void MakeHistogram(unsigned char* pImage,int* pulHistogram);
    	void ClipHistogram(int* pulHistogram);
    	void MapHistogram(int* pulHistogram);
    	void Interpolate (unsigned char * pImage);
    	int m_nMaxPixel;
    	int m_nMinPixel;
    	void ShiftInterpolate(unsigned char* pImage, int nSizeX, int nSizeY, int* pulMapLU,	int* pulMapRU, int *pulMapLB, int *pulMapRB);
    	int LineInterpolate(unsigned char* pImage, int nSizeX, int nSizeY, int* pLU, int* pRU, int* pLB, int* pRB);
    };
    
    CLAH.CPP
    <pre name="code" class="cpp">const unsigned int uiMAX_REG_X = 16;      /* max. # contextual regions in x-direction */
    const unsigned int uiMAX_REG_Y = 16;      /* max. # contextual regions in y-direction */
    #define	 uiNR_OF_GREY (256)
    #define	 uiNR_OF_GREY (4096)
    
    CLAHE::CLAHE(void)
    : m_nGridX(0)
    , m_nGridY(0)
    , m_nWidth(0)
    , m_nHeight(0)
    , m_nCliplimit(0)
    , m_nHistBins(0)
    , m_nGridSize_X(0)
    , m_nGridSize_Y(0)
    , m_pMapArray(0)
    , m_nGridSize(0)
    , m_nMaxPixel(0)
    , m_nMinPixel(0)
    {
    }
    
    CLAHE::~CLAHE(void)
    {
    }
    
    int CLAHE::Initial(int nMaxPixel, int nMinPixel, int uiNrBins, int nX, int nY, int nWidth, int nHeight,
    					float fCliplimit)
    {
    	if (nX > uiMAX_REG_X) return -1;					 /* # of regions x-direction too large */
    	if (nY > uiMAX_REG_Y) return -2;					 /* # of regions y-direction too large */
    	if (nWidth	% nX) return -3;						 /* x-resolution no multiple of uiNrX */
    	if (nHeight % nY) return -4;						 /* y-resolution no multiple of uiNrY */
    	if (nMaxPixel >= uiNR_OF_GREY) return -5;					 /* maximum too large */
    	if (nMinPixel >= nMaxPixel) return -6;						   /* minimum equal or larger than maximum */
    	if (nX < 2 || nY < 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 */
    
    
    	/* Make lookup table for mapping of greyvalues */
    	int i;
    	const unsigned char BinSize = (unsigned char)(1 + (nMaxPixel-nMinPixel)/uiNrBins);
    
    	for (i=nMinPixel; i<=nMaxPixel; i++) 
    		m_aLUT[i] = (i-nMinPixel) / BinSize;
    
    	m_nMinPixel = nMinPixel;
    	m_nMaxPixel = nMaxPixel;
    	m_nGridX = nX;
    	m_nGridY = nY;
    	m_nWidth = nWidth;
    	m_nHeight = nHeight;
    	m_nHistBins = uiNrBins;
    	m_nGridSize_X = m_nWidth / m_nGridX;
    	m_nGridSize_Y = m_nHeight / m_nGridY;
    	m_nGridSize = m_nGridSize_X * m_nGridSize_Y;
    	if(fCliplimit > 0.0) {          /* Calculate actual cliplimit     */
    		m_nCliplimit = fCliplimit * m_nGridSize / uiNrBins;
    		m_nCliplimit = (m_nCliplimit < 1) ? 1: m_nCliplimit;
    	}
    	m_pMapArray=(int *)malloc(sizeof(int)*m_nGridX*m_nGridY*m_nHistBins);
    	if (m_pMapArray == 0) return -8;      /* Not enough memory! (try reducing uiNrBins) */
    	return 1;
    }
    
    int CLAHE::ProcessCLAHE(unsigned char* pImage)
    {
    	int		x,y;
    	unsigned char* pImPointer;	
    	int			   *pulHist;
    	int				uiSubX, uiSubY;		
    	int				uiXL, uiXR, uiYU, uiYB;						/* auxiliary variables interpolation routine */					
    	int			* pulLU, *pulLB, *pulRU, *pulRB;			/* auxiliary pointers interpolation */
    
    
    	/* Calculate greylevel mappings for each contextual region */
    	for (y=0,pImPointer=pImage; y<m_nGridY; y++)
    	{
    		for (x=0; x<m_nGridX; x++,pImPointer+=m_nGridSize_X)
    		{
    			pulHist = &m_pMapArray[m_nHistBins * (y * m_nGridX + x)];
    			MakeHistogram(pImPointer,pulHist);
    			ClipHistogram(pulHist);
    			MapHistogram(pulHist);
    		}
    		pImPointer += (m_nGridSize_Y-1) * m_nWidth;          /* skip lines, set pointer */
    	}
    
    	/* Interpolate greylevel mappings to get CLAHE image */
    	Interpolate (pImage);
    	
    	return 0;  
    }
    
    void CLAHE::MakeHistogram(unsigned char* pImage,int* pulHistogram)
    {
    	unsigned char* pImagePointer;
    	int			   i;
    		
    	memset(pulHistogram, 0, sizeof(int) * m_nHistBins);			/* clear histogram */
    
    	for (i=0; i<m_nGridSize_Y; i++)
    	{
    		pImagePointer = &pImage[m_nGridSize_X];
    		while (pImage < pImagePointer) 
    			pulHistogram[m_aLUT[*pImage++]]++;
    		pImagePointer += m_nWidth;
    		pImage = &pImagePointer[-m_nGridSize_X];
    	}
    }
    /* 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).
     */
    void CLAHE::ClipHistogram(int* pulHistogram)
    {
    	int*	pulBinPointer, *pulEndPointer, *pulHisto;
    	int		ulNrExcess, ulUpper, ulBinIncr, ulStepSize, i;
    	int		lBinExcess;
    
    	ulNrExcess = 0; 
    	pulBinPointer = pulHistogram;
    	for (i = 0; i < m_nHistBins; i++) 
    	{ 
    		/* calculate total number of excess pixels */
    		lBinExcess = pulBinPointer[i] - m_nCliplimit;
    		if (lBinExcess > 0) 
    			ulNrExcess += lBinExcess;      /* excess in current bin */
    	}
    
    	/* Second part: clip histogram and redistribute excess pixels in each bin */
    	ulBinIncr = ulNrExcess / m_nHistBins;          /* average binincrement */
    	ulUpper =  m_nCliplimit - ulBinIncr;				  /* Bins larger than ulUpper set to cliplimit */
    
    	for (i=0; i<m_nHistBins; i++)
    	{
    		if (pulHistogram[i] > m_nCliplimit) 
    			pulHistogram[i] = m_nCliplimit;			/* clip bin */
    		else
    		{
    			if (pulHistogram[i] > ulUpper)
    			{        /* high bin count */
    				ulNrExcess -= (pulHistogram[i] - ulUpper);
    				pulHistogram[i]= m_nCliplimit;
    			}
    			else 
    			{  /* low bin count */
    				ulNrExcess -= ulBinIncr; 
    				pulHistogram[i] += ulBinIncr;
    		 }
    		}
    	}
    
    	while (ulNrExcess) 
    	{   
    		/* Redistribute remaining excess  */
    		pulEndPointer = &pulHistogram[m_nHistBins];
    		pulHisto = pulHistogram;
    
    		while (ulNrExcess && (pulHisto<pulEndPointer))
    		{
    			ulStepSize = m_nHistBins / ulNrExcess;
    			if (ulStepSize < 1) ulStepSize = 1;          /* stepsize at least 1 */
    			for (pulBinPointer=pulHisto; pulBinPointer<pulEndPointer&&ulNrExcess; pulBinPointer += ulStepSize)
    			{
    				if (*pulBinPointer < m_nCliplimit) 
    				{
    					(*pulBinPointer)++;   
    					ulNrExcess--;      /* reduce excess */
    				}
    			}
    			pulHisto++;          /* restart redistributing on other bin location */
    		}
    	}
    }
    
    /* This function calculates the equalized lookup table (mapping) by
     * cumulating the input histogram. Note: lookup table is rescaled in range [Min..Max].
     */
    void CLAHE::MapHistogram(int* pulHistogram)
    {
    	int			i;  
    	long		ulSum = 0;
    	const float fScale = ((float)(m_nMaxPixel - m_nMinPixel)) / m_nGridSize;
    	const long	ulMin = (long)m_nMinPixel;
    
    	for (i=0; i<m_nHistBins; i++)
    	{
    		ulSum += pulHistogram[i]; 
    		pulHistogram[i]	= (long)(ulMin+ulSum*fScale);
    		if (pulHistogram[i] > m_nMaxPixel)
    			pulHistogram[i] = m_nMaxPixel;
    	}
    }
    /* pImage      - pointer to input/output image
     * 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.
     */
    void CLAHE:: Interpolate (unsigned char * pImage)
    {
    	int		      x,y;
    	int		      uiSubY,uiSubX,uiYU,uiYB,uiXL,uiXR;
    	int			 *pulLU,*pulRU,*pulLB,*pulRB;
    	unsigned char	*pImPointer;
    
    	//x,y都为0
    	pImPointer = pImage;
    	uiSubY = m_nGridSize_Y>>1; 
    	uiSubX = m_nGridSize_X>>1;
    	pulLU = &m_pMapArray[0];
    	pulRU = &m_pMapArray[0];
    	pulLB = &m_pMapArray[0];
    	pulRB = &m_pMapArray[0];
    	LineInterpolate(pImPointer,uiSubX,uiSubY,pulLU,pulRU,pulLB,pulRB);
    	pImPointer += uiSubX;              /* set pointer on next matrix */
    
    	//y为0
    	uiYU = 0;
    	uiYB = 0;
    	for (x=1; x<m_nGridX; x++)
    	{
    		uiSubX = m_nGridSize_X; 
    		uiXL = x - 1;
    		uiXR = uiXL + 1;
    
    		pulLU = &m_pMapArray[m_nHistBins * uiXL];
    		pulRU = &m_pMapArray[m_nHistBins * uiXR];
    		pulLB = &m_pMapArray[m_nHistBins * uiXL];
    		pulRB = &m_pMapArray[m_nHistBins * uiXR];
    		LineInterpolate(pImPointer,uiSubX,uiSubY,pulLU,pulRU,pulLB,pulRB);
    		pImPointer += uiSubX;              /* set pointer on next matrix */
    
    	}
    
    	//y为0,x为m_nGridX
    	uiSubX = m_nGridSize_X>>1; 
    	uiXL = m_nGridX - 1;
    	uiXR = uiXL;
    
    	pulLU = &m_pMapArray[m_nHistBins * uiXL];
    	pulRU = &m_pMapArray[m_nHistBins * uiXR];
    	pulLB = &m_pMapArray[m_nHistBins * uiXL];
    	pulRB = &m_pMapArray[m_nHistBins * uiXR];
    	LineInterpolate(pImPointer,uiSubX,uiSubY,pulLU,pulRU,pulLB,pulRB);
    	pImPointer += uiSubX;              /* set pointer on next matrix */
    	pImPointer += (uiSubY - 1) * m_nWidth;
    
    	for (y=1; y<m_nGridY; y++)
    	{
    		uiSubY = m_nGridSize_Y; 
    		uiYU = y - 1;
    		uiYB = uiYU + 1;
    
    		//x为0
    		uiSubX = m_nGridSize_X >> 1;
    		pulLU = &m_pMapArray[m_nHistBins * uiYU * m_nGridX];
    		pulRU = &m_pMapArray[m_nHistBins * uiYU * m_nGridX];
    		pulLB = &m_pMapArray[m_nHistBins * uiYB * m_nGridX];
    		pulRB = &m_pMapArray[m_nHistBins * uiYB * m_nGridX];
    		LineInterpolate(pImPointer,uiSubX,uiSubY,pulLU,pulRU,pulLB,pulRB);
    		pImPointer += uiSubX;              /* set pointer on next matrix */
    
    		for (x=1; x<m_nGridX; x++)
    		{
    			uiSubX = m_nGridSize_X; 
    			uiXL = x - 1;
    			uiXR = uiXL + 1;
    			pulLU = &m_pMapArray[m_nHistBins * (uiYU * m_nGridX + uiXL)];
    			pulRU = &m_pMapArray[m_nHistBins * (uiYU * m_nGridX  + uiXR)];
    			pulLB = &m_pMapArray[m_nHistBins * (uiYB * m_nGridX  + uiXL)];
    			pulRB = &m_pMapArray[m_nHistBins * (uiYB * m_nGridX  + uiXR)];
    			LineInterpolate(pImPointer,uiSubX,uiSubY,pulLU,pulRU,pulLB,pulRB);
    			pImPointer += uiSubX;              /* set pointer on next matrix */
    
    		}
    
    		uiSubX = m_nGridSize_X >> 1; 
    		uiXL = x - 1;
    		uiXR = uiXL;
    		pulLU = &m_pMapArray[m_nHistBins * (uiYU * m_nGridX + uiXL)];
    		pulRU = &m_pMapArray[m_nHistBins * (uiYU * m_nGridX  + uiXR)];
    		pulLB = &m_pMapArray[m_nHistBins * (uiYB * m_nGridX  + uiXL)];
    		pulRB = &m_pMapArray[m_nHistBins * (uiYB * m_nGridX  + uiXR)];
    		LineInterpolate(pImPointer,uiSubX,uiSubY,pulLU,pulRU,pulLB,pulRB);
    		pImPointer += uiSubX;              /* set pointer on next matrix */
    		pImPointer += (uiSubY - 1) * m_nWidth;
    		
    	}
    
    	//y为m_nGridY
    	uiSubY = m_nGridSize_Y >> 1;   
    	uiYU = m_nGridY-1;   
    	uiYB = uiYU;
    
    	//x为0
    	uiSubX = m_nGridSize_X >> 1;
    	pulLU = &m_pMapArray[m_nHistBins * uiYU * m_nGridX];
    	pulRU = &m_pMapArray[m_nHistBins * uiYU * m_nGridX];
    	pulLB = &m_pMapArray[m_nHistBins * uiYB * m_nGridX];
    	pulRB = &m_pMapArray[m_nHistBins * uiYB * m_nGridX];
    	LineInterpolate(pImPointer,uiSubX,uiSubY,pulLU,pulRU,pulLB,pulRB);
    	pImPointer += uiSubX;              /* set pointer on next matrix */
    
    	for (x=1; x<m_nGridX; x++)
    	{
    		uiSubX = m_nGridSize_X; 
    		uiXL = x - 1;
    		uiXR = uiXL + 1;
    		pulLU = &m_pMapArray[m_nHistBins * (uiYU * m_nGridX + uiXL)];
    		pulRU = &m_pMapArray[m_nHistBins * (uiYU * m_nGridX  + uiXR)];
    		pulLB = &m_pMapArray[m_nHistBins * (uiYB * m_nGridX  + uiXL)];
    		pulRB = &m_pMapArray[m_nHistBins * (uiYB * m_nGridX  + uiXR)];
    		LineInterpolate(pImPointer,uiSubX,uiSubY,pulLU,pulRU,pulLB,pulRB);
    		pImPointer += uiSubX;              /* set pointer on next matrix */
    
    	}
    
    	uiSubX = m_nGridSize_X >> 1; 
    	uiXL = x - 1;
    	uiXR = uiXL;
    	pulLU = &m_pMapArray[m_nHistBins * (uiYU * m_nGridX + uiXL)];
    	pulRU = &m_pMapArray[m_nHistBins * (uiYU * m_nGridX  + uiXR)];
    	pulLB = &m_pMapArray[m_nHistBins * (uiYB * m_nGridX  + uiXL)];
    	pulRB = &m_pMapArray[m_nHistBins * (uiYB * m_nGridX  + uiXR)];
    	LineInterpolate(pImPointer,uiSubX,uiSubY,pulLU,pulRU,pulLB,pulRB);
    	pImPointer += uiSubX;              /* set pointer on next matrix */
    	pImPointer += (uiSubY - 1) * m_nWidth;
    
    		
    }
    
    void CLAHE::ShiftInterpolate(unsigned char* pImage, int nSizeX, int nSizeY, int* pulMapLU,int* pulMapRU, int *pulMapLB, int *pulMapRB)
    {
    	const int	  uiIncr = m_nWidth-nSizeX;							/* Pointer increment after processing row */
    	unsigned char GreyValue; 
    	int			  uiNum = nSizeX*nSizeY;	
    	int			  x, y, nInvX, nInvY, uiShift = 0;
    	while (uiNum >>= 1) uiShift++; /* Calculate 2log of uiNum */
    	for (y=0,nInvY=nSizeY; y<nSizeY; y++,nInvY--,pImage+=uiIncr) 
    	{
    		for (x=0, nInvX=nSizeX; x<nSizeX; x++,nInvX--) 
    		{
    			GreyValue = m_aLUT[*pImage];      /* get histogram bin value */
    			*pImage++ = (unsigned char)((nInvY*(nInvX*pulMapLU[GreyValue] + x*pulMapRU[GreyValue])
    				+ y*(nInvX*pulMapLB[GreyValue] + x*pulMapRB[GreyValue])) >> uiShift);
    		}
    	}
    }
    
    
    int CLAHE::LineInterpolate(unsigned char* pImage, int nSizeX, int nSizeY, int* pulMapLU,int* pulMapRU, int* pulMapLB, int* pulMapRB)
    {
    	const int	  uiIncr = m_nWidth-nSizeX;							/* Pointer increment after processing row */
    	unsigned char GreyValue; 
    	int			  uiNum = nSizeX*nSizeY;	
    	int			  x, y, nInvX, nInvY, uiShift = 0;
    	for (y=0, nInvY=nSizeY; y<nSizeY; y++, nInvY--,pImage+=uiIncr)
    	{
    		for (x=0,nInvX=nSizeX; x<nSizeX; x++, nInvX--) 
    		{
    			GreyValue = m_aLUT[*pImage];           /* get histogram bin value */
    			*pImage++ = (unsigned char) (( nInvY*(nInvX*pulMapLU[GreyValue] + x*pulMapRU[GreyValue])
    				+ y * (nInvX*pulMapLB[GreyValue] + x*pulMapRB[GreyValue])) / uiNum);
    		}
    	}
    	return 0;
    }
    


    
    
    展开全文
  • CLAHE图像增强算法.docx

    2021-04-11 13:19:12
    CLAHE图像增强算法.docx
  • CLAHE算法学习

    千次阅读 2019-12-30 19:49:12
    图像识别工程开发中需要增强图像对比度,便于后续处理,接触到了CLAHE(Contrast Limited Adaptive Histogram Equalization),记录一下其中的学习过程。 1.直方图均衡 1.1灰度直方图 灰度图中像素值的分布为0-...

    0.前言

    图像识别工程开发中需要增强图像对比度,便于后续处理,接触到了CLAHE(Contrast Limited Adaptive Histogram Equalization),记录一下其中的学习过程。

    1.直方图均衡

    1.1灰度直方图

    灰度图中像素值的分布为0-255,以灰度值为横坐标,纵坐标为该灰度值对应的像素点数目/比例,则得到了灰度图像的直方图,体现的是图像中灰度的整体分布情况。

    OpenCV中提供了相应的计算函数calcHist(),可以得到相应分布范围的像素点数;将其绘制出来观察,更为直观。下图为某输入图像和其灰度直方图分布。

    一般性规律:对于灰度图像,以个人观察意愿将其分为前景和背景区域,前景是我们所感兴趣的区域,背景则反之。若前景和背景的灰度差异大,则易于人们观察,否则不易观察。一般来说若图像的灰度直方图集中在某个区域,其整体对比度较差,不易于我们区分前景和背景;反之若灰度直方图分布较为均匀,则整体对比度较好,易于我们区分前景和背景。

    1.2直方图均衡化(Histogram Equalization, HE)

    1)效果