Numpy Bildgröße ändern / neu skalieren

94

Ich möchte ein Bild aufnehmen und den Maßstab des Bildes ändern, während es sich um ein Numpy-Array handelt.

Zum Beispiel habe ich dieses Bild einer Coca-Cola-Flasche: Flasche-1

Was sich in einer Reihe von Formen niederschlägt, (528, 203, 3)und ich möchte die Größe ändern, um die Größe dieses zweiten Bildes zu sagen: Flasche-2

Welches hat eine Form von (140, 54, 3).

Wie ändere ich die Größe des Bildes in eine bestimmte Form, während das Originalbild beibehalten wird? Andere Antworten schlagen vor, jede zweite oder dritte Zeile zu entfernen, aber ich möchte das Bild im Grunde so verkleinern, wie Sie es über einen Bildeditor tun würden, jedoch in Python-Code. Gibt es Bibliotheken, um dies in numpy / SciPy zu tun?

Brian Hamill
quelle
Können Sie Code für Ihr Numpy-Array anzeigen?
ShpielMeister
2
@sascha Veraltet, entsprechend der Seite, die Sie verlinkt haben.
Paul Panzer
@ShpielMeister Ich kann IntelliJ nicht dazu bringen, das numpy-Array vollständig auszudrucken. Aus irgendeinem Grund wird es bei großen Ausgängen ständig verwendet, sodass ich nur einen Teil der Array-Ausgabe in der Konsole sehen kann
Brian Hamill

Antworten:

116

Ja, Sie können installieren opencv(dies ist eine Bibliothek, die für die Bildverarbeitung und Computer Vision verwendet wird) und die cv2.resizeFunktion verwenden. Und zum Beispiel verwenden:

import cv2
import numpy as np

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

Hier imgist also ein Numpy-Array, das das Originalbild enthält, während resein Numpy-Array das verkleinerte Bild enthält. Ein wichtiger Aspekt ist der interpolationParameter: Es gibt verschiedene Möglichkeiten, die Größe eines Bildes zu ändern. Zumal Sie das Bild verkleinern und die Größe des Originalbilds nicht ein Vielfaches der Größe des Bildes mit geänderter Größe beträgt . Mögliche Interpolationsschemata sind:

  • INTER_NEAREST - eine Interpolation zum nächsten Nachbarn
  • INTER_LINEAR - eine bilineare Interpolation (standardmäßig verwendet)
  • INTER_AREA- Resampling unter Verwendung der Pixelflächenbeziehung. Es kann eine bevorzugte Methode zur Bilddezimierung sein, da es moirschfreie Ergebnisse liefert. Wenn das Bild jedoch gezoomt wird, ähnelt es der INTER_NEARESTMethode.
  • INTER_CUBIC - eine bikubische Interpolation über 4x4 Pixel Nachbarschaft
  • INTER_LANCZOS4 - eine Lanczos-Interpolation über eine Nachbarschaft von 8 x 8 Pixeln

Wie bei den meisten Optionen gibt es keine "beste" Option in dem Sinne, dass es für jedes Größenänderungsschema Szenarien gibt, in denen eine Strategie einer anderen vorgezogen werden kann.

