我正在努力教自己计算机化图像处理的基础知识,同时我也在教自己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?
答案 0 :(得分:5)
您可以使用更新的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()
答案 1 :(得分:3)
在纯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
答案 2 :(得分:2)
如果您只想计算数组中每个值的出现次数,numpy
可以使用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矩阵转换为数组。
修改:根据this answer,您可能需要使用numpy.asarray(img[:,:])
(或可能img[:,:,:]
)才能成功将图片转换为数组。另一方面,根据this,从较新版本的openCV中获得的内容已经是一个numpy数组。因此,在这种情况下,您可以完全跳过asarray
。
答案 3 :(得分:1)
看看at MatPlotLib。这将带您完成您想要做的所有事情,并且没有for循环。