template <typename Img>
concept bool Image_with_Neighborhood_and_Extension =
Image<Img> &&
Neighborhood<Img> &&
Extension<Img>;
template <
Image_with_Neighborhood_and_Extension Img,
ConvexDomain<Img>,
Applyable<Img::neiborhood_type, NeighborhoodMask>
>
Image chamfer_distance_transform(const Img& ima, const NeighborhoodMask& nbh_mask) {
auto ima_res = copy(ima);
extension::fill(ima, numeric_limits<Img::value_type>::max());
// forward pass
for(auto [&pix_res, pix] : zip(ima_res.pixels(), ima.pixels())) {
if(pix->val()) {
pix_res.val() = accumulate(
nbh_mask(pix_res.neighbors()),
numeric_limits<Img::value_type>::max(),
[&pix_res](auto vmin, auto&& neighbor) {
if (neighbor->index() < pix_res->index()) {
return min(vmin, neighbor->val() + neighbor->weight())
}
}
);
} else {
pix_res->val() = 0;
}
}
// backward pass
for(auto [&pix_res, pix] : zip_backward(ima_res.pixels(), ima.pixels())) {
pix_res.val() = accumulate(
nbh_mask(pix_res.neighbors()),
numeric_limits<Img::value_type>::max(),
[&pix_res](auto vmin, auto&& neighbor) {
if (neighbor->index() > pix_res->index()) {
return min(vmin, neighbor->val() + neighbor->weight())
}
}
);
}
return ima_res;
}