Kacheln von Bildern mit Überlappung#

Bei der Verarbeitung von Bildern in Kacheln können wir Artefakte an den Rändern der Kacheln im resultierenden Bild beobachten. Eine Strategie, um diese Artefakte zu verhindern, ist die Verarbeitung von Kacheln mit einer gewissen Überlappung. dask unterstützt dies ebenfalls. Zu Demonstrationszwecken verwenden wir wieder imshow, um die resultierenden Bilder anzuzeigen. Bei großen Datenmengen würde die Funktion imshow nicht funktionieren.

import dask
import dask.array as da
from skimage.filters import gaussian
from skimage.data import cells3d
from pyclesperanto_prototype import imshow

Ähnlich wie im Beispiel der letzten Lektion definieren wir eine Prozedur, die einen Gauß-Weichzeichner auf ein Bild anwendet und die Größe des Bildes ausgibt, nur damit wir es wissen:

def procedure(image):
    print("proceduring", image.shape)
    return gaussian(image, sigma=5)
image = cells3d()[30,1]
imshow(image)
../_images/e6d2393f7c605c769fb82b772552ade1566917e2a294f57aa9e4f747b0c78793.png

Nachdem wir das Bild geladen haben, kacheln wir es wie gewohnt.

tiles = da.from_array(image, chunks=(128, 128))
tiles
Array Chunk
Bytes 128.00 kiB 32.00 kiB
Shape (256, 256) (128, 128)
Count 4 Tasks 4 Chunks
Type uint16 numpy.ndarray
256 256

Als Nächstes teilen wir dask mit, was mit unseren Kacheln geschehen soll: Wir möchten die Funktion procedure auf die Kacheln mit einer definierten Überlappung anwenden.

overlap_width = 1
tile_map = da.map_overlap(procedure, tiles, depth=overlap_width)
proceduring (0, 0)
proceduring (1, 1)
/Users/haase/opt/anaconda3/envs/bio_39/lib/python3.9/site-packages/dask/array/overlap.py:642: FutureWarning: Default 'boundary' argument value will change from 'reflect' to 'none' in future versions from 2022.03.0 onwards. Use 'boundary="none"' to opt into the future behavior now or set 'boundary="reflect"' to maintain the current behavior going forward.
  warnings.warn(

Die Funktion wurde zweimal mit sehr kleinen Bildern (0x0 und 1x1 Pixel) ausgeführt, um zu prüfen, ob sie funktioniert. Als Nächstes berechnen wir tatsächlich das Ergebnis.

result = tile_map.compute() # Warnung: Dies lädt alle Bilddaten in den Speicher
proceduringproceduring (130, 130)
proceduring (130, 130)
 (130, 130)
proceduring (130, 130)

Anhand der ausgegebenen Bildgröße können wir sehen, dass die verarbeitete Bildgröße 2 Pixel größer ist als die Kachelgröße. Das ist die Überlappung von 1 Pixel in alle Richtungen.

Minimierung von Randeffekten#

Als Nächstes werden wir das Ergebnis der Verarbeitung des gesamten Bildes mit dem in Kacheln verarbeiteten Bild mit verschiedenen Überlappungen vergleichen. Dies gibt uns die Möglichkeit, die minimal notwendige Überlappungsbreite zur Eliminierung von Randeffekten zu ermitteln. Zunächst berechnen wir das Ergebnis für das gesamte Bild.

untiled_result = procedure(image)
proceduring (256, 256)

Dann führen wir eine Schleife mit verschiedenen border_widths aus.

for overlap_width in range(0, 25, 5):
    print("Overlap width", overlap_width)
    tile_map = da.map_overlap(procedure, tiles, depth=overlap_width, boundary='nearest')
    result = tile_map.compute()
    difference = result - untiled_result
    imshow(difference)
    print("sum difference", difference.sum())
    print("-----------------------------------")
Overlap width 0
proceduring (0, 0)
proceduring (1, 1)
proceduringproceduring (128, 128)
proceduring (128, 128) (128, 128)

proceduring (128, 128)
../_images/e4126918c50e4b551767159a05ace9b17cb60db4891f34ce4000369b3cfe9c54.png
sum difference 1.528818863147824
-----------------------------------
Overlap width 5
proceduring (0, 0)
proceduring (1, 1)
proceduring (138, 138)
proceduring (138, 138)
proceduring (138, 138)
proceduring (138, 138)
../_images/13eb6b058a8c32ec6d6cdd255cc7627467c4e18c5cc7345049e9843d1ec85552.png
sum difference 2.098167990865754
-----------------------------------
Overlap width 10
proceduring (0, 0)
proceduring (1, 1)
proceduringproceduring (148, 148)
 (148, 148)
proceduring (148, 148)
proceduring (148, 148)
../_images/4ba9572136695137b5fa129099e0fd00fbf9be14726ad27fc105eb7ac77396b6.png
sum difference -0.18132395183423158
-----------------------------------
Overlap width 15
proceduring (0, 0)
proceduring (1, 1)
proceduring (158, 158)
proceduring (158, 158)
proceduring (158, 158)
proceduring (158, 158)
../_images/475a541b79caf5ff201e8f11ef3f1c1d04afbb786c252c87e5ee31d2d560ac53.png
sum difference -0.005761703866654207
-----------------------------------
Overlap width 20
proceduring (0, 0)
proceduring (1, 1)
proceduring (168, 168)
proceduring (168, 168)
proceduring (168, 168)
proceduring (168, 168)
../_images/48eebdd7f37bda714d1326c6ccf8d5b9a96db531693e290ee45bd898b2277a4e.png
sum difference 0.0
-----------------------------------

Wie Sie sehen können, benötigen wir zur vollständigen Eliminierung des Randeffekts eine Überlappung von 25 Pixeln. Dies hängt offensichtlich mit der angewendeten procedure zusammen. In unserem Fall wurde der in procedure verwendete Gauß-Weichzeichner mit sigma=5 konfiguriert. Als Faustregel können wir sagen, dass im Fall eines Gauß-Weichzeichners die Randbreite mindestens das Vierfache des konfigurierten Sigma betragen muss. Bei komplizierteren Algorithmen gibt es jedoch keine solchen Regeln. Im Allgemeinen wird empfohlen, die gekachelte Bildverarbeitung an kleinen Bildern zu testen, wie hier gezeigt, und herauszufinden, ob Artefakte auftreten und welche Fehler sie in einem längeren Bildverarbeitungsworkflow verursachen können.