123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136(** Exif data analyzer
Due to its updated-on-demand and lots-of-tags nature,
This module is implemented in a separate file from exif.ml
and its interface file is auto created.
*)openExifutilopenExifopenExif.NumbersopenExif.Entry.Pack(* I have some photos from my old Android with non Ascii datetime.
They have encoded 32 bit int in Unix time instead! :-(
*)letanalyze_datetimes=matchDateTime.of_stringswith|(`Ok_asr)->r|`Errors->matchDateTime.of_string_packed_unix_timeswith|`Okv->`EncodedInUnixTimev|(`Error_ase)->eletanalyze_ifd(tag,pack)=matchtag,packwith|0x10f,Asciiss->`Makes|0x110,Asciiss->`Models|0x112,Shorts[|1|]->`Orientation`TopLeft|0x112,Shorts[|2|]->`Orientation`TopRight|0x112,Shorts[|3|]->`Orientation`BottomRight|0x112,Shorts[|4|]->`Orientation`BottomLeft|0x112,Shorts[|5|]->`Orientation`LeftTop|0x112,Shorts[|6|]->`Orientation`RightTop|0x112,Shorts[|7|]->`Orientation`RightBottom|0x112,Shorts[|8|]->`Orientation`LeftBottom|0x11a,Rationals[|r|]->`XResolutionr|0x11b,Rationals[|r|]->`YResolutionr|0x128,Shorts[|2|]->`ResolutionUnit`Inches|0x128,Shorts[|3|]->`ResolutionUnit`Centimeters|0x131,s->`Softwares|0x132,Asciiss->`DateTime(analyze_datetimes)|_->`Unknown(tag,pack)letanalyze_exif(tag,pack)=matchtag,packwith|0x9000,Undefineds->`ExifVersions|0x927c,Undefineds->`MakerNotes|0x9286,Undefineds->`UserComments(* The first 8 bytes indicate char code:
ASCII 41.H, 53.H, 43.H, 49.H, 49.H, 00.H, 00.H, 00.H
JIS 4A.H, 49.H, 53.H, 00.H, 00.H, 00.H, 00.H, 00.H JIS X0208-1990
Unicode 55.H, 4E.H, 49.H, 43.H, 4F.H, 44.H, 45.H, 00.H Unicode Standard
Undefined 00.H, 00.H, 00.H, 00.H, 00.H, 00.H, 00.H, 00.H
*)|0x9003,Asciiss->`DateTimeOriginal(analyze_datetimes)|0x9004,Asciiss->`DateTimeDigitized(analyze_datetimes)|0x9290,Asciiss->`SubsecTimes|0x9291,Asciiss->`SubsecTimeOriginals|0x9292,Asciiss->`SubsecTimeDigitizeds|_->`Unknown(tag,pack)moduleGPS=structtypelatitude=[`North|`South]*rationaltypelongitude=[`East|`West]*rationaltypealtitude=[`AboveSeaLevel|`BelowSeaLevel]*rationaltypetime_stamp_utc={hour:rational;min:rational;sec:rational;}typedirection=[`True|`Magnetic]*rationaltypemap_datum=stringtypet={version:(int*int*int*int)option;latitude:latitudeoption;longitude:longitudeoption;altitude:altitudeoption;time_stamp_utc:time_stamp_utcoption;direction:directionoption;map_datum:map_datumoption}endletanalyze_gps(tag,v)=matchtag,vwith|00,Bytes[|x;y;z;w|]->`GPSVersion(x,y,z,w)|01,Asciis"N"->`NorthLatitude|01,Asciis"S"->`SouthLatitude|02,Rationals[|r|]->`Latituder|03,Asciis"E"->`EastLongitude|03,Asciis"W"->`WestLongitude|04,Rationals[|r|]->`Longituder|05,Bytes[|0|]->`AboveSeaLevel|05,Bytes[|1|]->`BelowSeaLevel|06,Rationals[|r|]->`Altituder|07,Rationals[|h;m;s|]->`TimeStampUTC(float_of_rationalh,float_of_rationalm,float_of_rationals)|07,SRationals[|h;m;s|]->(* It is illegal in the spec but I see some photos with SRationals *)`TimeStampUTCinSRationals(float_of_srationalh,float_of_srationalm,float_of_srationals)|16,Asciis"T"->`ImgDirectionTrue|16,Asciis"M"->`ImgDirectionMagnetic|17,Rationals[|r|]->`ImgDirectionr|18,Asciiss->`GPSMapDatums|29,Asciiss->`GPSDate(Date.of_strings)|_->`Unknown(tag,v)letexif_datetimet=matchData.unpack_exiftwith|Someentries->List.find_map_opt(function|`DateTimeOriginalt->Somet|_->None)(List.mapanalyze_exifentries)|None->Noneletifd_0_datetimet=matchData.unpack_ifd_0twith|Someentries->List.find_map_opt(function|`DateTimet->Somet|_->None)(List.mapanalyze_ifdentries)|None->Noneletdatetimet=matchexif_datetimetwith|(Some_asres)->res|None->ifd_0_datetimet