sort photos by creation time
This commit is contained in:
parent
af4827a127
commit
3b85c5d21a
74
Program.cs
74
Program.cs
@ -147,9 +147,10 @@ void main() {
|
|||||||
|
|
||||||
// FIXME: this should probably be IDisposable?
|
// FIXME: this should probably be IDisposable?
|
||||||
public class Photo {
|
public class Photo {
|
||||||
public string File;
|
public string Filename;
|
||||||
public bool Loaded = false;
|
public bool Loaded = false;
|
||||||
public Vector2i Size;
|
public Vector2i Size;
|
||||||
|
public DateTime DateTimeOriginal;
|
||||||
public string CameraModel = "";
|
public string CameraModel = "";
|
||||||
public string LensModel = "";
|
public string LensModel = "";
|
||||||
public string FocalLength = "<unk>";
|
public string FocalLength = "<unk>";
|
||||||
@ -163,12 +164,14 @@ public class Photo {
|
|||||||
private Texture placeholder;
|
private Texture placeholder;
|
||||||
private Image<Rgba32>? image = null;
|
private Image<Rgba32>? image = null;
|
||||||
|
|
||||||
public Photo(string file, Texture placeholder) {
|
public Photo(string filename, Texture placeholder) {
|
||||||
File = file;
|
Filename = filename;
|
||||||
this.placeholder = placeholder;
|
this.placeholder = placeholder;
|
||||||
texture = placeholder;
|
texture = placeholder;
|
||||||
|
|
||||||
ImageInfo info = Image.Identify(file);
|
DateTime creationTime = File.GetCreationTime(filename); // Local time.
|
||||||
|
DateTimeOriginal = creationTime;
|
||||||
|
ImageInfo info = Image.Identify(filename);
|
||||||
Size = new(info.Size.Width, info.Size.Height);
|
Size = new(info.Size.Width, info.Size.Height);
|
||||||
ParseExif(info.Metadata.ExifProfile);
|
ParseExif(info.Metadata.ExifProfile);
|
||||||
TryParseRating(info.Metadata.XmpProfile, out Rating);
|
TryParseRating(info.Metadata.XmpProfile, out Rating);
|
||||||
@ -178,7 +181,7 @@ public class Photo {
|
|||||||
// We don't assign to this.image until Load() is done, because we might
|
// We don't assign to this.image until Load() is done, because we might
|
||||||
// edit the image due to rotation (etc) and don't want to try generating
|
// edit the image due to rotation (etc) and don't want to try generating
|
||||||
// a texture for it until that's already happened.
|
// a texture for it until that's already happened.
|
||||||
Image<Rgba32> tmp = await Image.LoadAsync<Rgba32>(File);
|
Image<Rgba32> tmp = await Image.LoadAsync<Rgba32>(Filename);
|
||||||
Util.RotateImageFromExif(tmp, Orientation);
|
Util.RotateImageFromExif(tmp, Orientation);
|
||||||
image = tmp;
|
image = tmp;
|
||||||
}
|
}
|
||||||
@ -206,14 +209,34 @@ public class Photo {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exif (and other image metadata) reference, from the now-defunct Metadata Working Group:
|
||||||
|
// https://web.archive.org/web/20180919181934/http://www.metadataworkinggroup.org/pdf/mwg_guidance.pdf
|
||||||
|
//
|
||||||
|
// Specifically:
|
||||||
|
//
|
||||||
|
// In general, date/time metadata is being used to describe the following scenarios:
|
||||||
|
// * Date/time original specifies when a photo was taken
|
||||||
|
// * Date/time digitized specifies when an image was digitized
|
||||||
|
// * Date/time modified specifies when a file was modified by the user
|
||||||
|
//
|
||||||
|
// Original Date/Time – Creation date of the intellectual content (e.g. the photograph), rather than the creation date of the content being shown
|
||||||
|
// Exif DateTimeOriginal (36867, 0x9003) and SubSecTimeOriginal (37521, 0x9291)
|
||||||
|
// IPTC DateCreated (IIM 2:55, 0x0237) and TimeCreated (IIM 2:60, 0x023C)
|
||||||
|
// XMP (photoshop:DateCreated)
|
||||||
|
//
|
||||||
|
// Digitized Date/Time – Creation date of the digital representation
|
||||||
|
// Exif DateTimeDigitized (36868, 0x9004) and SubSecTimeDigitized (37522, 0x9292)
|
||||||
|
// IPTC DigitalCreationDate (IIM 2:62, 0x023E) and DigitalCreationTime (IIM 2:63, 0x023F)
|
||||||
|
// XMP (xmp:CreateDate)
|
||||||
|
//
|
||||||
|
// Modification Date/Time – Modification date of the digital image file
|
||||||
|
// Exif DateTime (306, 0x132) and SubSecTime (37520, 0x9290)
|
||||||
|
// XMP (xmp:ModifyDate)
|
||||||
private void ParseExif(ExifProfile? exifs) {
|
private void ParseExif(ExifProfile? exifs) {
|
||||||
if (exifs == null) {
|
if (exifs == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: when we write out images, we'll want to correct the Exif Orientation to 1.
|
|
||||||
// FIXME: handle date shot / edited (and sort by shot date?)
|
|
||||||
|
|
||||||
IExifValue<ushort>? orientation;
|
IExifValue<ushort>? orientation;
|
||||||
if (exifs.TryGetValue(ExifTag.Orientation, out orientation)) {
|
if (exifs.TryGetValue(ExifTag.Orientation, out orientation)) {
|
||||||
Orientation = orientation.Value;
|
Orientation = orientation.Value;
|
||||||
@ -281,6 +304,23 @@ public class Photo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: I think the iPhone stores time in UTC but other cameras report it in local time.
|
||||||
|
IExifValue<string>? dateTimeOriginal;
|
||||||
|
if (exifs.TryGetValue(ExifTag.DateTimeOriginal, out dateTimeOriginal)) {
|
||||||
|
DateTime date;
|
||||||
|
if (DateTime.TryParseExact(
|
||||||
|
dateTimeOriginal.Value ?? "",
|
||||||
|
"yyyy:MM:dd HH:mm:ss",
|
||||||
|
System.Globalization.CultureInfo.InvariantCulture,
|
||||||
|
System.Globalization.DateTimeStyles.AssumeLocal,
|
||||||
|
out date)) {
|
||||||
|
DateTimeOriginal = date;
|
||||||
|
} else {
|
||||||
|
Console.WriteLine($"*** WARNING: unexpected DateTimeOriginal value: {dateTimeOriginal.Value}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// foreach (IExifValue exif in exifs.Values) {
|
// foreach (IExifValue exif in exifs.Values) {
|
||||||
// Console.WriteLine(exif.Tag.ToString() + " " + exif.GetValue().ToString());
|
// Console.WriteLine(exif.Tag.ToString() + " " + exif.GetValue().ToString());
|
||||||
// }
|
// }
|
||||||
@ -301,7 +341,7 @@ public class Photo {
|
|||||||
|
|
||||||
public string Description() {
|
public string Description() {
|
||||||
string shootingInfo = $"{FocalLength}, {FNumber} at {ExposureTime}, {IsoSpeed}";
|
string shootingInfo = $"{FocalLength}, {FNumber} at {ExposureTime}, {IsoSpeed}";
|
||||||
return String.Format("{0,-40} {1,-50} {2}", shootingInfo, $"{CameraModel} {LensModel}", File);
|
return String.Format("{0,-40} {1,-50} {2}", shootingInfo, $"{CameraModel} {LensModel}", Filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -603,8 +643,8 @@ public class Game : GameWindow {
|
|||||||
|
|
||||||
// Load textures from JPEGs.
|
// Load textures from JPEGs.
|
||||||
// string[] files = Directory.GetFiles(@"c:\users\colin\desktop\photos-test\");
|
// string[] files = Directory.GetFiles(@"c:\users\colin\desktop\photos-test\");
|
||||||
// string[] files = Directory.GetFiles(@"c:\users\colin\pictures\photos\2023\07\14\");
|
string[] files = Directory.GetFiles(@"c:\users\colin\pictures\photos\2023\07\14\");
|
||||||
string[] files = Directory.GetFiles(@"G:\DCIM\100EOSR6\");
|
// string[] files = Directory.GetFiles(@"G:\DCIM\100EOSR6\");
|
||||||
// string[] files = Directory.GetFiles(@"C:\Users\colin\Pictures\photos\2018\06\23");
|
// string[] files = Directory.GetFiles(@"C:\Users\colin\Pictures\photos\2018\06\23");
|
||||||
// string[] files = Directory.GetFiles(@"C:\Users\colin\Desktop\Germany all\104D7000");
|
// string[] files = Directory.GetFiles(@"C:\Users\colin\Desktop\Germany all\104D7000");
|
||||||
// string[] files = Directory.GetFiles(@"C:\Users\colin\Desktop\many-birds\");
|
// string[] files = Directory.GetFiles(@"C:\Users\colin\Desktop\many-birds\");
|
||||||
@ -617,11 +657,23 @@ public class Game : GameWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
photos.Sort(ComparePhotosByDate);
|
||||||
|
|
||||||
for (int i = 0; i < 40 && i < photos.Count; i++) {
|
for (int i = 0; i < 40 && i < photos.Count; i++) {
|
||||||
await Task.Run( () => { photos[i].Load(); });
|
await Task.Run( () => { photos[i].Load(); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int ComparePhotosByDate(Photo x, Photo y) {
|
||||||
|
int compare = x.DateTimeOriginal.CompareTo(y.DateTimeOriginal);
|
||||||
|
if (compare != 0) {
|
||||||
|
return compare;
|
||||||
|
}
|
||||||
|
// If the photos have the same seconds value, sort by filename
|
||||||
|
// (since cameras usually increment the filename for successive shots.)
|
||||||
|
return x.Filename.CompareTo(y.Filename);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnUnload() {
|
protected override void OnUnload() {
|
||||||
base.OnUnload();
|
base.OnUnload();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user