2014-03-03 51 views
3

我正在教自己计算机图像处理的基础知识,并且我在同一时间自学Python。Python - 计算图像的直方图

给定具有3个通道的尺寸2048×1354的图像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?

+1

http://stackoverflow.com/a/14728935/995394也许这是有帮助的。 – iMom0

+0

使用3个嵌套'for'循环,您的算法在O(n^3)时间执行,这非常缓慢。 – geoff

+0

这与您的原始问题并不完全相关,但考虑使用更好的算法来生成直方图。由于您可能对颜色有所感兴趣,因此您可以尝试使用亮度计算:http://geekoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color – akirilov

回答

1

看一看at MatPlotLib。这应该会引导您完成所有您想要执行的操作,并且不需要for循环。

+0

我知道工具已经存在。不过,我想用它作为语言和算法的学习机会。 – Jason

+0

python的一大部分是学习可用的工具,而matplotlib是我几乎在所有代码中使用的一个巨大的库。我知道你想学习这门语言,但是Python的实用性是有很多工具可以让你轻松高效地完成各种事情。 – NightHallow

+0

加快速度的另一种方法是使用numpy,但是您再次使用库来帮助您。 Python不是'for'循环的最佳选择。你可以用Numpy对这段代码进行矢量化处理,或者使用Matplotlib以更简单的方式进行处理。 – NightHallow

0

如果您只想计算数组中每个值的出现次数,则可使用numpy.bincount对此进行numpy的设置。你的情况:

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

在纯python中无法做到这一点(即不删除for循环)。 Python的for循环结构有太多事情要做得很快。如果你真的想保留for循环,唯一的解决方案是numba或者cython,但是它们有自己的问题。通常,这样的循环是用c/C++编写的(在我看来最直接),然后从python调用,它的主要作用是脚本语言。

话虽如此,opencv + numpy提供了足够的有用的例程,以便在90%的情况下,可以简单地使用内置函数,而不必诉诸编写自己的像素级代码。

下面是在不改变循环代码的情况下在numba中的解决方案。在我的电脑上,它比纯Python快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  
3

可以使用其本身使用numpy的阵列较新的OpenCV蟒接口并绘制使用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() 

enter image description here

UPDATE: 以上箱并不总是提供所期望的结果作为最小值和最大值是从实际值计算出的指定数量。此外,值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() 

enter image description here