Python OpenCV 直方圖的計(jì)算與顯示的方法示例
本篇文章介紹如何用OpenCV Python來計(jì)算直方圖,并簡略介紹用NumPy和Matplotlib計(jì)算和繪制直方圖
直方圖的背景知識、用途什么的就直接略過去了。這里直接介紹方法。
計(jì)算并顯示直方圖
與C++中一樣,在Python中調(diào)用的OpenCV直方圖計(jì)算函數(shù)為cv2.calcHist。
cv2.calcHist的原型為:
cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate ]]) #返回hist
通過一個例子來了解其中的各個參數(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]) #直方圖柱的范圍
其中第一個參數(shù)必須用方括號括起來。
第二個參數(shù)是用于計(jì)算直方圖的通道,這里使用灰度圖計(jì)算直方圖,所以就直接使用第一個通道;
第三個參數(shù)是Mask,這里沒有使用,所以用None。
第四個參數(shù)是histSize,表示這個直方圖分成多少份(即多少個直方柱)。第二個例子將繪出直方圖,到時候會清楚一點(diǎn)。
第五個參數(shù)是表示直方圖中各個像素的值,[0.0, 256.0]表示直方圖能表示像素值從0.0到256的像素。
最后是兩個可選參數(shù),由于直方圖作為函數(shù)結(jié)果返回了,所以第六個hist就沒有意義了(待確定)
最后一個accumulate是一個布爾值,用來表示直方圖是否疊加。
彩色圖像不同通道的直方圖
下面來看下彩色圖像的直方圖處理。以最著名的lena.jpg為例,首先讀取并分離各通道:
import cv2 import numpy as np img = cv2.imread("D:/lena.jpg") b, g, r = cv2.split(img)
接著計(jì)算每個通道的直方圖,這里將其封裝成一個函數(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;
這里只是之前代碼的簡單封裝,所以注釋就省掉了。
接著在主函數(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()
這樣就能得到三個通道的直方圖了,如下:
更進(jìn)一步
這樣做有點(diǎn)繁瑣,參考abid rahman的做法,無需分離通道,用折線來描繪直方圖的邊界可在一副圖中同時繪制三個通道的直方圖。方法如下:
#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é)果如下圖所示:
代碼說明:
這里的for循環(huán)是對三個通道遍歷一次,每次繪制相應(yīng)通道的直方圖的折線。for循環(huán)的第一行是計(jì)算對應(yīng)通道的直方圖,經(jīng)過上面的介紹,應(yīng)該很容易就能明白。
這里所不同的是沒有手動的計(jì)算直方圖的最大值再乘以一個系數(shù),而是直接調(diào)用了OpenCV的歸一化函數(shù)。該函數(shù)將直方圖的范圍限定在0-255×0.9之間,與之前的一樣。下面的hist= np.int32(np.around(originHist))先將生成的原始直方圖中的每個元素四舍六入五湊偶取整(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ù)可以對數(shù)組中的每個元素都進(jìn)行轉(zhuǎn)換,而Python的int(...)只能轉(zhuǎn)換一個元素,如果使用int(...),將導(dǎo)致only length-1 arrays can be converted to Python scalars錯誤。
下面的pts = np.column_stack((bins,hist))是將直方圖中每個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)繪制出折線,第三個False參數(shù)指出這個折線不需要閉合。第四個參數(shù)指定了折線的顏色。
當(dāng)所有完成后,別忘了用h = np.flipud(h)反轉(zhuǎn)繪制好的直方圖,因?yàn)槔L制時,[0,0]在圖像的左上角。這在直方圖可視化一節(jié)中有說明。
NumPy版的直方圖計(jì)算
在查閱abid rahman的資料時,發(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)
效果圖和上面的一個相同。NumPy的histogram函數(shù)將在NumPy通用函數(shù)這篇博文中介紹,這里就不詳細(xì)解釋了。這里采用的是與一開始相同的比例系數(shù)的方法,參考本文的第二節(jié)。
另外,通過NumPy和matplotlib可以更方便的繪制出直方圖,下面的代碼供大家參考,如果有機(jī)會,再寫的專門介紹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()
這里顯示的是綠色通道的直方圖。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
python使用xlrd實(shí)現(xiàn)檢索excel中某列含有指定字符串記錄的方法
這篇文章主要介紹了python使用xlrd實(shí)現(xiàn)檢索excel中某列含有指定字符串記錄的方法,涉及Python使用xlrd模塊檢索Excel的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-05-05wxpython 最小化到托盤與歡迎圖片的實(shí)現(xiàn)方法
這篇文章主要分享一個python實(shí)例代碼,使用wxpython實(shí)現(xiàn)最小化到托盤與歡迎圖片,需要的朋友可以參考下2014-06-06詳解基于Transformer實(shí)現(xiàn)電影評論星級分類任務(wù)
這篇文章主要為大家介紹了詳解基于Transformer實(shí)現(xiàn)電影評論星級分類任務(wù)過程解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04Django 權(quán)限認(rèn)證(根據(jù)不同的用戶,設(shè)置不同的顯示和訪問權(quán)限)
這篇文章主要介紹了Django 權(quán)限認(rèn)證(根據(jù)不同的用戶,設(shè)置不同的顯示和訪問權(quán)限),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07python進(jìn)行二次方程式計(jì)算的實(shí)例講解
在本篇內(nèi)容里小編給大家整理了一篇關(guān)于python進(jìn)行二次方程式計(jì)算的實(shí)例講解內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。2020-12-12Python?Asyncio?庫之同步原語常用函數(shù)詳解
這篇文章主要為大家介紹了Python?Asyncio?庫之同步原語常用函數(shù)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03