Медленная обработка изображений Python с PIL и NumPy - PullRequest
4 голосов
/ 02 февраля 2012

Я пытаюсь реализовать некоторую обработку изображений (поиск областей похожего цвета) в Python с помощью PIL и Numpy.Не могу понять, как ускорить этот код.Не могли бы вы помочь?

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 и другие библиотеки изображений имеют много функций фильтрации и обработки, которые очень быстры.Но как лучше реализовать собственные функции обработки изображений?

Ответы [ 2 ]

4 голосов
/ 02 февраля 2012

Вместо того, чтобы зацикливаться на каждой строке и столбце, вы можете сдвигать массив влево, вправо, вверх и вниз для соответствующего количества элементов.На каждой смене вы накапливаете свои значения в базовом массиве.После сдвига и накопления вы вычисляете свое среднее значение и применяете свой порог, чтобы вернуть маску.Смотрите этот пост , в котором обсуждается общая тема.Идея состоит в том, чтобы воспользоваться преимуществами трансляции 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
2 голосов
/ 07 февраля 2012

Существуют лучшие алгоритмы сегментации, включенные в 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)
...