Масштабирование массива numpy



У меня есть двумерный массив numpy, который представляет монохромное изображение с ПЗС-матрицы, которая была привязана 3x3 (то есть каждое значение в массиве представляет 9 пикселей (3x3) на физическом ПЗС-матрице).



Я хочу изменить его масштаб, чтобы он соответствовал исходному макету ПЗС (поэтому я могу легко наложить его на изображение без биннов с того же ПЗС).



Я видел пересчет массива numpy, представляющего изображение, но это, похоже, не делает того, что я хочу.



Предположим, что у меня есть массив g:



import numpy as np
import scipy.ndimage

g = np.array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])


Когда я пытаюсь масштабировать его в 2 раза:



o = scipy.ndimage.zoom(g, 2, order=0)


Я получаю именно то, что ожидаю - каждое значение теперь равно 2x2 идентичным значениям:



array([[0, 0, 1, 1, 2, 2],
[0, 0, 1, 1, 2, 2],
[3, 3, 4, 4, 5, 5],
[3, 3, 4, 4, 5, 5],
[6, 6, 7, 7, 8, 8],
[6, 6, 7, 7, 8, 8]])


Но когда я пытаюсь масштабировать в 3 раза, я получаю следующее:

o = scipy.ndimage.zoom(g, 3, order=0)


Дает мне:



array([[0, 0, 1, 1, 1, 1, 2, 2, 2],
[0, 0, 1, 1, 1, 1, 2, 2, 2],
[3, 3, 4, 4, 4, 4, 5, 5, 5],
[3, 3, 4, 4, 4, 4, 5, 5, 5],
[3, 3, 4, 4, 4, 4, 5, 5, 5],
[3, 3, 4, 4, 4, 4, 5, 5, 5],
[6, 6, 7, 7, 7, 7, 8, 8, 8],
[6, 6, 7, 7, 7, 7, 8, 8, 8],
[6, 6, 7, 7, 7, 7, 8, 8, 8]])


Я хотел, чтобы каждое значение в исходном массиве стало набором значений 3x3...это не то, что я понимаю.

Как я могу это сделать? (И почему я получаю этот неинтуитивный результат?)

772   2  

2 ответов:

Вы можете использовать np.kron:

In [16]: g
Out[16]: 
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])

In [17]: np.kron(g, np.ones((3,3), dtype=int))
Out[17]: 
array([[0, 0, 0, 1, 1, 1, 2, 2, 2],
       [0, 0, 0, 1, 1, 1, 2, 2, 2],
       [0, 0, 0, 1, 1, 1, 2, 2, 2],
       [3, 3, 3, 4, 4, 4, 5, 5, 5],
       [3, 3, 3, 4, 4, 4, 5, 5, 5],
       [3, 3, 3, 4, 4, 4, 5, 5, 5],
       [6, 6, 6, 7, 7, 7, 8, 8, 8],
       [6, 6, 6, 7, 7, 7, 8, 8, 8],
       [6, 6, 6, 7, 7, 7, 8, 8, 8]])

Вывод zoom(g, 3, order=0) немного удивителен. Рассмотрим первую строку: [0, 0, 1, 1, 1, 1, 2, 2, 2]. Почему их четыре 1?

Когда order=0 zoom (в действии) вычисляет np.linspace(0, 2, 9), который выглядит как

In [80]: np.linspace(0, 2, 9)
Out[80]: array([ 0.  ,  0.25,  0.5 ,  0.75,  1.  ,  1.25,  1.5 ,  1.75,  2.  ])

И затем округляет значения. Если вы используете np.round(), вы получаете:

In [71]: np.round(np.linspace(0, 2, 9)).astype(int)
Out[71]: array([0, 0, 0, 1, 1, 1, 2, 2, 2])
Обратите внимание, что np.round(0.5) дает 0, но np.round(1.5) дает 2. np.round() использует "круглую половину, чтобы уравнять" правило разрыва связей. По-видимому, округление сделано в zoom код использует правило "округлить половину вниз" : он округляет 0.5 до 0 и 1.5 до 1, как в следующем
In [81]: [int(round(x)) for x in np.linspace(0, 2, 9)]
Out[81]: [0, 0, 1, 1, 1, 1, 2, 2, 2]

И именно поэтому там есть четыре 1 s.

И почему я получаю этот неинтуитивный результат?

Потому что zoom является функцией интерполяции сплайнов. Другими словами, он рисует кубический сплайн от середины этого 1 до середины этого 0, и значения между ними получают значения сплайна в соответствующем месте.

Если вы хотите получить ближайшую, линейную или квадратичную интерполяцию вместо кубической, вы можете использовать аргумент order=0 или order=1 или order=2. Но если вы не хотите интерполяции вообще-что вы не ... не используете функцию интерполяции. Это похоже на вопрос, почему использование [int(i*2.3) for i in range(10)] для получения четных чисел от 0 до 20 дает вам некоторые нечетные числа. Это не функция для получения четных чисел от 0 до 20, поэтому она этого не делает, но делает именно то, что вы просили.


Как я могу это сделать?

Опять же, если вы хотите неинтерполированное масштабирование, не используйте функцию интерполяции. Самый простой способ, вероятно, использовать np.kron, to Kroenecker-умножьте свой массив с np.ones((scale, scale)).

Comments

    Ничего не найдено.