转自:http://www.cnblogs.com/Imageshop/archive/2013/04/07/3006334.html

一、自适应直方图均衡化(Adaptive histgram equalization/AHE)

1.简述

自适应直方图均衡化(AHE)用来提升图像的对比度的一种计算机图像处理技术。和普通的直方图均衡算法不同,AHE算法通过计算图像的局部直方图,然后重新分布亮度来来改变图像对比度。因此,该算法更适合于改进图像的局部对比度以及获得更多的图像细节。

不过,AHE有过度放大图像中相同区域的噪音的问题,另外一种自适应的直方图均衡算法即限制对比度直方图均衡(CLAHE)算法能有限的限制这种不利的放大。

2. 算法的解释

普通的直方图均衡算法对于整幅图像的像素使用相同的直方图变换,对于那些像素值分布比较均衡的图像来说,算法的效果很好。然后,如果图像中包括明显比图像其它区域暗或者亮的部分,在这些部分的对比度将得不到有效的增强。

AHE算法通过对局部区域执行响应的直方图均衡来改变上述问题。该算法首先被开发出来适用于改进航天器驾驶舱的显示效果。其最简单的形式,就是每个像素通过其周边一个矩形范围内的像素的直方图进行均衡化。均衡的方式则完全同普通的均衡化算法:变换函数同像素周边的累积直方图函数(CDF)成比例。

图像边缘的像素需要特殊处理,因为边缘像素的领域不完全在图像内部。这个通过镜像图像边缘的行像素或列像素来解决。直接复制边缘的像素进行扩充是不合适的。因为这会导致带有剑锋的领域直方图。

3. AHE的属性

  • 领域的大小是该方法的一个参数。领域小,对比度得到增强,领域大,则对比度降低。
  • 当某个区域包含的像素值非常相似,其直方图就会尖状化,此时直方图的变换函数会将一个很窄范围内的像素映射到整个像素范围。这将使得某些平坦区域中的少量噪音经AHE处理后过度放大。

二、限制对比度自适应直方图均衡(Contrast Limited Adaptive histgram equalization/CLAHE)

  1.简述

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

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

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

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

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

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

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

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

