image-rs / image

Encoding and decoding images in Rust
Apache License 2.0
4.83k stars 592 forks source link

Fix for orientation issue of JPEG images #1958

Open brajabi opened 1 year ago

brajabi commented 1 year ago

I believe I have resolved the orientation issue. For more information on this topic, you can refer to the following article: https://magnushoff.com/articles/jpeg-orientation/

To recreate the issue, simply rotate a JPEG image on macOS, which will add orientation metadata to the EXIF data of the image.

The problem at hand is as follows :

CleanShot 2023-07-12 at 11 32 47

this function will help you to get the orientation of image

pub fn get_jpeg_orientation(file_path: PathBuf) -> Result<u32, ()> {
  let file = std::fs::File::open(file_path).expect("problem opening the file");
  let mut bufreader = std::io::BufReader::new(&file);
  let exifreader = exif::Reader::new();
  let exif = exifreader
    .read_from_container(&mut bufreader)
    .expect("failed to read the exifreader");

  let orientation: u32 = match exif.get_field(Tag::Orientation, In::PRIMARY) {
    Some(orientation) => match orientation.value.get_uint(0) {
      Some(v @ 1..=8) => v,
      _ => 1,
    },
    None => 1,
  };

  Ok(orientation)
}

and you can use it like this to get the right orientation at the end


    if orientation == 2 {
      // filp H
      img = DynamicImage::ImageRgba8(imageops::flip_horizontal(&img));
    } else if orientation == 3 {
      // rotate180
      img = DynamicImage::ImageRgba8(imageops::rotate180(&img));
    } else if orientation == 4 {
      //  filp H
      img = DynamicImage::ImageRgba8(imageops::flip_horizontal(&img));
    } else if orientation == 5 {
      // rotate 90 & filp H
      img = DynamicImage::ImageRgba8(imageops::rotate90(&img));
      img = DynamicImage::ImageRgba8(imageops::flip_horizontal(&img));
    } else if orientation == 6 {
      // rotate90
      img = DynamicImage::ImageRgba8(imageops::rotate90(&img));
    } else if orientation == 7 {
      // filp H & ‌rotate 270
      img = DynamicImage::ImageRgba8(imageops::rotate270(&img));
      img = DynamicImage::ImageRgba8(imageops::flip_horizontal(&img));
    } else if orientation == 8 {
      // rotate 270
      img = DynamicImage::ImageRgba8(imageops::rotate270(&img));
    }
sxyazi commented 6 months ago

Thank you very much for sharing this code; it works really well.

I found there is a little typo, when orientation == 4, it should be a vertical flip:

    } else if orientation == 4 {
      //  filp V
      img = DynamicImage::ImageRgba8(imageops::flip_vertical(&img));

I have referenced it to create a simplified version and applied it to Yazi (a terminal file manager with built-in image preview):

fn rotate(mut img: DynamicImage, orientation: u8) -> DynamicImage {
  let rgba = img.color().has_alpha();
  img = match orientation {
    2 => DynamicImage::ImageRgba8(imageops::flip_horizontal(&img)),
    3 => DynamicImage::ImageRgba8(imageops::rotate180(&img)),
    4 => DynamicImage::ImageRgba8(imageops::flip_vertical(&img)),
    5 => DynamicImage::ImageRgba8(imageops::flip_horizontal(&imageops::rotate90(&img))),
    6 => DynamicImage::ImageRgba8(imageops::rotate90(&img)),
    7 => DynamicImage::ImageRgba8(imageops::flip_horizontal(&imageops::rotate270(&img))),
    8 => DynamicImage::ImageRgba8(imageops::rotate270(&img)),
    _ => img,
  };
  if !rgba {
    img = DynamicImage::ImageRgb8(img.into_rgb8());
  }
  img
}

Just leaving it here in case someone needs :)

Vedsaga commented 4 months ago

thank you, it works...