首页 文章

如何使用image :: ImageBuffer作为成员创建Rust结构?

提问于
浏览
2

我正在使用image crate来进行图片处理,并希望创建一个小包装器以使我的代码更有趣 .

extern crate image;

const BLACK: [u8; 4] = [0, 0, 0, 255];
const WHITE: [u8; 4] = [255, 255, 255, 255];
const RED: [u8; 4] = [255, 0, 0, 255];

pub struct Picture {
    buffer: image::ImageBuffer,
    width: u32,
    height: u32
}

impl Picture {
    // My functions like fill(), line() etc.
}

但是当我编译它时,我有一个错误:

src\pic.rs:11:13: 11:31 error: wrong number of type arguments: expected 2, found 0 [E0243]
src\pic.rs:11     buffer: image::ImageBuffer,
                          ^~~~~~~~~~~~~~~~~~

在源代码中,我看到 ImageBuffer 接受两个参数:

pub struct ImageBuffer<P: Pixel, Container> {
    width: u32,
    height: u32,
    _phantom: PhantomData<P>,
    data: Container,
}

所以我决定将这些参数放在 Picture 声明中:

pub struct Picture {
    buffer: image::ImageBuffer<image::Pixel>,
}

并得到另一个错误:

src\pic.rs:11:32: 11:44 error: the value of the associated type `Subpixel` (from the trait `pic::image::buffer::Pixel`) must be specified [E0191]
src\pic.rs:11     buffer: image::ImageBuffer<image::Pixel>,
                                             ^~~~~~~~~~~~

这意味着我必须为 Subpixel 类型指定一些值,并且我不知道如何声明 Container 类型,我在源代码中找不到任何有用的东西 . 我试图重新阅读Rust Book,例子, rustc --explain E0191 但我仍然迷失了 .

更新:

在消息来源发现下一个声明

impl<P, Container> ImageBuffer<P, Container>
where P: Pixel + 'static,
      P::Subpixel: 'static,
      Container: Deref<Target=[P::Subpixel]>

Subpixel 是:

/// Primitive trait from old stdlib
pub trait Primitive: Copy + NumCast + Num + PartialOrd<Self> + Clone + Bounded {
}

但它不公开我的箱子 .

1 回答

  • 6

    查看documentation for ImageBuffer,我们可以看到类型参数的特征要求:

    impl<P, Container> ImageBuffer<P, Container>
        where P: Pixel + 'static,
              P::Subpixel: 'static,
              Container: Deref<Target=[P::Subpixel]>
    

    因此,第二类参数可以取消引用一个子像素是很重要的 . SubpixelPixel trait确定,它有一些实现:

    • Rgb<T>

    • Luma<T>

    • Rgba<T>

    • LumaA<T>

    其中 T 是子像素类型 .

    这一切都非常复杂,但应该提供一点点的力量和普遍性 .

    请注意,我只展示了其中一个实现块 . 还有其他需要 DerefMut 或要求子像素为 u8 .


    你是如何实际使用它的?首先,您需要选择特定的颜色和子像素类型 . 根据您的颜色,您似乎想要使用4通道,8位颜色类型 . 那将是Rgba<u8> .

    有许多类型可以取消引用 &[u8]&mut [u8] . 明显的(但不仅仅是!)选择是 Vec<u8> .

    结合这两者,您的结构需要类似于:

    buffer: image::ImageBuffer<Rgba<u8>, Vec<u8>>,
    

    然后,这只是创建结构和做事的问题:

    extern crate image;
    
    use image::{ImageBuffer, Pixel, Rgba};
    
    pub struct Picture {
        buffer: ImageBuffer<Rgba<u8>, Vec<u8>>,
    }
    
    impl Picture {
        fn new(width: u32, height: u32) -> Self {
            // There's probably a cleaner way to figure out the right size
            let storage = vec![0; 4 * width as usize * height as usize];
            let buf = ImageBuffer::from_raw(width, height, storage).unwrap();
    
            Picture {
                buffer: buf,
            }
        }
    
        fn fill_red(&mut self) {
            let color = Rgba::from_channels(255, 0, 0, 255);
    
            for p in self.buffer.pixels_mut() {
                *p = color;
            }
        }
    }
    
    fn main() {
        let mut pic = Picture::new(256, 256);
        pic.fill_red();
    }
    

    请注意 ImageBuffer 已经跟踪了宽度和高度,因此没有理由再次跟踪它们 .


    然而,所有这些都是艰难的方式 . 有一些便利功能,如 new ,它将为您分配存储:

    buffer: ImageBuffer::new(width, height),
    

    这些函数是硬编码的,以返回 Vec 作为 Container . 如果您在文档中没有看到,可以尝试类似printing the type的内容:

    let () = ImageBuffer::new(width, height);
    

    哪个会告诉你错误信息中的类型:

    expected `image::buffer::ImageBuffer<_, collections::vec::Vec<_>>`,
    

相关问题