Resampling eines Numpy-Arrays, das ein Bild darstellt

74

Ich suche nach einer Möglichkeit, ein Numpy-Array, das Bilddaten in einer neuen Größe darstellt, neu abzutasten, wobei ich vorzugsweise die Interpolationsmethode (am nächsten, bilinear usw.) auswählen kann. Ich weiß, dass es gibt

scipy.misc.imresize

Dies geschieht genau, indem die Größenänderungsfunktion von PIL umschlossen wird. Das einzige Problem ist, dass das Numpy-Array, da es PIL verwendet, den Bildformaten entsprechen muss, sodass ich maximal 4 "Farb" -Kanäle habe.

Ich möchte in der Lage sein, die Größe beliebiger Bilder mit einer beliebigen Anzahl von "Farb" -Kanälen zu ändern. Ich habe mich gefragt, ob es einen einfachen Weg gibt, dies in scipy / numpy zu tun, oder ob ich meinen eigenen rollen muss.

Ich habe zwei Ideen, wie ich selbst eine zusammenstellen kann:

  • Eine Funktion, die scipy.misc.imresizeauf jedem Kanal separat ausgeführt wird
  • erstelle meine eigene mit scipy.ndimage.interpolation.affine_transform

Die erste wäre wahrscheinlich langsam für große Datenmengen, und die zweite scheint keine andere Interpolationsmethode als Splines zu bieten.

Gustav Larsson
quelle
Hast du angeschaut scipy.interpolate.griddata? Link
Isaac
Sieht nach einer großartigen Funktion aus, ist jedoch für völlig unstrukturierte Daten gedacht, für die ein viel zeitaufwändigerer Algorithmus ausgeführt wird als von mir benötigt. Ich habe es mir angesehen interp2d, aber es ist nicht nur extrem fehlerhaft, sondern ich bin mir nicht einmal sicher, ob es die Daten korrekt herunterrechnet.
Gustav Larsson

Antworten:

112

Basierend auf Ihrer Beschreibung möchten Sie scipy.ndimage.zoom.

Bilineare Interpolation wäre order=1, am nächsten ist order=0, und kubisch ist die Standardeinstellung ( order=3).

zoom ist speziell für regelmäßig gerasterte Daten gedacht, die Sie auf eine neue Auflösung neu abtasten möchten.

Als schnelles Beispiel:

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)

Und das Ergebnis:

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

Bearbeiten: Wie Matt S. betonte, gibt es einige Einschränkungen beim Zoomen von Multiband-Bildern. Ich kopiere den folgenden Teil fast wörtlich aus einer meiner früheren Antworten :

Das Zoomen funktioniert auch für 3D- (und nD-) Arrays. Beachten Sie jedoch, dass Sie, wenn Sie beispielsweise um das Zweifache zoomen, entlang aller Achsen zoomen .

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

Dies ergibt:

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)

Bei Mehrbandbildern möchten Sie normalerweise nicht entlang der "z" -Achse interpolieren, um neue Bänder zu erstellen.

Wenn Sie so etwas wie ein 3-Band-RGB-Bild haben, das Sie zoomen möchten, können Sie dies tun, indem Sie eine Folge von Tupeln als Zoomfaktor angeben:

print 'Zoomed by 2x along the last two axes:'
print ndimage.zoom(data, (1, 2, 2))

Dies ergibt:

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]]]
Joe Kington
quelle
1
Zu Ihrer Information für andere: Wenn Sie Mehrkanal- Bilddaten haben , rufen Sie diese mit jedem "Kanal-Slice" auf, um unerwünschte "Kanal-Erweiterungen" zu vermeiden. Beispielhaft erklärt: Wenn Sie ein Bild mit einer Pixelbreite von 10 und einer Höhe von 5 und dann 3 Kanälen (jeweils einen für RGB) verwenden, erhalten Sie nach dem Aufrufen zum Vergrößern um 7,0 x ein Array von zurück '70 x 35 'Pixel, aber mit 21 Kanälen. "scipy.ndimage.zoom (np.ones (10 * 5 * 3) .reshape (10, 5, 3), 7.0, order = 0) .shape" gibt Ihnen das Tupel: '(70, 35, 21) 'PS. Nicht verwandt: Gleitkommazoomfaktoren wie '0.37' oder '6.1' werden
Matt S.
7
@MattS. - Wie Sie beschreiben, müssen Sie es nicht für jede Band einzeln übernehmen. Geben Sie einfach ein Tupel als Zoomfaktor an. scipy.ndimage.zoom(data, (3,3,1))Zum Beispiel, um ein 3D-Array entlang der x- und y-Dimensionen um den Faktor 3 zu zoomen, während die dritte Dimension in Ruhe gelassen wird.
Joe Kington
1
@MattS. - (als Antwort auf Ihren gelöschten Kommentar) Guter Vorschlag! Entschuldigung, ich habe nicht früher geantwortet! Ich habe die Einschränkung zum Zoomen von Multiband-Bildern hinzugefügt.
Joe Kington
1
Ist es nur ich oder geht es scipy.ndimage.zoomtatsächlich anders mit den Kanten der Matrix um scipy.misc.imresize? Beim Zoomen mit einem Wert 10der Seiten sind nur 5 Werte breit (mit ist imresizees 10).
Chris
Zoom funktioniert nicht für Werte unter 1. Siehe github.com/scipy/scipy/issues/7324
Kevin Johnsrude
14

