如何对 Rust 活塞图像进行编码并在内存中获取结果?

How do I encode a Rust Piston image and get the result in memory?

我正在使用 the Piston image package 生成图像。

fn get_image() -> image::DynamicImage {
    image::DynamicImage::ImageRgba8(image::ImageBuffer::new(512, 512))
}

我有一个 hyper web server,我想从中提供动态生成的图像。

根据对 的回答,我认为我可以使用 Cursor<Vec<u8>> 作为目的地。但是,image 似乎只提供了一种写入文件名的方法,而不是像我的光标那样的 Writer

看了image的文档后,希望有什么方法可以直接使用the image::png::PNGEncoder struct。它提供了一个public方法encode(self, data: &[u8], width: u32, height: u32, color: ColorType) -> Result<()>。但是,我不确定 data 参数应该是什么,而且我找不到 ImageBuffer 使用的 ColorType 的任何 public 声明。

(&Get, "/image.png") => {
    let v = {
        let generated = get_image();
        let mut encoded_image = Cursor::new(Vec::new());
        let (width, heigth) = generated.dimensions();
        {
            let encoder = image::png::PNGEncoder::new(&encoded_image);
            encoder.encode(unimplemented!("what goes here?"));
        }
        encoded_image.get_mut()
    };

    Box::new(futures::future::ok(
        Response::new()
            .with_header(ContentLength(v.len() as u64))
            .with_body(v.clone()),
    ))
}

如何将 a Piston GenericImage 编码为 PNG 等格式并在内存中获取结果?

fn getImage() -> image::DynamicImage 

你有一个DynamicImage

image only seems to provide a method to write to a filename

我假设你的意思是 DynamicImage::save

save之前的方法是write_to:

pub fn write_to<W: Write, F: Into<ImageOutputFormat>>(
    &self, 
    w: &mut W, 
    format: F
) -> ImageResult<()>

这接受任何通用编写器:

let mut buf = Vec::new();

get_image()
    .write_to(&mut buf, image::ImageOutputFormat::PNG)
    .expect("Unable to write");

不需要Cursor


How do I encode a Piston GenericImage to a format like PNG?

我不知道最好的方法。我可能会将通用图像复制到 DynamicImage 中,然后按照上述说明进行操作。

我已经根据 image crate 文档改编了 OP 的代码。看起来可以使原始代码工作。不需要 Cursor,但需要为 Vec 实现的 Write 实例创建一个 "by reference" 适配器。

// This code have not been tested or even compiled
let v = {
    let generated = get_image();
    let mut encoded_image = Vec::new();
    let (width, height) = generated.dimensions();
    {
        image::png::PNGEncoder::new(encoded_image.by_ref())
            .encode(
                generated.raw_pixels(), 
                width, 
                height,
                generated.color()
            ).expected("error encoding pixels as PNG");
    }
    encoded_image
};

下面是我在项目中实际使用的代码。我获得了对作为 &[u8] 切片传入的原始像素的引用。每个像素代表一个 8 位灰度值。这是一个将像素编码到内存中 PNG 图像的函数。

pub fn create_png(pixels: &[u8], dimensions: (usize, usize)) -> Result<Vec<u8>> {
    let mut png_buffer = Vec::new();
    PNGEncoder::new(png_buffer.by_ref())
        .encode(
            pixels,
            dimensions.0 as u32,
            dimensions.1 as u32,
            ColorType::Gray(8),
        ).expect("error encoding pixels as PNG");

    Ok(png_buffer)
}

而不是 return从 get_image 生成 DynamicImage,return 特定的图像类型会更容易,例如彩色 RGBA 图像:

use image::RgbaImage;

fn get_image() -> RgbaImage {
    RgbaImage::new(512, 512)
}

RgbaImage实现了Deref<[u8]>,也就是说图像可以直接解引用为一个字节片,所以&img可以作为[=17]的第一个参数=].高度和宽度很容易,只需访问 img.height()img.width()encode 的最后一个参数是颜色类型,它更巧妙地存储为 ImageBufferPixel 类型参数的关联常量。从通用类型中,它可以作为 P::COLOR_TYPE 访问,因此我没有将 encode_png 特定于一种图像类型,而是将其编写为接受任何 ImageBuffer 类型。

完整的例子是:

use std::error::Error;
use std::ops::Deref;
use image::{png::PNGEncoder, ImageBuffer, ImageError, Pixel, RgbaImage, Rgba};

fn encode_png<P, Container>(img: &ImageBuffer<P, Container>) -> Result<Vec<u8>, ImageError>
where
    P: Pixel<Subpixel = u8> + 'static,
    Container: Deref<Target = [P::Subpixel]>,
{
    let mut buf = Vec::new();
    let encoder = PNGEncoder::new(&mut buf);
    encoder.encode(img, img.width(), img.height(), P::COLOR_TYPE)?;
    Ok(buf)
}

fn get_image() -> RgbaImage {
    let mut img = RgbaImage::new(32, 32);

    // Draw something to show in the final image
    for i in 0..32 {
        img.put_pixel(i, i, Rgba([255, 0, 0, 255]));
    }
    img
}

fn main() -> Result<(), Box<dyn Error>> {
    let img = get_image();
    let buf = encode_png(&img)?;

    // write out the image using the base64 crate as a data
    // URL so that it can be copy-pasted into a web browser
    println!("data:image/png;base64,{}", base64::encode(&buf));
    Ok(())
}

游乐场:https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e2611c5ef248667bf984a8dd3cedfec8