image-rs / image-gif

GIF en- and decoder
Apache License 2.0
147 stars 42 forks source link

Creating GIF from JPEG frames? #44

Open captainviet opened 6 years ago

captainviet commented 6 years ago

Hi,

I'm new to Rust, yet I need to write a piece of Rust code to create a GIF file from a series of JPEG screenshots.

Currently my generated GIF is glitchy, the content seems enlarged and the colors don't look right. I'm wondering whether this is related to the compatibility of JPEG as frames for GIF, or because I'm using the wrong palette for the GIF Encoder? I'm learning so comments are welcome! Thanks in advance!

A brief summary for the code below:

use image as im; use std::fs::File; use std::thread; use std::time::Duration; use time::PreciseTime as tpt; use gif::{Frame, Encoder, Repeat, SetParameter}; use std::borrow::Cow; use screenshot::screenshot;

fn main() { let mut image = File::create("sample.gif").unwrap(); let img: im::RgbaImage = screenshot(); println!("Len: {}", img.len()); println!("Original: {}x{}", img.width(), img.height()); let width: u16 = img.width() as u16; let height: u16 = img.height() as u16; println!("Compressed: {}x{}", width, height); let color_map = &[0xFF, 0xFF, 0xFF, 0xFF, 0, 0]; let mut encoder = Encoder::new(&mut image, width, height, color_map).unwrap(); encoder.set(Repeat::Infinite).unwrap(); for i in 0..6 { let start = tpt::now(); let img: im::RgbaImage = screenshot(); let mid1 = tpt::now(); println!("Getting raw buffer..."); let pixes: &mut [u8] = &mut img.into_raw(); println!("Getting frame..."); //let mut frame = Frame::from_rgba(width, height, pixes); let mut frame = Frame::default(); frame.width = width; frame.height = height; frame.buffer = Cow::Borrowed(pixes); println!("Writing frame..."); encoder.write_frame(&frame).unwrap(); let end = tpt::now(); println!("Duration: {} {}", start.to(mid1).num_milliseconds(), mid1.to(end).num_milliseconds()); } }

nwin commented 6 years ago

The color map maps the raw pixel values (0–255) onto rgb triplets. Frame::from_rgba will solve this for you.

This will make the image considerably larger as it then uses a local color map for every frame one strategy would be to extract the color map from the first frame and use it as a global color map. And create the successive frames like you did.

captainviet commented 6 years ago

Hi @nwin ,

Actually my first attempt was using Frame::from_rgba. The reason I try to find an alternative is because the performance of this method is terrible: 2 minutes to convert each JPEG images to a frame.

Regarding the color map, I don't know what it is in the first place, so I've searched the documentation but I cannot find any method to get that property. I may have missed something, so for clarification is there anyway to extract the color map from an RgbaImage or similar entities?

HeroicKatora commented 3 years ago

Actually my first attempt was using Frame::from_rgba. The reason I try to find an alternative is because the performance of this method is terrible: 2 minutes to convert each JPEG images to a frame.

Encoding performance of lzw, the current implementation of LZW encoding, was terrible. It's going to be much faster with the next release where that implementation is switched.

kornelski commented 3 years ago

BTW, the performance bottleneck is not in LZW, but in color_quant. This algorithm (neuquant) performs quite a lot of work per pixel. You could use another quantization algorithm, e.g. imagequant that is faster (mediancut scales sub-linearly with number of unique colors).