scipy.ndimage.

vectorized_filter#

scipy.ndimage.vectorized_filter(input, function, *, size=None, footprint=None, output=None, mode='reflect', cval=None, origin=None, axes=None, batch_memory=1073741824)[Quelle]#

Ein Array mit einer vektorisierten Python-Aufrufbaren als Kernel filtern

Parameter:
inputarray_like

Das Eingabearray.

functioncallable

Kernel, der über ein Fenster zentriert um jedes Element von input angewendet wird. Die Aufrufbare muss die Signatur haben

function(window: ndarray, *, axis: int | tuple) -> scalar

wobei axis die Achse (oder Achsen) von window angibt, entlang derer die Filterfunktion ausgewertet wird.

sizeSkalar oder Tupel, optional

Siehe footprint unten. Wird ignoriert, wenn footprint angegeben ist.

footprintArray, optional

Entweder size oder footprint muss definiert sein. size gibt die Form an, die aus dem Eingabearray entnommen wird, an jeder Elementposition, um die Eingabe für die Filterfunktion zu definieren. footprint ist ein boolesches Array, das (implizit) eine Form angibt, aber auch, welche der Elemente innerhalb dieser Form an die Filterfunktion übergeben werden. Somit ist size=(n, m) äquivalent zu footprint=np.ones((n, m)). Wir passen size an die Anzahl der durch axes angegebenen Dimensionen an. Zum Beispiel, wenn axes (0, 2, 1) ist und n für size übergeben wird, dann ist die effektive size (n, n, n).

outputarray oder dtype, optional

Das Array, in das die Ausgabe platziert werden soll, oder der Datentyp des zurückgegebenen Arrays. Standardmäßig wird ein Array mit demselben Datentyp wie input erstellt.

mode{‘reflect’, ‘constant’, ‘nearest’, ‘mirror’, ‘wrap’}, optional

Der Parameter mode bestimmt, wie das Eingabearray über seine Grenzen hinaus erweitert wird. Standard ist ‘reflect’. Das Verhalten für jeden gültigen Wert ist wie folgt:

‘reflect’ (d c b a | a b c d | d c b a)

Die Eingabe wird erweitert, indem über den Rand des letzten Pixels gespiegelt wird. Dieser Modus wird auch manchmal als halb-Sample-symmetrisch bezeichnet.

‘constant’ (k k k k | a b c d | k k k k)

Die Eingabe wird erweitert, indem alle Werte außerhalb des Rands mit demselben konstanten Wert gefüllt werden, der durch den Parameter cval definiert ist.

‘nearest’ (a a a a | a b c d | d d d d)

Die Eingabe wird erweitert, indem das letzte Pixel wiederholt wird.

‘mirror’ (d c b | a b c d | c b a)

Die Eingabe wird erweitert, indem über die Mitte des letzten Pixels gespiegelt wird. Dieser Modus wird auch manchmal als ganz-Sample-symmetrisch bezeichnet.

‘wrap’ (a b c d | a b c d | a b c d)

Die Eingabe wird erweitert, indem zum gegenüberliegenden Rand umgebrochen wird.

‘valid’ (| a b c d |)

Die Eingabe wird nicht erweitert; vielmehr wird die Ausgabegröße je nach Fenstergröße gemäß der folgenden Berechnung reduziert

window_size = np.asarray(size if size is not None else footprint.shape)
output_shape = np.asarray(input.shape)
output_shape[np.asarray(axes)] -= (window_size - 1)
cvalskalar, optional

Wert, mit dem die Ränder der Eingabe gefüllt werden, wenn mode ‘constant’ ist. Standard ist 0.0.

originint oder Sequenz, optional

Steuert die Platzierung des Filters auf den Pixeln des Eingangsarrays. Ein Wert von 0 (Standard) zentriert den Filter über dem Pixel, wobei positive Werte den Filter nach links und negative Werte nach rechts verschieben. Durch Übergabe einer Sequenz von Ursprüngen mit der Länge, die der Anzahl der Dimensionen des Eingangsarrays entspricht, können entlang jeder Achse unterschiedliche Verschiebungen angegeben werden.

axestuple of int, optional

Wenn None, wird input entlang aller Achsen gefiltert. Andernfalls wird input entlang der angegebenen Achsen gefiltert. Wenn axes angegeben ist, muss die Dimensionalität von footprint und die Länge von Tupeln, die für size oder origin verwendet werden, mit der Länge von axes übereinstimmen. Die i-te Achse von footprint und das i-te Element in diesen Tupeln entspricht dem i-ten Element von axes.

