TechnikTobi / little_exif

A little library for reading and writing EXIF data in pure Rust.
Apache License 2.0
22 stars 5 forks source link

Provide exif data for img_parts #2

Closed floric closed 1 month ago

floric commented 10 months ago

Hey :)

would it be possible to get the exif data as raw bytes instead of writing to an image? The background is, that I'm writing an image directly to an output stream without any filesystem. So I like to build the exif data, add this to the image with img_parts and render the result directly in a HTTP response.

And: would this exif payload be generic or is it already prepared for the output format (eg. jpeg or png)?

What do you think?

Kind regards, Florian

TechnikTobi commented 10 months ago

Hi,

getting the EXIF data itself as "raw bytes" (I assume a Vec should be fine?) can be added relatively easily. However, writing this to an output stream can be a bit tricky:

To conclude: Yes, getting the EXIF data itself (and performing at least some of the file-specific encoding) should be possible via little_exif after a small update. However, the other file-type specific stuff you'll have to figure out yourself.

Best, Tobi

floric commented 10 months ago

Hi,

getting the EXIF data itself as "raw bytes" (I assume a Vec should be fine?) can be added relatively easily. However, writing this to an output stream can be a bit tricky:

* As you mentioned, what you have to write to the stream depends on the filetype, e.g. PNG has a special header, needs additional PNG-specific encoding, etc. (sidenote: PNG is, of the currently supported file types, the _worst_ to work with from a programmer's perspective)

* You also have to figure out _where_ (or in case of a stream, _when_) to write the EXIF data

* With some file types (e.g. WebP) you need to have the encoded EXIF data _before_ you start your output stream as you need to know the size of the resulting file to write it at the start.

To conclude: Yes, getting the EXIF data itself (and performing at least some of the file-specific encoding) should be possible via little_exif after a small update. However, the other file-type specific stuff you'll have to figure out yourself.

Best, Tobi

Hey Tobi,

thanks a lot for this comprehensive and fast response. I hope, that using img_pars::ImageEXIF will be enough. At least there I can provide bytes (Vec<u8>) for EXIF data as well as ICC profiles. The exporting to the different formats is done with the resulting object. I could give it a try, if providing the EXIF data as bytes is really easy for you, and see if img_parts really handles all of the hard work mentioned by you for us.

TechnikTobi commented 10 months ago

Hi,

I've added a new function for the Metadata called as_u8_vec where you provide information about which image format you want to use (e.g. FileExtension::WEBP) and get the EXIF data as a Vec<u8>. However, as always, the PNG format is a pita to work with, so don't expect this to work on the first try. I'd be glad to help you with getting this to run with img_parts and PNGs if required, but there is the chance that this library will need an update as well due to how EXIF data is handled over there.

So, try it out with little_exif ver 0.3.0 and let me know what you think and if it works or where work is still required. And, in the likely case that you do run into problems, it would be great to get some sort of file stream as hex code or (best case) an image file you tried to create but are unable to use due to problems with the EXIF data so I can compare and debug.

Best, Tobi

floric commented 10 months ago

Hi,

I've added a new function for the Metadata called as_u8_vec where you provide information about which image format you want to use (e.g. FileExtension::WEBP) and get the EXIF data as a Vec<u8>. However, as always, the PNG format is a pita to work with, so don't expect this to work on the first try. I'd be glad to help you with getting this to run with img_parts and PNGs if required, but there is the chance that this library will need an update as well due to how EXIF data is handled over there.

So, try it out with little_exif ver 0.3.0 and let me know what you think and if it works or where work is still required. And, in the likely case that you do run into problems, it would be great to get some sort of file stream as hex code or (best case) an image file you tried to create but are unable to use due to problems with the EXIF data so I can compare and debug.

Best, Tobi

Hey :) thanks a lot. I will definitely give it a try. In the mean time I was also successful to use kamadak/exif-rs for the same purpose. But I only tried JPEG so far. But your lib is a little easier to write and read.

