Numpy Resize / Rescale Image

98

Chciałbym zrobić zdjęcie i zmienić skalę obrazu, podczas gdy jest to tablica numpy.

Na przykład mam ten obraz butelki coca-coli: butelka-1

Co przekłada się na numeryczną tablicę kształtów (528, 203, 3)i chcę zmienić jej rozmiar, aby powiedzieć rozmiar tego drugiego obrazu: butelka-2

Który ma kształt (140, 54, 3).

Jak zmienić rozmiar obrazu do określonego kształtu, zachowując oryginalny obraz? Inne odpowiedzi sugerują usunięcie co drugiego lub trzeciego wiersza, ale to, co chcę zrobić, to w zasadzie zmniejszyć obraz tak, jak byś to zrobił za pomocą edytora obrazów, ale w kodzie Pythona. Czy są jakieś biblioteki, które mogą to zrobić w numpy / SciPy?

Brian Hamill
źródło
czy możesz pokazać kod swojej tablicy numpy?
ShpielMeister,
2
@sascha Wycofane, zgodnie z połączoną stroną.
Paul Panzer,
@ShpielMeister Nie mogę zmusić IntelliJ do pełnego wydrukowania tablicy numpy, z jakiegoś powodu, gdy wyjścia są duże, umieszcza ... przez cały czas, więc widzę tylko część danych wyjściowych tablicy w konsoli
Brian Hamill

Odpowiedzi:

123

Tak, możesz zainstalować opencv(jest to biblioteka używana do przetwarzania obrazu i wizji komputerowej) i używać tej cv2.resizefunkcji. I na przykład użyj:

import cv2
import numpy as np

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

Oto imgtablica numpy zawierająca oryginalny obraz, podczas gdy restablica numpy zawierająca obraz o zmienionym rozmiarze . Ważnym aspektem jest interpolationparametr: istnieje kilka sposobów zmiany rozmiaru obrazu. Zwłaszcza, że ​​zmniejszasz obraz, a rozmiar oryginalnego obrazu nie jest wielokrotnością rozmiaru obrazu o zmienionym rozmiarze. Możliwe schematy interpolacji to:

  • INTER_NEAREST - interpolacja najbliższego sąsiada
  • INTER_LINEAR - interpolacja dwuliniowa (używana domyślnie)
  • INTER_AREA- resampling z wykorzystaniem relacji obszaru pikseli. Może to być preferowana metoda dziesiątkowania obrazu, ponieważ daje rezultaty pozbawione efektu mory. Ale kiedy obraz jest powiększony, jest podobny do INTER_NEARESTmetody.
  • INTER_CUBIC - interpolacja dwuścienna w sąsiedztwie pikseli 4x4
  • INTER_LANCZOS4 - interpolacja Lanczosa w sąsiedztwie 8x8 pikseli

Podobnie jak w przypadku większości opcji, nie ma „najlepszej” opcji w tym sensie, że dla każdego schematu zmiany rozmiaru istnieją scenariusze, w których jedna strategia może być preferowana.

Willem Van Onsem
źródło
5
Właśnie wypróbowałem ten kod i działa! Tylko jedna zmiana polega na tym, że dsizepowinno być dsize=(54, 140)tak, jak ma x, a następnie y, gdzie jako tablica numpy pokazuje kształt jako y, a następnie x (y to liczba wierszy, a x to liczba kolumn)
Brian Hamill
6
Staram się unikać cv2, zamienia wymiary i ładuje w formacie kanału BGR. Wolę skimage.io.imread('image.jpg')i skimage.transform.resize(img). scikit-image.org/docs/dev/install.html
Eduardo Pignatelli
1
@EduardoPignatelli Unikam skimage.transform.resize, ponieważ nie masz kontroli nad algorytmem interpolacji, którego używa. Ale to może nie być ważne, w zależności od przypadków użycia ludzi.
Decker
2
@Decker skimage.transform.resize zapewnia pewną kontrolę poprzez parametr „order”. rząd = 0 jest najbliższym sąsiadem, 1 = dwuliniowy, 2 = dwukwadratowy, 3 = dwuczęściowy itp. Jednak bez średniej powierzchniowej lub interpolacji lanczosa.
Tapio,
1
@TapioFriberg ahh tak, stoję poprawione; Widzę algorytmy zdefiniowane w dokumentacji parametru 'order' skimage.transform.warp. W pewnym momencie pomocne może być zaktualizowanie dokumentów w celu uwzględnienia odniesień do typów, na przykład „Bi-quartic” nie jest zdefiniowany nigdzie indziej w dokumentacji (stan na 10 grudnia 2019 r.) - jednowierszowy może być korzystne dla przyszłych użytkowników.
Decker
67

Chociaż może być możliwe użycie do tego samego numpy, operacja nie jest wbudowana. To powiedziawszy, możesz użyć scikit-image(który jest zbudowany na numpy) do wykonania tego rodzaju manipulacji obrazem.

Dokumentacja dotycząca skalowania Scikit-Image jest tutaj .

Na przykład możesz wykonać następujące czynności ze swoim obrazem:

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

To zajmie się takimi kwestiami, jak interpolacja, wygładzanie krawędzi itp.

