Hey,
I am coming back to talk about EXIF tag handling following what I think is a good enough prototype in #242.
The PR is in draft and will stay that way as I am not fully happy with the implementation and agree with @fintelia's comments. However I am now able to read/write tags using the changes and this is a milestone I would like the library to have. This is especially important as the tools available to do so is essentially just the excellent exiftool, but calling external perl scripts is just not the best in every situation.
Reading tags is as easy as iterating over them. This is done by calling get_exif_data on the decoder:
let mut exif: Directory<ProcessedEntry> = decoder
.get_exif_data()
.expect("Unable to read Exif data")
.into_iter()
.collect();
print!("{exif}");
Writing tags is done by creating a Directory<ProcessedEntry> and passing it to the encoder:
/// Create a tag directory
impl From<ExposureData> for Directory<ProcessedEntry> {
fn from(e: ExposureData) -> Self {
let mut out = Self::new();
if let Some(value) = e.author {
out.insert(Tag::Artist, ProcessedEntry::new(Value::Ascii(value)));
}
if let Some(make) = e.make {
out.insert(Tag::Make, ProcessedEntry::new(Value::Ascii(make)));
}
if let Some(model) = e.model {
out.insert(Tag::Model, ProcessedEntry::new(Value::Ascii(model)));
}
out
}
}
fn main() -> Result {
// [...]
let mut encoder = TiffEncoder::new(buffer)?
.with_compression(Compression::Lzw)
.with_predictor(Predictor::Horizontal)
.with_exif(data.into());
encoder.write_image::<colortype::RGB8>(
photo.width(),
photo.height(),
&photo,
)?;
// [...]
}
This is compatible with both regular tags, GPS tags, and Interop Tags.
To do so, I promote the IFD module to have a singular approach for both encoder and decoder. Tags are separated in types for each of the three "classes" above. The definition of the Directory object used above is essentially a map:
/// Type representing an Image File Directory
#[derive(Debug, Clone)]
pub struct ImageFileDirectory<T: Ord + Into<u16>, E>(BTreeMap<T, E>);
pub type Directory<E> = ImageFileDirectory<Tag, E>;
// ^^^ General purpose tags, no GPS or interop, those are other types
The entries are the values used. There are multiple definitions for convenience:
DecodedEntry is closest to what exists in the main branch today with the raw information from the buffer (type + count + offset to data), has no meaning without a exif file;
BufferedEntry is a type that contains a buffer with all the relevant information for the tag (type + count + data);
ProcessedEntry is an array of ifd::Value with all the above data chunked up in the logical representation of the information.
Ie a list of 5 shorts would be read from the file as a decoded entry with type short, count u32 (or u64 for bigtiff) with value 5 and offset a u32 or u64 with the offset to the data in the buffer. We build a BufferedEntry with the same type, count as a u64 for convenience, and all the 5*(sizeof short) bytes in the buffer. The user can then convert this buffer into a Vecifd::Value::Short of 5 elements. The inverse transformation is operated when arbitrary tags are given by the user to the encoder.
Are you interested by such a feature and this kind of approach. Any feedback would be greatly appreciated.
Hey,
I am coming back to talk about EXIF tag handling following what I think is a good enough prototype in #242.
The PR is in draft and will stay that way as I am not fully happy with the implementation and agree with @fintelia's comments. However I am now able to read/write tags using the changes and this is a milestone I would like the library to have. This is especially important as the tools available to do so is essentially just the excellent
exiftool, but calling external perl scripts is just not the best in every situation.Reading tags is as easy as iterating over them. This is done by calling
get_exif_dataon the decoder:Writing tags is done by creating a
Directory<ProcessedEntry>and passing it to the encoder:This is compatible with both regular tags, GPS tags, and Interop Tags.
To do so, I promote the IFD module to have a singular approach for both encoder and decoder. Tags are separated in types for each of the three "classes" above. The definition of the
Directoryobject used above is essentially a map:The entries are the values used. There are multiple definitions for convenience:
DecodedEntryis closest to what exists in the main branch today with the raw information from the buffer (type + count + offset to data), has no meaning without a exif file;BufferedEntryis a type that contains a buffer with all the relevant information for the tag (type + count + data);ProcessedEntryis an array ofifd::Valuewith all the above data chunked up in the logical representation of the information.Ie a list of 5 shorts would be read from the file as a decoded entry with type short, count u32 (or u64 for bigtiff) with value 5 and offset a u32 or u64 with the offset to the data in the buffer. We build a
BufferedEntrywith the same type, count as a u64 for convenience, and all the 5*(sizeof short) bytes in the buffer. The user can then convert this buffer into a Vecifd::Value::Short of 5 elements. The inverse transformation is operated when arbitrary tags are given by the user to the encoder.Are you interested by such a feature and this kind of approach. Any feedback would be greatly appreciated.