Передискретизація масиву numpy, що представляє зображення


74

Я шукаю, як зробити повторну вибірку масиву numpy, що представляє дані зображення в новому розмірі, бажано, вибираючи метод інтерполяції (найближчий, білінійний тощо). Я знаю, що є

scipy.misc.imresize

який робить саме це, обертаючи функцію зміни розміру PIL. Єдина проблема полягає в тому, що, оскільки він використовує PIL, масив numpy повинен відповідати форматам зображень, даючи мені максимум 4 "кольорових" канали.

Я хочу мати можливість змінювати розмір довільних зображень з будь-якою кількістю «кольорових» каналів. Мені було цікаво, чи є простий спосіб зробити це в scipy / numpy, чи мені потрібно прокрутити свій власний.

У мене є дві ідеї, як придумати одну:

  • функція, яка працює scipy.misc.imresizeна кожному каналі окремо
  • створити свій власний за допомогою scipy.ndimage.interpolation.affine_transform

Перший, мабуть, був би повільним для великих даних, а другий, схоже, не пропонує жодного іншого методу інтерполяції, крім сплайнів.


Ти дивився scipy.interpolate.griddata? посилання
Ісаак

Виглядає як чудова функція, але це для абсолютно неструктурованих даних, які будуть працювати набагато трудомісткіший алгоритм, ніж той, що мені потрібен. Я розглянув це interp2d, але це не тільки надзвичайно глючне завдання, але навіть я не впевнений, чи правильно він зменшить вибірку даних.
Густав Ларссон,

Відповіді:


112

Виходячи з вашого опису, ви хочете 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). Однак майте на увазі, що якщо ви збільшите масштаб у два рази, наприклад, ви збільшите масштаб вздовж усіх осей.

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]]]

1
FYI для інших: якщо у вас є багатоканальні дані зображень , зателефонуйте цьому кожному `` фрагменту каналу '', щоб уникнути небажаного `` розширення каналу ''. Пояснено на прикладі: якщо ви створюєте зображення з шириною пікселя 10 і висотою 5, а потім 3 каналами (по одному на кожен з RGB-повідомлень), після того, як ви покличете це для збільшення на 7,0 x, ви отримаєте масив із "70 на 35" пікселів, але з 21 каналом. "scipy.ndimage.zoom (np.ones (10 * 5 * 3) .reshape (10, 5, 3), 7.0, order = 0) .shape") надасть вам кортеж: '(70, 35, 21) 'PS. не пов'язані: він витончено обробляє фактори масштабування з плаваючою точкою, такі як '0,37' або '6,1'
Matt S.

7
@MattS. - Немає необхідності застосовувати його до кожної групи окремо, як ви описуєте. Просто вкажіть кортеж як коефіцієнт масштабування. Наприклад, scipy.ndimage.zoom(data, (3,3,1))збільшити тривимірний масив у 3 рази вздовж розмірів x та y, залишаючи третій вимір у спокої.
Джо Кінгтон,

1
@MattS. - (у відповідь на ваш видалений коментар) Гарна пропозиція! Вибачте, я не відповів раніше! Я додав застереження щодо масштабування багатосмугових зображень.
Джо Кінгтон,

1
Це лише я, чи scipy.ndimage.zoomнасправді обробка країв матриці відрізняється від scipy.misc.imresize? При масштабуванні зі значенням 10сторони мають лише 5 значень у ширину (разом із imresizeним 10).
Кріс

Масштабування не працює для значень менше 1. Див. Github.com/scipy/scipy/issues/7324
Кевін Джонсруд,

15

Якщо ви хочете зробити повторний зразок, тоді вам слід переглянути книгу кулінарних страв Scipy для відновлення . Зокрема, congridфункція, визначена в кінці, підтримуватиме повторне об'єднання або інтерполяцію (еквівалентно функції в IDL з тим самим іменем). Це повинен бути найшвидший варіант, якщо ви не хочете інтерполяції.

Ви також можете використовувати безпосередньо scipy.ndimage.map_coordinates, що зробить сплайн-інтерполяцію для будь-якого виду передискретизації (включаючи неструктуровані сітки). Я вважаю, що map_coordinate повільні для великих масивів (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', але в такому випадку позбудьтеся поперечних масивів.



7

Нещодавно я нещодавно знайшов проблему з scipy.ndimage.interpolation.zoom, яку я подав як звіт про помилку: https://github.com/scipy/scipy/issues/3203

В якості альтернативи (або принаймні для мене) я виявив, що skimage.transform.resize scikit-image працює правильно: http://scikit-image.org/docs/dev/api/skimage.transform.html#skimage .transform.resize

Однак це працює по-іншому, ніж interpyation.zoom scipy - замість того, щоб вказувати мультипликатор, ви вказуєте вихідну форму, яку хочете. Це працює для 2D та 3D зображень.

Лише для 2D-зображень ви можете використовувати transform.rescale і вказати множник або масштаб, як це було б при interpolation.zoom.


Дякую, я також раніше зауважував дивні результати zoom. Я буду пам’ятати про зображення resize, дякую!
Густав Ларссон,

Старий потік, але чи resizeзберігається величина значень у масиві (зображенні)? Я щойно спробував це вперше, а для 16-бітного відтінку сірого він цього не зробив; вихідний масив мав медіану ~ 32000, а розміри зображень мають медіани від 0 до 1.
Еван,

2

Можна використовувати interpolate.interp2d.

Наприклад, розглядаючи зображення, представлене масивом numpy 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))

Звичайно, якщо ваше зображення має кілька каналів, вам доведеться виконати інтерполяцію для кожного з них.


0

Це рішення масштабує 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))

Сподіваюся, це корисно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.