1. 程式人生 > >Python與機器視覺(x) 顏色直方圖

Python與機器視覺(x) 顏色直方圖

本系列部落格主要分享Python在機器視覺/計算機視覺下的程式設計應用
cv2包是著名的視覺庫OpenCV的Python實現

顏色直方圖一般用於統計圖片不同通道畫素強度的分佈,並可以基於此來實現對比度提升、以及簡單的目標識別、跟蹤以及分割等任務。在openCV中集成了函式cv2.calcHist()來實現直方圖的計算。
函式定義如下:

cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]]) → hist
其中images 可為單張或多張影象的array
channels

為要計算的通道數
mask 為影象掩膜
histSize 為直方圖的柱子數量,即將資料分佈在多少個區間上計數
range 為直方圖取值範圍
hist為返回值,不用填
accumulate 多張圖的時候是否疊加
所以一般呼叫的時候只需要填上面四個引數,掩膜為None,範圍0.0-255.0,數量255個:

hist = cv2.calcHist(img, [0]None[256], [0.0,255.0])

1. 看一個例子

import cv2
import numpy as np
import matplotlib.pyplot as plt

#讀入灰度影象並顯示(imread用0引數)
img = cv2.imread('img.jpeg',0) cv2.imshow('img',img) cv2.waitKey() cv2.destroyAllWindows()

在這裡插入圖片描述
img from pexels.com

接下來計算其畫素亮度分佈的直方圖,在0~255範圍內分為255級來計算

hist_255 = cv2.calcHist([img],[0], None, [256], [0.0,255.0])   #灰度圖只有一個通道,通道0
#注意,img一定要加[],使其變成三維,否則則會使用第一維進行計算,即不加中括號會計算img[0,:]的直方分佈

print('histrogram shape is'
,hist_255.shape) plt.plot(hist_255,'gray') plt.title('Histrogram of gray image') plt.show()

Output: ‘histrogram shape is’, (256L, 1L)
在這裡插入圖片描述

可以看到這幅灰度圖,其亮度集中在250附近,這是由於有大片的天空呈現白色,而暗區的峰值則來源於較暗的山體。

2.看第二個例子

我們讀入彩色圖,並將rgb通道的顏色直方圖分別畫出來:

img = cv2.imread('img.jpeg')

cv2.imshow('img',img)
cv2.waitKey()
cv2.destroyAllWindows()

在這裡插入圖片描述

我們對三個通道分別遍歷求直方圖,注意opencv中的顏色是按照b,g,r順序的。

color = ['blue','springgreen','red']  #稍微調整顯示顏色,提高視覺化效果
for i in [0,1,2]:
    hist = cv2.calcHist([img],[0], None, [256], [0.0,255.0])   #彩色圖有三個通道,通道b:0,g:1,r:2
    plt.plot(hist, color[i])
    plt.title('Histrogram of Color image')
plt.show()

在這裡插入圖片描述
可以看到在低亮度區域紅色比較多,對應了圖中上體的反光和雲彩的顏色。

可以改變區間分割的數量來得到更為平滑或者稠密的分佈曲線:

color = ['blue','springgreen','red']  #稍微調整顯示顏色,提高視覺化效果
for i in [0,1,2]:
    hist = cv2.calcHist([img],[i], None, [64], [0.0,256.0])   #用64端區間來統計0~256的灰度分佈
    plt.plot(hist, color[i])
    plt.title('Histrogram of Color image')
plt.show()

在這裡插入圖片描述

為了直觀的檢視每個通道的質量,我們畫出各個通道的影象來與直方圖對比:

#畫出各個通道檢視,opencv中為bgr順序
img_b = img.copy();img_b[:,:,2] = 0;img_b[:,:,1] = 0
img_g = img.copy();img_g[:,:,2] = 0;img_g[:,:,0] = 0
img_r = img.copy();img_r[:,:,0] = 0;img_r[:,:,1] = 0

