kamadak / exif-rs

Exif parsing library written in pure Rust
BSD 2-Clause "Simplified" License
189 stars 42 forks source link

Incorrect Orientation Tag Extraction #39

Open hsa00000 opened 1 month ago

hsa00000 commented 1 month ago

Issue Title: Incorrect Orientation Tag Extraction

Description

I am encountering an issue with the exif crate where the orientation tag extracted from an image file appears to be incorrect. Specifically, the image's orientation is reported as "row 0 at right and column 0 at top" even though other tools correctly report the orientation as "Horizontal (normal)".

Steps to Reproduce

  1. Extract the Exif metadata and check the orientation tag by the exif crate.

  2. Verify the correct Exif orientation using other tools like ExifTool or Python's Pillow library.

    • Using ExifTool:

      exiftool test_image.jpg

      Look for the Orientation tag in the output.

    • Using Python's Pillow Library:

      from PIL import Image, ExifTags
      
      image_path = 'test_image.jpg'
      image = Image.open(image_path)
      exif_data = image._getexif()
      if exif_data:
       for tag, value in exif_data.items():
           tag_name = ExifTags.TAGS.get(tag, tag)
           if tag_name == 'Orientation':
               print(f"{tag_name}: {value}")
  3. Compare the orientation tag extracted using the exif crate with the correct orientation from the other tools.

Expected Behavior

The orientation tag should be reported as "Horizontal (normal)" (Exif value 1).

Actual Behavior

The orientation tag is incorrectly reported as "row 0 at right and column 0 at top".

Code Sample

Here is a code snippet used to extract the Exif metadata:

use std::{io, path::Path};

pub fn process_image_info(source_path: &Path) {
    let exif = read_exif(&source_path);
    for field in exif.fields() {
        let tag = field.tag.to_string();
        let value = field.display_value().with_unit(&exif).to_string();
        println!("(tag, value) is {:?}", (tag.clone(), value.clone()));
    }
}

fn read_exif(file_path: &Path) -> exif::Exif {
    let exif_reader = exif::Reader::new();
    let file = std::fs::File::open(file_path).unwrap();
    let mut bufreader = io::BufReader::with_capacity(1024 * 1024, &file);
    let exif = exif_reader.read_from_container(&mut bufreader).unwrap();
    exif
}

Exif Output

(tag, value) is ("Make", "\"samsung\"")
(tag, value) is ("ImageLength", "2250 pixels")
(tag, value) is ("Orientation", "row 0 at right and column 0 at top")
(tag, value) is ("DateTime", "2023-12-29 12:39:11")
(tag, value) is ("GPSLatitude", "25 deg 5 min 34.96164 sec N")
(tag, value) is ("GPSAltitude", "79 meters above sea level")
(tag, value) is ("GPSLatitudeRef", "N")
(tag, value) is ("GPSAltitudeRef", "above sea level")
(tag, value) is ("GPSProcessingMethod", "\"ASCII\\x00\\x00\\x00CELLID\\x00\"")
(tag, value) is ("GPSVersionID", "2.2.0.0")
(tag, value) is ("GPSLongitudeRef", "E")
(tag, value) is ("GPSTimeStamp", "04:38:49")
(tag, value) is ("GPSLongitude", "121 deg 32 min 46.68684 sec E")
(tag, value) is ("GPSDateStamp", "2023-12-29")
(tag, value) is ("YResolution", "72 pixels per inch")
(tag, value) is ("XResolution", "72 pixels per inch")
(tag, value) is ("ImageWidth", "4000 pixels")
(tag, value) is ("Model", "\"SM-A336E\"")
(tag, value) is ("Software", "\"A336EDXS7CWJ1\"")
(tag, value) is ("YCbCrPositioning", "centered")
(tag, value) is ("ExifVersion", "2.2")
(tag, value) is ("ApertureValue", "1.69 EV")
(tag, value) is ("SceneType", "directly photographed image")
(tag, value) is ("ExposureBiasValue", "0 EV")
(tag, value) is ("ExposureProgram", "normal program")
(tag, value) is ("ColorSpace", "sRGB")
(tag, value) is ("ImageUniqueID", "\"J48ESOAR1SM\"")
(tag, value) is ("MaxApertureValue", "1.53 EV")
(tag, value) is ("PixelYDimension", "2250 pixels")
(tag, value) is ("BrightnessValue", "2.58 EV")
(tag, value) is ("DateTimeOriginal", "2023-12-29 12:39:11")
(tag, value) is ("FlashpixVersion", "1.0")
(tag, value) is ("SubSecTimeOriginal", "\"0813\"")
(tag, value) is ("WhiteBalance", "auto white balance")
(tag, value) is ("InteroperabilityIndex", "\"R98\"")
(tag, value) is ("CustomRendered", "normal process")
(tag, value) is ("ExposureMode", "auto exposure")
(tag, value) is ("ExposureTime", "1/60 s")
(tag, value) is ("OffsetTime", "\"+08:00\"")
(tag, value) is ("Flash", "not fired, no return light detection function, auto mode 0 (unknown)")
(tag, value) is ("SubSecTime", "\"0813\"")
(tag, value) is ("FNumber", "f/1.8")
(tag, value) is ("PhotographicSensitivity", "50")
(tag, value) is ("UserComment", "0x00000000000000000000000000")
(tag, value) is ("PixelXDimension", "4000 pixels")
(tag, value) is ("Saturation", "normal")
(tag, value) is ("OffsetTimeDigitized", "\"+08:00\"")
(tag, value) is ("ComponentsConfiguration", "YCbCr_")
(tag, value) is ("FocalLengthIn35mmFilm", "25 mm")
(tag, value) is ("SubSecTimeDigitized", "\"0813\"")
(tag, value) is ("Contrast", "normal")
(tag, value) is ("Sharpness", "normal")
(tag, value) is ("DigitalZoomRatio", "unused")
(tag, value) is ("DateTimeDigitized", "2023-12-29 12:39:11")
(tag, value) is ("ShutterSpeedValue", "5.91 EV")
(tag, value) is ("MeteringMode", "center-weighted average")
(tag, value) is ("FocalLength", "4.65 mm")
(tag, value) is ("OffsetTimeOriginal", "\"+08:00\"")
(tag, value) is ("SceneCaptureType", "standard")
(tag, value) is ("LightSource", "unknown")
(tag, value) is ("ResolutionUnit", "inch")
(tag, value) is ("ImageLength", "288 pixels")
(tag, value) is ("Orientation", "row 0 at right and column 0 at top")
(tag, value) is ("Compression", "JPEG")
(tag, value) is ("JPEGInterchangeFormat", "1242")
(tag, value) is ("JPEGInterchangeFormatLength", "13330")
(tag, value) is ("ImageWidth", "512 pixels")

Comparison with Other Tools

When extracting the same Exif data using other tools like ExifTool or Python's Pillow library, the orientation is reported as "Horizontal (normal)" (Exif value 1).

Attached Files

The attached zip file test_image.zip contains the image test_image.jpg which can be used to reproduce the problem. test_image.zip

kamadak commented 1 month ago

Please look for two Orientation fields in your example output. One is for field.ifd_num == exif::In::PRIMARY and another is for field.ifd_num == exif::In::THUMBNAIL.

In my environment, your sample code with your test_image.jpg correctly outputs two Orientation fields like this:

...
(tag, value) is ("Orientation", "row 0 at top and column 0 at left")
...
(tag, value) is ("Orientation", "row 0 at right and column 0 at top")
...