Передискретизація масиву 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.