通过平均法从珠子图像中确定点扩散函数#

为了正确地对显微镜图像进行反卷积,我们应该确定显微镜的点扩散函数(PSF)。

另请参阅

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

这里使用的示例图像数据是由Bert Nitzsche和Robert Haase(当时都在MPI-CBG)在MPI-CBG的光学显微镜设施中获取的。为了完整起见,体素大小为0.022x0.022x0.125 µm^3。

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

我们的示例图像显示了荧光珠子,理想情况下,其直径应小于成像设置的分辨率。此外,珠子应该发出与我们稍后想要反卷积的样本相同波长的光。在以下图像裁剪中,我们看到四个荧光珠子。建议成像更大的视野,至少有25个珠子。同时确保珠子不会相互粘连,并且分布稀疏。

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

为了确定平均PSF,从技术上讲,我们可以裁剪出所有单个珠子,对齐它们,然后对图像进行平均。因此,我们对对象进行分割并确定它们的质心。

# 分割对象
label_image = cle.voronoi_otsu_labeling(bead_image)
imshow(label_image, labels=True)
../_images/658f1ffe27f81bddfbe03b9a26de9f84b13519e5a1e8af84a198053b1701cdb6.png
# 确定每个对象的质心
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平均#

接下来,我们将遍历珠子并通过将它们平移到较小的PSF图像中来裁剪它们。

# 配置未来PSF图像的大小
psf_radius = 20
size = psf_radius * 2 + 1

# 初始化PSF
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("珠子", index, "位于位置", x, y, z)
    
    # 将PSF移动到较小图像中的正确位置
    cle.translate(bead_image, single_psf_image, 
                  translate_x= -x + psf_radius,
                  translate_y= -y + psf_radius,
                  translate_z= -z + psf_radius)

    # 可视化
    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])
    
    # 平均
    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

平均PSF看起来像这样:

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)

在我们确定了一个位置良好的PSF之后,我们可以保存它以供以后重用。在这样做之前,我们对PSF进行归一化。目标是得到一个总强度为1的图像。这确保了稍后使用此PSF进行反卷积的图像不会修改图像的强度范围。

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)