选择特征#
在选择合适的特征时,可以考虑一些经验法则。例如,如果想要非常精确地分割对象,应该使用小的半径/sigma值。如果只需要大致轮廓,或者需要消除对象边界上的单个像素,使用较大的半径和sigma值是有意义的。然而,这个话题也可以通过统计学的方法来处理。
from skimage.io import imread, imsave
import pyclesperanto_prototype as cle
import numpy as np
import apoc
import matplotlib.pyplot as plt
import pandas as pd
image = imread('../../data/blobs.tif')
manual_annotation = imread('../../data/blobs_annotations.tif')
fig, axs = plt.subplots(1,2)
cle.imshow(image, plot=axs[0])
cle.imshow(manual_annotation, labels=True, plot=axs[1])
训练 - 使用过多特征#
现在我们训练一个对象分割器,并提供许多特征。我们还需要提供参数来配置深度决策树和多棵树。这是必要的,以便下一步,即推导统计数据,有足够的统计能力。 之后,我们快速查看结果以进行理智检查。
# define features
features = apoc.PredefinedFeatureSet.small_dog_log.value + " " + \
apoc.PredefinedFeatureSet.medium_dog_log.value + " " + \
apoc.PredefinedFeatureSet.large_dog_log.value
# this is where the model will be saved
cl_filename = '../../data/blobs_object_segmenter2.cl'
apoc.erase_classifier(cl_filename)
classifier = apoc.ObjectSegmenter(opencl_filename=cl_filename,
positive_class_identifier=2,
max_depth=5,
num_ensembles=1000)
classifier.train(features, manual_annotation, image)
segmentation_result = classifier.predict(features=features, image=image)
cle.imshow(segmentation_result, labels=True)
分类器统计#
训练后,我们可以打印出分类器的一些统计信息。它给出了使用的特征表以及这些特征在像素分类决策中的重要性。
shares, counts = classifier.statistics()
def colorize(styler):
styler.background_gradient(axis=None, cmap="rainbow")
return styler
df = pd.DataFrame(shares).T
df.style.pipe(colorize)
| 0 | 1 | 2 | 3 | 4 | |
|---|---|---|---|---|---|
| original | 0.138000 | 0.046423 | 0.042312 | 0.037281 | 0.062112 |
| gaussian_blur=1 | 0.228000 | 0.092846 | 0.074303 | 0.105263 | 0.055901 |
| difference_of_gaussian=1 | 0.000000 | 0.108828 | 0.095975 | 0.074561 | 0.086957 |
| laplace_box_of_gaussian_blur=1 | 0.000000 | 0.105784 | 0.089783 | 0.081140 | 0.099379 |
| gaussian_blur=5 | 0.096000 | 0.064688 | 0.118679 | 0.096491 | 0.130435 |
| difference_of_gaussian=5 | 0.254000 | 0.182648 | 0.112487 | 0.120614 | 0.118012 |
| laplace_box_of_gaussian_blur=5 | 0.209000 | 0.194064 | 0.121775 | 0.118421 | 0.124224 |
| gaussian_blur=25 | 0.004000 | 0.061644 | 0.113519 | 0.127193 | 0.080745 |
| difference_of_gaussian=25 | 0.032000 | 0.072298 | 0.122807 | 0.127193 | 0.130435 |
| laplace_box_of_gaussian_blur=25 | 0.039000 | 0.070776 | 0.108359 | 0.111842 | 0.111801 |
在这个可视化中,你可以看到特征 gaussian_blur=1、difference_of_gaussian=5 和 laplace_box_of_gaussian_blur=5 占据了大约65%的决策。在第一级(级别 0)。如果这三个特征至关重要,我们可以训练另一个只考虑这些特征的分类器。此外,我们看到在更高的三个深度级别上,特征使用的份额分布更加均匀。这些级别在分类像素时可能不会产生很大的差异。下一个我们训练的分类器,我们可以用较低的 max_depth 来训练。
# define features
features = "gaussian_blur=1 difference_of_gaussian=5 laplace_box_of_gaussian_blur=5"
# this is where the model will be saved
cl_filename = '../../data/blobs_object_segmenter3.cl'
apoc.erase_classifier(cl_filename)
classifier = apoc.ObjectSegmenter(opencl_filename=cl_filename,
positive_class_identifier=2,
max_depth=3,
num_ensembles=1000)
classifier.train(features, manual_annotation, image)
segmentation_result = classifier.predict(features=features, image=image)
cle.imshow(segmentation_result, labels=True)
新的分类器仍然产生了非常相似的结果。它考虑的特征更少,这使得它更快,但可能也对图像和成像条件之间的差异不那么稳健。我们再看一下分类器统计:
shares, counts = classifier.statistics()
df = pd.DataFrame(shares).T
df.style.pipe(colorize)
| 0 | 1 | 2 | |
|---|---|---|---|
| gaussian_blur=1 | 0.331000 | 0.349194 | 0.344620 |
| difference_of_gaussian=5 | 0.356000 | 0.329839 | 0.337096 |
| laplace_box_of_gaussian_blur=5 | 0.313000 | 0.320968 | 0.318284 |
为了演示目的,我们现在将训练另一个具有非常相似特征的分类器。
# define features
features = "gaussian_blur=1 difference_of_gaussian=2 difference_of_gaussian=3 difference_of_gaussian=4 difference_of_gaussian=5 difference_of_gaussian=6 laplace_box_of_gaussian_blur=5"
# this is where the model will be saved
cl_filename = '../../data/blobs_object_segmenter3.cl'
apoc.erase_classifier(cl_filename)
classifier = apoc.ObjectSegmenter(opencl_filename=cl_filename,
positive_class_identifier=2,
max_depth=3,
num_ensembles=1000)
classifier.train(features, manual_annotation, image)
segmentation_result = classifier.predict(features=features, image=image)
cle.imshow(segmentation_result, labels=True)
再次,分割结果看起来非常相似,但分类器统计不同。
shares, counts = classifier.statistics()
df = pd.DataFrame(shares).T
df.style.pipe(colorize)
| 0 | 1 | 2 | |
|---|---|---|---|
| gaussian_blur=1 | 0.200000 | 0.093750 | 0.162829 |
| difference_of_gaussian=2 | 0.000000 | 0.120888 | 0.148026 |
| difference_of_gaussian=3 | 0.053000 | 0.064967 | 0.125000 |
| difference_of_gaussian=4 | 0.236000 | 0.162829 | 0.097039 |
| difference_of_gaussian=5 | 0.178000 | 0.222862 | 0.125822 |
| difference_of_gaussian=6 | 0.134000 | 0.120066 | 0.194901 |
| laplace_box_of_gaussian_blur=5 | 0.199000 | 0.214638 | 0.146382 |
通过这种方式,还可以微调指定特征需要使用的半径和sigma参数。
本节中给出的提示并不是选择正确特征的固定规则。不过,提供的工具可能有助于稍微深入了解特征,并衡量提供的特征列表和参数的影响。