1. 程式人生 > >Python 影象處理 OpenCV (16):影象直方圖

Python 影象處理 OpenCV (16):影象直方圖

![](https://cdn.geekdigging.com/opencv/opencv_header.png) 前文傳送門: [「Python 影象處理 OpenCV (1):入門」](https://www.geekdigging.com/2020/05/17/5513454552/) [「Python 影象處理 OpenCV (2):畫素處理與 Numpy 操作以及 Matplotlib 顯示影象」](https://www.geekdigging.com/2020/05/18/4936041986/) [「Python 影象處理 OpenCV (3):影象屬性、影象感興趣 ROI 區域及通道處理」](https://www.geekdigging.com/2020/05/19/1227329671/) [「Python 影象處理 OpenCV (4):影象算數運算以及修改顏色空間」](https://www.geekdigging.com/2020/05/21/1757913240/) [「Python 影象處理 OpenCV (5):影象的幾何變換」](https://www.geekdigging.com/2020/05/23/4331122737/) [「Python 影象處理 OpenCV (6):影象的閾值處理」](https://www.geekdigging.com/2020/06/03/6651375581/) [「Python 影象處理 OpenCV (7):影象平滑(濾波)處理」](https://www.geekdigging.com/2020/06/06/8676263283/) [「Python 影象處理 OpenCV (8):影象腐蝕與影象膨脹」](https://www.geekdigging.com/2020/06/08/5731186312/) [「Python 影象處理 OpenCV (9):影象處理形態學開運算、閉運算以及梯度運算」](https://www.geekdigging.com/2020/06/11/5023174082/) [「Python 影象處理 OpenCV (10):影象處理形態學之頂帽運算與黑帽運算」](https://www.geekdigging.com/2020/06/18/9182078666/) [「Python 影象處理 OpenCV (11):Canny 運算元邊緣檢測技術」](https://www.geekdigging.com/2020/06/25/4009152544/) [「Python 影象處理 OpenCV (12): Roberts 運算元、 Prewitt 運算元、 Sobel 運算元和 Laplacian 運算元邊緣檢測技術」](https://www.geekdigging.com/2020/06/26/7999051794/) [「Python 影象處理 OpenCV (13): Scharr 運算元和 LOG 運算元邊緣檢測技術」](https://www.geekdigging.com/2020/07/09/4977894293/) [「Python 影象處理 OpenCV (14):影象金字塔」](https://www.geekdigging.com/2020/07/12/7005924297/) [「Python 影象處理 OpenCV (15):影象輪廓」](https://www.geekdigging.com/2020/07/26/3988009869/) ## 直方圖 首先,第一個問題是什麼是直方圖? 直方圖這個應該都知道吧,不知道的話就是下面這玩意: ![](https://cdn.geekdigging.com/opencv/16/zhifangtu.jpg) 那麼影象灰度直方圖是什麼鬼? 直方圖是都是由橫縱座標組成的,而影象直方圖的橫座標 X 軸上表示的是畫素值(不總是從 0 到 255 的範圍),在縱座標 Y 軸上表示的相應畫素數。 所以,直方圖是可以對整幅圖的灰度分佈進行整體瞭解的圖示,通過直方圖我們可以對影象的對比度、亮度和灰度分佈等有一個直觀瞭解。 還沒看懂?簡單地說,就是把一幅影象中每一個畫素出現的次數都先統計出來,然後把每一個畫素出現的次數除以總的畫素個數,得到的就是這個畫素出現的頻率,然後再把畫素與該畫素出現的頻率用圖表示出來,就是灰度直方圖。 ![](https://cdn.geekdigging.com/opencv/16/histogram_sample.jpg) 上面這張圖來自官方網站,在這張圖中,我們可以得到如下資訊: - 左側區域顯示影象中較暗畫素的數量(左側的灰度級更趨近於 0 )。 - 右側區域則顯示明亮畫素的數量(右側的灰度級更趨近於 255)。 - 暗區域多於亮區域,而中間調的數量(中間值的畫素值,例如127附近)則非常少。 ## 繪製直方圖 在繪製直方圖的時候,有兩種方法: 1. 使用 Matplotlib 繪圖功能。 2. 使用 OpenCV 繪圖功能。 ### 使用 Matplotlib 繪圖 Matplotlib 帶有一個強大的直方圖繪圖功能:`matplotlib.pyplot.hist()` ,這個方法可以直接找到直方圖進行繪製。 在看示例程式碼之前,有兩個引數需要先介紹下: - 資料來源:資料來源必須是一維陣列,通常需要通過函式 `ravel()` 拉直影象,而函式 `ravel()` 的作用是將多維陣列降為一維陣列。 - 畫素級:一般是 256 ,表示 [0, 255] 。 程式碼實現: ```python import cv2 as cv import matplotlib.pyplot as plt img = cv.imread("maliao.jpg") cv.imshow("img", img) cv.waitKey(0) cv.destroyAllWindows() plt.hist(img.ravel(), 256, [0, 256]) plt.show() ``` 輸出結果: ![](https://cdn.geekdigging.com/opencv/16/plt_result.png) 當然,我們除了可以繪製灰度直方圖以外,還可以繪製出 `r,g,b` 不同通道的直方圖,可以看下面的程式碼: ```python import cv2 as cv import matplotlib.pyplot as plt img = cv.imread("tiankong.jpg") color = ('b', 'g', 'r') cv.imshow("img", img) cv.waitKey(0) cv.destroyAllWindows() for i, col in enumerate(color): histr = cv.calcHist([img], [i], None, [256], [0, 256]) plt.plot(histr, color = col) plt.xlim([0, 256]) plt.show() ``` ![](https://cdn.geekdigging.com/opencv/16/rgb_result.png) ### 使用 OpenCV 繪製直方圖 使用 OpenCV 繪製直方圖還是有點費勁兒的,首先我們確認橫座標是影象中各個畫素點的灰度級,縱座標是具有該灰度級的畫素個數。 接下來要介紹幾個新概念: **BINS:** 在前面的直方圖中,我們顯示的是每個畫素值的畫素數,即從 0 到 255 。那麼現在會有一個問題,如果一個直方圖我並不想找到所有的畫素數量,而是取一定範圍內的畫素值,如:先找到 0 到 15 之間的畫素數,然後找到 16 到 31 之間,......, 240 到 255 之間的畫素數。 這樣,我們將這個直方圖分成了 16 個子部分,每個子部分的值就是其中所有畫素數的總和。每個子部分都稱為 **BIN** 。在第一種情況下, **BIN** 的數量為 256 個(每個畫素一個),而在第二種情況下, **BIN** 的數量僅為 16 個。 **DIMS:** 這是我們為其收集資料的引數的數量。在這種情況下,我們僅收集關於強度值的一件事的資料。所以這裡是1。 **RANGE:** 這是要測量的強度值的範圍。通常,它是 `[0,256]` ,即所有強度值。 使用 OpenCV 的繪製直方圖,我們會用到一個新的函式 `calcHist()` ,它的原函式如下: ```python def calcHist(images, channels, mask, histSize, ranges, hist=None, accumulate=None): ``` - 引數1:要計算的原圖,以方括號的傳入,如:[img]。 - 引數2:灰度圖寫[0]就行,彩色圖 B/G/R 分別傳入 [0]/[1]/[2] 。 - 引數3:要計算的區域ROI,計算整幅圖的話,寫None。 - 引數4:就是我們上面提到的 BINS ,子區段數目。 - 引數5:range,要計算的畫素值範圍,一般為 [0,256] 。 接下來我們開始畫圖,首先我們需要使用 `calcHist()` 來查詢整個影象的直方圖。 ```python import cv2 as cv import matplotlib.pyplot as plt img = cv.imread("tiankong.jpg") # 引數:原影象 通道[0]-B 掩碼 BINS為256 畫素範圍0-255 histB = cv.calcHist([img], [0], None, [256], [0, 255]) histG = cv.calcHist([img], [1], None, [256], [0, 255]) histR = cv.calcHist([img], [2], None, [256], [0, 255]) cv.imshow("img", img) cv.waitKey(0) cv.destroyAllWindows() plt.plot(histB, color='b') plt.plot(histG, color='g') plt.plot(histR, color='r') plt.show() ``` ![](https://cdn.geekdigging.com/opencv/16/cv_rgb_result.png) ## 直方圖均衡化 一副效果好的影象通常在直方圖上的分佈比較均勻,直方圖均衡化就是用來改善影象的全域性亮度和對比度。 ![](https://cdn.geekdigging.com/opencv/16/histogram_equalization.png) - 灰度圖均衡,直接使用 `equalizeHist()` 函式。 - 彩色圖均衡,分別在不同的通道均衡後合併。 示例程式碼如下: ```python import cv2 as cv import numpy as np img = cv.imread("dahai.jpg") gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) # 灰度圖均衡化 equ = cv.equalizeHist(gray) # 水平拼接原圖和均衡圖 result1 = np.hstack((gray, equ)) cv.imwrite('grey_equ.png', result1) # 彩色影象均衡化,需要分解通道 對每一個通道均衡化 (b, g, r) = cv.split(img) bH = cv.equalizeHist(b) gH = cv.equalizeHist(g) rH = cv.equalizeHist(r) # 合併每一個通道 equ2 = cv.merge((bH, gH, rH)) # 水平拼接原圖和均衡圖 result2 = np.hstack((img, equ2)) cv.imwrite('bgr_equ.png', result2) ``` 結果: ![](https://cdn.geekdigging.com/opencv/16/grey_equ.png) ![](https://cdn.geekdigging.com/opencv/16/bgr_equ.png) ## 自適應直方圖均衡 上面介紹的直方圖均值化是針對整幅圖片的,這樣有好處也有不好的地方,會導致一些圖片部位太亮,導致大部分細節丟失。如下面這兩張圖片: ![](https://cdn.geekdigging.com/opencv/16/clahe_1.jpg) 直方圖均衡後,背景對比度確實得到了改善。但是在兩個影象中比較雕像的臉,由於亮度過高,丟失了大多數資訊。 因此,為了解決這個問題,引入了 **自適應直方圖均衡** 來解決這個問題。 它在每一個小區域內(預設 8×8 )進行直方圖均衡化。當然,如果有噪點的話,噪點會被放大,需要對小區域內的對比度進行了限制。 ```python import cv2 as cv import numpy as np img = cv.imread('clahe_src.jpg', 0) # 全域性直方圖均衡 equ = cv.equalizeHist(img) # 自適應直方圖均衡 clahe = cv.createCLAHE(clipLimit = 2.0, tileGridSize = (8, 8)) cl1 = clahe.apply(img) # 水平拼接三張影象 result1 = np.hstack((img, equ, cl1)) cv.imwrite('clahe_result.jpg', result1) ``` ![](https://cdn.geekdigging.com/opencv/16/clahe_result.jpg) ## 參考 https://blog.csdn.net/Eastmount/article/details/83758402 http://www.woshicver.com/FifthSection/4_10_1_%E7%9B%B4%E6%96%B9%E5%9B%BE-1%EF%BC%9A%E6%9F%A5%E6%89%BE%EF%BC%8C%E7%BB%98%E5%88%B6%EF%BC%8C%E5%88%86%E6%9E%90/ https://zhuanlan.zhihu.com/p/