batch_memoryint, default: 2**30

Die maximale Anzahl von Bytes, die von Daten im window-Array belegt werden, das an function übergeben wird.

Rückgabe:
outputndarray

Gefiltertes Array. Der dtype ist der Ausgabedtype von function. Wenn function skalarwertig ist, wenn sie auf ein einzelnes Fenster angewendet wird, ist die Form der Ausgabe die von input (es sei denn, mode=='valid'; siehe mode-Dokumentation). Wenn function mehrwertig ist, wenn sie auf ein einzelnes Fenster angewendet wird, hängt die Platzierung der entsprechenden Dimensionen innerhalb der Ausgabegröße vollständig vom Verhalten von function ab; siehe Beispiele.

Hinweise

Diese Funktion arbeitet, indem sie input gemäß mode auffüllt und dann die bereitgestellte function auf Chunks einer gleitenden Fensteransicht über dem aufgefüllten Array aufruft. Dieser Ansatz ist sehr einfach und flexibel, und daher hat die Funktion viele Funktionen, die von einigen anderen Filterfunktionen nicht angeboten werden (z. B. Speichersteuerung, Unterstützung für float16 und komplexe Datentypen sowie alle NaN-Behandlungsfunktionen, die von der function-Argument bereitgestellt werden).

Dieser Brute-Force-Ansatz kann jedoch erhebliche redundante Arbeit leisten. Verwenden Sie einen spezialisierten Filter (z. B. minimum_filter anstelle dieser Funktion mit numpy.min als Aufrufbare; uniform_filter anstelle dieser Funktion mit numpy.mean als Aufrufbare), wenn möglich, da sie möglicherweise einen effizienteren Algorithmus verwendet.

Wenn kein spezialisierter Filter verfügbar ist, ist diese Funktion ideal, wenn function eine vektorisierte, reine Python-Aufrufbare ist. Noch bessere Leistung kann durch Übergeben einer scipy.LowLevelCallable an generic_filter erzielt werden. generic_filter kann auch für teure Aufrufbare mit großen Filter-Footprints und Aufrufbare, die nicht vektorisiert sind (d. h. solche ohne axis-Unterstützung), bevorzugt werden.

Diese Funktion bietet nicht die Argumente extra_arguments oder extra_keywords, die von einigen ndimage-Funktionen bereitgestellt werden. Dafür gibt es zwei Gründe:

  • Die Weiterleitungsfunktionalität kann vom Benutzer erreicht werden: Wickeln Sie einfach die ursprüngliche Aufrufbare in eine andere Funktion, die die erforderlichen Argumente bereitstellt; z. B. function=lambda input, axis: function(input, *extra_arguments, axis=axis, **extra_keywords).

  • Es gibt Anwendungsfälle, bei denen function zusätzliche *gleitende Fensterdaten* zusätzlich zu input an function übergeben werden sollen. Dies ist noch nicht implementiert, aber wir reservieren diese Argumentnamen für eine solche Funktion, die die Fähigkeit hinzufügt und keine doppelte Schnittstelle zur vorhandenen Fähigkeit bietet.

Beispiele

Angenommen, wir möchten einen Medianfilter mit gerader Fenstergröße auf einem float16-Bild durchführen. Darüber hinaus hat das Bild NaNs, die ignoriert werden sollen (und effektiv vom Filter entfernt werden). median_filter unterstützt keine float16-Daten, sein Verhalten bei Vorhandensein von NaNs ist nicht definiert, und für gerade Fenstergrößen gibt es nicht den üblichen Stichprobenmedian zurück – den Durchschnitt der beiden mittleren Elemente. Dies wäre ein hervorragender Anwendungsfall für vectorized_filter mit function=np.nanmedian, das die erforderliche Schnittstelle unterstützt: es akzeptiert ein Datenarray beliebiger Form als erstes Positionsargument und ein Achsentupel als Schlüsselwortargument axis.

>>> import numpy as np
>>> from scipy import datasets, ndimage
>>> from scipy.ndimage import vectorized_filter
>>> import matplotlib.pyplot as plt
>>> ascent = ndimage.zoom(datasets.ascent(), 0.5).astype(np.float16)
>>> ascent[::16, ::16] = np.nan
>>> result = vectorized_filter(ascent, function=np.nanmedian, size=4)

