Numpy Resize / Rescale Image

98

Я хотел бы взять изображение и изменить масштаб изображения, пока это массив numpy.

Например, у меня есть это изображение бутылки кока-колы: bottle-1

Что переводится в массив формы, (528, 203, 3)и я хочу изменить его размер, чтобы сказать размер этого второго изображения: бутылка-2

Которая имеет форму (140, 54, 3).

Как изменить размер изображения до определенной формы, сохранив исходное изображение? В других ответах предлагается удалить каждую вторую или третью строку, но я хочу в основном сжать изображение, как вы это делаете с помощью редактора изображений, но в коде python. Есть ли библиотеки для этого в numpy / SciPy?

Брайан Хэмилл
источник
вы можете показать код для своего массива numpy?
ShpielMeister 05
1
scipy.misc.imresize
sascha 05
2
@sascha Устарело, в зависимости от страницы, на которую вы указали.
Paul Panzer
@ShpielMeister Я не могу заставить IntelliJ полностью распечатать массив numpy, по какой-то причине, когда выходы большие, он помещает ... все время, поэтому я могу видеть только часть вывода массива в консоли
Брайан Хэмилл,

Ответы:

123

Да, вы можете установить 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 - бикубическая интерполяция по окрестности 4x4 пикселя
  • 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, потому что у вас нет контроля над используемым им алгоритмом интерполяции. Но это может быть не важно, в зависимости от вариантов использования людьми.
Decker
2
@Decker skimage.transform.resize обеспечивает некоторый контроль через параметр 'order'. 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))

Это позаботится о таких вещах, как интерполяция, сглаживание и т. Д.

jakevdp
источник
2
Спасибо! Этот ответ тоже работает! Хотя у меня возникают проблемы с anti_aliasingфлагом, похоже, что он был удален из последней версии 0.13.1
Брайан Хэмилл
8
Это возвращает изображение как float ndarray, даже если ваше исходное изображение uint8
sziraqui
3
Это хороший метод, потому что он работает с любым количеством каналов. Я попробовал это с данными rgb в сочетании с данными облака точек глубины, и они сохранили отношения, как я хотел.
Дарт Эгрегиус
@DarthEgregious, jakevdp -> он превратил мои случайные данные шума в один цвет, когда я изменил размер массива (137,236,3) на (64,64), как описанный вами метод. Это нормально, потому что похоже, что вся информация потеряна?
Дешвал,
1
Разве это не должно быть (64,64,3)
Дарт Эгрегиус
15

Для людей, приезжающих сюда из 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] возвращает изображение с уменьшенным вдвое разрешением.
Л.
1
@ LasseKärkkäinen, но он не уменьшает разрешение, а просто выбирает все остальные пиксели. Разница в том, что окончательную функцию «max» можно изменить, чтобы выбрать или вычислить пиксели немного лучше (например, используя «min» или «среднее»). Ваш метод полезен (и быстрее), если это не имеет значения.
Уэйлон Флинн
@ L.Kärkkäinen что противоположно двойному разрешению?
rayzinnz
2
@rayzinnznp.repeat(np.repeat(a, 2, axis=0), 2, axis=1)
Л. Кярккяйнен
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.

Romwell
источник
4

imresize()Метод SciPy был другим методом изменения размера, но он будет удален, начиная с SciPy v 1.3.0. SciPy относится к методу изменения размера изображения PIL :Image.resize(size, resample=0)

size - требуемый размер в пикселях в виде кортежа из двух элементов: (ширина, высота).
resample - необязательный фильтр передискретизации. Это может быть один из 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

Cemsazara
источник
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, вы должны выполнить интерполяцию для каждого канала.

Если вы хотите понять больше, я предлагаю посмотреть Resizing Images - Computerphile .

fabda01
источник
Может не работать на основе этого ответа: 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