1.直方圖繪制
直方圖顯示圖像數據時會以左暗又亮的分布曲線形式呈現出來,而不是顯示原圖像數據。利用opencv-python中的庫函數繪制彩色圖像直方圖的自定義函數如下
# 直方圖繪制函數 def draw_my_hist(image): color = ('b', 'g', 'r') for i, color in enumerate(color): # calcHist()函數有5個參數: # image輸入圖像,傳入時應該用中括號[]括起來 # channels::傳入圖像的通道,如果是灰度圖像,值為0,如果是彩色圖像(有3個通道),那么值為0,1,2,中選擇一個,對應着BGR各個通道。這個值也得用[]傳入。 # mask:掩膜圖像。如果統計整幅圖,那么為none。主要是如果要統計部分圖的直方圖,就得構造相應的炎掩膜來計算。 # histSize:灰度級的個數,需要中括號,比如[256] hist = cv.calcHist([image], [i], None, [256], [0, 256]) # 計算直方圖 plt.plot(hist, color) plt.xlim([0, 256])
不同圖像有相差很大的直方圖

2.整體直方圖均衡
直方圖均衡是通過調整圖像的直方圖來改變圖像的對比度。用這種方法亮度可以更好地在直方圖上分布,從而就可以增強局部對比度而對整體對比度沒有太大影響。直方圖均衡函數:
# 彩色圖像直方圖均衡函數 # equalizeHist()函數可以直接求解灰度圖的直方圖均衡結果,所以如果是灰度圖像則不需要進行通道分解 def get_equalizeHist_image_color(image): (b, g, r) = cv.split(image) # 通道分解 bH = cv.equalizeHist(b) gH = cv.equalizeHist(g) rH = cv.equalizeHist(r) result = cv.merge((bH, gH, rH), ) # 通道合成 return result
圖像均衡效果如下

3.局部直方圖均衡
上述的直方圖均衡化可以達到一種全局意義上的均衡化,但是有的時候這種操作並不是很好,會把某些不該調整的部分給調整了。Opencv中還有一種直方圖均衡化方法,它是一種在圖像局部進行均衡化的方法,也就是是說把整個圖像分成許多小塊(比如按7*7作為一個小塊),那么對每個小塊進行均衡化。這種方法主要對於圖像直方圖不是那么單一的(比如存在多峰情況)圖像比較實用。Opencv中的局部均衡化實例如下:
# createCLAHE()函數中clipLimit參數為顏色對比度的閾值,titleGridSize參數為進行像素均衡化的網格大小 def get_part_equalizeHist(image): (image_b, image_r, image_g) = cv2.split(image) # 創建均衡化對象 clahe = cv2.createCLAHE(clipLimit=2, tileGridSize=(10, 10)) # 分別對三個通道進行局部均衡化,當然如果是灰度圖則不需要通道分解操作 result_b = clahe.apply(image_b) result_r = clahe.apply(image_r) result_g = clahe.apply(image_g) result = cv2.merge((result_b, result_r, result_g)) return result
局部直方圖均衡效果如下

4.直方圖匹配
雖然直方圖均衡在一定程度上能夠增強圖像的對比度,但是有的時候和我們理想的效果並不一致,如果我們能夠規定變換后直方圖的形狀,進行直方圖匹配,很多情況下要比直方圖均衡中的常量直方圖效果好。查了很多資料都表示Opencv-python中沒有免費的直方圖匹配的函數可以使用,所以這里借鑒博客上的思路自己編寫了一個求解直方圖匹配的函數
# 求累積直方圖 def my_get_add_hist(original_hist): acc_hist = np.zeros([256, 1], np.float32) next_number = 0. for i in range(256): acc_hist[i, 0] = next_number + original_hist[i, 0] next_number = acc_hist[i, 0] acc_hist /= next_number return acc_hist def my_hist_match_gray(original_image, target_image): # 求原始圖像直方圖 original_hist = cv.calcHist([original_image], [0], None, [256], [0.0, 255.]) # 求目標圖像直方圖 target_hist = cv.calcHist([target_image], [0], None, [256], [0.0, 255.]) # 求原始圖像累計直方圖 original_acc_hist = my_get_add_hist(original_hist) # 求目標圖像累計直方圖 target_acc_hist = my_get_add_hist(target_hist) # 計算原始圖像和目標圖像在所有點概率密度的差值,並取絕對值 my_subtract = abs(np.tile(target_acc_hist.reshape((256, 1)), (1, 256)) - original_acc_hist.reshape(1, 256)) # 得到插值最小處的索引 my_index = np.argmin(my_subtract, axis=0) # 將索引轉化為uint8,對於灰度圖像cv2.LUT的table必須是uint8類型 my_index = my_index.astype(np.uint8) # 將原圖像進行映射 result = cv.LUT(original_image, my_index) # 返回結果 return result def my_hist_match_RGB(original_image, target_image): # 將RGB圖像進行分解 (original_image_b, original_image_r, original_image_g) = cv.split(original_image) (target_image_b, target_image_r, target_image_g) = cv.split(target_image) # 對每個圖像進行灰度圖像匹配 result_b = my_hist_match_gray(original_image_b, target_image_b) result_r = my_hist_match_gray(original_image_r, target_image_r) result_g = my_hist_match_gray(original_image_g, target_image_g) # 將所有通道匹配結果合並 result = cv.merge((result_b, result_r, result_g)) return result
圖像進行直方圖匹配后的效果如下