jakevdp
źródło
2
Dziękuję Ci! Ta odpowiedź też działa! Chociaż mam problem z anti_aliasingflagą, wygląda na to, że została usunięta z najnowszej wersji 0.13.1
Brian Hamill
8
Ten obraz powraca jako pływaka ndarray nawet jeśli oryginalny obraz jest Uint8
sziraqui
3
To fajna technika, ponieważ działa z dowolną liczbą kanałów. Wypróbowałem to z danymi rgb w połączeniu z danymi chmury punktów głębokości i zachowało to zależność tak, jak chciałem.
Darth Egregious
@DarthEgregious, jakevdp -> zmienił moje losowe dane szumu w jeden kolor, gdy zmieniłem rozmiar (137,236,3) tablicy na (64,64), tak jak opisałeś metodę. Czy to normalne, ponieważ wygląda na to, że utraciło wszystkie informacje?
Deshwal
1
Czy nie powinno być (64,64,3)
Darth Egregious
14

Dla osób przybywających tutaj z Google, którzy szukają szybkiego sposobu na zmniejszenie próbkowania obrazów w numpytablicach do wykorzystania w aplikacjach uczenia maszynowego, oto super szybka metoda (dostosowana stąd ). Ta metoda działa tylko wtedy, gdy wymiary wejściowe są wielokrotnością wymiarów wyjściowych.

Poniższe przykłady zmniejszają rozmiar ze 128x128 do 64x64 (można to łatwo zmienić).

Kanały - ostatnie zamówienie

# 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)

Kanały w pierwszej kolejności

# 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)

Dla skali szarości tylko zmianę 3Do 1tak:

Kanały w pierwszej kolejności

# 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)

Ta metoda używa odpowiednika maksymalnej puli. To najszybszy sposób, jaki znalazłem.

Waylon Flinn
źródło
4
large_image [:, :: 2, :: 2] zwraca obraz z rozdzielczością zmniejszoną o połowę.
L. Kärkkäinen
1
@ LasseKärkkäinen, ale nie zmniejsza próbkowania, po prostu wybiera co drugi piksel. Różnica polega na tym, że ostateczną funkcję „max” można zmienić, aby wybierać lub obliczać piksele w nieco lepszy sposób (na przykład za pomocą „min” lub „mean”). Twoja metoda jest przydatna (i szybsza), jeśli to nie ma znaczenia.
Waylon Flinn
@ L.Kärkkäinen co jest przeciwieństwem podwójnej rozdzielczości?
rayzinnz
2
@rayzinnznp.repeat(np.repeat(a, 2, axis=0), 2, axis=1)
L. Kärkkäinen
11

Jeśli ktoś przyszedł tutaj, szukając prostej metody skalowania / zmiany rozmiaru obrazu w Pythonie, bez korzystania z dodatkowych bibliotek, oto bardzo prosta funkcja zmiany rozmiaru obrazu:

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

Przykładowe użycie: zmiana rozmiaru obrazu (30 x 30) na (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))

Wynik: skalowany obraz

Działa to w celu zmniejszania / skalowania obrazów i działa dobrze z tablicami numpy.

Romwell
źródło
4

imresize()Metoda SciPy była kolejną metodą zmiany rozmiaru, ale zostanie usunięta począwszy od SciPy v 1.3.0. SciPy odnosi się do metody zmiany rozmiaru obrazu PIL :Image.resize(size, resample=0)

rozmiar - żądany rozmiar w pikselach, jako dwie krotki: (szerokość, wysokość).
resample - opcjonalny filtr ponownego próbkowania. Może to być jeden z PIL.Image.NEAREST (użyj najbliższego sąsiada), PIL.Image.BILINEAR (interpolacja liniowa), PIL.Image.BICUBIC (interpolacja sześciennego splajnu) lub PIL.Image.LANCZOS (wysokiej jakości filtr próbkujący ). W przypadku pominięcia lub jeśli obraz ma tryb „1” lub „P”, ustawiany jest PIL.Image.NEAREST.

Link tutaj: https://pillow.readthedocs.io/en/3.1.x/reference/Image.html#PIL.Image.Image.resize

cemsazara
źródło
3
Niestety, imresize () jest przestarzałe, zostanie usunięte w SciPy 1.3.0
MiniQuark
1

Czy są jakieś biblioteki, które to robią w numpy / SciPy

Pewnie. Możesz to zrobić bez OpenCV, scikit-image lub PIL.

Zmiana rozmiaru obrazu polega w zasadzie na odwzorowaniu współrzędnych każdego piksela z oryginalnego obrazu na jego zmienioną pozycję.

Ponieważ współrzędne obrazu muszą być liczbami całkowitymi (traktuj to jak macierz), jeśli odwzorowana współrzędna ma wartości dziesiętne, należy interpolować wartość piksela, aby przybliżyć ją do pozycji całkowitej (np. Znalezienie najbliższego piksela do tej pozycji jest znane jako interpolacja najbliższego sąsiada ).

Wszystko, czego potrzebujesz, to funkcja, która wykona tę interpolację za Ciebie. SciPy ma interpolate.interp2d.

Możesz go użyć do zmiany rozmiaru obrazu w tablicy numpy, powiedzmy arrw następujący sposób:

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

Oczywiście, jeśli twój obraz jest RGB, musisz wykonać interpolację dla każdego kanału.

Jeśli chcesz dowiedzieć się więcej, polecam obejrzenie Resize Images - Computerphile .

fabda01
źródło
Może nie działać w oparciu o tę odpowiedź: 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)
M. Farzalizadeh
źródło
4
Witamy w StackOverflow. Świetnie, że chcesz pomagać innym, odpowiadając na ich pytania. Jednak nie widzę, w jaki sposób twoja odpowiedź dodaje wartości w porównaniu z istniejącą odpowiedzią, która już używa cv2i używa właściwej funkcji zmiany rozmiaru zamiast ponownego wdrożenia „nieoptymalnej” funkcji zmiany rozmiaru, która działa gorzej niż interpolacja najbliższego sąsiada.
NIE,