CLAHE算法的源代码参考:

  1. /*
  2. * ANSI C code from the article
  3. * "Contrast Limited Adaptive Histogram Equalization"
  4. * by Karel Zuiderveld, karel@cv.ruu.nl
  5. * in "Graphics Gems IV", Academic Press, 1994
  6. *
  7. *
  8. * These functions implement Contrast Limited Adaptive Histogram Equalization.
  9. * The main routine (CLAHE) expects an input image that is stored contiguously in
  10. * memory; the CLAHE output image overwrites the original input image and has the
  11. * same minimum and maximum values (which must be provided by the user).
  12. * This implementation assumes that the X- and Y image resolutions are an integer
  13. * multiple of the X- and Y sizes of the contextual regions. A check on various other
  14. * error conditions is performed.
  15. *
  16. * #define the symbol BYTE_IMAGE to make this implementation suitable for
  17. * 8-bit images. The maximum number of contextual regions can be redefined
  18. * by changing uiMAX_REG_X and/or uiMAX_REG_Y; the use of more than 256
  19. * contextual regions is not recommended.
  20. *
  21. * The code is ANSI-C and is also C++ compliant.
  22. *
  23. * Author: Karel Zuiderveld, Computer Vision Research Group,
  24. * Utrecht, The Netherlands (karel@cv.ruu.nl)
  25. */
  26.  
  27. #ifdef BYTE_IMAGE
  28. typedef unsigned char kz_pixel_t; /* for 8 bit-per-pixel images */
  29. #define uiNR_OF_GREY (256)
  30. #else
  31. typedef unsigned short kz_pixel_t; /* for 12 bit-per-pixel images (default) */
  32. # define uiNR_OF_GREY ()
  33. #endif
  34.  
  35. /******** Prototype of CLAHE function. Put this in a separate include file. *****/
  36. int CLAHE(kz_pixel_t* pImage, unsigned int uiXRes, unsigned int uiYRes, kz_pixel_t Min,
  37. kz_pixel_t Max, unsigned int uiNrX, unsigned int uiNrY,
  38. unsigned int uiNrBins, float fCliplimit);
  39.  
  40. /*********************** Local prototypes ************************/
  41. static void ClipHistogram (unsigned long*, unsigned int, unsigned long);
  42. static void MakeHistogram (kz_pixel_t*, unsigned int, unsigned int, unsigned int,
  43. unsigned long*, unsigned int, kz_pixel_t*);
  44. static void MapHistogram (unsigned long*, kz_pixel_t, kz_pixel_t,
  45. unsigned int, unsigned long);
  46. static void MakeLut (kz_pixel_t*, kz_pixel_t, kz_pixel_t, unsigned int);
  47. static void Interpolate (kz_pixel_t*, int, unsigned long*, unsigned long*,
  48. unsigned long*, unsigned long*, unsigned int, unsigned int, kz_pixel_t*);
  49.  
  50. /************** Start of actual code **************/
  51. #include <stdlib.h> /* To get prototypes of malloc() and free() */
  52.  
  53. const unsigned int uiMAX_REG_X = ; /* max. # contextual regions in x-direction */
  54. const unsigned int uiMAX_REG_Y = ; /* max. # contextual regions in y-direction */
  55.  
  56. /************************** main function CLAHE ******************/
  57. int CLAHE (kz_pixel_t* pImage, unsigned int uiXRes, unsigned int uiYRes,
  58. kz_pixel_t Min, kz_pixel_t Max, unsigned int uiNrX, unsigned int uiNrY,
  59. unsigned int uiNrBins, float fCliplimit)
  60. /* pImage - Pointer to the input/output image
  61. * uiXRes - Image resolution in the X direction
  62. * uiYRes - Image resolution in the Y direction
  63. * Min - Minimum greyvalue of input image (also becomes minimum of output image)
  64. * Max - Maximum greyvalue of input image (also becomes maximum of output image)
  65. * uiNrX - Number of contextial regions in the X direction (min 2, max uiMAX_REG_X)
  66. * uiNrY - Number of contextial regions in the Y direction (min 2, max uiMAX_REG_Y)
  67. * uiNrBins - Number of greybins for histogram ("dynamic range")
  68. * float fCliplimit - Normalized cliplimit (higher values give more contrast)
  69. * The number of "effective" greylevels in the output image is set by uiNrBins; selecting
  70. * a small value (eg. 128) speeds up processing and still produce an output image of
  71. * good quality. The output image will have the same minimum and maximum value as the input
  72. * image. A clip limit smaller than 1 results in standard (non-contrast limited) AHE.
  73. */
  74. {
  75. unsigned int uiX, uiY; /* counters */
  76. unsigned int uiXSize, uiYSize, uiSubX, uiSubY; /* size of context. reg. and subimages */
  77. unsigned int uiXL, uiXR, uiYU, uiYB; /* auxiliary variables interpolation routine */
  78. unsigned long ulClipLimit, ulNrPixels;/* clip limit and region pixel count */
  79. kz_pixel_t* pImPointer; /* pointer to image */
  80. kz_pixel_t aLUT[uiNR_OF_GREY]; /* lookup table used for scaling of input image */
  81. unsigned long* pulHist, *pulMapArray; /* pointer to histogram and mappings*/
  82. unsigned long* pulLU, *pulLB, *pulRU, *pulRB; /* auxiliary pointers interpolation */
  83.  
  84. if (uiNrX > uiMAX_REG_X) return -; /* # of regions x-direction too large */
  85. if (uiNrY > uiMAX_REG_Y) return -; /* # of regions y-direction too large */
  86. if (uiXRes % uiNrX) return -; /* x-resolution no multiple of uiNrX */
  87. if (uiYRes & uiNrY) return -; /* y-resolution no multiple of uiNrY */
  88. if (Max >= uiNR_OF_GREY) return -; /* maximum too large */
  89. if (Min >= Max) return -; /* minimum equal or larger than maximum */
  90. if (uiNrX < || uiNrY < ) return -;/* at least 4 contextual regions required */
  91. if (fCliplimit == 1.0) return ; /* is OK, immediately returns original image. */
  92. if (uiNrBins == ) uiNrBins = ; /* default value when not specified */
  93.  
  94. pulMapArray=(unsigned long *)malloc(sizeof(unsigned long)*uiNrX*uiNrY*uiNrBins);
  95. if (pulMapArray == ) return -; /* Not enough memory! (try reducing uiNrBins) */
  96.  
  97. uiXSize = uiXRes/uiNrX; uiYSize = uiYRes/uiNrY; /* Actual size of contextual regions */
  98. ulNrPixels = (unsigned long)uiXSize * (unsigned long)uiYSize;
  99.  
  100. if(fCliplimit > 0.0) { /* Calculate actual cliplimit */
  101. ulClipLimit = (unsigned long) (fCliplimit * (uiXSize * uiYSize) / uiNrBins);
  102. ulClipLimit = (ulClipLimit < 1UL) ? 1UL : ulClipLimit;
  103. }
  104. else ulClipLimit = 1UL<<; /* Large value, do not clip (AHE) */
  105. MakeLut(aLUT, Min, Max, uiNrBins); /* Make lookup table for mapping of greyvalues */
  106. /* Calculate greylevel mappings for each contextual region */
  107. for (uiY = , pImPointer = pImage; uiY < uiNrY; uiY++) {
  108. for (uiX = ; uiX < uiNrX; uiX++, pImPointer += uiXSize) {
  109. pulHist = &pulMapArray[uiNrBins * (uiY * uiNrX + uiX)];
  110. MakeHistogram(pImPointer,uiXRes,uiXSize,uiYSize,pulHist,uiNrBins,aLUT);
  111. ClipHistogram(pulHist, uiNrBins, ulClipLimit);
  112. MapHistogram(pulHist, Min, Max, uiNrBins, ulNrPixels);
  113. }
  114. pImPointer += (uiYSize - ) * uiXRes; /* skip lines, set pointer */
  115. }
  116.  
  117. /* Interpolate greylevel mappings to get CLAHE image */
  118. for (pImPointer = pImage, uiY = ; uiY <= uiNrY; uiY++) {
  119. if (uiY == ) { /* special case: top row */
  120. uiSubY = uiYSize >> ; uiYU = ; uiYB = ;
  121. }
  122. else {
  123. if (uiY == uiNrY) { /* special case: bottom row */
  124. uiSubY = uiYSize >> ; uiYU = uiNrY-; uiYB = uiYU;
  125. }
  126. else { /* default values */
  127. uiSubY = uiYSize; uiYU = uiY - ; uiYB = uiYU + ;
  128. }
  129. }
  130. for (uiX = ; uiX <= uiNrX; uiX++) {
  131. if (uiX == ) { /* special case: left column */
  132. uiSubX = uiXSize >> ; uiXL = ; uiXR = ;
  133. }
  134. else {
  135. if (uiX == uiNrX) { /* special case: right column */
  136. uiSubX = uiXSize >> ; uiXL = uiNrX - ; uiXR = uiXL;
  137. }
  138. else { /* default values */
  139. uiSubX = uiXSize; uiXL = uiX - ; uiXR = uiXL + ;
  140. }
  141. }
  142.  
  143. pulLU = &pulMapArray[uiNrBins * (uiYU * uiNrX + uiXL)];
  144. pulRU = &pulMapArray[uiNrBins * (uiYU * uiNrX + uiXR)];
  145. pulLB = &pulMapArray[uiNrBins * (uiYB * uiNrX + uiXL)];
  146. pulRB = &pulMapArray[uiNrBins * (uiYB * uiNrX + uiXR)];
  147. Interpolate(pImPointer,uiXRes,pulLU,pulRU,pulLB,pulRB,uiSubX,uiSubY,aLUT);
  148. pImPointer += uiSubX; /* set pointer on next matrix */
  149. }
  150. pImPointer += (uiSubY - ) * uiXRes;
  151. }
  152. free(pulMapArray); /* free space for histograms */
  153. return ; /* return status OK */
  154. }
  155. void ClipHistogram (unsigned long* pulHistogram, unsigned int
  156. uiNrGreylevels, unsigned long ulClipLimit)
  157. /* This function performs clipping of the histogram and redistribution of bins.
  158. * The histogram is clipped and the number of excess pixels is counted. Afterwards
  159. * the excess pixels are equally redistributed across the whole histogram (providing
  160. * the bin count is smaller than the cliplimit).
  161. */
  162. {
  163. unsigned long* pulBinPointer, *pulEndPointer, *pulHisto;
  164. unsigned long ulNrExcess, ulUpper, ulBinIncr, ulStepSize, i;
  165. long lBinExcess;
  166.  
  167. ulNrExcess = ; pulBinPointer = pulHistogram;
  168. for (i = ; i < uiNrGreylevels; i++) { /* calculate total number of excess pixels */
  169. lBinExcess = (long) pulBinPointer[i] - (long) ulClipLimit;
  170. if (lBinExcess > ) ulNrExcess += lBinExcess; /* excess in current bin */
  171. };
  172.  
  173. /* Second part: clip histogram and redistribute excess pixels in each bin */
  174. ulBinIncr = ulNrExcess / uiNrGreylevels; /* average binincrement */
  175. ulUpper = ulClipLimit - ulBinIncr; /* Bins larger than ulUpper set to cliplimit */
  176.  
  177. for (i = ; i < uiNrGreylevels; i++) {
  178. if (pulHistogram[i] > ulClipLimit) pulHistogram[i] = ulClipLimit; /* clip bin */
  179. else {
  180. if (pulHistogram[i] > ulUpper) { /* high bin count */
  181. ulNrExcess -= pulHistogram[i] - ulUpper; pulHistogram[i]=ulClipLimit;
  182. }
  183. else { /* low bin count */
  184. ulNrExcess -= ulBinIncr; pulHistogram[i] += ulBinIncr;
  185. }
  186. }
  187. }
  188.  
  189. while (ulNrExcess) { /* Redistribute remaining excess */
  190. pulEndPointer = &pulHistogram[uiNrGreylevels]; pulHisto = pulHistogram;
  191.  
  192. while (ulNrExcess && pulHisto < pulEndPointer) {
  193. ulStepSize = uiNrGreylevels / ulNrExcess;
  194. if (ulStepSize < ) ulStepSize = ; /* stepsize at least 1 */
  195. for (pulBinPointer=pulHisto; pulBinPointer < pulEndPointer && ulNrExcess;
  196. pulBinPointer += ulStepSize) {
  197. if (*pulBinPointer < ulClipLimit) {
  198. (*pulBinPointer)++; ulNrExcess--; /* reduce excess */
  199. }
  200. }
  201. pulHisto++; /* restart redistributing on other bin location */
  202. }
  203. }
  204. }
  205. void MakeHistogram (kz_pixel_t* pImage, unsigned int uiXRes,
  206. unsigned int uiSizeX, unsigned int uiSizeY,
  207. unsigned long* pulHistogram,
  208. unsigned int uiNrGreylevels, kz_pixel_t* pLookupTable)
  209. /* This function classifies the greylevels present in the array image into
  210. * a greylevel histogram. The pLookupTable specifies the relationship
  211. * between the greyvalue of the pixel (typically between 0 and 4095) and
  212. * the corresponding bin in the histogram (usually containing only 128 bins).
  213. */
  214. {
  215. kz_pixel_t* pImagePointer;
  216. unsigned int i;
  217.  
  218. for (i = ; i < uiNrGreylevels; i++) pulHistogram[i] = 0L; /* clear histogram */
  219.  
  220. for (i = ; i < uiSizeY; i++) {
  221. pImagePointer = &pImage[uiSizeX];
  222. while (pImage < pImagePointer) pulHistogram[pLookupTable[*pImage++]]++;
  223. pImagePointer += uiXRes;
  224. pImage = &pImagePointer[-uiSizeX];
  225. }
  226. }
  227.  
  228. void MapHistogram (unsigned long* pulHistogram, kz_pixel_t Min, kz_pixel_t Max,
  229. unsigned int uiNrGreylevels, unsigned long ulNrOfPixels)
  230. /* This function calculates the equalized lookup table (mapping) by
  231. * cumulating the input histogram. Note: lookup table is rescaled in range [Min..Max].
  232. */
  233. {
  234. unsigned int i; unsigned long ulSum = ;
  235. const float fScale = ((float)(Max - Min)) / ulNrOfPixels;
  236. const unsigned long ulMin = (unsigned long) Min;
  237.  
  238. for (i = ; i < uiNrGreylevels; i++) {
  239. ulSum += pulHistogram[i]; pulHistogram[i]=(unsigned long)(ulMin+ulSum*fScale);
  240. if (pulHistogram[i] > Max) pulHistogram[i] = Max;
  241. }
  242. }
  243.  
  244. void MakeLut (kz_pixel_t * pLUT, kz_pixel_t Min, kz_pixel_t Max, unsigned int uiNrBins)
  245. /* To speed up histogram clipping, the input image [Min,Max] is scaled down to
  246. * [0,uiNrBins-1]. This function calculates the LUT.
  247. */
  248. {
  249. int i;
  250. const kz_pixel_t BinSize = (kz_pixel_t) ( + (Max - Min) / uiNrBins);
  251.  
  252. for (i = Min; i <= Max; i++) pLUT[i] = (i - Min) / BinSize;
  253. }
  254.  
  255. void Interpolate (kz_pixel_t * pImage, int uiXRes, unsigned long * pulMapLU,
  256. unsigned long * pulMapRU, unsigned long * pulMapLB, unsigned long * pulMapRB,
  257. unsigned int uiXSize, unsigned int uiYSize, kz_pixel_t * pLUT)
  258. /* pImage - pointer to input/output image
  259. * uiXRes - resolution of image in x-direction
  260. * pulMap* - mappings of greylevels from histograms
  261. * uiXSize - uiXSize of image submatrix
  262. * uiYSize - uiYSize of image submatrix
  263. * pLUT - lookup table containing mapping greyvalues to bins
  264. * This function calculates the new greylevel assignments of pixels within a submatrix
  265. * of the image with size uiXSize and uiYSize. This is done by a bilinear interpolation
  266. * between four different mappings in order to eliminate boundary artifacts.
  267. * It uses a division; since division is often an expensive operation, I added code to
  268. * perform a logical shift instead when feasible.
  269. */
  270. {
  271. const unsigned int uiIncr = uiXRes-uiXSize; /* Pointer increment after processing row */
  272. kz_pixel_t GreyValue; unsigned int uiNum = uiXSize*uiYSize; /* Normalization factor */
  273.  
  274. unsigned int uiXCoef, uiYCoef, uiXInvCoef, uiYInvCoef, uiShift = ;
  275.  
  276. if (uiNum & (uiNum - )) /* If uiNum is not a power of two, use division */
  277. for (uiYCoef = , uiYInvCoef = uiYSize; uiYCoef < uiYSize;
  278. uiYCoef++, uiYInvCoef--,pImage+=uiIncr) {
  279. for (uiXCoef = , uiXInvCoef = uiXSize; uiXCoef < uiXSize;
  280. uiXCoef++, uiXInvCoef--) {
  281. GreyValue = pLUT[*pImage]; /* get histogram bin value */
  282. *pImage++ = (kz_pixel_t ) ((uiYInvCoef * (uiXInvCoef*pulMapLU[GreyValue]
  283. + uiXCoef * pulMapRU[GreyValue])
  284. + uiYCoef * (uiXInvCoef * pulMapLB[GreyValue]
  285. + uiXCoef * pulMapRB[GreyValue])) / uiNum);
  286. }
  287. }
  288. else { /* avoid the division and use a right shift instead */
  289. while (uiNum >>= ) uiShift++; /* Calculate 2log of uiNum */
  290. for (uiYCoef = , uiYInvCoef = uiYSize; uiYCoef < uiYSize;
  291. uiYCoef++, uiYInvCoef--,pImage+=uiIncr) {
  292. for (uiXCoef = , uiXInvCoef = uiXSize; uiXCoef < uiXSize;
  293. uiXCoef++, uiXInvCoef--) {
  294. GreyValue = pLUT[*pImage]; /* get histogram bin value */
  295. *pImage++ = (kz_pixel_t)((uiYInvCoef* (uiXInvCoef * pulMapLU[GreyValue]
  296. + uiXCoef * pulMapRU[GreyValue])
  297. + uiYCoef * (uiXInvCoef * pulMapLB[GreyValue]
  298. + uiXCoef * pulMapRB[GreyValue])) >> uiShift);
  299. }
  300. }
  301. }
  302. }

