天天看點

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個:

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()
           
Python與機器視覺(x) 顔色直方圖

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)

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

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

2.看第二個例子

我們讀入彩色圖,并将rgb通道的顔色直方圖分别畫出來:

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

cv2.imshow('img',img)
cv2.waitKey()
cv2.destroyAllWindows()
           
Python與機器視覺(x) 顔色直方圖

我們對三個通道分别周遊求直方圖,注意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()
           
Python與機器視覺(x) 顔色直方圖

可以看到在低亮度區域紅色比較多,對應了圖中上體的反光和雲彩的顔色。

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

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()
           
Python與機器視覺(x) 顔色直方圖

為了直覺的檢視每個通道的品質,我們畫出各個通道的圖像來與直方圖對比:

#畫出各個通道檢視,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()
           

可以與上圖的直方圖相比較。

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

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()
           
Python與機器視覺(x) 顔色直方圖
Python與機器視覺(x) 顔色直方圖

可以看到這幅圖的暗部和亮部分對比度不夠好,每一部分像素分布過于集中了,在直方圖中更為明顯:

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()
           
Python與機器視覺(x) 顔色直方圖

下面我們進行直方圖均衡化,然後檢視對比度修正的結果:

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()
           
Python與機器視覺(x) 顔色直方圖

可以看到修正後的直方圖分布更均勻,對比度也比原來好了很多,更能看清暗處的部分了。

讓我們再來看看彩色圖:

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()
           
Python與機器視覺(x) 顔色直方圖

對比度有很大改觀,但是顔色産生了一定的失真。

上面一種均衡化是針對全局進行調節,但可以看到某些地方發生了失真。這時候就需要利用自适應直方圖均衡。opencv中提供了限制對比度自适應直方圖均衡(Contrast Limited Adaptive Histogram Equalization,CLAHE),其函數為

cv2.createCLAHE(clipLimit=2,tileGridSize=(10,10))

。它将圖像中的區域分塊來進行處理,其中包含兩個參數,

clipLimit

為限制門檻值,

tileGridSize

為區域的大小。

img3_color = cv2.imread('img.jpg')   #讀入彩色圖

img3_color_equl = img3_color.copy()
#建立均化方法 
clahe = cv2.createCLAHE(clipLimit=2,tileGridSize=(20,20))
#按通道分别局部均衡化
img3_color_equl[:,:,0] = clahe.apply(img3_color[:,:,0])
img3_color_equl[:,:,1] = clahe.apply(img3_color[:,:,1])
img3_color_equl[:,:,2] = clahe.apply(img3_color[:,:,2])

plt.figure(figsize=(20,10))
plt.subplot(2,2,1)
plt.imshow(cv2.cvtColor(img3_color,cv2.COLOR_BGR2RGB))
plt.title('source')
plt.subplot(2,2,2)
plt.imshow(cv2.cvtColor(img3_color_equl,cv2.COLOR_BGR2RGB))
plt.title('Histrogram of Adaptive equalized image')
plt.subplot(2,2,3)
for i in [0,1,2]:
    hist = cv2.calcHist([img3_color],[i], None, [255], [0.0,256.0])   #灰階圖隻有一個通道,通道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([img3_color_equl],[i], None, [255], [0.0,256.0])   #灰階圖隻有一個通道,通道0
    plt.plot(hist, color[i])
    plt.title('Histrogram of Adaptive equalized Color image')
plt.show()
           
Python與機器視覺(x) 顔色直方圖

可以看到直方圖比前述方法的變得更為緩和,更為均勻的分布在了各個灰階值上。

pic from pexels.com

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

createCLahe

Clahe

Clahe algorithm

繼續閱讀