#轉為rgb顯示
plt.figure(figsize=(10,30))
plt.subplot(1,3,1)
plt.imshow(cv2.cvtColor(img_r,cv2.COLOR_BGR2RGB))  #r
plt.title('red')
plt.subplot(1,3,2)
plt.imshow(cv2.cvtColor(img_g,cv2.COLOR_BGR2RGB))  #g
plt.title('green')
plt.subplot(1,3,3)
plt.imshow(cv2.cvtColor(img_b,cv2.COLOR_BGR2RGB))  #b
plt.title('blue')
plt.show()

可以與上圖的直方圖相比較。
在這裡插入圖片描述

3.直方圖均衡

有的時候我們需要調整影象的對比度,讓整幅圖在各個取值區間的畫素數變得更加均衡,就需要利用上面計算出的直方圖進行直方圖均衡。OpenCV中主要利用cv2.equalizeHist()來實現。

import cv2  
import numpy as np
import matplotlib.pyplot as plt

img2= cv2.imread('img2.jpeg')
# img from:https://www.pexels.com/photo/white-yacht-on-body-of-water-under-bridge-1529625/
cv2.imshow('img',img2)
cv2.waitKey()
cv2.destroyAllWindows()

在這裡插入圖片描述在這裡插入圖片描述
可以看到這幅圖的暗部和亮部分對比度不夠好,每一部分畫素分佈過於集中了,在直方圖中更為明顯:

equal_hist_255 = cv2.calcHist([img2],[0], None, [256], [0.0,256.0])   #灰度圖只有一個通道,通道0
print('histrogram shape is',hist_255.shape)
plt.plot(hist_255,'gray')
plt.title('Histrogram of gray image')
plt.show()

在這裡插入圖片描述
下面我們進行直方圖均衡化,然後檢視對比度修正的結果:

img_equal = cv2.equalizeHist(img2)
hist_255_equal = cv2.calcHist([img_equal],[0], None, [256], [0.0,256.0])   #灰度圖只有一個通道,通道0
print('histrogram shape is',hist_255.shape)

plt.figure(figsize=(20,20))
plt.subplot(2,2,1)
plt.imshow(img2,'gray')
plt.title('source')
plt.subplot(2,2,2)
plt.imshow(img_equal,'gray')
plt.title('histrogram equilized')
plt.subplot(2,2,3)
plt.plot(hist_255,'gray')
plt.title('Histrogram of gray image')
plt.subplot(2,2,4)
plt.plot(hist_255_equal,'gray')
plt.title('Histrogram of equalized image')
plt.show()

在這裡插入圖片描述
可以看到修正後的直方圖分佈更均勻,對比度也比原來好了很多,更能看清暗處的部分了。

讓我們再來看看彩色圖:

img2_color = cv2.imread('img2.jpeg')   #讀入彩色圖

img2_color_equl = img2_color.copy()
#需要按通道分別均衡化
img2_color_equl[:,:,0] = cv2.equalizeHist(img2_color[:,:,0])
img2_color_equl[:,:,1] = cv2.equalizeHist(img2_color[:,:,1])
img2_color_equl[:,:,2] = cv2.equalizeHist(img2_color[:,:,2])


plt.figure(figsize=(20,10))
plt.subplot(2,2,1)
plt.imshow(cv2.cvtColor(img2_color,cv2.COLOR_BGR2RGB))
plt.title('source')
plt.subplot(2,2,2)
plt.imshow(cv2.cvtColor(img2_color_equl,cv2.COLOR_BGR2RGB))
plt.title('Histrogram of equalized image')

plt.subplot(2,2,3)
for i in [0,1,2]:
    hist = cv2.calcHist([img2_color],[i], None, [255], [0.0,256.0])   #彩色圖三通道
    plt.plot(hist, color[i])
    plt.title('Histrogram of Color image')
plt.subplot(2,2,4)
for i in [0,1,2]:
    hist = cv2.calcHist([img2_color_equl],[i], None, [255], [0.0,256.0])   #彩色圖
    plt.plot(hist, color[i])
    plt.title('Histrogram of equalized Color image')
plt.show()

在這裡插入圖片描述
對比度有很大改觀,但是顏色產生了一定的失真。


ref:
Doc:https://docs.opencv.org/2.4/modules/imgproc/doc/histograms.html?highlight=hist
Tutorial:http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/table_of_content_imgproc/table_of_content_imgproc.html
blog1
blog2