數字圖像處理,經典對比度增強算法

轉自: https://blog.csdn.net/ebowtang/article/details/38236441#

關於圖像增強必須清楚的基本概念

1.圖像增強的目的:

1)改善圖像的視覺效果,
2)轉換爲更適合於人或機器分析處理的形式
3)突出對人或機器分析有意義的信息
4)抑制無用信息,提高圖像的使用價值
5)增強後的圖像並不一定保真


2,圖像增強的方法分類:

1)從處理對象分類:灰度圖像,(僞)彩色圖像
2)從處理策略分類:全局處理,局部處理(ROI ROI,Region of Interest Interest)
3)從處理方法分類:空間域(點域運算,即灰度變換;鄰域方法,即空域濾波),頻域方法
4)從處理目的分類:圖像銳化,平滑去噪,灰度調整(對比度增強)


3,圖像增強的方法之對比度增強

1)灰度變換法

線性變換(已實現)
對數變換(已實現)
指數變換(已實現)

2)直方圖調整法
直方圖均衡化(已實現)
直方圖匹配(未實現)



一,直方圖均衡化 

直方圖均衡化的英文名稱是Histogram Equalization. 

  圖像對比度增強的方法可以分成兩類:一類是直接對比度增強方法;另一類是間接對比度增強方法。直方圖拉伸和直方圖均衡化是兩種最常見的間接對比度增強方法。直方圖拉伸是通過對比度拉伸對直方圖進行調整,從而“擴大”前景和背景灰度的差別,以達到增強對比度的目的,這種方法可以利用線性或非線性的方法來實現;直方圖均衡化則通過使用累積函數對灰度值進行“調整”以實現對比度的增強。
  直方圖均衡化處理的“中心思想”是把原始圖像的灰度直方圖從比較集中的某個灰度區間變成在全部灰度範圍內的均勻分佈。直方圖均衡化就是對圖像進行非線性拉伸,重新分配圖像像素值,使一定灰度範圍內的像素數量大致相同。直方圖均衡化就是把給定圖像的直方圖分佈改變成“均勻”分佈直方圖分佈。
  缺點: 
  1)變換後圖像的灰度級減少,某些細節消失; 
  2)某些圖像,如直方圖有高峯,經處理後對比度不自然的過分增強。 
  直方圖均衡化是圖像處理領域中利用圖像直方圖對對比度進行調整的方法。 
  這種方法通常用來增加許多圖像的局部對比度,尤其是當圖像的有用數據的對比度相當接近的時候。通過這種方法,亮度可以更好地在直方圖上分佈。這樣就可以用於增強局部的對比度而不影響整體的對比度,直方圖均衡化通過有效地擴展常用的亮度來實現這種功能。
  這種方法對於背景和前景都太亮或者太暗的圖像非常有用,這種方法尤其是可以帶來X光圖像中更好的骨骼結構顯示以及曝光過度或者曝光不足照片中更好的細節。這種方法的一個主要優勢是它是一個相當直觀的技術並且是可逆操作,如果已知均衡化函數,那麼就可以恢復原始的直方圖,並且計算量也不大。這種方法的一個缺點是它對處理的數據不加選擇,它可能會增加背景雜訊的對比度並且降低有用信號的對比度。
  

關於編程實現,同樣是不調用matlab庫函數,自己編程實現。這樣可以更深刻地理解直方圖均衡化技術,提高編程能力。

實現代碼(matlab):


  1. clc;
  2. close all;
  3. clear all;
  4. src_img = imread('flyman_gray.bmp');
  5. figure (1)
  6. subplot(321),imshow(src_img),title('原圖像');%顯示原始圖像
  7. subplot(322),imhist(src_img),title('原圖像直方圖');%顯示原始圖像直方圖
  8. matlab_eq=histeq(src_img); %利用matlab的函數直方圖均衡化
  9. subplot(323),imshow(matlab_eq),title('matlab直方圖均衡化原圖像');%顯示原始圖像
  10. subplot(324),imhist(matlab_eq),title('matlab均衡化後的直方圖');%顯示原始圖像直方圖
  11. dst_img=myHE(src_img); %利用自己寫的函數直方圖均衡化
  12. subplot(325),imshow(dst_img),title('手寫均衡化效果');%顯示原始圖像
  13. subplot(326),imhist(dst_img),title('手寫均衡化直方圖');%顯示原始圖像直方圖

直方圖均衡化函數的實現:


  1. function dst_img=myHE(src_img)
  2. [height,width] = size(src_img);
  3. dst_img=uint8(zeros(height,width));
  4. %進行像素灰度統計;
  5. NumPixel = zeros(1,256);%統計各灰度數目,共256個灰度級
  6. for i = 1:height
  7. for j = 1: width
  8. NumPixel(src_img(i,j) + 1) = NumPixel(src_img(i,j) + 1) + 1;%對應灰度值像素點數量增加一
  9. end
  10. end
  11. %計算灰度分佈密度
  12. ProbPixel = zeros(1,256);
  13. for i = 1:256
  14. ProbPixel(i) = NumPixel(i) / (height * width * 1.0);
  15. end
  16. %計算累計直方圖分佈
  17. CumuPixel = zeros(1,256);
  18. for i = 1:256
  19. if i == 1
  20. CumuPixel(i) = ProbPixel(i);
  21. else
  22. CumuPixel(i) = CumuPixel(i - 1) + ProbPixel(i);
  23. end
  24. end
  25. % 指定範圍進行均衡化
  26. % pixel_max=max(max(I));
  27. % pixel_min=min(min(I));
  28. pixel_max=255;
  29. pixel_min=0;
  30. %對灰度值進行映射(均衡化)
  31. for i = 1:height
  32. for j = 1: width
  33. dst_img(i,j) = CumuPixel(src_img(i,j)+1)*(pixel_max-pixel_min)+pixel_min;
  34. end
  35. end
  36. return;



爲什們和matlab的直方圖不一樣呢???



二,指數變換

指數變換(Power-Law )的公式:S=c*R^r,通過合理的選擇c和r可以壓縮灰度範圍,算法以c=1.0/255.0, r=2實現。
要做該圖像增強變換需要先做歸一化,再指數變換,最後反歸一化
增強效果展示:可以看見,改增強算法並不能很好的將像素儘可能的碾平。
指數增強參考程序爲:
  1. clc;
  2. close all;
  3. clear all;
  4. % -------------Gamma Transformations-----------------
  5. %f = imread('Fig0316(4)(bottom_left).tif');
  6. f = imread('seed.tif');
  7. Gamma = 0.4;
  8. g2 = myExpEnhance(f,Gamma);
  9. figure();
  10. subplot(221); imshow(f); xlabel('a).Original Image');
  11. subplot(222),imhist(f),title('原圖像直方圖');%顯示原始圖像直方圖
  12. subplot(223); imshow(g2); xlabel('b).Gamma Transformations \gamma = 0.4');
  13. subplot(224),imhist(g2),title('增強圖像直方圖');%顯示原始圖像直方圖
指數增強核心函數爲:
  1. function dst_img=myExpEnhance(src_img,Gamma)
  2. src_img = mat2gray(src_img,[0 255]);%將圖像矩陣A中介於amin和amax的數據歸一化處理, 其餘小於amin的元素都變爲0, 大於amax的元素都變爲1
  3. C = 1;
  4. g2 = C*(src_img.^Gamma);
  5. %反歸一化
  6. max=255;
  7. min=0;
  8. dst_img=uint8(g2*(max-min)+min);




三,對數變換

       對數變換主要用於將圖像的低灰度值部分擴展,將其高灰度值部分壓縮,以達到強調圖像低灰度部分的目的。變換方法由下式給出。

這裏的對數變換,底數爲(v+1),實際計算的時候,需要用換底公式。其輸入範圍爲歸一化的【0-1】,其輸出也爲【0-1】。對於不同的底數,其對應的變換曲線如下圖所示。

底數越大,對低灰度部分的強調就越強,對高灰度部分的壓縮也就越強。相反的,如果想強調高灰度部分,則用反對數函數就可以了。看下面的實驗就可以很直觀的理解,下圖是某圖像的二維傅里葉變換圖像,其爲了使其灰度部分較爲明顯,一般都會使用灰度變換處理一下。

效果圖:


參考代碼:
  1. clc;
  2. close all;
  3. clear all;
  4. %-------------Log Transformations-----------------
  5. f = imread('seed.tif');
  6. g_1 = myLogEnhance(f,10);
  7. g_2 = myLogEnhance(f,100);
  8. g_3 = myLogEnhance(f,200);
  9. figure();
  10. subplot(2,2,1);
  11. imshow(f);xlabel('a).Original Image');
  12. subplot(2,2,2);
  13. imshow(g_1);xlabel('b).Log Transformations v=10');
  14. subplot(2,2,3);
  15. imshow(g_2);xlabel('c).Log Transformations v=100');
  16. subplot(2,2,4);
  17. imshow(g_3);
  18. xlabel('d).Log Transformations v=200');

對數變換核心函數
  1. function dst_img=myLogEnhance(src_img,v)
  2. c=1.0;
  3. src_img = mat2gray(src_img,[0 255]);
  4. g =c*log2(1 + v*src_img)/log2(v+1);
  5. %反歸一化
  6. max=255;
  7. min=0;
  8. dst_img=uint8(g*(max-min)+min);





四,灰度拉伸

灰度拉伸也用於強調圖像的某個部分,與伽馬變換與對數變換不同的是,灰度拉昇可以改善圖像的動態範圍。可以將原來低對比度的圖像拉伸爲高對比度圖像。實現灰度拉昇的方法很多,其中最簡單的一種就是線性拉伸。而這裏介紹的方法稍微複雜一些。灰度拉伸所用數學式如下所示。

同樣的,其輸入r爲【0-1】,其輸出s也爲【0-1】。這個式子再熟悉不過了,跟巴特沃斯高通濾波器像極了,其輸入輸出關係也大致能猜到是個什麼形狀的。但是,這裏就出現一個問題了,輸入爲0時候,式子無意義了。所以,在用Matlab計算的時候,將其變爲如下形式。

這裏的eps,就是Matlab裏面,一個很小數。如此做的話,式子變得有意義了。但是,其輸入範圍爲【0-1】的時候,其輸出範圍變爲了。輸出範圍大致爲【0-1】,爲了精確起見,使用mat2gray函數將其歸一化到精確的[0-1]。調用格式如下。



五,線性拉伸

爲了突出感興趣的目標或者灰度區間,相對抑制那些不感興趣的灰度區域,可採用分段線性法,常用的是三段線性變換




參考程序:

  1. clc;
  2. close all;
  3. clear all;
  4. I=imread('seed.tif');
  5. [m,n,k]=size(I);
  6. figure (1)
  7. imshow('seed.tif');title(' 原圖像');
  8. mid=mean(mean(I));
  9. %橫軸
  10. fa=20; fb=80;
  11. %縱軸
  12. ga=50; gb=230;
  13. J=myLinearEnhance(I,fa,fb,ga,gb);
  14. figure (2)
  15. imshow(J);title(' 線性拉伸圖像');
  16. pixel_f=1:256;
  17. pixel_g=zeros(1,256);
  18. %三段斜率,小於1表示該段將會被收縮
  19. k1=double(ga/fa);
  20. k2=(gb- ga)/(fb- fa);
  21. k3=(256- gb)/(256- fb);
  22. for i=1:256
  23. if i <= fa
  24. pixel_g(i)= k1*i;
  25. elseif fa < i && i <= fb
  26. pixel_g(i)= k2*( i- fa)+ ga;
  27. else
  28. pixel_g(i)= k3*( i - fb)+ gb;
  29. end
  30. end
  31. figure (3)
  32. plot(pixel_f,pixel_g);


