Return the medial axis of a binary image: the locus of foreground pixels that are local maxima of the (squared) Euclidean distance transform in at least one of the four principal directions (horizontal, vertical, NW-SE diagonal, NE-SW diagonal). Each skeleton pixel carries width information via the distance value at that point.
Arguments
- image
A binary image: a matrix where non-zero values are foreground and zero values are background. Logical, integer, and numeric inputs are accepted.
- return_distance
Logical. If
FALSE(default), return only the binary skeleton in the same storage mode asimage. IfTRUE, return a list with elementsskeleton(the binary skeleton) anddistance(a numeric matrix of Euclidean distances from each foreground pixel to the nearest background pixel, with 0 on background pixels).
Value
Either a matrix (when return_distance = FALSE) or a
list(skeleton, distance) (when return_distance = TRUE).
Details
This is different from thin(): classical thinning algorithms
produce a connected, 1-pixel-wide skeleton without width
information. The medial axis transform (Blum 1967) produces a
skeleton with width information, useful for shape analysis
where local thickness matters.
References
Blum, H. (1967). A transformation for extracting new descriptors of shape. In Models for the Perception of Speech and Visual Form (pp. 362-380). MIT Press.
Felzenszwalb, P. F., & Huttenlocher, D. P. (2012). Distance transforms of sampled functions. Theory of Computing, 8(19), 415-428. doi:10.4086/toc.2012.v008a019
Examples
# A 7x9 solid rectangle: the medial axis is the middle row.
m <- matrix(0L, nrow = 7, ncol = 9)
m[3:5, 3:7] <- 1L
medial_axis(m)
#> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
#> [1,] 0 0 0 0 0 0 0 0 0
#> [2,] 0 0 0 0 0 0 0 0 0
#> [3,] 0 0 1 1 0 1 1 0 0
#> [4,] 0 0 1 1 1 1 1 0 0
#> [5,] 0 0 1 1 0 1 1 0 0
#> [6,] 0 0 0 0 0 0 0 0 0
#> [7,] 0 0 0 0 0 0 0 0 0
# Returning width information alongside the skeleton.
result <- medial_axis(m, return_distance = TRUE)
result$skeleton
#> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
#> [1,] 0 0 0 0 0 0 0 0 0
#> [2,] 0 0 0 0 0 0 0 0 0
#> [3,] 0 0 1 1 0 1 1 0 0
#> [4,] 0 0 1 1 1 1 1 0 0
#> [5,] 0 0 1 1 0 1 1 0 0
#> [6,] 0 0 0 0 0 0 0 0 0
#> [7,] 0 0 0 0 0 0 0 0 0
round(result$distance, 3)
#> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
#> [1,] 0 0 0 0 0 0 0 0 0
#> [2,] 0 0 0 0 0 0 0 0 0
#> [3,] 0 0 1 1 1 1 1 0 0
#> [4,] 0 0 1 2 2 2 1 0 0
#> [5,] 0 0 1 1 1 1 1 0 0
#> [6,] 0 0 0 0 0 0 0 0 0
#> [7,] 0 0 0 0 0 0 0 0 0