上面的代码中对于各块之间的插值部分的编码技巧很值得学习和参考。

以上描述均翻译自:http://en.wikipedia.org/wiki/CLAHE#Contrast_Limited_AHE

Karel Zuiderveld提供的代码:

  1. if (pulHistogram[i] > ulUpper)
  2. { /* high bin count */
  3. ulNrExcess -= (pulHistogram[i] - ulUpper); pulHistogram[i]=ulClipLimit;
  4. }

应该修正为:

  1. if (pulHistogram[i] > ulUpper)
  2. { /* high bin count */
  3. ulNrExcess -= (ulClipLimit -pulHistogram[i]); pulHistogram[i]=ulClipLimit;
  4. }

同时,各位也可以参考下matlab的adapthisteq.m文件,该文件的代码基本是严格按照 Karel Zuiderveld作者的原文写的,贴出如下:

  1. function out = adapthisteq(varargin)
  2. %ADAPTHISTEQ Contrast-limited Adaptive Histogram Equalization (CLAHE).
  3. % ADAPTHISTEQ enhances the contrast of images by transforming the
  4. % values in the intensity image I. Unlike HISTEQ, it operates on small
  5. % data regions (tiles), rather than the entire image. Each tile's
  6. % contrast is enhanced, so that the histogram of the output region
  7. % approximately matches the specified histogram. The neighboring tiles
  8. % are then combined using bilinear interpolation in order to eliminate
  9. % artificially induced boundaries. The contrast, especially
  10. % in homogeneous areas, can be limited in order to avoid amplifying the
  11. % noise which might be present in the image.
  12. %
  13. % J = ADAPTHISTEQ(I) Performs CLAHE on the intensity image I.
  14. %
  15. % J = ADAPTHISTEQ(I,PARAM1,VAL1,PARAM2,VAL2...) sets various parameters.
  16. % Parameter names can be abbreviated, and case does not matter. Each
  17. % string parameter is followed by a value as indicated below:
  18. %
  19. % 'NumTiles' Two-element vector of positive integers: [M N].
  20. % [M N] specifies the number of tile rows and
  21. % columns. Both M and N must be at least .
  22. % The total number of image tiles is equal to M*N.
  23. %
  24. % Default: [ ].
  25. %
  26. % 'ClipLimit' Real scalar from to .
  27. % 'ClipLimit' limits contrast enhancement. Higher numbers
  28. % result in more contrast.
  29. %
  30. % Default: 0.01.
  31. %
  32. % 'NBins' Positive integer scalar.
  33. % Sets number of bins for the histogram used in building a
  34. % contrast enhancing transformation. Higher values result
  35. % in greater dynamic range at the cost of slower processing
  36. % speed.
  37. %
  38. % Default: .
  39. %
  40. % 'Range' One of the strings: 'original' or 'full'.
  41. % Controls the range of the output image data. If 'Range'
  42. % is set to 'original', the range is limited to
  43. % [min(I(:)) max(I(:))]. Otherwise, by default, or when
  44. % 'Range' is set to 'full', the full range of the output
  45. % image class is used (e.g. [ ] for uint8).
  46. %
  47. % Default: 'full'.
  48. %
  49. % 'Distribution' Distribution can be one of three strings: 'uniform',
  50. % 'rayleigh', 'exponential'.
  51. % Sets desired histogram shape for the image tiles, by
  52. % specifying a distribution type.
  53. %
  54. % Default: 'uniform'.
  55. %
  56. % 'Alpha' Nonnegative real scalar.
  57. % 'Alpha' is a distribution parameter, which can be supplied
  58. % when 'Dist' is set to either 'rayleigh' or 'exponential'.
  59. %
  60. % Default: 0.4.
  61. %
  62. % Notes
  63. % -----
  64. % - 'NumTiles' specify the number of rectangular contextual regions (tiles)
  65. % into which the image is divided. The contrast transform function is
  66. % calculated for each of these regions individually. The optimal number of
  67. % tiles depends on the type of the input image, and it is best determined
  68. % through experimentation.
  69. %
  70. % - The 'ClipLimit' is a contrast factor that prevents over-saturation of the
  71. % image specifically in homogeneous areas. These areas are characterized
  72. % by a high peak in the histogram of the particular image tile due to many
  73. % pixels falling inside the same gray level range. Without the clip limit,
  74. % the adaptive histogram equalization technique could produce results that,
  75. % in some cases, are worse than the original image.
  76. %
  77. % - ADAPTHISTEQ can use Uniform, Rayleigh, or Exponential distribution as
  78. % the basis for creating the contrast transform function. The distribution
  79. % that should be used depends on the type of the input image.
  80. % For example, underwater imagery appears to look more natural when the
  81. % Rayleigh distribution is used.
  82. %
  83. % Class Support
  84. % -------------
  85. % Intensity image I can be uint8, uint16, int16, double, or single.
  86. % The output image J has the same class as I.
  87. %
  88. % Example
  89. % ---------
  90. % Apply Contrast-Limited Adaptive Histogram Equalization to an
  91. % image and display the results.
  92. %
  93. % I = imread('tire.tif');
  94. % A = adapthisteq(I,'clipLimit',0.02,'Distribution','rayleigh');
  95. % figure, imshow(I);
  96. % figure, imshow(A);
  97. %
  98. % Example
  99. % ---------
  100. %
  101. % Apply Contrast-Limited Adaptive Histogram Equalization to a color
  102. % photograph.
  103. %
  104. % [X MAP] = imread('shadow.tif');
  105. % RGB = ind2rgb(X,MAP); % convert indexed image to truecolor format
  106. % cform2lab = makecform('srgb2lab');
  107. % LAB = applycform(RGB, cform2lab); %convert image to L*a*b color space
  108. % L = LAB(:,:,)/; % scale the values to range from to
  109. % LAB(:,:,) = adapthisteq(L,'NumTiles',[ ],'ClipLimit',0.005)*;
  110. % cform2srgb = makecform('lab2srgb');
  111. % J = applycform(LAB, cform2srgb); %convert back to RGB
  112. % figure, imshow(RGB); %display the results
  113. % figure, imshow(J);
  114. %
  115. % See also HISTEQ.
  116.  
  117. % Copyright - The MathWorks, Inc.
  118. % $Revision: 1.1.6.12 $ $Date: // :: $
  119.  
  120. % References:
  121. % Karel Zuiderveld, "Contrast Limited Adaptive Histogram Equalization",
  122. % Graphics Gems IV, p. -, code: p. -
  123. %
  124. % Hanumant Singh, Woods Hole Oceanographic Institution, personal
  125. % communication
  126.  
  127. %--------------------------- The algorithm ----------------------------------
  128. %
  129. % . Obtain all the inputs:
  130. % * image
  131. % * number of regions in row and column directions
  132. % * number of bins for the histograms used in building image transform
  133. % function (dynamic range)
  134. % * clip limit for contrast limiting (normalized from to )
  135. % * other miscellaneous options
  136. % . Pre-process the inputs:
  137. % * determine real clip limit from the normalized value
  138. % * if necessary, pad the image before splitting it into regions
  139. % . Process each contextual region (tile) thus producing gray level mappings
  140. % * extract a single image region
  141. % * make a histogram for this region using the specified number of bins
  142. % * clip the histogram using clip limit
  143. % * create a mapping (transformation function) for this region
  144. % . Interpolate gray level mappings in order to assemble final CLAHE image
  145. % * extract cluster of four neighboring mapping functions
  146. % * process image region partly overlapping each of the mapping tiles
  147. % * extract a single pixel, apply four mappings to that pixel, and
  148. % interpolate between the results to obtain the output pixel; repeat
  149. % over the entire image
  150. %
  151. % See code for further details.
  152. %
  153. %-----------------------------------------------------------------------------
  154.  
  155. [I, selectedRange, fullRange, numTiles, dimTile, clipLimit, numBins, ...
  156. noPadRect, distribution, alpha, int16ClassChange] = parseInputs(varargin{:});
  157.  
  158. tileMappings = makeTileMappings(I, numTiles, dimTile, numBins, clipLimit, ...
  159. selectedRange, fullRange, distribution, alpha);
  160.  
  161. %Synthesize the output image based on the individual tile mappings.
  162. out = makeClaheImage(I, tileMappings, numTiles, selectedRange, numBins,...
  163. dimTile);
  164.  
  165. if int16ClassChange
  166. % Change uint16 back to int16 so output has same class as input.
  167. out = uint16toint16(out);
  168. end
  169.  
  170. if ~isempty(noPadRect) %do we need to remove padding?
  171. out = out(noPadRect.ulRow:noPadRect.lrRow, ...
  172. noPadRect.ulCol:noPadRect.lrCol);
  173. end
  174.  
  175. %-----------------------------------------------------------------------------
  176.  
  177. function tileMappings = ...
  178. makeTileMappings(I, numTiles, dimTile, numBins, clipLimit,...
  179. selectedRange, fullRange, distribution, alpha)
  180.  
  181. numPixInTile = prod(dimTile);
  182.  
  183. tileMappings = cell(numTiles);
  184.  
  185. % extract and process each tile
  186. imgCol = ;
  187. for col=:numTiles(),
  188. imgRow = ;
  189. for row=:numTiles(),
  190.  
  191. tile = I(imgRow:imgRow+dimTile()-,imgCol:imgCol+dimTile()-);
  192.  
  193. % for speed, call MEX file directly thus avoiding costly
  194. % input parsing of imhist
  195. tileHist = imhistc(tile, numBins, , fullRange());
  196.  
  197. tileHist = clipHistogram(tileHist, clipLimit, numBins);
  198.  
  199. tileMapping = makeMapping(tileHist, selectedRange, fullRange, ...
  200. numPixInTile, distribution, alpha);
  201.  
  202. % assemble individual tile mappings by storing them in a cell array;
  203. tileMappings{row,col} = tileMapping;
  204.  
  205. imgRow = imgRow + dimTile();
  206. end
  207. imgCol = imgCol + dimTile(); % move to the next column of tiles
  208. end
  209.  
  210. %-----------------------------------------------------------------------------
  211. % Calculate the equalized lookup table (mapping) based on cumulating the input
  212. % histogram. Note: lookup table is rescaled in the selectedRange [Min..Max].
  213.  
  214. function mapping = makeMapping(imgHist, selectedRange, fullRange, ...
  215. numPixInTile, distribution, alpha)
  216.  
  217. histSum = cumsum(imgHist);
  218. valSpread = selectedRange() - selectedRange();
  219.  
  220. switch distribution
  221. case 'uniform',
  222. scale = valSpread/numPixInTile;
  223. mapping = min(selectedRange() + histSum*scale,...
  224. selectedRange()); %limit to max
  225.  
  226. case 'rayleigh', % suitable for underwater imagery
  227. % pdf = (t./alpha^).*exp(-t.^/(*alpha^))*U(t)
  228. % cdf = -exp(-t.^./(*alpha^))
  229. hconst = *alpha^;
  230. vmax = - exp(-/hconst);
  231. val = vmax*(histSum/numPixInTile);
  232. val(val>=) = -eps; % avoid log()
  233. temp = sqrt(-hconst*log(-val));
  234. mapping = min(selectedRange()+temp*valSpread,...
  235. selectedRange()); %limit to max
  236.  
  237. case 'exponential',
  238. % pdf = alpha*exp(-alpha*t)*U(t)
  239. % cdf = -exp(-alpha*t)
  240. vmax = - exp(-alpha);
  241. val = (vmax*histSum/numPixInTile);
  242. val(val>=) = -eps;
  243. temp = -/alpha*log(-val);
  244. mapping = min(selectedRange()+temp*valSpread,selectedRange());
  245.  
  246. otherwise,
  247. error(message('images:adapthisteq:distributionType')) %should never get here
  248.  
  249. end
  250.  
  251. %rescale the result to be between and for later use by the GRAYXFORM
  252. %private mex function
  253. mapping = mapping/fullRange();
  254.  
  255. %-----------------------------------------------------------------------------
  256. % This function clips the histogram according to the clipLimit and
  257. % redistributes clipped pixels across bins below the clipLimit
  258.  
  259. function imgHist = clipHistogram(imgHist, clipLimit, numBins)
  260.  
  261. % total number of pixels overflowing clip limit in each bin
  262. totalExcess = sum(max(imgHist - clipLimit,));
  263.  
  264. % clip the histogram and redistribute the excess pixels in each bin
  265. avgBinIncr = floor(totalExcess/numBins);
  266. upperLimit = clipLimit - avgBinIncr; % bins larger than this will be
  267. % set to clipLimit
  268.  
  269. % this loop should speed up the operation by putting multiple pixels
  270. % into the "obvious" places first
  271. for k=:numBins
  272. if imgHist(k) > clipLimit
  273. imgHist(k) = clipLimit;
  274. else
  275. if imgHist(k) > upperLimit % high bin count
  276. totalExcess = totalExcess - (clipLimit - imgHist(k));
  277. imgHist(k) = clipLimit;
  278. else
  279. totalExcess = totalExcess - avgBinIncr;
  280. imgHist(k) = imgHist(k) + avgBinIncr;
  281. end
  282. end
  283. end
  284.  
  285. % this loops redistributes the remaining pixels, one pixel at a time
  286. k = ;
  287. while (totalExcess ~= )
  288. %keep increasing the step as fewer and fewer pixels remain for
  289. %the redistribution (spread them evenly)
  290. stepSize = max(floor(numBins/totalExcess),);
  291. for m=k:stepSize:numBins
  292. if imgHist(m) < clipLimit
  293. imgHist(m) = imgHist(m)+;
  294. totalExcess = totalExcess - ; %reduce excess
  295. if totalExcess ==
  296. break;
  297. end
  298. end
  299. end
  300.  
  301. k = k+; %prevent from always placing the pixels in bin #
  302. if k > numBins % start over if numBins was reached
  303. k = ;
  304. end
  305. end
  306.  
  307. %-----------------------------------------------------------------------------
  308. % This function interpolates between neighboring tile mappings to produce a
  309. % new mapping in order to remove artificially induced tile borders.
  310. % Otherwise, these borders would become quite visible. The resulting
  311. % mapping is applied to the input image thus producing a CLAHE processed
  312. % image.
  313.  
  314. function claheI = makeClaheImage(I, tileMappings, numTiles, selectedRange,...
  315. numBins, dimTile)
  316.  
  317. %initialize the output image to zeros (preserve the class of the input image)
  318. claheI = I;
  319. claheI(:) = ;
  320.  
  321. %compute the LUT for looking up original image values in the tile mappings,
  322. %which we created earlier
  323. if ~(isa(I,'double') || isa(I,'single'))
  324. k = selectedRange()+ : selectedRange()+;
  325. aLut = zeros(length(k),);
  326. aLut(k) = (k-)-selectedRange();
  327. aLut = aLut/(selectedRange()-selectedRange());
  328. else
  329. % remap from .. to ..numBins-
  330. if numBins ~=
  331. binStep = /(numBins-);
  332. start = ceil(selectedRange()/binStep);
  333. stop = floor(selectedRange()/binStep);
  334. k = start+:stop+;
  335. aLut(k) = :/(length(k)-):;
  336. else
  337. aLut() = ; %in case someone specifies numBins = , which is just silly
  338. end
  339. end
  340.  
  341. imgTileRow=;
  342. for k=:numTiles()+
  343. if k == %special case: top row
  344. imgTileNumRows = dimTile()/; %always divisible by because of padding
  345. mapTileRows = [ ];
  346. else
  347. if k == numTiles()+ %special case: bottom row
  348. imgTileNumRows = dimTile()/;
  349. mapTileRows = [numTiles() numTiles()];
  350. else %default values
  351. imgTileNumRows = dimTile();
  352. mapTileRows = [k-, k]; %[upperRow lowerRow]
  353. end
  354. end
  355.  
  356. % loop over columns of the tileMappings cell array
  357. imgTileCol=;
  358. for l=:numTiles()+
  359. if l == %special case: left column
  360. imgTileNumCols = dimTile()/;
  361. mapTileCols = [, ];
  362. else
  363. if l == numTiles()+ % special case: right column
  364. imgTileNumCols = dimTile()/;
  365. mapTileCols = [numTiles(), numTiles()];
  366. else %default values
  367. imgTileNumCols = dimTile();
  368. mapTileCols = [l-, l]; % right left
  369. end
  370. end
  371.  
  372. % Extract four tile mappings
  373. ulMapTile = tileMappings{mapTileRows(), mapTileCols()};
  374. urMapTile = tileMappings{mapTileRows(), mapTileCols()};
  375. blMapTile = tileMappings{mapTileRows(), mapTileCols()};
  376. brMapTile = tileMappings{mapTileRows(), mapTileCols()};
  377.  
  378. % Calculate the new greylevel assignments of pixels
  379. % within a submatrix of the image specified by imgTileIdx. This
  380. % is done by a bilinear interpolation between four different mappings
  381. % in order to eliminate boundary artifacts.
  382.  
  383. normFactor = imgTileNumRows*imgTileNumCols; %normalization factor
  384. imgTileIdx = {imgTileRow:imgTileRow+imgTileNumRows-, ...
  385. imgTileCol:imgTileCol+imgTileNumCols-};
  386.  
  387. imgPixVals = grayxform(I(imgTileIdx{},imgTileIdx{}), aLut);
  388.  
  389. % calculate the weights used for linear interpolation between the
  390. % four mappings
  391. rowW = repmat((:imgTileNumRows-)',1,imgTileNumCols);
  392. colW = repmat(:imgTileNumCols-,imgTileNumRows,);
  393. rowRevW = repmat((imgTileNumRows:-:)',1,imgTileNumCols);
  394. colRevW = repmat(imgTileNumCols:-:,imgTileNumRows,);
  395.  
  396. claheI(imgTileIdx{}, imgTileIdx{}) = ...
  397. (rowRevW .* (colRevW .* double(grayxform(imgPixVals,ulMapTile)) + ...
  398. colW .* double(grayxform(imgPixVals,urMapTile)))+ ...
  399. rowW .* (colRevW .* double(grayxform(imgPixVals,blMapTile)) + ...
  400. colW .* double(grayxform(imgPixVals,brMapTile))))...
  401. /normFactor;
  402.  
  403. imgTileCol = imgTileCol + imgTileNumCols;
  404. end %over tile cols
  405. imgTileRow = imgTileRow + imgTileNumRows;
  406. end %over tile rows
  407.  
  408. %-----------------------------------------------------------------------------
  409.  
  410. function [I, selectedRange, fullRange, numTiles, dimTile, clipLimit,...
  411. numBins, noPadRect, distribution, alpha, ...
  412. int16ClassChange] = parseInputs(varargin)
  413.  
  414. narginchk(,);
  415.  
  416. I = varargin{};
  417. validateattributes(I, {'uint8', 'uint16', 'double', 'int16', 'single'}, ...
  418. {'real', '2d', 'nonsparse', 'nonempty'}, ...
  419. mfilename, 'I', );
  420.  
  421. % convert int16 to uint16
  422. if isa(I,'int16')
  423. I = int16touint16(I);
  424. int16ClassChange = true;
  425. else
  426. int16ClassChange = false;
  427. end
  428.  
  429. if any(size(I) < )
  430. error(message('images:adapthisteq:inputImageTooSmall'))
  431. end
  432.  
  433. %Other options
  434. %%%%%%%%%%%%%%
  435.  
  436. %Set the defaults
  437. distribution = 'uniform';
  438. alpha = 0.4;
  439.  
  440. if isa(I, 'double') || isa(I,'single')
  441. fullRange = [ ];
  442. else
  443. fullRange() = I(); %copy class of the input image
  444. fullRange(:) = [-Inf Inf]; %will be clipped to min and max
  445. fullRange = double(fullRange);
  446. end
  447.  
  448. selectedRange = fullRange;
  449.  
  450. %Set the default to bins regardless of the data type;
  451. %the user can override this value at any time
  452. numBins = ;
  453. normClipLimit = 0.01;
  454. numTiles = [ ];
  455.  
  456. checkAlpha = false;
  457.  
  458. validStrings = {'NumTiles','ClipLimit','NBins','Distribution',...
  459. 'Alpha','Range'};
  460.  
  461. if nargin >
  462. done = false;
  463.  
  464. idx = ;
  465. while ~done
  466. input = varargin{idx};
  467. inputStr = validatestring(input, validStrings,mfilename,'PARAM',idx);
  468.  
  469. idx = idx+; %advance index to point to the VAL portion of the input
  470.  
  471. if idx > nargin
  472. error(message('images:adapthisteq:missingValue', inputStr))
  473. end
  474.  
  475. switch inputStr
  476.  
  477. case 'NumTiles'
  478. numTiles = varargin{idx};
  479. validateattributes(numTiles, {'double'}, {'real', 'vector', ...
  480. 'integer', 'finite','positive'},...
  481. mfilename, inputStr, idx);
  482.  
  483. if (any(size(numTiles) ~= [,]))
  484. error(message('images:adapthisteq:invalidNumTilesVector', inputStr))
  485. end
  486.  
  487. if any(numTiles < )
  488. error(message('images:adapthisteq:invalidNumTilesValue', inputStr))
  489. end
  490.  
  491. case 'ClipLimit'
  492. normClipLimit = varargin{idx};
  493. validateattributes(normClipLimit, {'double'}, ...
  494. {'scalar','real','nonnegative'},...
  495. mfilename, inputStr, idx);
  496.  
  497. if normClipLimit >
  498. error(message('images:adapthisteq:invalidClipLimit', inputStr))
  499. end
  500.  
  501. case 'NBins'
  502. numBins = varargin{idx};
  503. validateattributes(numBins, {'double'}, {'scalar','real','integer',...
  504. 'positive'}, mfilename, 'NBins', idx);
  505.  
  506. case 'Distribution'
  507. validDist = {'rayleigh','exponential','uniform'};
  508. distribution = validatestring(varargin{idx}, validDist, mfilename,...
  509. 'Distribution', idx);
  510.  
  511. case 'Alpha'
  512. alpha = varargin{idx};
  513. validateattributes(alpha, {'double'},{'scalar','real',...
  514. 'nonnan','positive','finite'},...
  515. mfilename, 'Alpha',idx);
  516. checkAlpha = true;
  517.  
  518. case 'Range'
  519. validRangeStrings = {'original','full'};
  520. rangeStr = validatestring(varargin{idx}, validRangeStrings,mfilename,...
  521. 'Range',idx);
  522.  
  523. if strmatch(rangeStr,'original')
  524. selectedRange = double([min(I(:)), max(I(:))]);
  525. end
  526.  
  527. otherwise
  528. error(message('images:adapthisteq:inputString')) %should never get here
  529. end
  530.  
  531. if idx >= nargin
  532. done = true;
  533. end
  534.  
  535. idx=idx+;
  536. end
  537. end
  538.  
  539. %% Pre-process the inputs
  540. %%%%%%%%%%%%%%%%%%%%%%%%%%
  541.  
  542. dimI = size(I);
  543. dimTile = dimI ./ numTiles;
  544.  
  545. %check if tile size is reasonable
  546. if any(dimTile < )
  547. error(message('images:adapthisteq:inputImageTooSmallToSplit', num2str( numTiles )))
  548. end
  549.  
  550. if checkAlpha
  551. if strcmp(distribution,'uniform')
  552. error(message('images:adapthisteq:alphaShouldNotBeSpecified', distribution))
  553. end
  554. end
  555.  
  556. %check if the image needs to be padded; pad if necessary;
  557. %padding occurs if any dimension of a single tile is an odd number
  558. %and/or when image dimensions are not divisible by the selected
  559. %number of tiles
  560. rowDiv = mod(dimI(),numTiles()) == ;
  561. colDiv = mod(dimI(),numTiles()) == ;
  562.  
  563. if rowDiv && colDiv
  564. rowEven = mod(dimTile(),) == ;
  565. colEven = mod(dimTile(),) == ;
  566. end
  567.  
  568. noPadRect = [];
  569. if ~(rowDiv && colDiv && rowEven && colEven)
  570. padRow = ;
  571. padCol = ;
  572.  
  573. if ~rowDiv
  574. rowTileDim = floor(dimI()/numTiles()) + ;
  575. padRow = rowTileDim*numTiles() - dimI();
  576. else
  577. rowTileDim = dimI()/numTiles();
  578. end
  579.  
  580. if ~colDiv
  581. colTileDim = floor(dimI()/numTiles()) + ;
  582. padCol = colTileDim*numTiles() - dimI();
  583. else
  584. colTileDim = dimI()/numTiles();
  585. end
  586.  
  587. %check if tile dimensions are even numbers
  588. rowEven = mod(rowTileDim,) == ;
  589. colEven = mod(colTileDim,) == ;
  590.  
  591. if ~rowEven
  592. padRow = padRow+numTiles();
  593. end
  594.  
  595. if ~colEven
  596. padCol = padCol+numTiles();
  597. end
  598.  
  599. padRowPre = floor(padRow/);
  600. padRowPost = ceil(padRow/);
  601. padColPre = floor(padCol/);
  602. padColPost = ceil(padCol/);
  603.  
  604. I = padarray(I,[padRowPre padColPre ],'symmetric','pre');
  605. I = padarray(I,[padRowPost padColPost],'symmetric','post');
  606.  
  607. %UL corner (Row, Col), LR corner (Row, Col)
  608. noPadRect.ulRow = padRowPre+;
  609. noPadRect.ulCol = padColPre+;
  610. noPadRect.lrRow = padRowPre+dimI();
  611. noPadRect.lrCol = padColPre+dimI();
  612. end
  613.  
  614. %redefine this variable to include the padding
  615. dimI = size(I);
  616.  
  617. %size of the single tile
  618. dimTile = dimI ./ numTiles;
  619.  
  620. %compute actual clip limit from the normalized value entered by the user
  621. %maximum value of normClipLimit= results in standard AHE, i.e. no clipping;
  622. %the minimum value minClipLimit would uniformly distribute the image pixels
  623. %across the entire histogram, which would result in the lowest possible
  624. %contrast value
  625. numPixInTile = prod(dimTile);
  626. minClipLimit = ceil(numPixInTile/numBins);
  627. clipLimit = minClipLimit + round(normClipLimit*(numPixInTile-minClipLimit));
  628.  
  629. %-----------------------------------------------------------------------------

参考上述代码,作者分别用VB和C#实现了该算法,提供个编译好的文件给有兴趣研究该算法的朋友看看效果(不提供源代码的):

http://files.cnblogs.com/Imageshop/CLAHE.rar

C#示例代码下载:http://files.cnblogs.com/Imageshop/AdaptHistEqualizeTest.rar

感谢作者!

限制对比度自适应直方图均衡(Contrast Limited Adaptive histgram equalization/CLAHE)的更多相关文章

  1. Paper | Contrast Limited Adaptive Histogram Equalization

    目录 1. 背景 1.1. 对比度和直方图均衡HE 1.2. HE的问题 1.3. AHE 1.4. 底噪问题 2. CLAHE 2.1. 效果展示 2.2. 算法格式和细节 论文:Contrast ...

  2. (原)opencv中使用限制对比度自适应直方图均衡CLAHE

    转载请注明出处: http://www.cnblogs.com/darkknightzh/p/5462656.html Ptr<CLAHE> clahe = createCLAHE(); ...

  3. 图像增强 | CLAHE 限制对比度自适应直方图均衡化

    1 基本概述 CLAHE是一个比较有意思的图像增强的方法,主要用在医学图像上面.之前的比赛中,用到了这个,但是对其算法原理不甚了解.在这里做一个复盘. CLAHE起到的作用简单来说就是增强图像的对比度 ...

  4. 【图像增强】CLAHE 限制对比度自适应直方图均衡化

    文章目录: 目录 1 基本概述 2 竞赛中的CLAHE实现 3 openCV绘制直方图 4 对比度Contrast 5 Contrast Stretching 6 Histogram Equaliza ...

  5. 干货 | 自适应大邻域搜索(Adaptive Large Neighborhood Search)入门到精通超详细解析-概念篇

    01 首先来区分几个概念 关于neighborhood serach,这里有好多种衍生和变种出来的胡里花俏的算法.大家在上网搜索的过程中可能看到什么Large Neighborhood Serach, ...

  6. 自适应哈希索引(Adaptive Hash Index, AHI) 转

    Adaptive Hash Index, AHI 场景 比如我们每次从辅助索引查询到对应记录的主键,然后还要用主键作为search key去搜索主键B+tree才能找到记录. 当这种搜索变多了,inn ...

  7. 直方图均衡(HE)与局部色调映射(LTM) .

    直方图均衡(Histogram Equalization)是图像处理中一个十分基础的概念,具有调整图像灰度,增强对比度的作用.    限制对比度自适应直方图均衡(Contrast Limited Ad ...

  8. python skimage图像处理(一)

    python skimage图像处理(一) This blog is from: https://www.jianshu.com/p/f2e88197e81d 基于python脚本语言开发的数字图片处 ...

  9. 关于 CLAHE 的理解及实现

    CLAHE CLAHE 是一种非常有效的直方图均衡算法, 目前网上已经有很多文章进行了说明, 这里说一下自己的理解. CLAHE是怎么来的 直方图均衡是一种简单快速的图像增强方法, 其原理和实现过程以 ...

随机推荐

  1. B - The Accomodation of Students - hdu 2444(最大匹配)

    题意:现在有一些学生给你一下朋友关系(不遵守朋友的朋友也是朋友),先确认能不能把这些人分成两组(组内的人要相互不认识),不能分的话输出No(小写的‘o’ - -,写成了大写的WA一次),能分的话,在求 ...

  2. K - The Unique MST - poj 1679

    题目的意思已经说明了一切,次小生成树... ****************************************************************************** ...

  3. Android入门之ActionBar实现Tab导航

    效果图: <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android=&qu ...

  4. [React] Radium: Updating Button Styles via Props

    In a CSS library like Bootstrap we can set a button's style to be "primary" or "secon ...

  5. [转] shell文本字符串处理

    第一种方法:#%*,#即截取变量前的字符(左向右截取),%表示截取后面字符(右向左截取),*匹配符 var=foodforthought.jpg ${varible##*string} 从左向右截取最 ...

  6. TCP/IP协议原理与应用笔记05:TCP/IP协议下的网关

    大家都知道,从一个房间走到另一个房间,必然要经过一扇门.同样,从一个网络向另一个网络发送信息,也必须经过一道“关口”,这道关口就是网关.顾名思义,网关(Gateway)就是一个网络连接到另一个网络的& ...

  7. C#_DBHelper_SQL数据库操作类.

    using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Data; ...

  8. ImageIO.wtrie生成jpg图片质量损失方案:BufferedImage生成jpg图片文件流

    Iterator<ImageWriter> iterator = ImageIO.getImageWritersByFormatName("jpeg"); ImageW ...

  9. CriticalFinalizerObject的作用

    CriticalFinalizerObject 在从 CriticalFinalizerObject 类派生的类中,公共语言运行库 (CLR) 保证所有关键终止代码都有机会执行, 即使是在 CLR 强 ...

  10. 操作sql - 类型初始值设定项引发异常

    这个异常我还是第一次看见,网上有人说,若出现异常,则访问所有的静态成员,均会抛出异常. 在我碰到的问题中,如下代码: ; static private System.Data.DataTable Re ...