Willem Van Onsem
quelle
5
Ich habe diesen Code gerade ausprobiert und er funktioniert! Nur eine Änderung ist, dass es so sein dsizesollte, dsize=(54, 140)wie es x dann y braucht, wobei ein numpy Array die Form als y dann x zeigt (y ist die Anzahl der Zeilen und x ist die Anzahl der Spalten)
Brian Hamill
5
Ich versuche, cv2 zu vermeiden, es tauscht Dimensionen und Lasten im BGR-Kanalformat aus. Ich bevorzuge skimage.io.imread('image.jpg')und skimage.transform.resize(img). scikit-image.org/docs/dev/install.html
Eduardo Pignatelli
1
@EduardoPignatelli Ich vermeide skimage.transform.resize, weil Sie keine Kontrolle über den verwendeten Interpolationsalgorithmus haben. Dies ist jedoch möglicherweise nicht wichtig, abhängig von den Anwendungsfällen der Benutzer.
Decker
1
@Decker skimage.transform.resize bietet eine gewisse Kontrolle über den 'order'-Parameter. order = 0 ist der nächste Nachbar, 1 = bi-linear, 2 = bi-quadratisch, 3 = bi-kubisch usw. Es gibt jedoch keinen Flächenmittelwert oder keine Lanczos-Interpolation.
Tapio Friberg
1
@ TapioFriberg ahh ja, ich stehe korrigiert; Ich sehe die Algorithmen, die in der Dokumentation für den Parameter 'order' von skimage.transform.warp definiert sind. Irgendwann kann es hilfreich sein, die Dokumente so zu aktualisieren, dass sie Verweise für die Typen enthalten. Beispielsweise ist "Bi-Quartic" an keiner anderen Stelle in der Dokumentation definiert (Stand: 10. Dezember 2019) - möglicherweise ein Einzeiler für zukünftige Benutzer von Vorteil sein.
Decker
64

Möglicherweise kann dazu nur numpy verwendet werden, der Vorgang ist jedoch nicht integriert. Das heißt, Sie können scikit-imagediese Art der Bildmanipulation verwenden (die auf Numpy basiert).

Scikit-Bild Umskalierungs Dokumentation ist hier .

Mit Ihrem Bild können Sie beispielsweise Folgendes tun:

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

Dies erledigt für Sie Dinge wie Interpolation, Anti-Aliasing usw.

jakevdp
quelle
2
Danke dir! Diese Antwort funktioniert auch! Obwohl ich ein Problem mit der anti_aliasingFlagge bekomme, sieht es so aus, als ob sie aus der neuesten Version von 0.13.1 entfernt wurde
Brian Hamill
8
Dies gibt Bild als float ndarray zurück, auch wenn Ihr Originalbild uint8
sziraqui
3
Dies ist eine schöne Technik, da sie mit einer beliebigen Anzahl von Kanälen funktioniert. Ich habe dies mit RGB-Daten in Kombination mit Tiefenpunktwolkendaten versucht und dabei die gewünschte Beziehung beibehalten.
Darth Egregious
@DarthEgregious, jakevdp -> meine zufälligen Rauschdaten wurden einfarbig, als ich die Größe des Arrays (137,236,3) auf (64,64) wie bei der von Ihnen beschriebenen Methode änderte. Ist das normal, weil es so aussieht, als hätte es alle Informationen verloren?
Deshwal
1
Sollte es nicht sein (64,64,3)
Darth Egregious
13

Für Leute, die von Google hierher kommen und nach einer schnellen Möglichkeit suchen, Bilder in numpyArrays zur Verwendung in Anwendungen für maschinelles Lernen herunterzusampeln, ist hier eine superschnelle Methode (angepasst von hier ). Diese Methode funktioniert nur, wenn die Eingabedimensionen ein Vielfaches der Ausgabedimensionen sind.

In den folgenden Beispielen wird das Beispiel von 128 x 128 auf 64 x 64 heruntergerechnet (dies kann leicht geändert werden).

Letzte Bestellung der Kanäle

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

Erstbestellung der Kanäle

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

Für Graustufenbilder nur um die Änderung 3zu einem 1wie folgt aus :

Erstbestellung der Kanäle

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

Diese Methode verwendet das Äquivalent von max Pooling. Es ist der schnellste Weg, den ich gefunden habe.

