Suppose I have an image represented as an Array2<u32>
, for example:
const SIZE: usize = 8;
let image = Array2::from_elem((SIZE, SIZE), 0_u32);
I want to obtain an Array2<bool>
that has true
in all locations where at least one pixel (in a 4-connected neighbourhood) is different from the current one. It's fine if this is 2 pixels smaller on each side.
Right now I'm doing it like this:
let edges = Zip::from(image.slice(s![1..(INNER + 1), 1..(INNER + 1)]))
.and(image.slice(s![0..(INNER + 0), 1..(INNER + 1)]))
.and(image.slice(s![2..(INNER + 2), 1..(INNER + 1)]))
.and(image.slice(s![1..(INNER + 1), 0..(INNER + 0)]))
.and(image.slice(s![1..(INNER + 1), 2..(INNER + 2)]))
.map_collect(|c, n, s, w, e| {
[n, s, w, e].into_iter().any(|v| v != c)
});
However, I have several other operations that act on neighbouring elements, so I would like to build an abstraction. I can probably write an extension trait for Zip
that lets me write this:
let edges = Zip::from(image.slice(s![1..(INNER + 1), 1..(INNER + 1)]))
.and_neighbors(&image) // From the extension trait
.map_collect(|c, n, s, w, e| {
[n, s, w, e].into_iter().any(|v| v != c)
});
Sadly, I still have to repeat the four arguments every time, and turn them back into an array inside the closure. Is there a better way?
It would be great to have an NdProducer
that has an extra axis of length 4, that contains the four neighbour views as slices. I tried to write that, but it turns out that the NdProducer
trait doesn't allow implementations outside of its crate.
I also tried ArrayView::from_shape_ptr
to create a view like that, but we have to specify a fixed stride for each index, while the index of the "neighbours" axis doesn't have a fixed stride.