Die ursprünglichen und gefilterten Bilder plotten.

>>> fig = plt.figure()
>>> plt.gray()  # show the filtered result in grayscale
>>> ax1 = fig.add_subplot(121)  # left side
>>> ax2 = fig.add_subplot(122)  # right side
>>> ax1.imshow(ascent)
>>> ax2.imshow(result)
>>> fig.tight_layout()
>>> plt.show()
../../_images/scipy-ndimage-vectorized_filter-1_00_00.png

Ein weiterer Bedarf, der von vectorized_filter erfüllt wird, ist die Durchführung von Multi-Output-Filtern. Angenommen, wir möchten ein Bild zusätzlich zum Median nach dem 25. und 75. Perzentil filtern. Wir könnten die drei Filter separat durchführen.

>>> ascent = ndimage.zoom(datasets.ascent(), 0.5)
>>> def get_quantile_fun(p):
...     return lambda x, axis: np.quantile(x, p, axis=axis)
>>> ref1 = vectorized_filter(ascent, get_quantile_fun(0.25), size=4)
>>> ref2 = vectorized_filter(ascent, get_quantile_fun(0.50), size=4)
>>> ref3 = vectorized_filter(ascent, get_quantile_fun(0.75), size=4)
>>> ref = np.stack([ref1, ref2, ref3])

Allerdings unterstützt vectorized_filter auch Filter, die mehrere Ausgaben zurückgeben, solange output nicht spezifiziert ist und batch_memory ausreichend hoch ist, um die Berechnung in einem einzigen Chunk durchzuführen.

>>> def quartiles(x, axis):
...     return np.quantile(x, [0.25, 0.50, 0.75], axis=axis)
>>> res = vectorized_filter(ascent, quartiles, size=4, batch_memory=np.inf)
>>> np.all(np.isclose(res, ref))
np.True_

Die Platzierung der zusätzlichen Dimension(en), die mit mehreren Ausgaben korrespondieren, liegt im Ermessen von function. quartiles stellt einfach eine Dimension voran, die den drei Ausgaben entspricht, weil dies das Verhalten von np.quantile ist

>>> res.shape == (3,) + ascent.shape
True

Wenn wir wollten, dass diese Dimension angehängt wird

>>> def quartiles(x, axis):
...     return np.moveaxis(np.quantile(x, [0.25, 0.50, 0.75], axis=axis), 0, -1)
>>> res = vectorized_filter(ascent, quartiles, size=4, batch_memory=np.inf)
>>> res.shape == ascent.shape + (3,)
True

Angenommen, wir möchten einen "Modus"-Filter implementieren – einen Filter, der den am häufigsten vorkommenden Wert innerhalb des Fensters auswählt. Ein einfacher (aber ziemlich langsamer) Ansatz ist die Verwendung von generic_filter mit scipy.stats.mode.

>>> from scipy import stats
>>> rng = np.random.default_rng()
>>> input = rng.integers(255, size=(50, 50)).astype(np.uint8)
>>> def simple_mode(input):
...     return stats.mode(input, axis=None).mode
>>> ref = ndimage.generic_filter(input, simple_mode, size=5)

Wenn Geschwindigkeit wichtig ist, kann vectorized_filter den Leistungsvorteil einer vektorisierten Aufrufbaren nutzen.

>>> def vectorized_mode(x, axis=(-1,)):
...     n_axes = 1 if np.isscalar(axis) else len(axis)
...     x = np.moveaxis(x, axis, tuple(range(-n_axes, 0)))
...     x = np.reshape(x, x.shape[:-n_axes] + (-1,))
...     y = np.sort(x, axis=-1)
...     i = np.concatenate([np.ones(y.shape[:-1] + (1,), dtype=bool),
...                         y[..., :-1] != y[..., 1:]], axis=-1)
...     indices = np.arange(y.size)[i.ravel()]
...     counts = np.diff(indices, append=y.size)
...     counts = np.reshape(np.repeat(counts, counts), y.shape)
...     k = np.argmax(counts, axis=-1, keepdims=True)
...     return np.take_along_axis(y, k, axis=-1)[..., 0]
>>> res = vectorized_filter(input, vectorized_mode, size=5)
>>> np.all(res == ref)
np.True_

Abhängig von der Maschine kann die Version vectorized_filter bis zu 100x schneller sein.