Champfer distance transform

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;
}