溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Python OpenCV 直方圖的計(jì)算與顯示的方法示例

發(fā)布時(shí)間:2020-08-22 04:50:20 來(lái)源:腳本之家 閱讀:225 作者:Daetalus 欄目:開發(fā)技術(shù)

本篇文章介紹如何用OpenCV Python來(lái)計(jì)算直方圖,并簡(jiǎn)略介紹用NumPy和Matplotlib計(jì)算和繪制直方圖

直方圖的背景知識(shí)、用途什么的就直接略過(guò)去了。這里直接介紹方法。

計(jì)算并顯示直方圖

與C++中一樣,在Python中調(diào)用的OpenCV直方圖計(jì)算函數(shù)為cv2.calcHist。

cv2.calcHist的原型為:

cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate ]]) #返回hist 

通過(guò)一個(gè)例子來(lái)了解其中的各個(gè)參數(shù):

#coding=utf-8 
import cv2 
import numpy as np 
 
image = cv2.imread("D:/histTest.jpg", 0) 
hist = cv2.calcHist([image], 
  [0], #使用的通道 
  None, #沒有使用mask 
  [256], #HistSize 
  [0.0,255.0]) #直方圖柱的范圍 

其中第一個(gè)參數(shù)必須用方括號(hào)括起來(lái)。

第二個(gè)參數(shù)是用于計(jì)算直方圖的通道,這里使用灰度圖計(jì)算直方圖,所以就直接使用第一個(gè)通道;

第三個(gè)參數(shù)是Mask,這里沒有使用,所以用None。

第四個(gè)參數(shù)是histSize,表示這個(gè)直方圖分成多少份(即多少個(gè)直方柱)。第二個(gè)例子將繪出直方圖,到時(shí)候會(huì)清楚一點(diǎn)。

第五個(gè)參數(shù)是表示直方圖中各個(gè)像素的值,[0.0, 256.0]表示直方圖能表示像素值從0.0到256的像素。

最后是兩個(gè)可選參數(shù),由于直方圖作為函數(shù)結(jié)果返回了,所以第六個(gè)hist就沒有意義了(待確定)

最后一個(gè)accumulate是一個(gè)布爾值,用來(lái)表示直方圖是否疊加。

彩色圖像不同通道的直方圖

下面來(lái)看下彩色圖像的直方圖處理。以最著名的lena.jpg為例,首先讀取并分離各通道:

import cv2   
import numpy as np   
   
img = cv2.imread("D:/lena.jpg")   
b, g, r = cv2.split(img) 

接著計(jì)算每個(gè)通道的直方圖,這里將其封裝成一個(gè)函數(shù):

def calcAndDrawHist(image, color):  
  hist= cv2.calcHist([image], [0], None, [256], [0.0,255.0])  
  minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(hist)  
  histImg = np.zeros([256,256,3], np.uint8)  
  hpt = int(0.9* 256);  
    
  for h in range(256):  
    intensity = int(hist[h]*hpt/maxVal)  
    cv2.line(histImg,(h,256), (h,256-intensity), color)  
      
  return histImg;  

這里只是之前代碼的簡(jiǎn)單封裝,所以注釋就省掉了。

接著在主函數(shù)中使用:

if __name__ == '__main__':  
  img = cv2.imread("D:/lena.jpg")  
  b, g, r = cv2.split(img)  
  
  histImgB = calcAndDrawHist(b, [255, 0, 0])  
  histImgG = calcAndDrawHist(g, [0, 255, 0])  
  histImgR = calcAndDrawHist(r, [0, 0, 255])  
    
  cv2.imshow("histImgB", histImgB)  
  cv2.imshow("histImgG", histImgG)  
  cv2.imshow("histImgR", histImgR)  
  cv2.imshow("Img", img)  
  cv2.waitKey(0)  
  cv2.destroyAllWindows()  

這樣就能得到三個(gè)通道的直方圖了,如下:

Python OpenCV 直方圖的計(jì)算與顯示的方法示例

更進(jìn)一步

這樣做有點(diǎn)繁瑣,參考abid rahman的做法,無(wú)需分離通道,用折線來(lái)描繪直方圖的邊界可在一副圖中同時(shí)繪制三個(gè)通道的直方圖。方法如下:

#coding=utf-8  
import cv2  
import numpy as np  
     
img = cv2.imread('D:/lena.jpg')  
h = np.zeros((256,256,3)) #創(chuàng)建用于繪制直方圖的全0圖像  
     
bins = np.arange(256).reshape(256,1) #直方圖中各bin的頂點(diǎn)位置  
color = [ (255,0,0),(0,255,0),(0,0,255) ] #BGR三種顏色  
for ch, col in enumerate(color):  
  originHist = cv2.calcHist([img],[ch],None,[256],[0,256])  
  cv2.normalize(originHist, originHist,0,255*0.9,cv2.NORM_MINMAX)  
  hist=np.int32(np.around(originHist))  
  pts = np.column_stack((bins,hist))  
  cv2.polylines(h,[pts],False,col)  
     
