Simulation der Bildentstehung + Bildwiederherstellung#
In diesem Notebook stellen wir künstlich ein Mikroskopbild aus simulierten Zellkernen, Rauschen und Hintergrund zusammen. Anschließend verwenden wir klassische Bildverarbeitungstechniken, um Rauschen und Hintergrund zu entfernen.
import pyclesperanto_prototype as cle
import numpy as np
image_size = (100, 100)
# noise configuration
noise_level = 2
# background configuration
camera_offset = 100
background_sigma = 25
background_intensity = 5
# nuclei configuration
nuclei_radius = 5
nuclei_blur_sigma = 1
nuclei_number = 10
nuclei_intensity = 5
# by pinning the random seed, we can make the code repeatable
np.random.seed(42)
Rauschen#
Hier nehmen wir an, dass das Rauschen im Bild Poisson-verteilt ist, eine häufige Annahme in der Mikroskopie.
noise_image = np.random.poisson(noise_level, image_size)
cle.imshow(noise_image, colorbar=True)
Hintergrund#
Die Hintergrundintensität in Fluoreszenzmikroskopiebildern stammt typischerweise von unfokussiertem Licht. Wir können dies simulieren, indem wir Lichtquellen als einzelne Pixel platzieren und sie mit einem Gaußschen Filter verwischen. Darüber hinaus haben viele Mikroskopkameras einen sogenannten Kamera-Offset. Kein Pixel wird jemals eine Intensität unter diesem Wert haben.
# create empty image
background = np.zeros(image_size)
# place light sources
background[20, 10] += 1
background[50, 80] += 1
background[60, 50] += 1
# blur them massively
background = cle.gaussian_blur(background, sigma_x=background_sigma, sigma_y=background_sigma)
# normalize the image so that the maximum intensity has a defined value
background = background / background.max() * background_intensity
# add camera offsert
background = background + camera_offset
background
cle._ image
|
Zellkerne#
Als nächstes platzieren wir Zellkerne in einem Bild an zufälligen Positionen. Wir verwischen sie ein wenig, um die Punktspreizfunktion des Mikroskops zu simulieren.
# retrieve a defined number of random positions
nuclei_positions = np.random.random((nuclei_number, 2)) * image_size
# write 1 at these locations
nuclei_image = cle.pointlist_to_labelled_spots(nuclei_positions.T, np.zeros(image_size))
nuclei_image = (nuclei_image > 0) * nuclei_intensity
# enlarge the nuclei by a define radius
nuclei_image = cle.maximum_sphere(nuclei_image, radius_x=nuclei_radius, radius_y=nuclei_radius)
# blur the image to make it look more realistic
nuclei_image = cle.gaussian_blur(nuclei_image, sigma_x=nuclei_blur_sigma, sigma_y=nuclei_blur_sigma)
nuclei_image
cle._ image
|
Bildentstehung#
Ein Mikroskopbild ist die Summe der Szene und der oben beschriebenen Effekte.
sum_image = np.asarray(noise_image + background + nuclei_image)
cle.imshow(sum_image, colorbar=True)
Bildsegmentierung#
Wenn wir jetzt einen Segmentierungsalgorithmus auf dieses Bild anwenden würden, wie es ist, könnte dies zu einem falschen Ergebnis führen.
binary = cle.threshold_otsu(sum_image.astype(np.float32))
binary
cle._ image
|
Hintergrundentfernung#
Um dieses Problem zu beheben, müssen wir zuerst die Hintergrundintensität entfernen.
background_removed = cle.top_hat_box(sum_image, radius_x=10, radius_y=10)
background_removed
cle._ image
|
Rauschentfernung#
Wir können auch das Rauschen aus dem Bild entfernen.
noise_removed1 = cle.mean_sphere(sum_image, radius_x=3, radius_y=3)
noise_removed1
cle._ image
|
Und dies kann auch auf dem hintergrundsubtrahierten Bild durchgeführt werden.
noise_removed = cle.mean_sphere(background_removed, radius_x=3, radius_y=3)
noise_removed
cle._ image
|
Bildsegmentierung II#
Nach der Korrektur des Bildes können wir die Segmentierung erneut versuchen.
binary2 = cle.threshold_otsu(noise_removed.astype(np.float32))
binary2
cle._ image
|
# sneak preview: watershed
import napari_segment_blobs_and_things_with_membranes as nsbatwm
binary3 = nsbatwm.split_touching_objects(binary2)
binary3
<__array_function__ internals>:180: RuntimeWarning: Converting input from bool to <class 'numpy.uint8'> for compatibility.
nsbatwm made image
|