图像形成和图像恢复的模拟#

在这个笔记本中,我们人为地从模拟的细胞核、噪声和背景组装一幅显微镜图像。之后,我们使用经典的图像处理技术来去除噪声和背景。

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)

噪声#

这里我们假设图像中的噪声是泊松分布的,这是显微镜学中的一个常见假设。

noise_image = np.random.poisson(noise_level, image_size)

cle.imshow(noise_image, colorbar=True)
../_images/a0be8d81e2a7b002e7841e89c7ac2ef43be4a5c58d9062e56886b3be8b5c308b.png

背景#

荧光显微镜图像中的背景强度通常来自于离焦光。我们可以通过将光源放置为单个像素并用高斯滤波器模糊它们来模拟这一点。此外,许多显微镜相机都有所谓的相机偏移。没有任何像素的强度会低于这个值。

# 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
shape(100, 100)
dtypefloat32
size39.1 kB
min100.14104
max105.0

细胞核#

接下来,我们在图像中随机位置放置细胞核。我们稍微模糊它们以模拟显微镜的点扩散函数

# 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
shape(100, 100)
dtypefloat32
size39.1 kB
min0.0
max5.0

图像形成#

显微镜图像是场景和上述效果的总和。

sum_image = np.asarray(noise_image + background + nuclei_image)

cle.imshow(sum_image, colorbar=True)
../_images/731583ea9532d7236719d77e5d2767170a6c819272fc513fb07ef289008685dd.png

图像分割#

如果我们现在直接对这个图像应用分割算法,可能会导致错误的结果。

binary = cle.threshold_otsu(sum_image.astype(np.float32))

binary
cle._ image
shape(100, 100)
dtypeuint8
size9.8 kB
min0.0
max1.0

背景去除#

为了解决这个问题,我们首先需要去除背景强度。

background_removed = cle.top_hat_box(sum_image, radius_x=10, radius_y=10)

background_removed
cle._ image
shape(100, 100)
dtypefloat32
size39.1 kB
min0.0
max12.833778

噪声去除#

我们还可以从图像中去除噪声。

noise_removed1 = cle.mean_sphere(sum_image, radius_x=3, radius_y=3)

noise_removed1
cle._ image
shape(100, 100)
dtypefloat32
size39.1 kB
min101.35629
max111.36778

这也可以在背景减去后的图像上进行。

noise_removed = cle.mean_sphere(background_removed, radius_x=3, radius_y=3)

noise_removed
cle._ image
shape(100, 100)
dtypefloat32
size39.1 kB
min0.7578272
max7.5516324

图像分割 II#

在校正图像后,我们可以再次尝试分割。

binary2 = cle.threshold_otsu(noise_removed.astype(np.float32))

binary2
cle._ image
shape(100, 100)
dtypeuint8
size9.8 kB
min0.0
max1.0
# 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
shape(100, 100)
dtypebool
size9.8 kB
minFalse
maxTrue