Waylon Flinn
quelle
4
large_image [:, :: 2, :: 2] gibt das Bild mit halbierter Auflösung zurück.
L. Kärkkäinen
1
@ LasseKärkkäinen, aber es wird kein Downsample durchgeführt, sondern nur jedes zweite Pixel ausgewählt. Der Unterschied besteht darin, dass die endgültige Funktion 'max' geändert werden kann, um Pixel etwas besser auszuwählen oder zu berechnen (z. B. mit 'min' oder 'mean'). Ihre Methode ist nützlich (und schneller), wenn das keine Rolle spielt.
Waylon Flinn
@ L.Kärkkäinen was ist das Gegenteil von doppelter Auflösung?
Rayzinnz
2
@rayzinnznp.repeat(np.repeat(a, 2, axis=0), 2, axis=1)
L. Kärkkäinen
7

Wenn jemand hierher gekommen ist, um nach einer einfachen Methode zum Skalieren / Ändern der Größe eines Bilds in Python zu suchen, ohne zusätzliche Bibliotheken zu verwenden, ist hier eine sehr einfache Funktion zum Ändern der Bildgröße:

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

Anwendungsbeispiel: Ändern der Größe eines (30 x 30) Bilds auf (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))

Ausgabe: skaliertes Bild

Dies funktioniert zum Verkleinern / Skalieren von Bildern und funktioniert gut mit numpy Arrays.

Romwell
quelle
4

Die SciPy- imresize()Methode war eine andere Methode zur Größenänderung, wird jedoch ab SciPy v 1.3.0 entfernt. SciPy bezieht sich auf die PIL- Bildgrößenänderungsmethode:Image.resize(size, resample=0)

Größe - Die angeforderte Größe in Pixel als 2-Tupel: (Breite, Höhe).
Resample - Ein optionaler Resampling-Filter. Dies kann PIL.Image.NEAREST (nächster Nachbar verwenden), PIL.Image.BILINEAR (lineare Interpolation), PIL.Image.BICUBIC (kubische Spline-Interpolation) oder PIL.Image.LANCZOS (ein qualitativ hochwertiges Downsampling-Filter) sein ). Wenn es weggelassen wird oder wenn das Bild den Modus "1" oder "P" hat, wird PIL.Image.NEAREST gesetzt.

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

Cemsazara
quelle
3
Leider ist imresize () veraltet, es wird in SciPy 1.3.0
MiniQuark
1

Gibt es Bibliotheken, um dies in numpy / SciPy zu tun?

Sicher. Sie können dies ohne OpenCV, Scikit-Image oder PIL tun.

Bei der Bildgrößenänderung werden im Wesentlichen die Koordinaten jedes Pixels vom Originalbild auf seine Größenänderungsposition abgebildet.

Da die Koordinaten eines Bildes Ganzzahlen sein müssen (stellen Sie es sich als Matrix vor), sollten Sie den Pixelwert interpolieren, um ihn an die Ganzzahlposition anzunähern, wenn die zugeordnete Koordinate Dezimalwerte aufweist (z. B. ist es bekannt, das nächste Pixel zu dieser Position zu ermitteln als Interpolation des nächsten Nachbarn ).

Sie benötigen lediglich eine Funktion, die diese Interpolation für Sie ausführt. SciPy hat interpolate.interp2d.

Sie können damit die Größe eines Bilds in einem numpy-Array arrwie folgt ändern :

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

Wenn Ihr Bild RGB ist, müssen Sie natürlich die Interpolation für jeden Kanal durchführen.

Wenn Sie mehr verstehen möchten, empfehle ich Ihnen , sich Resizing Images - Computerphile anzusehen .

gelb01
quelle
Funktioniert
möglicherweise
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
quelle
4
Willkommen bei StackOverflow. Schön, dass Sie anderen helfen möchten, indem Sie ihre Fragen beantworten. Ich sehe jedoch nicht, wie Ihre Antwort im Vergleich zu der vorhandenen Antwort, die bereits cv2eine ordnungsgemäße Größenänderungsfunktion verwendet und verwendet, einen Mehrwert bietet, anstatt eine "suboptimale" Größenänderungsfunktion erneut zu implementieren, die schlechter abschneidet als die Interpolation des nächsten Nachbarn.
NOhs