Змінення розміру / масштабування зображення Numpy


98

Я хотів би зробити знімок і змінити масштаб зображення, поки це масив numpy.

Наприклад, у мене є зображення пляшки кока-коли: пляшка-1

Що перекладається на масивний масив фігури, (528, 203, 3)і я хочу змінити розмір, щоб сказати розмір цього другого зображення: пляшка-2

Яка має форму (140, 54, 3).

Як змінити розмір зображення на певну форму, зберігаючи оригінальне зображення? Інші відповіді пропонують видалити кожен другий або третій рядок, але я хочу зробити це, в основному, зменшити зображення так, як це було б за допомогою редактора зображень, але в коді python. Чи є бібліотеки для цього в numpy / SciPy?


ви можете показати код для вашого масиву numpy?
ShpielMeister


2
@sascha Не підтримується відповідно до сторінки, на яку ви посилаєтесь.
Paul Panzer,

@ShpielMeister Я не можу змусити IntelliJ повністю роздрукувати масив numpy, чомусь, коли виходи великі, це ставить ... весь час, тому я можу бачити лише частину виводу масиву в консолі
Брайан Хемілл

Відповіді:


122

Так, ви можете встановити opencv(це бібліотека, яка використовується для обробки зображень та комп'ютерного зору) і використовувати цю cv2.resizeфункцію. Наприклад, використовуйте:

import cv2
import numpy as np

img = cv2.imread('your_image.jpg')
res = cv2.resize(img, dsize=(54, 140), interpolation=cv2.INTER_CUBIC)

Тут img, таким чином, NumPy масив , що містить вихідне зображення, в той час як resце NumPy масив , що містить зменшене зображення. Важливим аспектом є interpolationпараметр: існує кілька способів зміни розміру зображення. Тим більше, що ви зменшуєте масштаб зображення, і розмір вихідного зображення не кратний розміру зображення, зміненого. Можливі схеми інтерполяції:

  • INTER_NEAREST - інтерполяція найближчого сусіда
  • INTER_LINEAR - дволінійна інтерполяція (використовується за замовчуванням)
  • INTER_AREA- передискретизація з використанням відношення площі пікселів. Це може бути кращим методом децимації зображення, оскільки він дає результати без муару. Але при збільшенні зображення це схоже на INTER_NEARESTметод.
  • INTER_CUBIC - бікубічна інтерполяція в районі 4х4 пікселі
  • INTER_LANCZOS4 - інтерполяція Ланцоша в районі 8x8 пікселів

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


5
Я щойно випробував цей код, і він працює! Тільки одна зміна полягає в тому, що воно dsizeмає бути dsize=(54, 140)таким, як потрібно x, тоді y, де як масив numpy показує форму як y, тоді x (y - кількість рядків, а x - кількість стовпців)
Брайан Хемілл

6
Я намагаюся уникати cv2, він міняє розміри та навантаження у форматі каналу BGR. Я віддаю перевагу skimage.io.imread('image.jpg')і skimage.transform.resize(img). scikit-image.org/docs/dev/install.html
Едуардо Піньятеллі

1
@EduardoPignatelli Я уникаю skimage.transform.resize, оскільки ти не маєш контролю над алгоритмом інтерполяції, який він використовує. Але це може бути не важливо, залежно від випадків використання людьми.
Декер

2
@Decker skimage.transform.resize забезпечує певний контроль через параметр 'order'. порядок = 0 - найближчий сусід, 1 = білінійний, 2 = біквадратичний, 3 = бікубічний тощо. Однак середнє значення площі або інтерполяція ланцоса не існує.
Тапіо

1
@TapioFriberg ах, так, я виправлений; Я бачу алгоритми, визначені в документації до параметра порядку замовлення skimage.transform.warp. У якийсь момент може бути корисно оновити документи, включивши посилання на типи, наприклад, "двоквартичний", наприклад, не визначений ніде в документації (станом на 10 грудня 2019 р.) - бути корисним для майбутніх користувачів.
Decker

67

Хоча для цього можливо використовувати лише numpy, операція не вбудована. Тим не менш, ви можете використовувати scikit-image(що побудовано на numpy), щоб робити такий вид маніпуляцій із зображеннями.

Документація щодо масштабування Scikit-Image тут .

Наприклад, ви можете зробити наступне зі своїм зображенням:

from skimage.transform import resize
bottle_resized = resize(bottle, (140, 54))

Це подбає про такі речі, як інтерполяція, згладжування тощо.


2
Дякую! Ця відповідь також працює! Хоча у мене anti_aliasingвиникають певні проблеми з прапором, схоже, його було вилучено з останньої версії 0.13.1
Брайан Хемілл

8
Це повертає зображення як плаваючий ndarray, навіть якщо ваше оригінальне зображення uint8
sziraqui

3
Це приємна техніка, оскільки вона працює з будь-якою кількістю каналів. Я спробував це з даними rgb в поєднанні з даними хмарних точок глибини, і це зберегло стосунки, як я хотів.
Дарт

@DarthEgremous, jakevdp -> це зробило мої випадкові дані про шум єдиними кольорами, коли я змінив розмір (137 236,3) масиву до (64,64), як описаний вами метод. Це нормально, оскільки схоже, що він втратив всю інформацію?
Дешваль

1
Не повинно бути (64,64,3)
Дарт Егреґедж

14

Для людей, які приходять сюди від Google і шукають швидкий спосіб зменшити вибірку зображень у numpyмасивах для використання в програмах машинного навчання, ось супершвидкий метод (адаптований звідси ). Цей метод працює лише тоді, коли вхідні розміри кратні вихідним розмірам.

Наступні приклади зменшують вибірку зі 128x128 на 64x64 (це можна легко змінити).

Останнє замовлення каналів

# large image is shape (128, 128, 3)
# small image is shape (64, 64, 3)
input_size = 128
output_size = 64
bin_size = input_size // output_size
small_image = large_image.reshape((output_size, bin_size, 
                                   output_size, bin_size, 3)).max(3).max(1)

Перше замовлення каналів

# large image is shape (3, 128, 128)
# small image is shape (3, 64, 64)
input_size = 128
output_size = 64
bin_size = input_size // output_size
small_image = large_image.reshape((3, output_size, bin_size, 
                                      output_size, bin_size)).max(4).max(2)

Для зображень у градаціях сірого просто змініть значення 3на наступне 1:

Перше замовлення каналів

# large image is shape (1, 128, 128)
# small image is shape (1, 64, 64)
input_size = 128
output_size = 64
bin_size = input_size // output_size
small_image = large_image.reshape((1, output_size, bin_size,
                                      output_size, bin_size)).max(4).max(2)

Цей метод використовує еквівалент максимального об’єднання. Це найшвидший спосіб зробити це, який я знайшов.


4
large_image [:, :: 2, :: 2] повертає зображення з роздільною здатністю вдвічі.
L. Kärkkäinen

1
@ LasseKärkkäinen, але це не зменшує вибірку, воно просто відбирає кожен другий піксель. Різниця полягає в тому, що остаточну функцію "max" можна змінити, щоб вибрати або обчислити пікселі дещо кращими способами (наприклад, використовуючи "min" або "mean"). Ваш метод корисний (і швидший), якщо це не має значення.
Вейлон Флінн,

@ L.Kärkkäinen, що протилежне цьому подвійному дозволу?
rayzinnz

2
@rayzinnznp.repeat(np.repeat(a, 2, axis=0), 2, axis=1)
L. Kärkkäinen

11

Якщо хтось прийшов сюди, шукаючи простий метод масштабування / зміни розміру зображення в Python, без використання додаткових бібліотек, ось дуже проста функція зміни розміру зображення:

#simple image scaling to (nR x nC) size
def scale(im, nR, nC):
  nR0 = len(im)     # source number of rows 
  nC0 = len(im[0])  # source number of columns 
  return [[ im[int(nR0 * r / nR)][int(nC0 * c / nC)]  
             for c in range(nC)] for r in range(nR)]

Приклад використання: зменшення розміру зображення (30 x 30) до (100 x 200):

import matplotlib.pyplot as plt

def sqr(x):
  return x*x

def f(r, c, nR, nC):
  return 1.0 if sqr(c - nC/2) + sqr(r - nR/2) < sqr(nC/4) else 0.0

# a red circle on a canvas of size (nR x nC)
def circ(nR, nC):
  return [[ [f(r, c, nR, nC), 0, 0] 
             for c in range(nC)] for r in range(nR)]

plt.imshow(scale(circ(30, 30), 100, 200))

Вихід: масштабоване зображення

Це працює для зменшення / масштабування зображень і чудово працює з масивами numpy.


4

imresize()Метод SciPy був ще одним методом зміни розміру, але його буде видалено, починаючи з SciPy v 1.3.0. SciPy посилається на метод зміни розміру зображення PIL :Image.resize(size, resample=0)

розмір - запитуваний розмір у пікселях у вигляді двох крапок: (ширина, висота).
перепробовувати - необов’язковий фільтр для вибірки. Це може бути один із PIL.Image.NEAREST (використовуйте найближчого сусіда), PIL.Image.BILINEAR (лінійна інтерполяція), PIL.Image.BICUBIC (кубічна сплайн-інтерполяція) або PIL.Image.LANCZOS (високоякісний фільтр зменшення дискретизації ). Якщо опущено, або якщо зображення має режим “1” або “P”, для нього встановлюється PIL.Image.NEAREST.

Посилання тут: https://pillow.readthedocs.io/en/3.1.x/reference/Image.html#PIL.Image.Image.resize


3
На жаль, imresize () застарілий, його буде видалено в SciPy 1.3.0
MiniQuark

1

Чи є бібліотеки для цього в numpy / SciPy

Звичайно. Ви можете зробити це без OpenCV, scikit-image або PIL.

Зміна розміру зображення - це, в основному, відображення координат кожного пікселя від вихідного зображення до його розміру.

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

Все, що вам потрібно, це функція, яка робить цю інтерполяцію за вас. SciPy має 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))

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

Якщо ви хочете зрозуміти більше, пропоную переглянути перегляд розміру зображень - Computerphile .


Не може працювати на основі цієї відповіді: stackoverflow.com/questions/37872171 / ...
random_dsp_guy

0
import cv2
import numpy as np

image_read = cv2.imread('filename.jpg',0) 
original_image = np.asarray(image_read)
width , height = 452,452
resize_image = np.zeros(shape=(width,height))

for W in range(width):
    for H in range(height):
        new_width = int( W * original_image.shape[0] / width )
        new_height = int( H * original_image.shape[1] / height )
        resize_image[W][H] = original_image[new_width][new_height]

print("Resized image size : " , resize_image.shape)

cv2.imshow(resize_image)
cv2.waitKey(0)

4
Ласкаво просимо до StackOverflow. Чудово, що ви хочете допомогти іншим, відповідаючи на їх запитання. Однак я не бачу, як ваша відповідь додає значення порівняно з існуючою відповіддю, яка вже використовує cv2та використовує належну функцію зміни розміру, замість того, щоб повторно застосувати функцію "неоптимального" розміру, яка гірша, ніж інтерполяція найближчого сусіда.
NOhs
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.