使用PIL和numpy进行Python图像处理速度缓慢

4

我正在尝试使用PIL和Numpy在Python中实现一些图像处理(查找相似颜色的区域)。但是无法想出如何加速此代码。你能帮忙吗?

def findRegions(self, data):
#data is numpy.array
    ret = [[False for _ in range(self.width)] for _ in range(self.heigth)]

    for i in range(self.heigth):
        for j in range(self.width):
            k = 0
            acc = 0
            for x,y in [(-1,0),(0,-1),(0,1),(1,0)]:
                if (self.heigth>i+x>=0 and self.width>j+y>=0):
                    k = k+1
                    acc += math.sqrt(sum((data[i][j][c]-data[i+x][j+y][c])**2 for c in range(3)))
            if (acc/k<self.threshold):
                ret[i][j]= True
    return ret 

PIL和其他图像库具有许多滤波和处理功能,速度非常快。但是,实现自己的图像处理功能的最佳方法是什么?


1
摆脱嵌套循环,而是一次性操作整个numpy数组,而不是每个像素。 - wim
使用 PyPy http://pypy.org/ 和 PIL https://dev59.com/dFPTa4cB1Zd3GeqPnuwd - Mikko Ohtamaa
还要看看scipy.ndimage,它对这种事情非常有用。 - Bi Rico
2个回答

4
不必循环每一行和列,可以将数组左、右、上、下移动适当数量的元素。在每次移动时,累积基数组中的值。移动和累加后,计算平均值并应用阈值返回掩码。请参见此帖子,其中讨论了该主题的一般性问题。想法是利用numpy的广播功能,在C中将函数或运算符应用于数组的所有元素,而不是Python。
我已经修改了链接帖子中的代码,以适应您尝试实现的内容。无论如何,一般模式都应该加速。您必须确定在返回掩码中如何处理边缘。在这里,我只是将返回掩码设置为False,但您也可以通过在每个方向上扩展输入数据并使用最近的像素、零、灰色等填充来消除边缘。
def findRegions(self,data):
    #define the shifts for the kernel window
    shifts = [(-1,0),(0,-1),(0,1),(1,0)]

    #make the base array of zeros 
    #  array size by 2 in both dimensions
    acc = numpy.zeros(data.shape[:2])

    #compute the square root of the sum of squared color 
    # differences between a pixel and it's 
    # four cardinal neighbors
    for dx,dy in shifts:
        xstop = -1+dx or None
        ystop = -1+dy or None
        #per @Bago's comment, use the sum method to add up the color dimension
        #  instead of the list comprehension
        acc += ((data[1:-1,1:-1] - data[1+dx:xstop, 1+dy:ystop])**2).sum(-1)**.5

    #compute the average 
    acc /= (len(shifts) + 1)

    #build a mask array the same size as the original
    ret = numpy.zeros(data.shape[:2],dtype=numpy.bool)

    #apply the threshold
    #  note that the edges will be False
    ret[1:-1,1:-1] acc < self.threshold    

    return ret

您可以使用数组的sum方法避免循环遍历颜色维度,即((data[1:-1, 1:-1] - data[1+dx:xstop, 1+dy:ystop])**2).sum(-1)**.5 - Bi Rico

2

http://scikits-image.org中包含更好的分割算法,但如果您想要构建自己的算法,可以参考这个基于聚类的示例——ICM分割。指定N=4以识别四个区域。

import numpy as np
from scipy.cluster.vq import kmeans2

def ICM(data, N, beta):
    print "Performing ICM segmentation..."

    # Initialise segmentation using kmeans
    print "K-means initialisation..."
    clusters, labels = kmeans2(np.ravel(data), N)

    print "Iterative segmentation..."
    f = data.copy()

    def _minimise_cluster_distance(data, labels, N, beta):
        data_flat = np.ravel(data)
        cluster_means = np.array(
            [np.mean(data_flat[labels == k]) for k in range(N)]
            )
        variance = np.sum((data_flat - cluster_means[labels])**2) \
                   / data_flat.size

        # How many of the 8-connected neighbouring pixels are in the
        # same cluster?
        count = np.zeros(data.shape + (N,), dtype=int)
        count_inside = count[1:-1, 1:-1, :]

        labels_img = labels.reshape(data.shape)
        for k in range(N):
            count_inside[..., k] += (k == labels_img[1:-1:, 2:])
            count_inside[..., k] += (k == labels_img[2:, 1:-1])
            count_inside[..., k] += (k == labels_img[:-2, 1:-1])
            count_inside[..., k] += (k == labels_img[1:-1, :-2])

            count_inside[..., k] += (k == labels_img[:-2, :-2])
            count_inside[..., k] += (k == labels_img[2:, 2:])
            count_inside[..., k] += (k == labels_img[:-2, 2:])
            count_inside[..., k] += (k == labels_img[2:, :-2])

        count = count.reshape((len(labels), N))
        cluster_measure = (data_flat[:, None] - cluster_means)**2 \
                          - beta * variance * count
        labels = np.argmin(cluster_measure, axis=1)

        return cluster_means, labels

    # Initialise segmentation
    cluster_means, labels = _minimise_cluster_distance(f, labels, N, 0)

    stable_counter = 0
    old_label_diff = 0
    i = 0
    while stable_counter < 3:
        i += 1

        cluster_means, labels_ = \
                       _minimise_cluster_distance(f, labels, N, beta)

        new_label_diff = np.sum(labels_ != labels)
        if  new_label_diff != old_label_diff:
            stable_counter = 0
        else:
            stable_counter += 1
        old_label_diff = new_label_diff

        labels = labels_

    print "Clustering converged after %d steps." % i

    return labels.reshape(data.shape)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接