我刚开始学习Rust,我真的很想通过构建“真实”来学习。所以我通读Book,安装了Rust,使用该语言并使cargo run
正常工作。
然后我决定尝试从磁盘读取图像并将该图像转换为Vec
。在这种情况下,我想检测像素的颜色并以某种方式存储。
我把它分成了几个部分来学习Rust及其语法:
从:
开始这使我使用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
。这是阅读和理解文档完全失败的地方。
我认为我需要做的是:
read_scanline
但是如何?!
代码读取:
read_scanline(&mut self, buf: &mut [u8]) -> ImageResult<u32>
我将其分解如下:
ImageDecoder
对象。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
。我也不明白我应该怎么做。
我希望有人可以解释我错过的是什么,并指出我正确的方向。
答案 0 :(得分:3)
注意我实际打开GIF时遇到问题,因为每行都说它是0字节,所以我错过了重要的事情......我将使用JPEG来演示。
ImageDecoder
是a trait。您需要使用特征的具体实现。文档列出了特征的所有已知实现者,其中一个是image::gif::Decoder
,另一个是image::jpeg::JPEGDecoder
。
read_scanline
接受可变self
,这意味着您通常会使用方法语法:object.method(arg1)
来调用它。第一个非self
参数是一个可变的字节切片。文档说明:
将图像中的一行读入
buf
并返回行索引
因此像素数据将存储在缓冲区中。下一个技巧是弄清楚一行需要多少字节以及有多少行。 ImageDecoder::row_len
和ImageDecoder::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个字节,但我没有看到任何明显的东西。