Повторная выборка массива numpy, представляющего изображение
Я ищу, как выполнить повторную выборку массива numpy, представляющего данные изображения, с новым размером, предпочтительно с выбором метода интерполяции (ближайшего, билинейного и т.д.). Я знаю, что есть
scipy.misc.imresize
который выполняет именно это, обертывая функцию изменения размера PIL. Единственная проблема заключается в том, что поскольку он использует PIL, массив numpy должен соответствовать форматам изображений, предоставляя мне максимум 4 "цветных" канала.
Я хочу, чтобы иметь возможность изменять размеры произвольных изображений с любым количеством "цветных" каналов. Мне было интересно, есть ли простой способ сделать это в scipy/numpy, или если мне нужно катиться самостоятельно.
У меня есть две идеи о том, как самостоятельно придумать:
- функция, которая запускает
scipy.misc.imresize
по каждому каналу отдельно
- создайте собственное приложение
scipy.ndimage.interpolation.affine_transform
Первый, вероятно, будет медленным для больших данных, а второй, похоже, не предлагает другого метода интерполяции, кроме сплайнов.
Ответы
Ответ 1
На основе вашего описания вы хотите scipy.ndimage.zoom
.
Билинейная интерполяция будет order=1
, ближайшая - order=0
, а кубика - по умолчанию (order=3
).
zoom
предназначен специально для данных с фиксированной сеткой, которые вы хотите переделать в новое разрешение.
В качестве быстрого примера:
import numpy as np
import scipy.ndimage
x = np.arange(9).reshape(3,3)
print 'Original array:'
print x
print 'Resampled by a factor of 2 with nearest interpolation:'
print scipy.ndimage.zoom(x, 2, order=0)
print 'Resampled by a factor of 2 with bilinear interpolation:'
print scipy.ndimage.zoom(x, 2, order=1)
print 'Resampled by a factor of 2 with cubic interpolation:'
print scipy.ndimage.zoom(x, 2, order=3)
И результат:
Original array:
[[0 1 2]
[3 4 5]
[6 7 8]]
Resampled by a factor of 2 with nearest interpolation:
[[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]]
Resampled by a factor of 2 with bilinear interpolation:
[[0 0 1 1 2 2]
[1 2 2 2 3 3]
[2 3 3 4 4 4]
[4 4 4 5 5 6]
[5 5 6 6 6 7]
[6 6 7 7 8 8]]
Resampled by a factor of 2 with cubic interpolation:
[[0 0 1 1 2 2]
[1 1 1 2 2 3]
[2 2 3 3 4 4]
[4 4 5 5 6 6]
[5 6 6 7 7 7]
[6 6 7 7 8 8]]
Изменить: Как указал Мэтт С., есть несколько предостережений для масштабирования многодиапазонных изображений. Я копирую часть ниже почти дословно из одного из моих более ранних ответов:
Масштабирование также работает для массивов 3D (и nD). Однако имейте в виду, что если вы увеличиваете масштаб на 2x, например, вы будете масштабировать по всем осям.
data = np.arange(27).reshape(3,3,3)
print 'Original:\n', data
print 'Zoomed by 2x gives an array of shape:', ndimage.zoom(data, 2).shape
Это дает:
Original:
[[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]]
[[ 9 10 11]
[12 13 14]
[15 16 17]]
[[18 19 20]
[21 22 23]
[24 25 26]]]
Zoomed by 2x gives an array of shape: (6, 6, 6)
В случае многодиапазонных изображений вы обычно не хотите интерполировать вдоль оси "z", создавая новые полосы.
Если у вас есть что-то вроде 3-полосного изображения RGB, которое вы хотите увеличить, вы можете сделать это, указав последовательность кортежей как коэффициент масштабирования:
print 'Zoomed by 2x along the last two axes:'
print ndimage.zoom(data, (1, 2, 2))
Это дает:
Zoomed by 2x along the last two axes:
[[[ 0 0 1 1 2 2]
[ 1 1 1 2 2 3]
[ 2 2 3 3 4 4]
[ 4 4 5 5 6 6]
[ 5 6 6 7 7 7]
[ 6 6 7 7 8 8]]
[[ 9 9 10 10 11 11]
[10 10 10 11 11 12]
[11 11 12 12 13 13]
[13 13 14 14 15 15]
[14 15 15 16 16 16]
[15 15 16 16 17 17]]
[[18 18 19 19 20 20]
[19 19 19 20 20 21]
[20 20 21 21 22 22]
[22 22 23 23 24 24]
[23 24 24 25 25 25]
[24 24 25 25 26 26]]]
Ответ 2
Если вы хотите выполнить повторную выборку, вы должны посмотреть кулинарную книгу Scipy на rebinning. В частности, функция congrid
, определенная в конце, будет поддерживать перестроение или интерполяцию (эквивалентную функции в IDL с тем же именем). Это должен быть самый быстрый вариант, если вы не хотите интерполяции.
Вы также можете напрямую использовать scipy.ndimage.map_coordinates
, который будет выполнять сплайн-интерполяцию для любого вида повторной выборки (включая неструктурированные сетки). Я нахожу map_coordinates медленным для больших массивов (nx, ny > 200).
Для интерполяции на структурированных сетках я обычно использую scipy.interpolate.RectBivariateSpline
. Вы можете выбрать порядок сплайнов (линейный, квадратичный, кубический и т.д.) И даже независимо для каждой оси. Пример:
import scipy.interpolate as interp
f = interp.RectBivariateSpline(x, y, im, kx=1, ky=1)
new_im = f(new_x, new_y)
В этом случае вы выполняете двухлинейную интерполяцию (kx = ky = 1)
. "Ближайший" вид интерполяции не поддерживается, так как все это делает сплайновую интерполяцию по прямоугольной сетке. Это также не самый быстрый метод.
Если вы пользуетесь билинейной или би-кубической интерполяцией, то, как правило, гораздо быстрее выполнять две интерполяции 1D:
f = interp.interp1d(y, im, kind='linear')
temp = f(new_y)
f = interp.interp1d(x, temp.T, kind='linear')
new_im = f(new_x).T
Вы также можете использовать kind='nearest'
, но в этом случае избавиться от поперечных массивов.
Ответ 3
Вы посмотрели Scikit-image? Его функции transform.pyramid_*
могут быть полезны для вас.
Ответ 4
Недавно я обнаружил проблему с scipy.ndimage.interpolation.zoom, которую я отправил в виде отчета об ошибке: https://github.com/scipy/scipy/issues/3203
В качестве альтернативы (или хотя бы для меня) я обнаружил, что scikit-image skimage.transform.resize работает правильно: http://scikit-image.org/docs/dev/api/skimage.transform.html#skimage.transform.resize
Однако он работает по-другому для scipy interpolation.zoom - вместо того, чтобы указывать множитель, вы указываете желаемую выходную форму. Это работает для 2D и 3D изображений.
Для двухмерных изображений вы можете использовать transform.rescale и указать множитель или масштаб, как если бы вы использовали interpolation.zoom.
Ответ 5
Это решение масштабирует X и Y подаваемого изображения без влияния на каналы RGB:
import numpy as np
import scipy.ndimage
matplotlib.pyplot.imshow(scipy.ndimage.zoom(image_np_array, zoom = (7,7,1), order = 1))
Надеюсь, это полезно.
Ответ 6
Вы можете использовать interpolate.interp2d
.
Например, рассматривая изображение, представленное массивом пустышек arr
, вы можете изменить его размер до произвольной высоты и ширины следующим образом:
W, H = arr.shape[:2]
new_W, new_H = (600,300)
xrange = lambda x: np.linspace(0, 1, x)
f = interp2d(xrange(W), xrange(H), arr, kind="linear")
new_arr = f(xrange(new_W), xrange(new_H))
Конечно, если у вашего изображения есть несколько каналов, вы должны выполнить интерполяцию для каждого из них.