Changelog
Source:NEWS.md
thinr 0.3.0.9000 (development version)
Development version bump following the 0.3.0 CRAN release.
Internal, with no change to any output: the unused
thinr::is_border_4()helper was removed fromsrc/thinr_common.h. Its last callers were the stentiford and pavlidis kernels, dropped 2026-05-20; nothing has referenced it since. The header now declares only the two helpers that are actually used.Internal, with no change to any output: the Zhang-Suen crossing number
A(p)and neighbour countB(p)were re-implemented inline insrc/zhang_suen.cpp,src/lee.cpp, andsrc/holt.cppinstead of calling the sharedthinr::crossing_number()/thinr::neighbour_count()insrc/thinr_common.h, which onlysrc/hilditch.cppused. A future correctness fix to the shared definition would have reached one of the four kernels that need it and silently missed the other three. All four now call the shared helpers. The replacement expressions are token-identical to the inline copies they replace; identical output was verified empirically across 257 binary images (degenerate, boundary, synthetic, and real chart masks) for all seven methods (#F018).
thinr 0.3.0
CRAN release: 2026-07-26
Correctness fixes from the figureextract ecosystem review (2026-07-03). All are boundary conditions the published algorithms assume away; see the new vignette("correctness-properties") for the guarantees they restore.
thin()now skeletonises shapes that touch the matrix border correctly. The kernels inspect an 8-neighbourhood and so never deleted pixels in the outermost row or column, leaving edge-touching shapes two to three pixels thick;thin()now pads the image with a one-pixel background margin and crops it back, so a shape thins identically whether or not it touches the frame. Applies to all seven methods (#F012).thin(method = "opta")now keeps two-pixel-wide strokes connected. The SPTA kernel evaluated all four contour directions against the pre-cycle snapshot and deleted them in one batch, so both sides of a 2px stroke were removed together and the skeleton fragmented (a 2×9 bar collapsed to its four corner pixels). Deletion is now sequential per direction, as Naccache & Shinghal’s two-scan formulation requires (#F011).distance_transform(metric = "manhattan" | "chessboard")now returnsInffor a fully-foreground image (no background pixel exists), matching the"euclidean"metric andEBImage::distmap(). The two-pass metrics previously seeded foreground with a finite sentinel that leaked out as a plausible-looking but meaningless finite distance (#F015).thin(),distance_transform(), andmedial_axis()now rejectNAinput with a clear error at the coercion boundary instead of silently turning it intoNA_integer_(INT_MIN) in the C++ kernels (#F016).thin(method = "hilditch")now thins junctions to the published parallel form. The look-ahead conditions 3 and 4 skip deleting a pixel only when a cardinal neighbour has crossing numberA == 1on the current image; the kernel had compared the look-ahead crossing number (computed with the centre already removed) against 1, which is strictly stronger and also spared junction neighbours whereA >= 2, leaving a redundant pixel beside the junction. Skeletons are now equal to or thinner than before, never thicker (verified against a reference implementation of the published form over random images) (#F014).Added a connectivity-preservation property test across all seven methods and tightened the Holt straight-line test (#F013).
Documented and pinned the isolated-2×2-block behaviour: the default
zhang_suenerases an isolated 2×2 block entirely whileguo_hallkeeps one pixel. Seevignette("choosing-a-method")for guidance on small-blob masks (#F017).Removed
thinImage(). Usethin()(Zhang-Suen is the default method).