Home > Blockchain >  How do I read an image from RAM into a consistent format?
How do I read an image from RAM into a consistent format?

Time:09-03

In C I am used to using stb_image to load image data to and from RAM.

I am writing a rust program where I have loaded some PNGs and JPEGs as raw binary data.

I am trying to use the image crate to read and decompress the ray byte data into the data and metadata (i.e. image dimensions and raw pixel byte data). I need to then re compress the data s a png and print it to disk, to make sure the data is ok (I will use the raw buffers later on).

To that effect I have this

let image_data = image::load_from_memory(bytes).unwrap();

where bytes is the raw image data.

Problem n1, this seems to create a 3 channel image for jpegs, I need a 4 channel image for both pngs and jpegs. So for jpegs I need the image crate to add padding. But if I try to cast it using as_rgba8 I can no longer get the width and the height of the image.

Then I am trying to read the data into a custom struct, like this:

let mut raw_data = Vec::<u8>::new();

let width = image_data.width() as usize;
let height = image_data.height() as usize;

raw_data.extend_from_slice(image_data.as_bytes());
println!("{}", image_data.width());
println!("{}", image_data.height());
println!("{}", image_data.height() * image_data.width() * 3);
println!("{}", raw_data.len());

let texture = 
    Texture
    {
        width,
        height,
        channel_num : 4,
        format : ImageFormat::RGBA8,
        data : raw_data,
    };

This part seems to work, next I am trying to re-compress the data dn print to disk:

let tmp = RgbaImage::from_raw(
    texture.width as u32,
    texture.height as u32,
    texture.data.as_bytes().to_vec()).unwrap();

tmp.save("tmp.png");

In this case I am getting a None error on attempting the unwrap. I don't understand why since the byte buffer does have enough data to contain the full image, it was literally created by that image.

I am somewhat lost.

CodePudding user response:

[...] this seems to create a 3 channel image for jpegs, I need a 4 channel image for both pngs and jpegs. [...] But if I try to cast it using as_rgba8 I can no longer get the width and the height of the image.

You want to convert the underlying image buffer, rather than cast the image. This is done with the to_* family of methods in DynamicImage. This works regardless of whether the dynamic image was obtained from a file or from memory (both open and load_from_memory return a DynamicImage).

use image::{RgbaImage, open}; // 0.24.3

let img = open("example.jpg")?;

println!(
    "Before: {}x{} {:?} ({} channels)",
    img.width(),
    img.height(),
    img.color(),
    img.color().channel_count()
);

let img: RgbaImage = img.to_rgba8();

println!(
    "After: {}x{} ({} channels)",
    img.width(),
    img.height(),
    img.sample_layout().channels
);

Note how the second img is already known at compile time to be an RGBA image. As an image buffer, you can freely retrieve any property or pixel data that you wish.

Possible output:

Before: 2864x2480 Rgb8 (3 channels)
After: 2864x2480 (4 channels)
  • Related