image-rs / image

Encoding and decoding images in Rust
Apache License 2.0
4.88k stars 605 forks source link

Decoding TIFF images as 1 bit black/white images with Fax4 compression #1822

Open pratik-mahamuni opened 1 year ago

pratik-mahamuni commented 1 year ago

Thank you for creating this library and reading my question.

I am obtaining byte data of images from an external API using the reqwest library. Specifically, I am getting the byte data using the .bytes() method.

I then try to use those bytes to save the image or convert the image into a different format (TIFF to PNG) and I get the following error: Unsupported(UnsupportedError { format: Exact(Tiff), kind: Color(Unknown(1)) })'.

I am using the following code to parse the bytes and obtain the decoded image:

let mut reader = image::io::Reader::with_format(std::io::Cursor::new(response), image::ImageFormat::Tiff);
let img = reader.decode().expect("Could not decode image");

Portion of the byte data received for a particular image: image

How do I add the ColorFormat information to bytes data once I have received them? Or is there an alternative way I can still decode the image without the ColorFormat?

fintelia commented 1 year ago

The error is actually because this crate doesn't support 1 bit black/white images. Luckily, the underlying tiff and png crates we depend on do support that color type, so I'd recommend trying to use those directly.

The reason we chose not to handle 1-bit images here is because having many pixels packed into a single byte would interfere with a bunch of the other parts of the crate. (Exactly which color types to support is a constant tension of balancing complexity and functionality)

pratik-mahamuni commented 1 year ago

Thank you for the quick response @fintelia. I appreciate you pointing me in the right direction.

I couldn't find many examples in those crates. I was wondering if you had an example where I can take the byte data from the API response and convert that to a PNG without storing it to the file system using the crates above? So far I have gotten this far (here I am trying to save the TIFF image for testing rather than converting to PNG, but I cannot get past that step either):

let mut reader = tiff::decoder::Decoder::new(std::io::Cursor::new(&response)).expect("Could not read buffer");
println!("{:?}", reader.colortype());
println!("{:?}", reader.chunk_dimensions());

let image_data = reader.read_image().expect("Could not read image data"); <-- This throws an error

let mut file = std::fs::File::create("./example.tiff").expect("Could not create file");
file.write_all(&response[..])
    .expect("Could not write to file"); <-- This works as intended.

I feel I am using the reader wrong. The read_image method throws an Unsupported compression Fax4 error. I believe that is the key to getting the data converted to PNG. Please advice.

Eventually I will convert the PNG data into base64 for my own API response.

fintelia commented 1 year ago

Unfortunately, that is a more serious error. Your code looks correct, but it is signalling that the TIFF file you're trying to load uses a feature (Fax4 compression) that we don't support. TIFF has tons and tons of rarely used features -- to the point that it was once jokingly said that the acronym stood for "Thousands of Incompatible File Formats" -- so it is inevitable that we'll occasionally run into files that the library can't read.

We'd certainly welcome a PR adding support for Fax4 compression, but if you don't want to work on that I'd personally probably try using the ImageMagick command line utility.

pratik-mahamuni commented 1 year ago

@fintelia, thank you for your response. That is very interesting about TIFF, was unaware of this complexity.

I will work on understanding more about Fax4 and TIFF; my understanding of the former is negligible and of the latter fledgling. Please let me know if you have any resources on those. I will also contribute with a PR to this (collection) of library.

In the short term and to progress on the project, I have resorted to using Python's Pillow for image processing and using PyO3 to call it from Rust because it is a rather small portion of the project. That is, this bottleneck is not an issue (so far).

More to come..