Wenn Sie ein Resample durchführen möchten, sollten Sie sich Scipys Kochbuch ansehen, um es erneut zu erstellen . Insbesondere unterstützt die congridam Ende definierte Funktion das Rebinning oder die Interpolation (entspricht der Funktion in IDL mit demselben Namen). Dies sollte die schnellste Option sein, wenn Sie keine Interpolation wünschen.

Sie können auch direkt verwenden scipy.ndimage.map_coordinates, wodurch eine Spline-Interpolation für jede Art von Resampling (einschließlich unstrukturierter Gitter) durchgeführt wird. Ich finde map_coordinates für große Arrays langsam (nx, ny> 200).

Für die Interpolation auf strukturierten Gittern verwende ich eher scipy.interpolate.RectBivariateSpline. Sie können die Reihenfolge des Splines (linear, quadratisch, kubisch usw.) und sogar unabhängig für jede Achse auswählen. Ein Beispiel:

    import scipy.interpolate as interp
    f = interp.RectBivariateSpline(x, y, im, kx=1, ky=1)
    new_im = f(new_x, new_y)

In diesem Fall führen Sie eine bi-lineare Interpolation durch (kx = ky = 1). Die "nächste" Art der Interpolation wird nicht unterstützt, da dies lediglich eine Spline-Interpolation über ein rechteckiges Netz ist. Es ist auch nicht die schnellste Methode.

Wenn Sie nach einer bi-linearen oder bi-kubischen Interpolation suchen, ist es im Allgemeinen viel schneller, zwei 1D-Interpolationen durchzuführen:

    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

Sie können auch verwenden kind='nearest', aber in diesem Fall die Querfelder entfernen.

tiago
quelle
7

Ich habe kürzlich ein Problem mit scipy.ndimage.interpolation.zoom gefunden, das ich als Fehlerbericht eingereicht habe: https://github.com/scipy/scipy/issues/3203

Als Alternative (oder zumindest für mich) habe ich festgestellt, dass skimage.transform.resize von scikit-image ordnungsgemäß funktioniert: http://scikit-image.org/docs/dev/api/skimage.transform.html#skimage .transform.resize

Es funktioniert jedoch anders als die Interpolation.zoom von scipy. Anstatt einen Multiplikator anzugeben, geben Sie die gewünschte Ausgabeform an. Dies funktioniert für 2D- und 3D-Bilder.

Für nur 2D-Bilder können Sie transform.rescale verwenden und einen Multiplikator oder eine Skalierung wie bei interpolation.zoom angeben.

Zehan
quelle
Danke, ich habe auch schon seltsame Ausgänge bemerkt zoom. Ich werde Skimage resizeim Auge behalten, danke!
Gustav Larsson
Alter Thread, aber behält resizedie Größe der Werte im Array (Bild) bei? Ich habe es gerade zum ersten Mal versucht, und für ein 16-Bit-Graustufenbild war dies nicht der Fall. Das ursprüngliche Array hatte einen Median von ~ 32000 und die Größe der Bilder hat einen Median zwischen 0 und 1.
Evan
2

Sie können verwenden interpolate.interp2d.

Wenn Sie beispielsweise ein Bild betrachten, das durch ein Numpy-Array dargestellt wird arr, können Sie die Größe wie folgt auf eine beliebige Höhe und Breite ä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 mehrere Kanäle hat, müssen Sie natürlich die Interpolation für jeden Kanal durchführen.

fabda01
quelle
0

Diese Lösung skaliert X und Y des zugeführten Bildes, ohne die RGB-Kanäle zu beeinflussen:

import numpy as np
import scipy.ndimage

matplotlib.pyplot.imshow(scipy.ndimage.zoom(image_np_array, zoom = (7,7,1), order = 1))

Hoffe das ist nützlich.

Martin Frasch
quelle