圖像直方圖(histogram)是圖像的統計學特征,常用於了解圖像的基本特征以便分析。不過圖像的直方圖不具有空間特征。
圖像的灰度直方圖(histogram),就是將圖像轉化成灰度圖像之后,統計各個像素點的灰度值,繪制成直方圖,其橫軸是灰度值(0,255),縱軸是該灰度值所對應的像素的數目。對灰度直方圖做積分=圖像的size。
灰度直方圖
1 def plt_hist(img): 2 plt.hist(img.ravel(), 256, [0, 256]) 3 plt.show()
三色折線圖(可以直觀的看出三原色的占比分布)
1 def img_hist(img): 2 color = ("blue", "green", "red") 3 for i, color in enumerate(color): 4 hist = cv.calcHist([img], [i], None, [256], [0, 256]) 5 plt.plot(hist, color=color) 6 plt.xlim([0, 25]) 7 plt.show()
一般來說,直方圖有雙峰性,會有兩個峰值以區分前景和背景,但並不是每張圖像都如此。分析圖像的變化,進而確定最優二值化值。在兩個峰值之間的最小值,經常是我們需要找的二值化值。我們可以發現,兩個峰值之間的波谷是兩個峰值的疊加區。有了灰度直方圖,我們可以給圖片做二值化。確定哪些pixel是我們需要關注的哪些是不重要的。二值化有兩種主要形式:一種是全局閾值二值化,另外一種是局部閾值二值化。
選擇二值化的值(找到兩個雙峰的最低點,但不是都有多峰可能存在多個最小值,可以用局部閾值)
經典算法:isodata algorithm 預設初始的二值化值,然后不斷迭代,效果不是最好的
Otsu algorithm 研究雙峰的關聯性求二值化值
Entropy algorithm 熵算法,前景熵hb和背景熵hw。0-255個灰度值分別求熵,最大的h值就是我們要找的二值化值。
其他算法:triangle algorithm 灰度值的最大值和最左側的最小值連接,然后做垂線,當垂線距離最大時,所對應的灰度值就是我們需要的二值化值,不過這個算法效果不是很好,應用於細胞分離比較多。
使用opencvAPI:(otsu和triangle)
1 def threshold_binary(src): 2 #把BGR圖像轉化成灰度圖像
3 gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) 4 #獲得灰度直方圖以便調整算法的使用
5 plt.hist(src.ravel(), 256, [0, 256]) 6 plt.show() 7 #幾種二值化方法
8 ret, binary = cv.threshold(gray, 50, 255, cv.THRESH_BINARY)#指定閾值50
9 print("二值閾值: %s" % ret) 10 cv.imshow("threshold_binary", binary) 11 ret_otsu, binary_otsu = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU) 12 print("二值閾值_otsu: %s" %ret_otsu) 13 cv.imshow("threshold_binary_otsu", binary_otsu) 14 ret_tri, binary_tri = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_TRIANGLE) 15 print("二值閾值_tri: %s" % ret_tri)
運行示例:
二值閾值: 50.0
二值閾值_otsu: 173.0
二值閾值_tri: 250.0
以上是全局閾值二值化的結果,下面還有局部閾值二值化。個人覺得局部閾值更好的描繪了圖片的輪廓。
1 def local_threshold_binary(src): 2 gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) 3 binary_mean = cv.adaptiveThreshold(gray, 255, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 25, 10) 4 cv.imshow("binary_mean", binary_mean)5
6 binary_gau = cv.adaptiveThreshold(gray, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 25, 10) 7 cv.imshow("binary_gau", binary_gau)
兩種方法,
超大圖像及其二值化
經常會遇到一些比較大的圖像,我們可以把圖片拆分成很多個小方塊分別進行全局二值化或者使用局部閾值。這時候可以用opencv處理圖像顯示不全,需要把圖片保存到本地才行。
1 def big_image_binary(src): 2 print(src.shape) 3 cw = 256
4 ch = 256
5 h, w, c = src.shape 6 gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) 7 for row in range(0, h, ch): 8 for col in range(0, w, cw): 9 roi = gray[row:row+ch, col:col+cw] 10 dst = cv.adaptiveThreshold(roi, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 25, 10) 11 # ret_otsu, dst = cv.threshold(roi, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
12 gray[row:row+ch, col:col+cw] = dst 13 #計算方差和平均值,當這兩個數值小於某一值時,可以認為圖像為空白,做空白圖像過濾
14 print(np.std(dst), np.mean(dst)) 15 cv.imwrite("E:/photo/big_img_binary.jpg", gray)