h=np.flipud(h)  
     
cv2.imshow('colorhist',h)  
cv2.waitKey(0)  

結(jié)果如下圖所示:

Python OpenCV 直方圖的計(jì)算與顯示的方法示例

代碼說(shuō)明:

這里的for循環(huán)是對(duì)三個(gè)通道遍歷一次,每次繪制相應(yīng)通道的直方圖的折線。for循環(huán)的第一行是計(jì)算對(duì)應(yīng)通道的直方圖,經(jīng)過(guò)上面的介紹,應(yīng)該很容易就能明白。

這里所不同的是沒有手動(dòng)的計(jì)算直方圖的最大值再乘以一個(gè)系數(shù),而是直接調(diào)用了OpenCV的歸一化函數(shù)。該函數(shù)將直方圖的范圍限定在0-255×0.9之間,與之前的一樣。下面的hist= np.int32(np.around(originHist))先將生成的原始直方圖中的每個(gè)元素四舍六入五湊偶取整(cv2.calcHist函數(shù)得到的是float32類型的數(shù)組),接著將整數(shù)部分轉(zhuǎn)成np.int32類型。即61.123先轉(zhuǎn)成61.0,再轉(zhuǎn)成61。注意,這里必須使用np.int32(...)進(jìn)行轉(zhuǎn)換,numpy的轉(zhuǎn)換函數(shù)可以對(duì)數(shù)組中的每個(gè)元素都進(jìn)行轉(zhuǎn)換,而Python的int(...)只能轉(zhuǎn)換一個(gè)元素,如果使用int(...),將導(dǎo)致only length-1 arrays can be converted to Python scalars錯(cuò)誤。

下面的pts = np.column_stack((bins,hist))是將直方圖中每個(gè)bin的值轉(zhuǎn)成相應(yīng)的坐標(biāo)。比如hist[0] =3,...,hist[126] = 178,...,hist[255] = 5;而bins的值為[[0],[1],[2]...,[255]]。使用np.column_stack將其組合成[0, 3]、[126, 178]、[255, 5]這樣的坐標(biāo)作為元素組成的數(shù)組。

最后使用cv2.polylines函數(shù)根據(jù)這些點(diǎn)繪制出折線,第三個(gè)False參數(shù)指出這個(gè)折線不需要閉合。第四個(gè)參數(shù)指定了折線的顏色。

當(dāng)所有完成后,別忘了用h = np.flipud(h)反轉(zhuǎn)繪制好的直方圖,因?yàn)槔L制時(shí),[0,0]在圖像的左上角。這在直方圖可視化一節(jié)中有說(shuō)明。

NumPy版的直方圖計(jì)算

在查閱abid rahman的資料時(shí),發(fā)現(xiàn)他用NumPy的直方圖計(jì)算函數(shù)np.histogram也實(shí)現(xiàn)了相同的效果。如下:

#coding=utf-8 
import cv2 
import numpy as np 
 
img = cv2.imread('D:/lena.jpg') 
h = np.zeros((300,256,3)) 
bins = np.arange(257) 
bin = bins[0:-1] 
color = [ (255,0,0),(0,255,0),(0,0,255) ] 
 
for ch,col in enumerate(color): 
  item = img[:,:,ch] 
  N,bins = np.histogram(item,bins) 
  v=N.max() 
  N = np.int32(np.around((N*255)/v)) 
  N=N.reshape(256,1) 
  pts = np.column_stack((bin,N)) 
  cv2.polylines(h,[pts],False,col) 
 
h=np.flipud(h) 
 
cv2.imshow('img',h) 
cv2.waitKey(0) 

效果圖和上面的一個(gè)相同。NumPy的histogram函數(shù)將在NumPy通用函數(shù)這篇博文中介紹,這里就不詳細(xì)解釋了。這里采用的是與一開始相同的比例系數(shù)的方法,參考本文的第二節(jié)。

另外,通過(guò)NumPy和matplotlib可以更方便的繪制出直方圖,下面的代碼供大家參考,如果有機(jī)會(huì),再寫的專門介紹matplotlib的文章。

import matplotlib.pyplot as plt 
import numpy as np 
import cv2 
 
img = cv2.imread('D:/lena.jpg') 
bins = np.arange(257) 
 
item = img[:,:,1] 
hist,bins = np.histogram(item,bins) 
width = 0.7*(bins[1]-bins[0]) 
center = (bins[:-1]+bins[1:])/2 
plt.bar(center, hist, align = 'center', width = width) 
plt.show() 

Python OpenCV 直方圖的計(jì)算與顯示的方法示例

這里顯示的是綠色通道的直方圖。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI