numpy Python -计算图像的直方图

lo8azlld  于 2023-04-21  发布在  Python
关注(0)|答案(5)|浏览(258)

我正在自学计算机图像处理的基础知识,同时我也在自学Python。
给定尺寸为2048x1354的图像x,具有3个通道,有效地计算像素强度的直方图。

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秒。我如何加快速度,更像Python?

eqoofvh9

eqoofvh91#

你可以使用新的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()

yzuktlbb

yzuktlbb2#

如果你只想计算数组中每个值的出现次数,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

niknxzdl

niknxzdl3#

这是不可能的(即不删除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
gcxthw6b

gcxthw6b4#

看一下at MatPlotLib。这应该会带你完成所有你想做的事情,而且没有for循环。

kq0g1dla

kq0g1dla5#

OpenCV docs
单通道直方图(图像转换为灰度):

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()

相关问题