img-parts icon indicating copy to clipboard operation
img-parts copied to clipboard

Problem to copy exif data of image made with Canon EOS 650D

Open chriamue opened this issue 3 years ago • 4 comments

While trying to integrate img-parts into my project I observed a problem processing images of one camera. In the following example I take the image IMG_0818 extract the exif data and save it to large.jpg.

I observe the tool, which is based on libexif

exif -v
0.6.21

reports the exif data is corrupt.

exif large.jpg
Corrupt data
The data provided does not follow the specification.
ExifLoader: The data supplied does not seem to contain EXIF data.

Also many other programs like filebrowser nemo can not view the exif data. The tool exiftool still can read the exif data.

exiftool large.jpg
ExifTool Version Number         : 11.88
File Name                       : large.jpg
Directory                       : .
File Size                       : 5.9 MB
File Modification Date/Time     : 2021:05:25 06:57:57+02:00
File Access Date/Time           : 2021:05:25 06:57:59+02:00
File Inode Change Date/Time     : 2021:05:25 06:57:57+02:00
File Permissions                : rw-rw-r--
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
Rating                          : 0
Image Width                     : 5184
Image Height                    : 3456
...

If I use the image https://github.com/paolobarbolini/img-parts/blob/main/tests/images/P1133897_sRGB.jpg as source for the image data or the other way around as source for the exif data, the resulting images are both readable with exif.

Here is the code with Cargo.toml dependencies: img-parts = { git = "https://github.com/paolobarbolini/img-parts"}

#[cfg(test)]
mod tests {
    use img_parts::jpeg::Jpeg;
    use img_parts::{ImageEXIF, ImageICC};

    #[test]
    fn large_jpg() {
        replace_exif(
            "IMG_0818.JPG".into(),
            "IMG_0818.JPG".into(),
            "large.jpg".into(),
        );
    }

    #[test]
    fn normal_large_jpg() {
        replace_exif(
            "P1133897_sRGB.jpg".into(),
            "IMG_3911.JPG".into(),
            "normal_large.jpg".into(),
        );
    }

    #[test]
    fn large_normal_jpg() {
        replace_exif(
            "IMG_3911.JPG".into(),
            "P1133897_sRGB.jpg".into(),
            "large_normal.jpg".into(),
        );
    }

    fn replace_exif(image: String, exif_image: String, out_file: String) {
        let buf = std::fs::read(image).expect("image read");
        let buf_exif = std::fs::read(exif_image).expect("image read");
        let (iccp, exif) = img_parts::DynImage::from_bytes(buf_exif.clone().into())
            .expect("image loaded")
            .map_or((None, None), |dimg| (dimg.icc_profile(), dimg.exif()));
        let mut out_file = std::fs::File::create(out_file).expect("create output file");

        let mut jpeg = Jpeg::from_bytes(buf.into()).unwrap();
        jpeg.set_icc_profile(iccp);
        jpeg.set_exif(Some(exif.unwrap()));
        jpeg.encoder().write_to(&mut out_file).unwrap();
    }
}

chriamue avatar May 25 '21 05:05 chriamue

Same problem when I run the example:

examples/image-rs$ cargo run IMG_0818.JPG out.jpg
examples/image-rs$ exif out.jpg
Corrupt data
The data provided does not follow the specification.
ExifLoader: The data supplied does not seem to contain EXIF data.

Here is a diff made with exiftool:

img-parts/examples/image-rs$ exiftool IMG_0818.JPG > img.txt
img-parts/examples/image-rs$ exiftool out.jpg > out.txt
img-parts/examples/image-rs$ diff img.txt out.txt
2c2
< File Name                       : IMG_0818.JPG
---
> File Name                       : out.jpg
4,7c4,7
< File Size                       : 5.9 MB
< File Modification Date/Time     : 2021:05:26 12:16:06+02:00
< File Access Date/Time           : 2021:05:26 12:16:42+02:00
< File Inode Change Date/Time     : 2021:05:26 12:16:06+02:00
---
> File Size                       : 36 kB
> File Modification Date/Time     : 2021:05:26 12:17:27+02:00
> File Access Date/Time           : 2021:05:26 12:18:16+02:00
> File Inode Change Date/Time     : 2021:05:26 12:17:27+02:00
11a12,18
> JFIF Version                    : 1.02
> Image Width                     : 256
> Image Height                    : 170
> Encoding Process                : Baseline DCT, Huffman coding
> Bits Per Sample                 : 8
> Color Components                : 3
> Y Cb Cr Sub Sampling            : YCbCr4:4:4 (1 1)
286c293
< Thumbnail Offset                : 10936
---
> Thumbnail Offset                : 11042
288,294d294
< Rating                          : 0
< Image Width                     : 5184
< Image Height                    : 3456
< Encoding Process                : Baseline DCT, Huffman coding
< Bits Per Sample                 : 8
< Color Components                : 3
< Y Cb Cr Sub Sampling            : YCbCr4:2:2 (2 1)
302c302
< Image Size                      : 5184x3456
---
> Image Size                      : 256x170
304c304
< Megapixels                      : 17.9
---
> Megapixels                      : 0.044

I just see 'JFIF Version' Tag is new and 'Thumbnail Offset' and 'Y Cb Cr Sub Sampling' changed.

chriamue avatar May 26 '21 10:05 chriamue

Thanks for the issue. I haven't yet had time to look into this. I hope to get to it this weekend.

paolobarbolini avatar May 27 '21 14:05 paolobarbolini

Hi, thank you for the response. Libexif could make a fix, I can read the exif data in the images now. It looks like the image data is stored before the exif data, which is not very common. https://github.com/libexif/libexif/issues/67#issuecomment-849689069

chriamue avatar May 28 '21 05:05 chriamue

I think I found the issue. Your jpeg contains two EXIF segments, which should be concatenated before reading. This can be seen by running

cargo run --example list-chunks 119443336-bc932200-bd29-11eb-94bf-0fc4b6aea471.JPG 

---------------------------------
| i   | marker | total length |
---------------------------------
| 0   | E1     | 25434        |
| 1   | E1     | 2562         |
| 2   | DB     | 134          |
| 3   | C0     | 19           |
| 4   | C4     | 420          |
| 5   | DA     | 6144145      |

I had already implemented this kind of flattening for ICCP segments, but didn't consider that the same could be necessary for EXIF.

I'll commit a fix later in the day.

paolobarbolini avatar Jun 04 '21 07:06 paolobarbolini