图像的索引和分割
本演示演示如何使用IndirectArrays处理索引图像,以及使用ImageSegmentation进行图像分割的可能性。
索引化
索引图像由两部分组成:索引和像素本身。
以下是图像作为数组的"直接"表示。
Pkg.add(["IndirectArrays", "TestImages"])
using Images
img = [
RGB(0.0, 0.0, 0.0) RGB(1.0, 0.0, 0.0) RGB(0.0, 1.0, 0.0) RGB(0.0, 0.0, 1.0) RGB(1.0, 1.0, 1.0)
RGB(1.0, 0.0, 0.0) RGB(0.0, 1.0, 0.0) RGB(0.0, 0.0, 1.0) RGB(1.0, 1.0, 1.0) RGB(0.0, 0.0, 0.0)
RGB(0.0, 1.0, 0.0) RGB(0.0, 0.0, 1.0) RGB(1.0, 1.0, 1.0) RGB(0.0, 0.0, 0.0) RGB(1.0, 0.0, 0.0)
RGB(0.0, 0.0, 1.0) RGB(1.0, 1.0, 1.0) RGB(0.0, 0.0, 0.0) RGB(1.0, 0.0, 0.0) RGB(0.0, 1.0, 0.0)
RGB(1.0, 1.0, 1.0) RGB(0.0, 0.0, 0.0) RGB(1.0, 0.0, 0.0) RGB(0.0, 1.0, 0.0) RGB(0.0, 0.0, 1.0)]
或者,我们可以将其保存为索引图像格式。:
indices = [1 2 3 4 5; 2 3 4 5 1; 3 4 5 1 2; 4 5 1 2 3; 5 1 2 3 4] # `i` records the pixel value `palette[i]`
palette = [RGB(0.0, 0.0, 0.0), RGB(1.0, 0.0, 0.0), RGB(0.0, 1.0, 0.0), RGB(0.0, 0.0, 1.0), RGB(1.0, 1.0, 1.0)]
palette[indices] == img
现在我们来分析一下使用IndirectArrays的利弊。
由于存储两个单独的字段,常规索引图像可能需要更多内存。 如果我们使用unique(img),则大小接近图像自身的大小。
此外,标准索引需要两个操作,因此它的使用可能比直接图像表示慢(并且不适合SIMD矢量化)。
此外,将索引图像格式与两个单独的数组一起使用可能会很不方便,因此IndirectArrays提供了一个数组抽象来组合这两个部分。:
using IndirectArrays
indexed_img = IndirectArray(indices, palette)
img == indexed_img
由于IndirectArray只是一个数组,因此常见的图像操作适用于此类型,例如:
new_img = imresize(indexed_img; ratio=2)
dump(indexed_img)
dump(new_img)
正如我们所看到的,在对索引数组进行操作后,它被简化为标准的像素数组。
分割;分割
接下来,我们使用分水岭算法对图像进行分割。 它是图像分割的主要数学形态学工具。
using TestImages
using IndirectArrays
img = testimage("blobs")
img_example = zeros(Gray, 5, 5)
img_example[2:4,2:4] .= Gray(0.6)
bw = Gray.(img) .> 0.5
bw_example = img_example .> 0.5
Feature_transform函数允许我们找到二进制图像(bw)特征的转换。 它为bw中的每个位置找到最近的"对象"(bw处于真实状态的位置)。
在两个或更多对象处于相同距离的情况下,选择任意对象。
例如,Bw_example[1,1]中最接近的true存在于CartesianIndex(2,2)中,因此它被赋值CartesianIndex(2,2)。
bw_transform = feature_transform(bw)
bw_transform_example = feature_transform(bw_example)
Distance transform函数根据指定的标签执行变换。
例如,在bw_transform[1,1]中有CartesianIndex(2,2),而D[i]对于这个元素,CartesianIndex(1,1)和CartesianIndex(2,2)之间会有一个距离,它等于sqrt(2)。
dist = 1 .- distance_transform(bw_transform)
dist_example = 1 .- distance_transform(bw_transform_example)
label_components函数在二进制dist_trans数组中查找相关组件。 您可以提供一个列表,指示哪些维度用于确定连接性。 例如,不会检查region=[1,3]维度2中邻居的连通性。 这仅对应于最近邻居,即2d中的4连通性和3d矩阵中的6连通性。 默认值为region=1:ndims(A):。 Label的输出是一个整数数组,其中0用于背景像素,连接像素的每个单独区域都获得自己的整数索引。
dist_trans = dist .< 1
markers = label_components(dist_trans)
markers_example = label_components(dist_example .< 0.5)
Gray.(markers/32.0)
分水岭方法函数使用分水岭变换对图像进行分段。
流域变换形成的每个盆地对应一段。 在标记矩阵中,零表示没有标记。 如果两个标记具有相同的索引,则它们的区域将合并为一个区域。
segments = watershed(dist, markers)
segments_example = watershed(dist_example , markers_example)
下图从左到右显示了原始图像、标记区域和最终的分割图像,它们按颜色分解了图像段。
labels = labels_map(segments)
colored_labels = IndirectArray(labels, distinguishable_colors(maximum(labels)))
masked_colored_labels = colored_labels .* (1 .- bw)
mosaic(img, colored_labels, masked_colored_labels; nrow=1)
结论
在这个演示中,我们展示了如何使用图像索引来分割单个图像区域。 这些方法可用于从原始图像中提取感兴趣区域。