h3x4d3c1m4l commented 8 months ago

This works for me:

https://github.com/h3x4d3c1m4l/momento-booth/pull/353/files#diff-01e8fa73909b86c309b9e31ac7ca842cadabf5e374ff06f1a4fe0b43822b6de8R16-R27

Note that you need to strip the first 10 bytes of the little_exif output (see line 25). Took me a while to find out 😄 !

mschnell1 commented 1 month ago

My task is copy (lots of) images and modify the EXIF of the copies. While this is easy using "little" it would be m0re effective to modify EXIF while copying. Would this issue - if resolved - allow for this ?

TechnikTobi commented 1 month ago

My task is copy (lots of) images and modify the EXIF of the copies. While this is easy using "little" it would be m0re effective to modify EXIF while copying. Would this issue - if resolved - allow for this ?

I guess what could be done is to

Would that workflow suit you?

mschnell1 commented 1 month ago

Exactly. It was not obvious to me that it can be done that way, i.e. having little_exif work on a memory buffer I'll do some more research....

TechnikTobi commented 1 month ago

Exactly. It was not obvious to me that it can be done that way, i.e. having little_exif work on a memory buffer I'll do some more research....

Sorry, that's not yet possible. I just wanted to make sure that I understand correctly what you mean before I start working on that feature.

Edit: Just for clarification: During your copy operation you are handling the file as a Vec<u8> that is read/written using std::fs functions, right? In that case I'll add a function to little_exif to additionally handle data that is not contained in a file but given as a &mut Vec<u8>.

mschnell1 commented 1 month ago

I did not yet implement the copying, as I intended just to use a rust std functionality for copying a file. But of course when doing the file copy manually the content will be a &mut Vec<u8>.

TechnikTobi commented 1 month ago

I added the required functions to perform this actions for JPGs stored as Vec<u8>, see 0.5.0-beta.1

mschnell1 commented 1 month ago

GREAT ! Many thanks. I'll check this out ASAP .

mschnell1 commented 1 month ago

I tested new_from_vec from the current GitHub version something seems to work.... I get a panic!("from_u8_vec: Mangled EXIF data encountered!") in the build_u8conversion macro. It seems to have been called for byte 19912 in the jpg file. the vector content at that point is "Nicon\0...." which seems OK. The caller seems to have been from_u8_vec_macro. And that came from

                    let subifd_decode_result = Self::decode_ifd(
                        &encoded_data[relative_offset..].to_vec(),
                        &subifd_group,
                        offset,
                        endian
                    );

with relative_offset = 340.

then

        let ifd0_decode_result = Self::decode_ifd(
            &encoded_data[14..].to_vec(),
            &ExifTagGroup::IFD0,
            8,                                                                  // TODO: What if IFD0 is at another offset? Can this even happen?
            &endian
        );

with endian = Little and encoded_data = "Exif\0..."

any idea ? Thanks again....

mschnell1 commented 1 month ago

Update: I found that the new_from_vec function is not to blame, but the same file creates the same panic when opend by new_from_path

This is the file: www.bitvibe.de/Upload/0010_A0420427.JPG

I tested that it also happens with the released version from crates.io.

I found that I do have many more files that trigger this.

Thanks for taking a look ....

TechnikTobi commented 1 month ago

This has to do with the MakerNotes IFD and, as I recently found out, is also the cause for issue #6. For now, with the fix in 0.5.0-beta2, this data will be handled as a single tag and not further processed. Please let me know if this solves the issue for you as well on other photos.

mschnell1 commented 1 month ago

I added the required functions to perform this actions for JPGs stored as Vec, see 0.5.0-beta.1

That was easy enough to find out after I modified the sting in the references to be the github linlk :+1:

mschnell1 commented 1 month ago

