image-rs / image

Encoding and decoding images in Rust
Apache License 2.0
4.76k stars 588 forks source link

Support EXIF #1045

Open schulzch opened 4 years ago

schulzch commented 4 years ago

I would like to be able to read and write EXIF meta data, especially for PNG and JPEG.

My specific use case for this functionality is a) storing/loading configuration within/from screenshots for reproducibility, b) maintaining metadata when processing images.

If there is interest I would be very willing to implement this - hints and feedback welcome :)

fintelia commented 4 years ago

:+1: I think the way to do this would be to implement this first for a single format, directly on the encoder/decoder and then if we can find a clean/general API extend it to more formats.

HeroicKatora commented 4 years ago

Seems like a problem similar to Limits for decoder but for the encoders.

kaj commented 4 years ago

There is a few packages providing exif info currently. I use kamadak-exif in a project and is quite happy with that. But yes, better integration with the image echosystem would probably be nice.

birktj commented 4 years ago

You may be interested in https://github.com/image-rs/image-png/issues/116

maghoff commented 4 years ago

This is also neccessary for correctly orienting JPEGs.

It could be argued that orientation correction should be automatically applied to the pixel buffer, making it even more important to integrate this functionality rather than pointing to other crates :slightly_smiling_face:

lovasoa commented 4 years ago

I recently got a feature request that would require image-rs to support writing EXIF data: https://github.com/lovasoa/dezoomify-rs/issues/12

So, 👍 on this !

zeh commented 3 years ago

One year later (!) but this would be great.

I'm saving images using image and would like to add some basic metadata to them, but there's no good way to do it!

Strangely, currently, there's no good create for writing EXIF data for Rust. There's a bunch of crates to read metadata; a few wrappers for some C libraries (ugh); and a bunch of really old crates of dubious effectiveness. BA pure Rust EXIF writer is nowhere to be found.

I could see this being a separate crate just for EXIF writing (depending on format) and then exposed somehow so Image can make use of it when writing images to disk.

paolobarbolini commented 3 years ago

currently there's no good create for writing EXIF data for Rust

You should be able to do so with the help of kamadak-exif to encode the EXIF data and img-parts (I'm the author of it) to write it to an existing image.

See also https://github.com/kamadak/exif-rs/issues/1

zeh commented 3 years ago

currently there's no good create for writing EXIF data for Rust

You should be able to do so with the help of kamadak-exif to encode the EXIF data and img-parts (I'm the author of it) to write it to an existing image.

Thanks for the tip. I hadn't ran into img-parts before when trying to find libraries for my use case. I managed to add metadata writing to a project of mine using img-parts and it seems to be working well. A bit suboptimal since it writes the file, reads it, and writes it again with metadata, but it's "good enough" for now.


After implementing this feature and doing some research on the problem space I think it might actually not be a good idea for image to try and provide a way to write EXIF or other metadata. The feature is more complicated than I thought; it would make total sense to have this as a dedicated, separate library.

In fact, I wonder if it makes sense for image to implement save() at all, given the edge cases required for it to properly work (say, JPEG rotation as mentioned). It's convenient, for sure, but not always enough; it can probably work for 98% of the use cases out there, but the rest 2% end up requiring a radically different solution. For my specific use case, since I require metadata too, to avoid having to write the file twice it would be better to have a to_file_buffer() that saves the encoded image to a buffer (basically exposing save_buffer_impl()) so it can be used elsewhere - adding metadata, etc - before writing the file.

(Note: I'm not an advanced Rust programmer, so I might be missing a better solution for my use case.)

paolobarbolini commented 3 years ago

For my specific use case, since I require metadata too, to avoid having to write the file twice it would be better to have a to_file_buffer() that saves the encoded image to a buffer

The image crate already supports it. Instead of using save(), which takes the path to where you want the file to be saved to, you can use write_to(), which asks for a Writer instead. With it you can make it write into a buffer, just as I show in this example.

zeh commented 3 years ago

The image crate already supports it. Instead of using save(), which takes the path to where you want the file to be saved to, you can use write_to(), which asks for a Writer instead. With it you can make it write into a buffer, just as I show in this example.

Ah, I see. I was using ImageBuffer, which didn't expose write_to(), unlike DynamicImage. Still a but confused about those abstractions. I'll use that instead.

stephanbogner commented 1 year ago

I'd also appreciate this (mainly to preserve image orientation/rotation).

My workaround: I found an example on how to integrate img-parts with image ... but I couldn't get it to work.

What I now did instead: I use kamadak-exif (which I already use anyway to get the exif date information) and rotate/flip the image myself using image::imageops (with information from here on what the exif orientation values mean). Not an ideal solution, but it works and I only load and save the image once.

marcalj commented 4 weeks ago

I'd also appreciate this (mainly to preserve image orientation/rotation).

My workaround: I found an example on how to integrate img-parts with image ... but I couldn't get it to work.

What I now did instead: I use kamadak-exif (which I already use anyway to get the exif date information) and rotate/flip the image myself using image::imageops (with information from here on what the exif orientation values mean). Not an ideal solution, but it works and I only load and save the image once.

I'm using your approach and I'm wondering how to read the EXIF data without having to clone the image buffer. Since loading the buffer as an image moves the value. Any thoughts? Thanks.

marcalj commented 4 weeks ago

Ok, since my input is a buffer, I can read EXIF data without modifying the buffer like this:

// input: image_buffer: &[u8]
let exif_reader = exif::Reader::new();
let exif = exif_reader.read_raw(image_buffer.to_vec());

Update: Oh, just figured that this is actually cloning the buffer... :-/

marvinruder commented 4 weeks ago

I'd also appreciate this (mainly to preserve image orientation/rotation). My workaround: I found an example on how to integrate img-parts with image ... but I couldn't get it to work. What I now did instead: I use kamadak-exif (which I already use anyway to get the exif date information) and rotate/flip the image myself using image::imageops (with information from here on what the exif orientation values mean). Not an ideal solution, but it works and I only load and save the image once.

I'm using your approach and I'm wondering how to read the EXIF data without having to clone the image buffer. Since loading the buffer as an image moves the value. Any thoughts? Thanks.

I am doing something similar with

// input: image_buffer: &[u8]
exif::Reader::new().read_from_container(&mut std::io::Cursor::new(image_buffer))
marcalj commented 3 weeks ago
// input: image_buffer: &[u8]
exif::Reader::new().read_from_container(&mut std::io::Cursor::new(image_buffer))

AFAIK is also creating a copy of the buffer.