Масштабирование массива 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...это не то, что я понимаю.
Как я могу это сделать? (И почему я получаю этот неинтуитивный результат?)
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=0zoom (в действии) вычисляет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]И именно поэтому там есть четыре
1s.
И почему я получаю этот неинтуитивный результат?Потому что
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