当前位置:   article > 正文

限制对比度自适应直方图均衡化(自我理解)_限制对比度直方图阈值均衡化

限制对比度直方图阈值均衡化

CLAHE算法对于医学图像,特别是医学红外图像的增强效果非常明显。

CLAHE  https://en.wikipedia.org/wiki/Adaptive_histogram_equalization

中文方面非常好的资料 限制对比度自适应直方图均衡化算法原理、实现及效果(我自己直接看解释没怎么看懂,不如直接看本篇博文下面的代码)

OpenCV中已经实现了CLAHE,但是它在使用过程中,存在参数选择的问题。为了从根本上搞明白,我参考了网络上的一些代码

主要是来源 http://blog.csdn.net/abcd1992719g/article/details/25483395 (我觉的这篇博客的java代码更容易使人理解)

看完上面的java代码,我的理解

首先你得了解直方图均衡化(https://blog.csdn.net/u013066730/article/details/82969768)。

限制对比度自适应直方图均衡化方法步骤为:

将整幅图像切分为5*5的块block(但通常代码中为8*8,但本文以5*5举例),如图一右侧的黑色实线框所示。

                                                 图一

接着计算每个block的累积分布,在计算累积分布时,限制一下他的对比度,别让直方图出现特别陡峭的情况,直观图像就如图二所示。具体操作就是将直方图中高于某个阈值a的数值全都找出来构成一个集合B,然后B中的每一个元素b都变为a,其中b高于a的部分进行求和得到c,c除以256(该256是指灰度等级,也就是0-255)得到d,将d加在每一个灰度等级下,其实就是图二中蓝色尖状部分变为平坦状。

                                  图二

累积分布求好后,就需要将原始图像中的每个像素值通过累积分布映射为新的像素值。其中使用了双线性插值的方法,而在使用双线性插值时,需要将原图重新分配,也就是图一的粉色,绿色和紫色区域。其中粉色区域的像素值就直接使用对应block的累积分布直接计算即可得到新的像素值(这里如何计算可以参考直方图均衡化)。绿色区域的像素值a,使用绿色区域中邻近的2个block的累计分布计算得到全新的2个像素值b1,b2,然后根据a与这两个block的距离比例得到求和比例c1,1-c1,所以原始像素a对应的最终像素d = b1*c1+b2*(1-c1)。紫色区域的像素值p,使用紫色区域中邻近的4个block的累积分布计算得到全新的4个像素值q1,q2,q3,q4,然后像素p根据与这4个block的距离远近得到对应比例,第一个block的比例为u,v(就是图一中紫色方框叉距离第一个黑色实心框的x,y的比例),第二个block的比例为1-u,v,第三个block的比例为u,1-v,第四个block的比例为1-u,1-v,所以原始像素p对应的最终结果i=u*v*q1+(1-u)*v*q2+u*(1-v)*q3+(1-u)*(1-v)*q4。

哎,我这里说的也有点乱,我感觉不如直接花点时间看下面的代码,真是一看就懂。

  1. /*
  2. * CLAHE
  3. * 自适应直方图均衡化
  4. */
  5. public int[][] AHE(int[][] oldmat,int pblock)
  6. {
  7. int block = pblock;
  8. //将图像均匀分成等矩形大小,8864个块是常用的选择
  9. int width_block = width/block;
  10. int height_block = height/block;
  11. //存储各个直方图
  12. int[][] tmp = new int[block*block][256];
  13. //存储累积函数
  14. float[][] C = new float[block*block][256];
  15. //计算累积函数
  16. for(int i = 0 ; i < block ; i ++)
  17. {
  18. for(int j = 0 ; j < block ; j++)
  19. {
  20. int start_x = i * width_block;
  21. int end_x = start_x + width_block;
  22. int start_y = j * height_block;
  23. int end_y = start_y + height_block;
  24. int num = i+block*j;
  25. int total = width_block * height_block;
  26. for(int ii = start_x ; ii < end_x ; ii++)
  27. {
  28. for(int jj = start_y ; jj < end_y ; jj++)
  29. {
  30. int index = oldmat[jj][ii];
  31. tmp[num][index]++;
  32. }
  33. }
  34. //裁剪操作
  35. int average = width_block * height_block / 255;
  36. int LIMIT = 4 * average;
  37. int steal = 0;
  38. for(int k = 0 ; k < 256 ; k++)
  39. {
  40. if(tmp[num][k] > LIMIT){
  41. steal += tmp[num][k] - LIMIT;
  42. tmp[num][k] = LIMIT;
  43. }
  44. }
  45. int bonus = steal/256;
  46. //hand out the steals averagely
  47. for(int k = 0 ; k < 256 ; k++)
  48. {
  49. tmp[num][k] += bonus;
  50. }
  51. //计算累积分布直方图
  52. for(int k = 0 ; k < 256 ; k++)
  53. {
  54. if( k == 0)
  55. C[num][k] = 1.0f * tmp[num][k] / total;
  56. else
  57. C[num][k] = C[num][k-1] + 1.0f * tmp[num][k] / total;
  58. }
  59. }
  60. }
  61. int[][] new_mat = new int[height][width];
  62. //计算变换后的像素值
  63. //根据像素点的位置,选择不同的计算方法
  64. for(int i = 0 ; i < width; i++)
  65. {
  66. for(int j = 0 ; j < height; j++)
  67. {
  68. //four coners
  69. if(i <= width_block/2 && j <= height_block/2)
  70. {
  71. int num = 0;
  72. new_mat[j][i] = (int)(C[num][oldmat[j][i]] * 255);
  73. }else if(i <= width_block/2 && j >= ((block-1)*height_block + height_block/2)){
  74. int num = block*(block-1);
  75. new_mat[j][i] = (int)(C[num][oldmat[j][i]] * 255);
  76. }else if(i >= ((block-1)*width_block+width_block/2) && j <= height_block/2){
  77. int num = block-1;
  78. new_mat[j][i] = (int)(C[num][oldmat[j][i]] * 255);
  79. }else if(i >= ((block-1)*width_block+width_block/2) && j >= ((block-1)*height_block + height_block/2)){
  80. int num = block*block-1;
  81. new_mat[j][i] = (int)(C[num][oldmat[j][i]] * 255);
  82. }
  83. //four edges except coners
  84. else if( i <= width_block/2 )
  85. {
  86. //线性插值
  87. int num_i = 0;
  88. int num_j = (j - height_block/2)/height_block;
  89. int num1 = num_j*block + num_i;
  90. int num2 = num1 + block;
  91. float p = (j - (num_j*height_block+height_block/2))/(1.0f*height_block);
  92. float q = 1-p;
  93. new_mat[j][i] = (int)((q*C[num1][oldmat[j][i]]+ p*C[num2][oldmat[j][i]])* 255);
  94. }else if( i >= ((block-1)*width_block+width_block/2)){
  95. //线性插值
  96. int num_i = block-1;
  97. int num_j = (j - height_block/2)/height_block;
  98. int num1 = num_j*block + num_i;
  99. int num2 = num1 + block;
  100. float p = (j - (num_j*height_block+height_block/2))/(1.0f*height_block);
  101. float q = 1-p;
  102. new_mat[j][i] = (int)((q*C[num1][oldmat[j][i]]+ p*C[num2][oldmat[j][i]])* 255);
  103. }else if( j <= height_block/2 ){
  104. //线性插值
  105. int num_i = (i - width_block/2)/width_block;
  106. int num_j = 0;
  107. int num1 = num_j*block + num_i;
  108. int num2 = num1 + 1;
  109. float p = (i - (num_i*width_block+width_block/2))/(1.0f*width_block);
  110. float q = 1-p;
  111. new_mat[j][i] = (int)((q*C[num1][oldmat[j][i]]+ p*C[num2][oldmat[j][i]])* 255);
  112. }else if( j >= ((block-1)*height_block + height_block/2) ){
  113. //线性插值
  114. int num_i = (i - width_block/2)/width_block;
  115. int num_j = block-1;
  116. int num1 = num_j*block + num_i;
  117. int num2 = num1 + 1;
  118. float p = (i - (num_i*width_block+width_block/2))/(1.0f*width_block);
  119. float q = 1-p;
  120. new_mat[j][i] = (int)((q*C[num1][oldmat[j][i]]+ p*C[num2][oldmat[j][i]])* 255);
  121. }
  122. //inner area
  123. else{
  124. int num_i = (i - width_block/2)/width_block;
  125. int num_j = (j - height_block/2)/height_block;
  126. int num1 = num_j*block + num_i;
  127. int num2 = num1 + 1;
  128. int num3 = num1 + block;
  129. int num4 = num2 + block;
  130. float u = (i - (num_i*width_block+width_block/2))/(1.0f*width_block);
  131. float v = (j - (num_j*height_block+height_block/2))/(1.0f*height_block);
  132. new_mat[j][i] = (int)((u*v*C[num4][oldmat[j][i]] +
  133. (1-v)*(1-u)*C[num1][oldmat[j][i]] +
  134. u*(1-v)*C[num2][oldmat[j][i]] +
  135. v*(1-u)*C[num3][oldmat[j][i]]) * 255);
  136. }
  137. new_mat[j][i] = new_mat[j][i] + (new_mat[j][i] << 8) + (new_mat[j][i] << 16);
  138. }
  139. }
  140. return new_mat;
  141. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/78865
推荐阅读
相关标签
  

闽ICP备14008679号