【问题标题】:Rust 2D sub-window iteratorRust 2D 子窗口迭代器
【发布时间】:2020-05-13 07:43:22
【问题描述】:

我正在使用Vec 来存储二维(行主要)值矩阵。我想用一个滑动的 2D 子窗口迭代这个矩阵来应用一个过滤器(不幸的是它是不可分离的)。

我在 slice 文档中看到存在 windows 函数,这是我想要的,但是是二维的。

我想过将其实现为:

fn main() {
    // 4 rows 3 columns
    let dim: (usize, usize) = (4, 3);
    // Place-holder matrix
    #[rustfmt::skip]
    let mat = vec![0,  1,  2,
                   3,  4,  5,
                   6,  7,  8,
                   9, 10, 11];

    // 2D index to 1D index
    let linearize = |r, c| r * dim.1 + c;
    // The dimensions of my sub-window
    let win_size: usize = 2;
    // Calculate the bounds for which the top left corner of each window may exist
    let bounds: (usize, usize) = (dim.0 - win_size + 1, dim.1 - win_size + 1);
    // Convert window 1D index into a 2D index
    let split = |i| (i / win_size, i % win_size);

    // Iterate over all the top left corners
    let window_2d = (0..bounds.0 * bounds.1).map(|i| {
        // Get the 2D index of the top left corner
        let (r, c) = (i / bounds.1, i % bounds.1);
        // Borrow the matrix, so our closure may own the reference
        let bmat = &mat;
        // Return an iterator for this window
        return (0..win_size * win_size).map(move |x| {
            let (wr, wc) = split(x);
            return bmat[linearize(wr + r, wc + c)];
        });
    });

    // Print the windows out
    window_2d.for_each(|it| {
        print!("[ ");
        it.for_each(|x| print!("{} ", x));
        println!("]");
    });
}

本质上是在一系列索引上创建一个迭代器,然后映射到矩阵的方括号运算符。 据我所知,这将对迭代器的每个 deref 进行边界检查。

我想知道是否有替代方案可以省略边界检查?也许使用chunkswindowszip 的组合,将矩阵分块成行,每行都有一个滑动窗口,然后压缩行的窗口并将结果展平?

谢谢!

编辑: 我不想简单地迭代 2D 数组,我想在数组上滑动 2D 窗口,类似于 std::slice::windows 函数的工作原理。

【问题讨论】:

标签: matrix rust iterator


【解决方案1】:

我现在最好的方法是将矩阵访问包装在一个不安全的块中以省略边界检查。

还有其他一些其他更改,现在这是完整的示例:

fn split_factory(cols: usize) -> impl Fn(usize) -> (usize, usize) {
    // Declaring that cols must be positive allows more aggressive optimisation of div and mod.
    if cols < 1 {
        unreachable!()
    }
    move |i| (i / cols, i % cols)
}

fn main() {
    // 4 rows 3 columns
    let dim: (usize, usize) = (4, 3);
    // Place-holder matrix
    #[rustfmt::skip]
    let mat = vec![0,  1,  2,
                   3,  4,  5,
                   6,  7,  8,
                   9, 10, 11];

    // The dimensions of my sub-window
    let win_dim = (3usize, 2usize);
    // Calculate the bounds for which the top left corner of each window may exist
    let bounds = (dim.0 - win_dim.0 + 1, dim.1 - win_dim.1 + 1);
    // Iterate over all the top left corners
    let convolution_iter = (0..bounds.0 * bounds.1)
        .map(split_factory(bounds.1))
        .map(|(r, c)| {
            // Borrow the matrix, so our closure may own the reference
            let bmat = &mat;
            // Return an iterator for this window
            return (0..win_dim.0 * win_dim.1)
                .map(split_factory(win_dim.1))
                .map(move |(wr, wc)| {
                    let px = (wr + r) * dim.1 + (wc + c);
                    (px, unsafe { *bmat.get_unchecked(px) })
                });
        });

    // Print the windows out (badly...)
    convolution_iter.for_each(|it| println!("{:?}", it.collect::<Vec<(usize, i32)>>()));
}

最好避免unsafe 和使用索引查找矩阵的间接性。

【讨论】:

    猜你喜欢
    • 2010-11-25
    • 1970-01-01
    • 1970-01-01
    • 2011-10-12
    • 2016-06-23
    • 2021-04-25
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    相关资源
    最近更新 更多