Skip to content

Commit

Permalink
Merge pull request #88 from picture-pro/64-exif-data
Browse files Browse the repository at this point in the history
64 exif data - follow up
  • Loading branch information
johnbchron authored Mar 11, 2024
2 parents 6740b26 + 45a3fb2 commit 6cb3379
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 76 deletions.
72 changes: 72 additions & 0 deletions crates/bl/src/upload/exif_ops.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use exif::{DateTime, Exif, In, Tag};

/// Creates a `PhotoMeta` from an optional `Exif` object.
pub fn photo_meta_from_exif(input: Option<Exif>) -> core_types::PhotoMeta {
let mut meta = core_types::PhotoMeta::default();

let Some(exif) = input else {
return meta;
};

// extract datetime
if let Some(field) = exif.get_field(Tag::DateTime, In::PRIMARY) {
match field.value {
exif::Value::Ascii(ref vec) if !vec.is_empty() => {
if let Ok(datetime) = DateTime::from_ascii(&vec[0]) {
meta.date_time = chrono::NaiveDate::from_ymd_opt(
datetime.year.into(),
datetime.month.into(),
datetime.day.into(),
)
.and_then(|date| {
chrono::NaiveTime::from_hms_opt(
datetime.hour.into(),
datetime.minute.into(),
datetime.second.into(),
)
.map(|time| date.and_time(time))
});
}
}
_ => {}
}
}

// extract orientation
meta.orientation = orientation_from_exif(Some(&exif));

// extract gps
// this isn't implemented yet

meta
}

/// Extracts the orientation from an optional `Exif` object.
pub fn orientation_from_exif(input: Option<&Exif>) -> Option<u32> {
if let Some(exif) = input {
if let Some(orientation) = exif.get_field(Tag::Orientation, In::PRIMARY) {
return orientation.value.as_uint().ok().and_then(|v| v.get(0));
}
}
None
}

/// Rotates the given image based on the orientation from the exif data.
pub fn rotate_image_from_exif(
img: &mut image::DynamicImage,
orientation: Option<&Exif>,
) {
let Some(value) = orientation_from_exif(orientation) else {
return;
};
*img = match value {
2 => img.fliph(),
3 => img.rotate180(),
4 => img.flipv(),
5 => img.rotate90().fliph(),
6 => img.rotate90(),
7 => img.rotate270().fliph(),
8 => img.rotate270(),
_ => img.clone(),
};
}
77 changes: 4 additions & 73 deletions crates/bl/src/upload/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#[cfg(feature = "ssr")]
mod exif_ops;
#[cfg(feature = "ssr")]
mod watermark;

use std::collections::HashMap;
Expand Down Expand Up @@ -140,77 +142,6 @@ pub async fn upload_photo_group(
Ok(group.id)
}

#[cfg(feature = "ssr")]
fn photo_meta_from_exif(input: Option<exif::Exif>) -> core_types::PhotoMeta {
let mut meta = core_types::PhotoMeta::default();

let Some(exif) = input else {
return meta;
};

// extract datetime
if let Some(field) = exif.get_field(exif::Tag::DateTime, exif::In::PRIMARY) {
match field.value {
exif::Value::Ascii(ref vec) if !vec.is_empty() => {
if let Ok(datetime) = exif::DateTime::from_ascii(&vec[0]) {
meta.date_time = chrono::NaiveDate::from_ymd_opt(
datetime.year.into(),
datetime.month.into(),
datetime.day.into(),
)
.and_then(|date| {
chrono::NaiveTime::from_hms_opt(
datetime.hour.into(),
datetime.minute.into(),
datetime.second.into(),
)
.map(|time| date.and_time(time))
});
}
}
_ => {}
}
}

// extract gps
// this isn't implemented yet

meta
}

#[cfg(feature = "ssr")]
fn rotate_image_from_exif(
img: &mut image::DynamicImage,
orientation: Option<&exif::Exif>,
) {
let Some(exif) = orientation else {
return;
};

let Some(orientation) =
exif.get_field(exif::Tag::Orientation, exif::In::PRIMARY)
else {
return;
};

let Some(value) = orientation.value.as_uint().ok().and_then(|v| v.get(0))
else {
return;
};

*img = match value {
1 => img.clone(),
2 => img.fliph(),
3 => img.rotate180(),
4 => img.flipv(),
5 => img.rotate90().fliph(),
6 => img.rotate90(),
7 => img.rotate270().fliph(),
8 => img.rotate270(),
_ => img.clone(),
};
}

#[cfg(feature = "ssr")]
async fn create_photo(
mut img: image::DynamicImage,
Expand All @@ -222,7 +153,7 @@ async fn create_photo(
use crate::model_ext::ModelExt;

// rotate image based on exif orientation
rotate_image_from_exif(&mut img, meta.as_ref());
exif_ops::rotate_image_from_exif(&mut img, meta.as_ref());

// encode original image as jpeg
let mut original_jpeg_bytes = Vec::new();
Expand Down Expand Up @@ -298,7 +229,7 @@ async fn create_photo(
size: (thumbnail_image.width(), thumbnail_image.height()),
},
},
photo_meta: photo_meta_from_exif(meta),
photo_meta: exif_ops::photo_meta_from_exif(meta),
meta: Default::default(),
};

Expand Down
8 changes: 5 additions & 3 deletions crates/core_types/src/photo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@ pub struct Photo {
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct PhotoMeta {
/// The date and time the photo was taken.
pub date_time: Option<chrono::NaiveDateTime>,
pub date_time: Option<chrono::NaiveDateTime>,
/// The GPS coordinates where the photo was taken.
pub gps: Option<(f64, f64)>,
pub gps: Option<(f64, f64)>,
/// The original orientation of the photo (uses EXIF orientations).
pub orientation: Option<u32>,
/// Extra EXIF data.
pub extra: HashMap<String, String>,
pub extra: HashMap<String, String>,
}

/// The artifacts for a photo. Not a table.
Expand Down

0 comments on commit 6cb3379

Please sign in to comment.