您好,登錄后才能下訂單哦!
本篇文章介紹如何用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è)通道的直方圖了,如下:
更進(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é)果如下圖所示:
代碼說(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()
這里顯示的是綠色通道的直方圖。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。
免責(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)容。