什么是直方圖均衡化?
直方圖均衡化是一種簡單有效的圖像增強技術,通過改變圖像的直方圖來改變圖像中各像素的灰度,主要用於增強動態范圍偏小的圖像的對比度。原始圖像由於其灰度分布可能集中在較窄的區間,造成圖像不夠清晰。例如,過曝光圖像的灰度級集中在高亮度范圍內,而曝光不足將使圖像灰度級集中在低亮度范圍內。采用直方圖均衡化,可以把原始圖像的直方圖變換為均勻分布(均衡)的形式,這樣就增加了像素之間灰度值差別的動態范圍,從而達到增強圖像整體對比度的效果。換言之,直方圖均衡化的基本原理是:對在圖像中像素個數多的灰度值(即對畫面起主要作用的灰度值)進行展寬,而對像素個數少的灰度值(即對畫面不起主要作用的灰度值)進行歸並,從而增大對比度,使圖像清晰,達到增強的目的。舉個例子,如圖中所示,左圖為原始圖像,右圖為直方圖均衡化后的圖像。
個人的理解是,直方圖可以提高圖像的對比度,對於原本對比度較低的圖像效果比較明顯。
直方圖的概念
對一幅灰度圖像,其直方圖反映了該圖像中不同灰度級出現的統計情況。圖2給出了一個直方圖的示例,其中圖(a)是一幅圖像,其灰度直方圖可表示為圖(b),其中橫軸表示圖像的各灰度級,縱軸表示圖像中各灰度級像素的個數。(需要注意,灰度直方圖表示了在圖像中各個單獨灰度級的分布,而圖像對比度則取決於相鄰近像素之間灰度級的關系。)
嚴格地說,圖像的灰度直方圖是一個一維的離散函數,可寫成:
(公式1)
式中,是圖像中灰度級為的像素的個數。直方圖的每一列(稱為bin)的高度對應。直方圖提供了原圖中各種灰度值分布的情況,也可以說直方圖給出了一幅圖像所有灰度值的整體描述。直方圖的均值和方差也是圖像灰度的均值和方差。圖像的視覺效果與其直方圖有對應關系,或者說,直方圖的形狀和改變對圖像有很大的影響。
在直方圖的基礎上,進一步定義歸一化的直方圖為灰度級出現的相對頻率。即:
(公式2)
式中,表示圖像的像素的總數,是圖像中灰度級為的像素的個數。
直方圖均衡化的步驟
使用java實現直方圖均衡化
import java.util.LinkedList; import java.util.List; import org.opencv.core.Core; import org.opencv.core.CvType; import org.opencv.core.Mat; import org.opencv.core.MatOfFloat; import org.opencv.core.MatOfInt; import org.opencv.core.Scalar; import org.opencv.highgui.HighGui; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc; public class huiduhua { //靜態代碼塊加載動態鏈接庫 static { System.loadLibrary(Core.NATIVE_LIBRARY_NAME); } public static void main(String[] args) { /* * IMREAD_UNCHANGED = -1 :不進行轉化,比如保存為了16位的圖片,讀取出來仍然為16位。 * IMREAD_GRAYSCALE = 0 :進行轉化為灰度圖,比如保存為了16位的圖片,讀取出來為8位,類型為CV_8UC1。 * IMREAD_COLOR = 1 :進行轉化為三通道圖像。 * IMREAD_ANYDEPTH = 2 :如果圖像深度為16位則讀出為16位,32位則讀出為32位,其余的轉化為8位。 * IMREAD_ANYCOLOR = 4 :圖像以任何可能的顏色格式讀取 * IMREAD_LOAD_GDAL = 8 :使用GDAL驅動讀取文件,GDAL(Geospatial Data Abstraction * Library)是一個在X/MIT許可協議下的開源柵格空間數據轉換庫。它利用抽象數據模型來表達所支持的各種文件格式。 * 它還有一系列命令行工具來進行數據轉換和處理。 */ Mat src = Imgcodecs.imread("./data/huiduhua.png",0); //Imgproc.resize(src, src, new Size(src.cols()/2,src.rows()/2)); HighGui.imshow("原圖", src); HighGui.waitKey(); ImgCalcHist(src,"原圖直方圖"); Mat dst = new Mat(); //直方圖均衡化,該算法對亮度進行歸一化並增加圖像的對比度。 Imgproc.equalizeHist(src,dst); HighGui.imshow("直方圖均衡化", dst); HighGui.waitKey(); ImgCalcHist(dst,"直方圖均衡化后的直方圖"); } /** * 直方圖 * @param src * @param windowName */ public static void ImgCalcHist(Mat src,String windowName) { List<Mat> matList = new LinkedList<Mat>(); matList.add(src); Mat histogram = new Mat(); MatOfFloat ranges=new MatOfFloat(0,256); MatOfInt histSize = new MatOfInt(300); /* * 計算直方圖 * List<Mat> images:輸入圖像 * MatOfInt channels:需要統計直方圖的第幾通道 * Mat mask:掩膜,,計算掩膜內的直方圖 * Mat hist:輸出的直方圖數組 * MatOfInt histSize:指的是直方圖分成多少個區間,就是bin的個數 * MatOfFloat ranges: 統計像素值得區間 */ Imgproc.calcHist(matList,new MatOfInt(0),new Mat(),histogram,histSize ,ranges); //創建直方圖面板 Mat histImage = Mat.zeros( 150, (int)histSize.get(0, 0)[0], CvType.CV_8UC1); //歸一化直方圖 詳見https://blog.csdn.net/ren365880/article/details/103923813 Core.normalize(histogram, histogram, 1, histImage.rows() , Core.NORM_MINMAX, -1, new Mat() ); //繪制直線 詳見:https://blog.csdn.net/ren365880/article/details/103952856 for( int i = 0; i < (int)histSize.get(0, 0)[0]; i++ ){ Imgproc.line(histImage,new org.opencv.core.Point(i, histImage.rows()),new org.opencv.core.Point(i, histImage.rows()-Math.round( histogram.get(i,0)[0])) ,new Scalar( 255, 255, 255),1, 8, 0 ); } HighGui.imshow(windowName, histImage); HighGui.waitKey(); } }
效果:
參考資料:https://blog.csdn.net/qq_15971883/article/details/88699218
https://blog.csdn.net/zaibeijixing/article/details/96336864