簡體   English   中英

Python - 計算圖像的直方圖

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM