Найти расстояние между всеми парами пикселей в изображении

#python #performance #numpy #linear-algebra

#python #Производительность #numpy #линейная алгебра

Вопрос:

Вопрос

У меня есть numpy.array форма (H, W) , хранящая интенсивность пикселей изображения. Я хочу сгенерировать новый массив формы (H, W, H, W) , в котором хранится евклидово расстояние между каждой парой пикселей на изображении («пространственное» расстояние между пикселями, а не разница в их интенсивности).

Попытка решения

Следующий метод делает именно то, что я хочу, но очень медленно. Я ищу быстрый способ сделать это.

 d = numpy.zeros((H, W, H, W)) # array to store distances.
for x1 in range(H):
    for y1 in range(W):
        for x2 in range(H):
            for y2 in range(W):
                d[x1, y1, x2, y2] = numpy.sqrt( (x2-x1)**2   (y2-y1)**2 )
  

Дополнительные сведения

Вот более подробная информация о моей проблеме. Вероятно, мне будет достаточно решения более простой проблемы, описанной выше, чтобы разобраться с остальным.

  • В моем случае изображение на самом деле является медицинским 3D-изображением (т. Е. numpy.array Формой (H, W, D) ).
  • 3D-пиксели могут быть не кубическими (например, каждый пиксель может представлять объем 1 мм x 2 мм x 3 мм).

Комментарии:

1. Итак, вы пытаетесь вычислить расстояния между координатами пикселей , а не сами интенсивности пикселей?

2. Сами пиксели могут быть разного объема?

3. @Jensun Это 3D-медицинское изображение, где каждый 3D-пиксель («воксель») представляет небольшой объем. Объемы представляют собой прямоугольные призмы, но они могут быть не кубическими. Все вокселы имеют одинаковую форму.

4. Должны ли быть вычислены расстояния между центроидами вокселей?

5. @Jensun да, между центроидами.

Ответ №1:

Мы можем настроить открытые сетки с 1D использованием разнесенных массивов np.ogrid , с которыми можно работать в той же нотации итератора для векторизованного решения, и это позволит использовать broadcasting perf. boost :

 X1,Y1,X2,Y2 = np.ogrid[:H,:W,:H,:W]
d_out = numpy.sqrt( (X2-X1)**2   (Y2-Y1)**2 )
  

Чтобы сэкономить на двух открытых сетках :

 X,Y = np.ogrid[:H,:W]
d_out = numpy.sqrt( (X[:,:,None,None]-X)**2   (Y[:,:,None,None]-Y)**2 )
  

Если мы работаем с большими массивами, рассмотрите возможность использования numexpr для дальнейшего увеличения :

 import numexpr as ne

d_out = ne.evaluate('sqrt( (X2-X1)**2   (Y2-Y1)**2 )')
  

Комментарии:

1. Я попробовал это, и все они, похоже, работают и работают быстрее, чем мой метод цикла. У меня большая проблема в том, что изображение слишком велико, чтобы реально выполнить это вычисление или сохранить результат — например, изображение размером 100x100x100 будет иметь ~ 10 ^ 6 пикселей и ~ 10 ^ 12 пар пикселей. Я пометил ваш ответ как принятый, потому что я думаю, что он отвечает на вопрос, который я задал.