This has to do with the MakerNotes IFD and, as I recently found out, is also the cause for issue #6. For now, with the fix in 0.5.0-beta2, this data will be handled as a single tag and not further processed. Please let me know if this solves the issue for you as well on other photos.

Yea ! The set of files I am currently testing with allows reading with new_from,_path. Many files also are handled without an error by new_from_vec. but in the set also is a file with the extension .tif. FileExtension::from_strcant handle this as there is no "tif" arm in the FileExtension enum yet. I did not yet test writing a file.....

mschnell1 commented 1 month ago

Testing more files:

with some (and new_from_path I get : Error during decoding: Custom { kind: Other, error: "No EXIF data found!" } I'll check those files ASAP.... with those files, other software shows exif data

with some it's Illegal format for known tag! Tag: SubjectDistance([]) Expected: RATIONAL64S Got: RATIONAL64U

with some I get a panic:

thread 'main' panicked at C:\Users\Michael Schnell\.cargo\git\checkouts\little_exif-fdd18c59a5ea2fbd\b6ab7bc\src\metadata.rs:591:34:
attempt to subtract with overflow
mschnell1 commented 1 month ago

BTW.: in the released version, FileExtension::from_str should issue a dedicated Error, rather than ().

Thanks for listening !

TechnikTobi commented 1 month ago

This has to do with the MakerNotes IFD and, as I recently found out, is also the cause for issue #6. For now, with the fix in 0.5.0-beta2, this data will be handled as a single tag and not further processed. Please let me know if this solves the issue for you as well on other photos.

Yea ! The set of files I am currently testing with allows reading with new_from,_path. Many files also are handled without an error by new_from_vec. but in the set also is a file with the extension .tif. FileExtension::from_strcant handle this as there is no "tif" arm in the FileExtension enum yet. I did not yet test writing a file.....

Added TIFF to the todo list

TechnikTobi commented 1 month ago

Testing more files:

with some (and new_from_path I get : Error during decoding: Custom { kind: Other, error: "No EXIF data found!" } I'll check those files ASAP.... with those files, other software shows exif data

If you could share an example image where little_exif isn't able to locate the exif data that would be great!

with some it's Illegal format for known tag! Tag: SubjectDistance([]) Expected: RATIONAL64S Got: RATIONAL64U

My bad! Wrong format from my side. Fixed in 0.5.0-beta3.

with some I get a panic:

thread 'main' panicked at C:\Users\Michael Schnell\.cargo\git\checkouts\little_exif-fdd18c59a5ea2fbd\b6ab7bc\src\metadata.rs:591:34:
attempt to subtract with overflow

Again, example image would be helpful!

mschnell1 commented 1 month ago

I'll upload examples ASAP...

(In fact I do need TIFF 👍 )

Just trying to write to a vec. Here deriving clone and copy for FileExtension would be useful. Or maybe storing the file type with the Metadata struct might make sense ?

mschnell1 commented 1 month ago

testing write No errors issued, the resulting file seems OK.

but when trying new_from_vec on the file created using write_to_vec or write_to_path I get:

thread 'main' panicked at C:\Users\mschnell\.cargo\git\checkouts\little_exif-fdd18c59a5ea2fbd\c62d0d4\src\metadata.rs:592:40:
range end index 510 out of range for slice of length 508
TechnikTobi commented 1 month ago

testing write No errors issued, the resulting file seems OK.

but when trying new_from_vec on the file created using write_to_vec or write_to_path I get:

thread 'main' panicked at C:\Users\mschnell\.cargo\git\checkouts\little_exif-fdd18c59a5ea2fbd\c62d0d4\src\metadata.rs:592:40:
range end index 510 out of range for slice of length 508

Again, without example I can't tell what is going on :-(

mschnell1 commented 1 month ago

I 'll upload the file ASAP. BTW.: when reading the Metadata of the same file with read_from_path, I don't get the error.

mschnell1 commented 1 month ago

Here the file that works with new_from_path but not with new_from_vec code:

    let extension = file_path.extension().unwrap();
    let extension = extension.to_str().unwrap();
    let file_type = filetype::FileExtension::from_str(extension).unwrap();
    let mut content = std::fs::read(file_path).unwrap();
    let metadata = Metadata::new_from_vec(&content, file_type);

-> www.bitvibe.de/Upload/x.jpg

mschnell1 commented 1 month ago

the file that does

14: copying //marianne-1/public/hdd_1_1_1/Fotos/Fremde & Scans/Isabell/2019_Stuttgart_Emilio_SL_925-014-126.JPG -> t:/test/2019_Stuttgart_Emilio_SL_925-014-126.JPG <>
Error during decoding: Custom { kind: Other, error: "No EXIF data found!" }
WARNING: Can't read metadata - Create new & empty struct

is

-> www.bitvibe.de/Upload/2019_Stuttgart_Emilio_SL_925-014-126.JPG

mschnell1 commented 1 month ago

the file that does:

thread 'main' panicked at C:\Users\Michael Schnell\.cargo\git\checkouts\little_exif-fdd18c59a5ea2fbd\b6ab7bc\src\metadata.rs:591:34:
attempt to subtract with overflow

is www.bitvibe.de/Upload/2017_emilio_meister_IMG_4436.JPG

mschnell1 commented 1 month ago

My bad! Wrong format from my side. Fixed in 0.5.0-beta3.

In cargo.toml [dependenmcies] I have little_exif = {git= "https://github.com/TechnikTobi/little_exif.git"}

but when compiling I see Checking little_exif v0.5.0-beta.2 (https://github.com/TechnikTobi/little_exif.git#b6ab7bcd)

is this normal or do I need to do something special to access beta 3 ?

mschnell1 commented 1 month ago

With this file it seems to take a very long time until the error "No EXIF data found!" is returned.

www.bitvibe.de/Upload/A0462208sw.JPG

TechnikTobi commented 1 month ago

My bad! Wrong format from my side. Fixed in 0.5.0-beta3.

In cargo.toml [dependenmcies] I have little_exif = {git= "https://github.com/TechnikTobi/little_exif.git"}

but when compiling I see Checking little_exif v0.5.0-beta.2 (https://github.com/TechnikTobi/little_exif.git#b6ab7bcd)

is this normal or do I need to do something special to access beta 3 ?

No idea what is going on here, beta 3 has been published the same way as the previous beta versions; I personally never declare a dependency via the git link. Perhaps this might work for you?

little_exif = { version = "0.5.0-beta.3" }

mschnell1 commented 1 month ago

I suppsed a giuthub ling would provide the latest commit on main be that beta or whatever. little_exif = { version = "0.5.0-beta.3" }does work as expected ! Thanks

Right now I see no difference with Beta.3

BTW.: there is another Black and White picture ( name ?sw.jpg) that takes a very long time to be decoded before issuing the Exif not found error. Maybe this is a hint for something....

www.bitvibe.de/Upload/A0386910sw.JPG

TechnikTobi commented 1 month ago

Right now I see no difference with Beta.3

Regarding what?

Edit: 0.5.0 is now available as a release.

mschnell1 commented 1 month ago

Right now I see no difference with Beta.3

Regarding what?

The files I am currently testing show the same behavior with either.

TechnikTobi commented 1 month ago

Right now I see no difference with Beta.3

Regarding what?

The files I am currently testing show the same behavior with either.

Can't be. SubjectDistance has been changed to RATIONAL64U. What does the error message say?

mschnell1 commented 1 month ago

(I'll create dedicated issues next time ....)

mschnell1 commented 1 month ago

Can't be. SubjectDistance has been changed to RATIONAL64U. What does the error message say?

Yep. I suppse just the compiling message stating "beta 2" is wrong. I changed the dependency as you suggested and now it correctly is beta 3.

Let me know when I need to change this to "beta 4".