image-rs / jpeg-decoder

JPEG decoder written in Rust
Apache License 2.0
149 stars 87 forks source link

color looks wrong when CMYK jpg changed to RGB after opened and saved by image-rs #185

Open wellitecho opened 3 years ago

wellitecho commented 3 years ago

This happens when I simply used image-rs to open and save a CMYK jpg file

Expected

I expect the CMYK jpg file stays the same(file size, colorspace, etc.) I did not expect a CMYK jpg be changed to RGB jpg automatically. Because this CMYK jpg is used for printing.

Actual behaviour

the original CMYK jpg file is 12.5Mb, in CMYK; after opened and saved by image-rs, the size was changed to 2.2Mb, color spaced was changed to RGB(found out with Photoshop), and the image color looks different than the original file.

Reproduction steps

Download the original CMYK image file from the link below, and simply run:

use image;
/*
Cargo.toml:
[dependencies]
image = "0.23"
*/
fn main() {
    let img = image::open("cmyk_original.jpg").unwrap();
    img.save("cmyk_open_and_saved.jpg").unwrap();
}

Related Resources

original cmyk image file uploaded to mega.io open and saved by image-rs

jrmuizel commented 3 years ago

It seems like this is caused by not using the ICC profile embedded in the image to do the CMYK to RGB conversion.

wellitecho commented 3 years ago

With the risk of being rude(I don't mean to), I just wanna ask is there any update/progress on this issue? Thanks in advance.

jrmuizel commented 3 years ago

It's not exactly what you're looking for but qcms now supports doing CMYK to RGB conversions. The following produces an sRGB png file that should have more correct colors.


    use jpeg_decoder::Decoder;
    use std::fs::File;
    use std::io::BufReader;

    let file = File::open("cmyk_original.jpg").expect("failed to open file");
    let mut decoder = Decoder::new(BufReader::new(file));
    let pixels = decoder.decode().expect("failed to decode image");
    let metadata = decoder.info().unwrap();
    let profile = decoder.icc_profile().unwrap();
    let profile = qcms::Profile::new_from_slice(&profile).unwrap();
    let srgb = qcms::Profile::new_sRGB();
    let xfm = qcms::Transform::new_to(&profile, &srgb, qcms::DataType::CMYK, qcms::DataType::RGB8, qcms::Intent::Perceptual).unwrap();
    let mut result = vec![0; pixels.len()/4 * 3];
    xfm.convert(&pixels, &mut result);

    // write the result to a PNG
    let mut encoder = png::Encoder::new(std::fs::File::create("out.png").unwrap(), metadata.width as u32, metadata.height as u32);

    encoder.set_color(png::ColorType::Rgb);
    encoder.set_srgb(png::SrgbRenderingIntent::Perceptual);
    let mut writer = encoder.write_header().unwrap();
    writer.write_image_data(&result).unwrap(); // Save