天天看點

Python 圖像處理 OpenCV (16):圖像直方圖

Python 圖像處理 OpenCV (16):圖像直方圖

前文傳送門:

「Python 圖像處理 OpenCV (1):入門」

「Python 圖像處理 OpenCV (2):像素處理與 Numpy 操作以及 Matplotlib 顯示圖像」

「Python 圖像處理 OpenCV (3):圖像屬性、圖像感興趣 ROI 區域及通道處理」

「Python 圖像處理 OpenCV (4):圖像算數運算以及修改顔色空間」

「Python 圖像處理 OpenCV (5):圖像的幾何變換」

「Python 圖像處理 OpenCV (6):圖像的門檻值處理」

「Python 圖像處理 OpenCV (7):圖像平滑(濾波)處理」

「Python 圖像處理 OpenCV (8):圖像腐蝕與圖像膨脹」

「Python 圖像處理 OpenCV (9):圖像處理形态學開運算、閉運算以及梯度運算」

「Python 圖像處理 OpenCV (10):圖像處理形态學之頂帽運算與黑帽運算」

「Python 圖像處理 OpenCV (11):Canny 算子邊緣檢測技術」

「Python 圖像處理 OpenCV (12): Roberts 算子、 Prewitt 算子、 Sobel 算子和 Laplacian 算子邊緣檢測技術」

「Python 圖像處理 OpenCV (13): Scharr 算子和 LOG 算子邊緣檢測技術」

「Python 圖像處理 OpenCV (14):圖像金字塔」

「Python 圖像處理 OpenCV (15):圖像輪廓」

直方圖

首先,第一個問題是什麼是直方圖?

直方圖這個應該都知道吧,不知道的話就是下面這玩意:

Python 圖像處理 OpenCV (16):圖像直方圖

那麼圖像灰階直方圖是什麼鬼?

直方圖是都是由橫縱坐标組成的,而圖像直方圖的橫坐标 X 軸上表示的是像素值(不總是從 0 到 255 的範圍),在縱坐标 Y 軸上表示的相應像素數。

是以,直方圖是可以對整幅圖的灰階分布進行整體了解的圖示,通過直方圖我們可以對圖像的對比度、亮度和灰階分布等有一個直覺了解。

還沒看懂?簡單地說,就是把一幅圖像中每一個像素出現的次數都先統計出來,然後把每一個像素出現的次數除以總的像素個數,得到的就是這個像素出現的頻率,然後再把像素與該像素出現的頻率用圖表示出來,就是灰階直方圖。

Python 圖像處理 OpenCV (16):圖像直方圖

上面這張圖來自官方網站,在這張圖中,我們可以得到如下資訊:

  • 左側區域顯示圖像中較暗像素的數量(左側的灰階級更趨近于 0 )。
  • 右側區域則顯示明亮像素的數量(右側的灰階級更趨近于 255)。
  • 暗區域多于亮區域,而中間調的數量(中間值的像素值,例如127附近)則非常少。

繪制直方圖

在繪制直方圖的時候,有兩種方法:

  1. 使用 Matplotlib 繪圖功能。
  2. 使用 OpenCV 繪圖功能。

使用 Matplotlib 繪圖

Matplotlib 帶有一個強大的直方圖繪圖功能:

matplotlib.pyplot.hist()

,這個方法可以直接找到直方圖進行繪制。

在看示例代碼之前,有兩個參數需要先介紹下:

  • 資料源:資料源必須是一維數組,通常需要通過函數

    ravel()

    拉直圖像,而函數

    ravel()

    的作用是将多元數組降為一維數組。
  • 像素級:一般是 256 ,表示 [0, 255] 。

代碼實作:

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()
           

輸出結果:

Python 圖像處理 OpenCV (16):圖像直方圖

當然,我們除了可以繪制灰階直方圖以外,還可以繪制出

r,g,b

不同通道的直方圖,可以看下面的代碼:

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()
           
Python 圖像處理 OpenCV (16):圖像直方圖

使用 OpenCV 繪制直方圖

使用 OpenCV 繪制直方圖還是有點費勁兒的,首先我們确認橫坐标是圖像中各個像素點的灰階級,縱坐标是具有該灰階級的像素個數。

接下來要介紹幾個新概念:

BINS:

在前面的直方圖中,我們顯示的是每個像素值的像素數,即從 0 到 255 。那麼現在會有一個問題,如果一個直方圖我并不想找到所有的像素數量,而是取一定範圍内的像素值,如:先找到 0 到 15 之間的像素數,然後找到 16 到 31 之間,......, 240 到 255 之間的像素數。

這樣,我們将這個直方圖分成了 16 個子部分,每個子部分的值就是其中所有像素數的總和。每個子部分都稱為 BIN 。在第一種情況下, BIN 的數量為 256 個(每個像素一個),而在第二種情況下, BIN 的數量僅為 16 個。

DIMS:

這是我們為其收集資料的參數的數量。在這種情況下,我們僅收集關于強度值的一件事的資料。是以這裡是1。

RANGE:

這是要測量的強度值的範圍。通常,它是

[0,256]

,即所有強度值。

使用 OpenCV 的繪制直方圖,我們會用到一個新的函數

calcHist()

,它的原函數如下:

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()

來查找整個圖像的直方圖。

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()
           
Python 圖像處理 OpenCV (16):圖像直方圖

直方圖均衡化

一副效果好的圖像通常在直方圖上的分布比較均勻,直方圖均衡化就是用來改善圖像的全局亮度和對比度。

Python 圖像處理 OpenCV (16):圖像直方圖
  • 灰階圖均衡,直接使用

    equalizeHist()

    函數。
  • 彩色圖均衡,分别在不同的通道均衡後合并。

示例代碼如下:

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)
           

結果:

Python 圖像處理 OpenCV (16):圖像直方圖
Python 圖像處理 OpenCV (16):圖像直方圖

自适應直方圖均衡

上面介紹的直方圖均值化是針對整幅圖檔的,這樣有好處也有不好的地方,會導緻一些圖檔部位太亮,導緻大部分細節丢失。如下面這兩張圖檔:

Python 圖像處理 OpenCV (16):圖像直方圖

直方圖均衡後,背景對比度确實得到了改善。但是在兩個圖像中比較雕像的臉,由于亮度過高,丢失了大多數資訊。

是以,為了解決這個問題,引入了 自适應直方圖均衡 來解決這個問題。

它在每一個小區域内(預設 8×8 )進行直方圖均衡化。當然,如果有噪點的話,噪點會被放大,需要對小區域内的對比度進行了限制。

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)
           
Python 圖像處理 OpenCV (16):圖像直方圖

參考

https://blog.csdn.net/Eastmount/article/details/83758402

http://www.woshicver.com/FifthSection/4_10_1_直方圖-1:查找,繪制,分析/

https://zhuanlan.zhihu.com/p/61879400

掃描二維碼關注「極客挖掘機」公衆号!

作者:極客挖掘機

定期發表作者的思考:技術、産品、營運、自我提升等。

本文版權歸作者極客挖掘機和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

如果您覺得作者的文章對您有幫助,就來作者個人小站逛逛吧:

極客挖掘機