Bestimmung der Punktspreizfunktion aus einem Kugelbild durch Mittelung#

Um ein Mikroskopbild korrekt zu entfalten, sollten wir die Punktspreizfunktion (PSF) des Mikroskops bestimmen.

Siehe auch

import numpy as np
from skimage.io import imread, imsave
from pyclesperanto_prototype import imshow
import pyclesperanto_prototype as cle
import pandas as pd
import matplotlib.pyplot as plt

Die hier verwendeten Beispielbilddaten wurden von Bert Nitzsche und Robert Haase (beide damals am MPI-CBG) in der Light Microscopy Facility des MPI-CBG aufgenommen. Der Vollständigkeit halber beträgt die Voxelgröße 0,022x0,022x0,125 µm^3.

bead_image = imread('../../data/Bead_Image1_crop.tif')
bead_image.shape
(41, 150, 150)

Unser Beispielbild zeigt fluoreszierende Kugeln, idealerweise mit einem Durchmesser, der kleiner ist als die Auflösung des Bildgebungssystems. Darüber hinaus sollten die Kugeln Licht in der gleichen Wellenlänge emittieren wie die Probe, die wir später entfalten möchten. Im folgenden Bildausschnitt sehen wir vier fluoreszierende Kugeln. Es wird empfohlen, ein größeres Sichtfeld mit mindestens 25 Kugeln aufzunehmen. Stellen Sie auch sicher, dass die Kugeln nicht aneinander kleben und spärlich verteilt sind.

imshow(cle.maximum_x_projection(bead_image), colorbar=True)
imshow(cle.maximum_y_projection(bead_image), colorbar=True)
imshow(cle.maximum_z_projection(bead_image), colorbar=True)
../_images/bc61c20f19744054b7f1ff81a09c6eef21855bcb937ce7a918c70d19c0b145d6.png ../_images/0901ecfda696afda8465acea84c8b4908017df75f5f6fd78f74fb696935786ba.png ../_images/906ad5084326f9f315bf7726ebdd33b0bca743cc1a4798621e7f7592fea5aac5.png

Um eine durchschnittliche PSF zu bestimmen, können wir technisch gesehen alle einzelnen Kugeln ausschneiden, sie ausrichten und dann die Bilder mitteln. Daher segmentieren wir die Objekte und bestimmen ihren Massenschwerpunkt.

# Objekte segmentieren
label_image = cle.voronoi_otsu_labeling(bead_image)
imshow(label_image, labels=True)
../_images/658f1ffe27f81bddfbe03b9a26de9f84b13519e5a1e8af84a198053b1701cdb6.png
# Massenschwerpunkt für jedes Objekt bestimmen
stats = cle.statistics_of_labelled_pixels(bead_image, label_image)

df = pd.DataFrame(stats)
df[["mass_center_x", "mass_center_y", "mass_center_z"]]
mass_center_x mass_center_y mass_center_z
0 30.107895 73.028938 23.327475
1 44.293156 111.633430 23.329062
2 76.092850 82.453033 23.299677
3 125.439606 35.972496 23.390951

PSF-Mittelung#

Als nächstes werden wir über die Kugeln iterieren und sie ausschneiden, indem wir sie in ein kleineres PSF-Bild übersetzen.

# Größe des zukünftigen PSF-Bildes konfigurieren
psf_radius = 20
size = psf_radius * 2 + 1

# PSF initialisieren
single_psf_image = cle.create([size, size, size])
avg_psf_image = cle.create([size, size, size])

num_psfs = len(df)
for index, row in df.iterrows():
    x = row["mass_center_x"]
    y = row["mass_center_y"]
    z = row["mass_center_z"]
    
    print("Kugel", index, "an Position", x, y, z)
    
    # PSF in die richtige Position in einem kleineren Bild verschieben
    cle.translate(bead_image, single_psf_image, 
                  translate_x= -x + psf_radius,
                  translate_y= -y + psf_radius,
                  translate_z= -z + psf_radius)

    # visualisieren
    fig, axs = plt.subplots(1,3)    
    imshow(cle.maximum_x_projection(single_psf_image), plot=axs[0])
    imshow(cle.maximum_y_projection(single_psf_image), plot=axs[1])
    imshow(cle.maximum_z_projection(single_psf_image), plot=axs[2])
    
    # mitteln
    avg_psf_image = avg_psf_image + single_psf_image / num_psfs
Bead 0 at position 30.107894897460938 73.02893829345703 23.32747459411621
Bead 1 at position 44.293155670166016 111.63343048095703 23.32906150817871
Bead 2 at position 76.09284973144531 82.45303344726562 23.2996768951416
Bead 3 at position 125.43960571289062 35.972496032714844 23.39095115661621
../_images/dff2d693110f889bd993da0ee4703fedaf6723adf1a5f1ca87ae881d2395f2b4.png ../_images/afbb13b598502c783d87eaa1f928eed1f8736a2d01eb5e07e261015a488abd98.png ../_images/e5ee2ab02997327492647477f3862ac9dc7b55d0bd77b31ddc82226893c1eaf4.png ../_images/4afaf310f68a10d34b771c4d261c1a359c89e9746974e70025269d35ed3c93a7.png

Die durchschnittliche PSF sieht dann so aus:

fig, axs = plt.subplots(1,3)    
imshow(cle.maximum_x_projection(avg_psf_image), plot=axs[0])
imshow(cle.maximum_y_projection(avg_psf_image), plot=axs[1])
imshow(cle.maximum_z_projection(avg_psf_image), plot=axs[2])
../_images/df910e75d821babaf7d3beb4793305758a9e91879ec249c4815be26d36436ba5.png
avg_psf_image.min(), avg_psf_image.max()
(0.0, 94.5)

Nachdem wir eine gut zentrierte PSF bestimmt haben, können wir sie für die spätere Wiederverwendung speichern. Bevor wir das tun, normalisieren wir die PSF. Ziel ist es, ein Bild zu haben, bei dem die Gesamtintensität 1 beträgt. Dies stellt sicher, dass ein Bild, das später mit dieser PSF entfaltet wird, den Intensitätsbereich des Bildes nicht verändert.

normalized_psf = avg_psf_image / np.sum(avg_psf_image)

imshow(normalized_psf, colorbar=True)
../_images/c214ea1c85ad9e3868924c84710f0764cf0f21ab583f26430731c889cf5705e6.png
normalized_psf.min(), normalized_psf.max()
(0.0, 0.0006259646)
imsave('../../data/psf.tif', normalized_psf)
C:\Users\rober\AppData\Local\Temp\ipykernel_16716\3265681491.py:1: UserWarning: ../../data/psf.tif is a low contrast image
  imsave('../../data/psf.tif', normalized_psf)