[英]Python - Calculate histogram of image
我正在自學計算機圖像處理的基礎知識,同時我也在自學 Python。
給定具有 3 個通道的尺寸為 2048x1354 的圖像x
,有效地計算像素強度的直方圖。
import numpy as np, cv2 as cv
img = cv.imread("image.jpg")
bins = np.zeros(256, np.int32)
for i in range(0, img.shape[0]):
for j in range(0, img.shape[1]):
intensity = 0
for k in range(0, len(img[i][j])):
intensity += img[i][j][k]
bins[intensity/3] += 1
print bins
我的問題是這段代碼運行得很慢,大約 30 秒。 我怎樣才能加快速度並變得更加 Pythonic?
您可以使用更新的OpenCV python接口,它本身使用numpy數組並使用matplotlib hist
繪制像素強度的直方圖。 我的電腦只需不到秒。
import matplotlib.pyplot as plt
import cv2
im = cv2.imread('image.jpg')
# calculate mean value from RGB channels and flatten to 1D array
vals = im.mean(axis=2).flatten()
# plot histogram with 255 bins
b, bins, patches = plt.hist(vals, 255)
plt.xlim([0,255])
plt.show()
更新:上面指定的箱數並不總是提供所需的結果,因為最小值和最大值是根據實際值計算的。 此外,值254和255的計數在最后一個倉中求和。 這是更新的代碼,它總是正確繪制直方圖,條形圖以0..255為中心
import numpy as np
import matplotlib.pyplot as plt
import cv2
# read image
im = cv2.imread('image.jpg')
# calculate mean value from RGB channels and flatten to 1D array
vals = im.mean(axis=2).flatten()
# calculate histogram
counts, bins = np.histogram(vals, range(257))
# plot histogram centered on values 0..255
plt.bar(bins[:-1] - 0.5, counts, width=1, edgecolor='none')
plt.xlim([-0.5, 255.5])
plt.show()
在純python中不可能這樣做(即不刪除for循環)。 Python的for循環結構有太多東西要快。 如果你真的想要保持for循環,唯一的解決方案是numba或cython,但這些都有自己的問題。 通常,這樣的循環用c / c ++編寫(在我看來最直接),然后從python調用,它的主要作用是腳本語言。
話雖如此,opencv + numpy提供了足夠多的有用例程,因此在90%的情況下,可以簡單地使用內置函數,而無需編寫自己的像素級代碼。
這是numba的解決方案,無需更改循環代碼。 在我的電腦上,它比純蟒蛇快約150倍。
import numpy as np, cv2 as cv
from time import time
from numba import jit,int_,uint8
@jit(argtypes=(uint8[:,:,:],int_[:]),
locals=dict(intensity=int_),
nopython=True
)
def numba(img,bins):
for i in range(0, img.shape[0]):
for j in range(0, img.shape[1]):
intensity = 0
for k in range(0, len(img[i][j])):
intensity += img[i][j][k]
bins[intensity/3] += 1
def python(img,bins):
for i in range(0, img.shape[0]):
for j in range(0, img.shape[1]):
intensity = 0
for k in range(0, len(img[i][j])):
intensity += img[i][j][k]
bins[intensity/3] += 1
img = cv.imread("image.jpg")
bins = np.zeros(256, np.int32)
t0 = time()
numba(img,bins)
t1 = time()
#print bins
print t1 - t0
bins[...]=0
t0 = time()
python(img,bins)
t1 = time()
#print bins
print t1 - t0
如果您只想計算數組中每個值的出現次數, numpy
可以使用numpy.bincount
為您執行此numpy.bincount
。 在你的情況下:
arr = numpy.asarray(img)
flat = arr.reshape(numpy.prod(arr.shape[:2]),-1)
bins = numpy.bincount(np.sum(flat,1)/flat.shape[1],minsize=256)
我在這里使用numpy.asarray
來確保img
是一個numpy數組,所以我可以將它壓平到一維數組bincount
需要。 如果img
已經是一個數組,則可以跳過該步驟。 計數本身將非常快。 這里的大部分時間可能用於將cv矩陣轉換為數組。
編輯:根據這個答案 ,您可能需要使用numpy.asarray(img[:,:])
(或可能img[:,:,:]
才能成功將圖像轉換為數組。 另一方面,根據這一點 ,你從較新版本的openCV中獲得的是一個numpy陣列。 所以在這種情況下你可以完全跳過asarray
。
看看MatPlotLib 。 這將帶您完成您想要做的所有事情,並且沒有for循環。
來自OpenCV 文檔:
單通道直方圖(圖像轉灰度):
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('home.jpg',0)
plt.hist(img.ravel(),256,[0,256]); plt.show()
RGB 直方圖(每個通道單獨)
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('home.jpg')
color = ('b','g','r')
for i,col in enumerate(color):
histr = cv.calcHist([img],[i],None,[256],[0,256])
plt.plot(histr,color = col)
plt.xlim([0,256])
plt.show()
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.