核心函數:

  1. function dst_img=myLinearEnhance(src_img,fa,fb,ga,gb)
  2. [height,width] = size(src_img);
  3. dst_img=uint8(zeros(height,width));
  4. src_img=double(src_img);
  5. %三段斜率
  6. k1=ga/fa;
  7. k2=(gb- ga)/(fb- fa);
  8. k3=(255- gb)/(255- fb);
  9. for i=1:height
  10. for j=1:width
  11. if src_img(i,j) <= fa
  12. dst_img(i,j)= k1*src_img(i,j);
  13. elseif fa < src_img(i,j) && src_img(i,j) <= fb
  14. dst_img(i,j)= k2*( src_img(i,j)- fa)+ ga;
  15. else
  16. dst_img(i,j)= k3*( src_img(i,j)- fb)+ gb;
  17. end
  18. end
  19. end
  20. dst_img=uint8(dst_img);




附錄:

附錄網上的另一份講解:
直方圖均衡化算法分爲三個步驟,第一步是統計直方圖每個灰度級出現的次數,第二步是累計歸一化的直方圖,第三步是計算新的像素值。
第一步:
for(i=0;i<height;i++)
for(j=0;j<width;j++)
n[s[i][j]]++;

for(i=0;i<L;i++)
p[i]=n[i]/(width*height);

這裏,n[i]表示的是灰度級爲i的像素的個數,L表示的是最大灰度級,width和height分別表示的是原始圖像的寬度和高度,所以,p[i]表示的就是灰度級爲i的像素在整幅圖像中出現的概率(其實就是p[]這個數組存儲的就是這幅圖像的歸一化之後的直方圖)。
第二步:
for(i=0;i<=L;i++)
for(j=0;j<=i;j++)
c[i]+=p[j];

c[]這個數組存儲的就是累計的歸一化直方圖。
第三步:
max=min=s[0][0];
for(i=0;i<height;i++)
for(j=0;j<width;j++)
if(max<s[i][j]){
max=s[i][j];
}else if(min>s[i][j]){
min=s[i][j];
}

找出像素的最大值和最小值。
for(i=0;i<height;i++)
for(j=0;j<width;j++)
t[i][j]=c[s[i][j]]*(max-min)+min;

t[][]就是最終直方圖均衡化之後的結果。


收錄優秀代碼:

這份代碼寫得不錯,學習了,原博客地址見參考資源【3】!

  1. #include <stdio.h>
  2. #include <iostream>
  3. #include "fftw3.h"
  4. #include "string"
  5. #include "vector"
  6. #include <windows.h>
  7. #include <opencv2/legacy/legacy.hpp>
  8. #include <opencv2/nonfree/nonfree.hpp>//opencv_nonfree模塊:包含一些擁有專利的算法,如SIFT、SURF函數源碼。
  9. #include "opencv2/core/core.hpp"
  10. #include "opencv2/features2d/features2d.hpp"
  11. #include "opencv2/highgui/highgui.hpp"
  12. #include <opencv2/nonfree/features2d.hpp>
  13. using namespace cv;
  14. using namespace std;
  15. class hisEqt
  16. {
  17. public:
  18. hisEqt::hisEqt();
  19. hisEqt::~hisEqt();
  20. public:
  21. int w;
  22. int h;
  23. int nlen;
  24. int *pHis;
  25. float *pdf;
  26. //=====求像素分佈概率密度====
  27. void getPdf();
  28. //======統計像素個數=======
  29. void getHis(unsigned char*imgdata);
  30. //==========畫統計分佈直方圖===============
  31. void drawHistogram(const float*pdf,Mat &hist1);
  32. //===========直方圖均衡化==========
  33. void hisBal();
  34. //====直方圖均衡化後的圖像===
  35. void imgBal(unsigned char* img);
  36. };
  37. hisEqt::hisEqt() :nlen(0){
  38. pHis = new int[256 * sizeof(int)];
  39. memset(pHis, 0, 256 * sizeof(int));
  40. pdf = new float[255 * sizeof(float)];
  41. memset(pdf, 0, 255 * sizeof(float));
  42. }
  43. hisEqt::~hisEqt(){
  44. delete[]pHis;
  45. delete[]pdf;
  46. }
  47. //======統計像素個數=======
  48. void hisEqt::getHis(unsigned char*imgdata){
  49. for (int i = 0; i<nlen; i++)
  50. {
  51. pHis[imgdata[i]]++;
  52. }
  53. }
  54. //=====求像素分佈概率密度====
  55. void hisEqt::getPdf(){
  56. for (int k = 0; k<256; k++)
  57. {
  58. pdf[k] = pHis[k] / float(nlen);
  59. }
  60. }
  61. //===========直方圖均衡化==========
  62. void hisEqt::hisBal(){
  63. for (int k = 1; k<256; k++)
  64. {
  65. pdf[k] += pdf[k - 1];
  66. }
  67. for (int k = 0; k<256; k++)
  68. {
  69. pHis[k] = 255 * pdf[k];
  70. }
  71. }
  72. //====直方圖均衡化
  73. void hisEqt::imgBal(unsigned char* img){
  74. for (int i = 0; i<nlen; i++)
  75. {
  76. img[i] = pHis[img[i]];
  77. }
  78. }
  79. void hisEqt::drawHistogram(const float *pdf, Mat& hist1){
  80. for (int k = 0; k<256; k++)
  81. {
  82. if (k % 2 == 0)
  83. {
  84. Point a(k, 255), b(k, 255 - pdf[k] * 2550);
  85. line(hist1,
  86. a,
  87. b,
  88. Scalar(0, 0, 255),
  89. 1);
  90. }
  91. else
  92. {
  93. Point a(k, 255), b(k, 255 - pdf[k] * 2550);
  94. line(hist1,
  95. a,
  96. b,
  97. Scalar(0, 255, 0),
  98. 1);
  99. }
  100. }
  101. }
  102. int main()
  103. {
  104. Mat image = imread("Fig0651(a)(flower_no_compression).tif");
  105. if (!image.data)
  106. return -1;
  107. Mat hist2(256, 256, CV_8UC3, Scalar(0, 0, 0));
  108. Mat hist1(256, 256, CV_8UC3, Scalar(0, 0, 0));
  109. Mat imgOut = Mat(image.rows, image.cols, CV_8UC3, Scalar(0, 0, 0));
  110. vector<Mat> planes;
  111. int chn = image.channels();
  112. if (chn == 3)
  113. {
  114. split(image, planes);
  115. }
  116. while (chn)
  117. {
  118. chn--;
  119. unsigned char* imageData = new unsigned char[sizeof(unsigned char)*(image.cols*image.rows)];
  120. memcpy(imageData, planes[chn].data, planes[chn].cols*planes[chn].rows);
  121. hisEqt his;//自定義的類
  122. his.nlen = image.rows*image.cols;
  123. his.getHis(imageData);
  124. his.getPdf();
  125. // //======畫原圖直方圖並保存============
  126. his.drawHistogram(his.pdf, hist1);
  127. string pic_name = "hisline";
  128. pic_name = pic_name + to_string(chn);
  129. pic_name=pic_name+ ".jpg";
  130. imwrite(pic_name, hist1);
  131. his.hisBal();
  132. his.getPdf();
  133. // //======畫均衡化後直方圖並保存============
  134. his.drawHistogram(his.pdf, hist2);
  135. string pic_name0 = "his_balanceline";
  136. pic_name0 = pic_name0 + to_string(chn);
  137. pic_name0 = pic_name0 + ".jpg";
  138. imwrite(pic_name0, hist2);
  139. // //=====圖像均衡化===
  140. his.imgBal(imageData);
  141. memcpy(planes[chn].data, imageData, planes[chn].cols*planes[chn].rows);
  142. delete[] imageData;
  143. imageData = NULL;
  144. }
  145. merge(planes, imgOut);//單通道合併
  146. imwrite("result.jpg", imgOut);
  147. return 0;
  148. }



參考資源

【1】http://blog.csdn.net/xiajun07061225/article/details/6910129

【2】數字圖像處理,岡薩雷斯著

【3】http://blog.csdn.net/bettyshasha/article/details/46940805

【4】http://blog.csdn.net/terryzero/article/details/6043821

【5】http://www.myexception.cn/image/1450848.html


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章