如何使用活塞图像箱中的read_scanline?

时间:2016-02-10 13:06:49

标签: rust

我刚开始学习Rust,我真的很想通过构建“真实”来学习。所以我通读Book,安装了Rust,使用该语言并使cargo run正常工作。

然后我决定尝试从磁盘读取图像并将该图像转换为Vec。在这种情况下,我想检测像素的颜色并以某种方式存储。

我把它分成了几个部分来学习Rust及其语法:

从:

开始
  1. 从磁盘读取图像
  2. 获取图片的像素
  3. 显示颜色代码
  4. 这使我使用the image crate

    获得了以下代码
    extern crate image;
    
    use std::path::Path;
    use image::GenericImage;
    
    fn main() {
    
        let img = image::open(&Path::new("src/maze.gif")).unwrap();
    
        let pixels = img.pixels();
    
        for e in pixels {
            let (_, _, color) = e;
    
            println!("Pixel colour {:?}", color);
        }
    
        println!("Dimensions {:?}", img.dimensions());
    }
    

    我很自豪,我看到一些信息突然出现:

    * snip *
    Pixel colour Rgba { data: [255, 255, 255, 255] }
    Pixel colour Rgba { data: [0, 0, 0, 255] }
    Pixel colour Rgba { data: [255, 255, 255, 255] }
    * snip *
    

    现在我想为图像的每一行存储其像素信息。我希望有一个Vec(这是正确的吗?)与信息。 PHP数组看起来像这样:

    $a = [
       0 => [ Color, Color, Color, Color],
       1 => [ Color, Color, Color, Color]
    ];
    

    因此我的假设是使用read_scanline。这是阅读和理解文档完全失败的地方。

    我认为我需要做的是:

    1. 获取当前打开图像的解码器
    2. 在该解码器上调用read_scanline
    3. 对结果进行迭代
    4. 但是如何?!

      代码读取:

      read_scanline(&mut self, buf: &mut [u8]) -> ImageResult<u32>
      

      我将其分解如下:

      • 第一个参数,一个ImageDecoder对象。
      • 第二个参数是一个可变的8位值。
      • 返回ImageResult个对象。

      所以我尝试稍微改编一下代码:

      extern crate image;
      
      use std::path::Path;
      use image::GenericImage;
      use image::ImageDecoder;
      
      fn main() {
      
          let img = image::open(&Path::new("src/maze.gif")).unwrap();
      
          let pixels = img.pixels();
          let something: &mut [u8];
          let result = image::ImageDecoder::read_scanline(img, something);
      
          for e in pixels {
              let (_, _, color) = e;
      
              println!("Pixel colour {:?}", color);
          }
      
          println!("Dimensions {:?}", img.dimensions());
      }
      

      正如你可能已经猜到的那样悲惨地失败了。

      error: mismatched types:
       expected `&mut _`,
          found `image::dynimage::DynamicImage`
      (expected &-ptr,
          found enum `image::dynimage::DynamicImage`) [E0308]
      src/main.rs:13     let result = image::ImageDecoder::read_scanline(img, something);
      

      显然这是因为我没有传递ImageDecoder个对象。但我怎么能这样?我应该如何阅读和理解文档。我认为这是由于不了解&mut self。我也不明白我应该怎么做。

      我希望有人可以解释我错过的是什么,并指出我正确的方向。

1 个答案:

答案 0 :(得分:3)

注意我实际打开GIF时遇到问题,因为每行都说它是0字节,所以我错过了重要的事情......我将使用JPEG来演示。

ImageDecodera trait。您需要使用特征的具体实现。文档列出了特征的所有已知实现者,其中一个是image::gif::Decoder,另一个是image::jpeg::JPEGDecoder

read_scanline接受可变self,这意味着您通常会使用方法语法object.method(arg1)来调用它。第一个非self参数是一个可变的字节切片。文档说明:

  

将图像中的一行读入buf并返回行索引

因此像素数据将存储在缓冲区中。下一个技巧是弄清楚一行需要多少字节以及有多少行。 ImageDecoder::row_lenImageDecoder::dimensions分别解决了这个问题。放在一起,我们得到这样的东西:

extern crate image;

use std::fs::File;

use image::jpeg;
use image::{GenericImage, ImageDecoder};
use image::{ColorType, Rgb, Pixel};

fn main() {
    let f = File::open("/tmp/cat.jpg").unwrap();
    let mut decoder = jpeg::JPEGDecoder::new(f);

    let (width, height) = decoder.dimensions().unwrap();
    let row_len = decoder.row_len().unwrap();

    println!("{} x {}, {}", width, height, row_len);

    let rows: Vec<_> = (0..height).map(|_| {
        let mut row = vec![0; row_len];
        decoder.read_scanline(&mut row).unwrap();
        row
    }).collect();
}

这将JPEG逐行加载到Vec<Vec<u8>> - 一个字节向量的向量中。

要将原始数据转换为像素,我们可以使用另一种特征方法Pixel::from_slice。在这种情况下,我只处理一种类型的像素--8位RGB:

let colortype = decoder.colortype().unwrap();
assert_eq!(colortype, ColorType::RGB(8)); // Others are left to you!

let row_colors: Vec<Vec<_>> = rows.iter().map(|r| {
    r.chunks(3).map(|p| Rgb::from_slice(p)).collect()
}).collect();

由于硬编码3,我不喜欢这个。应该有一些方法可以知道这个像素类型只需要3个字节,但我没有看到任何明显的东西。