public PhotoViewModel(PhotoMetadata photoMetadata)
 {
     Caption   = photoMetadata.Caption;
     Timestamp = photoMetadata.Timestamp;
     Tags      = string.Join(" ", photoMetadata.Tags.Select(t => $"#{t}"));
     Photo     = ImageSource.FromFile(photoMetadata.FileName);
 }
Example #2
0
        private static string ExtractDescription(IReadOnlyList <PhotoMetadata> metadata, string url, DateTime creationDate)
        {
            string        description = string.Empty;
            PhotoMetadata desc        = metadata.FirstOrDefault(predicate: item => StringComparer.InvariantCultureIgnoreCase.Equals(x: item.Name, y: MetadataNames.COMMENT));

            if (desc != null)
            {
                description = desc.Value;
            }

            if (!string.IsNullOrWhiteSpace(description))
            {
                description += ". ";
            }

            description += "Source : ";

            description += url;

            description += " Photo taken by Mark Ridgwell";

            if (creationDate != DateTime.MinValue)
            {
                description += " (" + creationDate.ToString(format: "yyyy-MM-dd") + ")";
            }

            description += ".";

            return(description);
        }
Example #3
0
 private Photo WriteMetadataToPhoto(Photo photo, PhotoMetadata metadata)
 {
     photo.Camera       = metadata.Camera;
     photo.Aperture     = metadata.Aperture;
     photo.DateTaken    = metadata.DateTaken;
     photo.ExposureTime = metadata.ExposureTime;
     photo.Flash        = metadata.Flash;
     photo.Iso          = metadata.Iso;
     return(photo);
 }
Example #4
0
        private void listBoxMetadata_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            PhotoMetadata localPhotoMetadata = (sender as ListBox).SelectedItem as PhotoMetadata;

            if (localPhotoMetadata.Title == "URL")
            {
                System.Windows.Clipboard.SetText(localPhotoMetadata.Description);
                UrlCopied.Content = "URL Copied!";
            }
            else
            {
                UrlCopied.Content = "";
            }
        }
Example #5
0
        public static DateTime ExtractCreationDate(IReadOnlyList <PhotoMetadata> metadata)
        {
            PhotoMetadata dateTaken = metadata.FirstOrDefault(predicate: candidate => candidate.Name == MetadataNames.DATE_TAKEN);

            if (dateTaken == null)
            {
                return(DateTime.MinValue);
            }

            if (DateTime.TryParse(s: dateTaken.Value, out DateTime value))
            {
                return(value);
            }

            return(DateTime.MinValue);
        }
Example #6
0
        private static PhotoMetadata GeneratePhotoData(Random R)
        {
            var result = new PhotoMetadata()
            {
                Camera         = DBGeneratorUtils.AnyFromArray(R, cameras),
                Name           = DBGeneratorUtils.AnyFromArray(R, namePrefix) + DBGeneratorUtils.AnyFromArray(R, nameBase) + ".jpg",
                CameraSettings = GetCameraSettings(R),
                Date           = new DateTime(2004, 1, 1).AddDays(R.Next(365 * 16)),
            };

            (result.Longitude, result.Latitude) = GetRandomLatLong(R);
            int pixelIdx = R.Next(pixelWidths.Length);

            result.WidthPx  = pixelMap[pixelWidths[pixelIdx]];
            result.HeightPx = pixelMap[pixelHeights[pixelIdx]];
            return(result);
        }
Example #7
0
        public PhotoMetadata GetMetadata(IFormFile file)
        {
            if (file == null)
            {
                throw new ArgumentException("Input file cannot be null!");
            }

            var metadata = new PhotoMetadata();

            using (var ms = new MemoryStream())
            {
                file.CopyTo(ms);
                ms.Seek(0, SeekOrigin.Begin);
                var result = ImageMetadataReader.ReadMetadata(ms);

                // obtain the Exif directories
                var exifIdDirectory    = result.OfType <ExifIfd0Directory>().FirstOrDefault();
                var exifSubIfDirectory = result.OfType <ExifSubIfdDirectory>().FirstOrDefault();

                if (exifIdDirectory != null)
                {
                    var camera = exifIdDirectory.GetDescription(ExifIfd0Directory.TagMake) ?? "Unknown";
                    var model  = exifIdDirectory.GetDescription(ExifIfd0Directory.TagModel) ?? "Unknown";
                    metadata.Camera = $"{camera} - {model}";

                    var      takenDate = exifIdDirectory.GetDescription(ExifIfd0Directory.TagDateTime);
                    DateTime dateTime;

                    if (DateTime.TryParse(takenDate, out dateTime))
                    {
                        metadata.DateTaken = dateTime;
                    }
                }

                if (exifSubIfDirectory != null)
                {
                    var subIfdDescriptor = new ExifSubIfdDescriptor(exifSubIfDirectory);
                    metadata.ExposureTime = subIfdDescriptor.GetExposureTimeDescription();
                    metadata.Aperture     = subIfdDescriptor.GetApertureValueDescription();
                    metadata.Flash        = subIfdDescriptor.GetFlashDescription();
                    metadata.Iso          = subIfdDescriptor.GetIsoEquivalentDescription();
                }
            }

            return(metadata);
        }
Example #8
0
        private void PopulateDatabase()
        {
            using (var photoColorDB = new PhotoColorContext())
            {
                if (!photoColorDB.DatabaseExists())
                {
                    photoColorDB.CreateDatabase();
                }

                if (photoColorDB.PhotoColors.Count() != this.MaximumPhotoQuantity)
                {
                    // build photo palette dictionary
                    MediaLibrary          library = new MediaLibrary();
                    IEnumerable <Picture> photos  = library.Pictures.AsEnumerable().Take(this.MaximumPhotoQuantity);
                    this.photoDictionary = new List <PhotoMetadata>();

                    foreach (Picture photo in photos)
                    {
                        BitmapImage sourceImage = new BitmapImage();
                        sourceImage.SetSource(photo.GetThumbnail());
                        WriteableBitmap sourceBitmap = new WriteableBitmap(sourceImage);
                        Color           averageColor = sourceBitmap.GetAverageColor();
                        if (!photoColorDB.PhotoColors.Any(x => x.PhotoName != photo.Name))
                        {
                            var newPhotoMetadata = new PhotoMetadata {
                                PhotoName = photo.Name, PhotoColor = averageColor.ToString()
                            };
                            photoColorDB.PhotoColors.InsertOnSubmit(newPhotoMetadata);
                            this.photoDictionary.Add(newPhotoMetadata);
                        }
                    }

                    photoColorDB.SubmitChanges();
                    GoogleAnalytics.EasyTracker.GetTracker().SendEvent("Database updated", "The database was updated.", string.Empty, 0);
                }
                else
                {
                    this.photoDictionary = photoColorDB.PhotoColors.ToList <PhotoMetadata>();
                }
            }
        }
 public FrancePhoto(PhotoMetadata metadata, IDecoder <PhotoMetadata> decoder) => Photo = new Photo(metadata, decoder);
 public PolandPhoto(PhotoMetadata metadata, IDecoder <PhotoMetadata> decoder) => Photo = new Photo(metadata, decoder);
 public Photo(PhotoMetadata metadata, IDecoder <PhotoMetadata> decoder)
 {
     Name     = metadata.Name;
     WidthPx  = decoder.Decode(metadata.WidthPx);
     HeightPx = decoder.Decode(metadata.HeightPx);
 }
Example #12
0
 public PhotoViewModel(PhotoMetadata photoMetadata)
 {
     Description = photoMetadata.Description;
     FileName    = photoMetadata.BlobName;
     Photo       = ImageSource.FromFile(Path.Combine(FileSystem.CacheDirectory, $"{photoMetadata.BlobName}.jpg"));
 }
        public static async Task <(GenerationReturn generationReturn, PhotoMetadata metadata)> PhotoMetadataFromFile(
            FileInfo selectedFile, IProgress <string> progress)
        {
            progress?.Report("Starting Metadata Processing");

            selectedFile.Refresh();

            if (!selectedFile.Exists)
            {
                return(await GenerationReturn.Error("File Does Not Exist?"), null);
            }

            var toReturn = new PhotoMetadata();

            progress?.Report("Getting Directories");

            var exifSubIfDirectory = ImageMetadataReader.ReadMetadata(selectedFile.FullName)
                                     .OfType <ExifSubIfdDirectory>().FirstOrDefault();
            var exifDirectory = ImageMetadataReader.ReadMetadata(selectedFile.FullName).OfType <ExifIfd0Directory>()
                                .FirstOrDefault();
            var iptcDirectory = ImageMetadataReader.ReadMetadata(selectedFile.FullName).OfType <IptcDirectory>()
                                .FirstOrDefault();
            var gpsDirectory = ImageMetadataReader.ReadMetadata(selectedFile.FullName).OfType <GpsDirectory>()
                               .FirstOrDefault();
            var xmpDirectory = ImageMetadataReader.ReadMetadata(selectedFile.FullName).OfType <XmpDirectory>()
                               .FirstOrDefault();

            toReturn.PhotoCreatedBy = exifDirectory?.GetDescription(ExifDirectoryBase.TagArtist) ?? string.Empty;

            if (string.IsNullOrWhiteSpace(toReturn.PhotoCreatedBy))
            {
                toReturn.PhotoCreatedBy = xmpDirectory?.XmpMeta?.GetArrayItem(XmpConstants.NsDC, "creator", 1)?.Value ??
                                          string.Empty;
            }

            if (string.IsNullOrWhiteSpace(toReturn.PhotoCreatedBy))
            {
                toReturn.PhotoCreatedBy = iptcDirectory?.GetDescription(IptcDirectory.TagByLine) ?? string.Empty;
            }

            var createdOn = exifSubIfDirectory?.GetDescription(ExifDirectoryBase.TagDateTimeOriginal);

            if (string.IsNullOrWhiteSpace(createdOn))
            {
                var gpsDateTime = DateTime.MinValue;
                if (gpsDirectory?.TryGetGpsDate(out gpsDateTime) ?? false)
                {
                    if (gpsDateTime != DateTime.MinValue)
                    {
                        toReturn.PhotoCreatedOn = gpsDateTime.ToLocalTime();
                    }
                }
                else
                {
                    toReturn.PhotoCreatedOn = DateTime.Now;
                }
            }
            else
            {
                var createdOnParsed = DateTime.TryParseExact(
                    exifSubIfDirectory.GetDescription(ExifDirectoryBase.TagDateTimeOriginal), "yyyy:MM:dd HH:mm:ss",
                    CultureInfo.InvariantCulture, DateTimeStyles.None, out var parsedDate);

                toReturn.PhotoCreatedOn = createdOnParsed ? parsedDate : DateTime.Now;
            }

            var isoString = exifSubIfDirectory?.GetDescription(ExifDirectoryBase.TagIsoEquivalent);

            if (!string.IsNullOrWhiteSpace(isoString))
            {
                toReturn.Iso = int.Parse(isoString);
            }

            toReturn.CameraMake  = exifDirectory?.GetDescription(ExifDirectoryBase.TagMake) ?? string.Empty;
            toReturn.CameraModel = exifDirectory?.GetDescription(ExifDirectoryBase.TagModel) ?? string.Empty;
            toReturn.FocalLength = exifSubIfDirectory?.GetDescription(ExifDirectoryBase.TagFocalLength) ?? string.Empty;

            toReturn.Lens = exifSubIfDirectory?.GetDescription(ExifDirectoryBase.TagLensModel) ?? string.Empty;

            if (toReturn.Lens == string.Empty || toReturn.Lens == "----")
            {
                toReturn.Lens = xmpDirectory?.XmpMeta?.GetProperty(XmpConstants.NsExifAux, "Lens")?.Value ??
                                string.Empty;
            }
            if (toReturn.Lens == string.Empty || toReturn.Lens == "----")
            {
                toReturn.Lens =
                    xmpDirectory?.XmpMeta?.GetProperty(XmpConstants.NsCameraraw, "LensProfileName")?.Value ??
                    string.Empty;

                if (toReturn.Lens.StartsWith("Adobe ("))
                {
                    toReturn.Lens = toReturn.Lens.Substring(7, toReturn.Lens.Length - 7);
                    if (toReturn.Lens.EndsWith(")"))
                    {
                        toReturn.Lens = toReturn.Lens.Substring(0, toReturn.Lens.Length - 1);
                    }
                }
            }

            if (toReturn.Lens == "----")
            {
                toReturn.Lens = string.Empty;
            }

            toReturn.Aperture = exifSubIfDirectory?.GetDescription(ExifDirectoryBase.TagAperture) ?? string.Empty;

            toReturn.License = exifDirectory?.GetDescription(ExifDirectoryBase.TagCopyright) ?? string.Empty;

            if (string.IsNullOrWhiteSpace(toReturn.License))
            {
                toReturn.License = xmpDirectory?.XmpMeta?.GetArrayItem(XmpConstants.NsDC, "rights", 1)?.Value ??
                                   string.Empty;
            }

            if (string.IsNullOrWhiteSpace(toReturn.License))
            {
                toReturn.License = iptcDirectory?.GetDescription(IptcDirectory.TagCopyrightNotice) ?? string.Empty;
            }

            var shutterValue = new Rational();

            if (exifSubIfDirectory?.TryGetRational(37377, out shutterValue) ?? false)
            {
                toReturn.ShutterSpeed = ExifHelpers.ShutterSpeedToHumanReadableString(shutterValue);
            }
            else
            {
                toReturn.ShutterSpeed = string.Empty;
            }

            //The XMP data - vs the IPTC - will hold the full Title for a very long title (the IPTC will be truncated) -
            //for a 'from Lightroom with no other concerns' export Title makes the most sense, but there are other possible
            //metadata fields to pull from that could be relevant in other contexts.
            toReturn.Title = xmpDirectory?.XmpMeta?.GetArrayItem(XmpConstants.NsDC, "title", 1)?.Value;

            if (string.IsNullOrWhiteSpace(toReturn.Title))
            {
                toReturn.Title = iptcDirectory?.GetDescription(IptcDirectory.TagObjectName) ?? string.Empty;
            }
            //Use a variety of guess on common file names and make that the title - while this could result in an initial title
            //like DSC001 style out of camera names but after having experimented with loading files I think 'default' is better
            //than an invalid blank.
            if (string.IsNullOrWhiteSpace(toReturn.Title))
            {
                toReturn.Title = Path.GetFileNameWithoutExtension(selectedFile.Name).Replace("-", " ").Replace("_", " ")
                                 .SplitCamelCase();
            }

            toReturn.Summary = iptcDirectory?.GetDescription(IptcDirectory.TagObjectName) ?? string.Empty;

            //2020/3/22 - Process out a convention that I have used for more than a decade of pre-2020s do yyMM at the start of a photo title or in the
            //2020s yyyy MM at the start - hopefully this is matched specifically enough not to accidentally trigger on photos without this info... The
            //base case of not matching this convention is that the year and month from photo created on are added to the front of the title or if the
            //title is blank the photo created on is put as a timestamp into the title. One of the ideas here is to give photos as much of a chance
            //as possible to have valid data for an automated import even when a detail like timestamp title is probably better replaced with something
            //more descriptive.
            if (!string.IsNullOrWhiteSpace(toReturn.Title) && toReturn.Title.StartsWith("2"))
            {
                var possibleTitleDate =
                    Regex.Match(toReturn.Title, @"\A(?<possibleDate>\d\d\d\d[\s-]\d\d[\s-]*).*",
                                RegexOptions.IgnoreCase).Groups["possibleDate"].Value;
                if (!string.IsNullOrWhiteSpace(possibleTitleDate))
                {
                    try
                    {
                        var tempDate = new DateTime(int.Parse(possibleTitleDate.Substring(0, 4)),
                                                    int.Parse(possibleTitleDate.Substring(5, 2)), 1);

                        toReturn.Summary =
                            $"{toReturn.Title.Substring(possibleTitleDate.Length, toReturn.Title.Length - possibleTitleDate.Length)}";
                        toReturn.Title =
                            $"{tempDate:yyyy} {tempDate:MMMM} {toReturn.Title.Substring(possibleTitleDate.Length, toReturn.Title.Length - possibleTitleDate.Length)}";

                        progress?.Report("Title updated based on 2yyy MM start pattern for file name");
                    }
                    catch
                    {
                        progress?.Report("Did not successfully parse 2yyy MM start pattern for file name");
                    }
                }
            }
            else if (!string.IsNullOrWhiteSpace(toReturn.Title) &&
                     (toReturn.Title.StartsWith("0") || toReturn.Title.StartsWith("1")))
            {
                try
                {
                    if (Regex.IsMatch(toReturn.Title, @"\A[01]\d\d\d\s.*", RegexOptions.IgnoreCase))
                    {
                        var year  = int.Parse(toReturn.Title.Substring(0, 2));
                        var month = int.Parse(toReturn.Title.Substring(2, 2));

                        var tempDate = year < 20
                            ? new DateTime(2000 + year, month, 1)
                            : new DateTime(1900 + year, month, 1);

                        toReturn.Summary = $"{toReturn.Title.Substring(5, toReturn.Title.Length - 5)}";
                        toReturn.Title   =
                            $"{tempDate:yyyy} {tempDate:MMMM} {toReturn.Title.Substring(5, toReturn.Title.Length - 5)}";

                        progress?.Report("Title updated based on YYMM start pattern for file name");
                    }
                }
                catch
                {
                    progress?.Report("Did not successfully parse YYMM start pattern for file name");
                }
            }
            else if (Regex.IsMatch(toReturn.Title, @".*[\s-][01]\d\d\d\z", RegexOptions.IgnoreCase))
            {
                try
                {
                    var year  = int.Parse(toReturn.Title.Substring(toReturn.Title.Length - 4, 2));
                    var month = int.Parse(toReturn.Title.Substring(toReturn.Title.Length - 2, 2));

                    var tempDate = year < 20
                        ? new DateTime(2000 + year, month, 1)
                        : new DateTime(1900 + year, month, 1);

                    toReturn.Summary = $"{toReturn.Title.Substring(0, toReturn.Title.Length - 5)}";
                    toReturn.Title   =
                        $"{tempDate:yyyy} {tempDate:MMMM} {toReturn.Title.Substring(0, toReturn.Title.Length - 5)}";

                    progress?.Report("Title updated based on YYMM end pattern for file name");
                }
                catch
                {
                    progress?.Report("Did not successfully parse YYMM end pattern for file name");
                }
            }
            else if (Regex.IsMatch(toReturn.Title, @".*[\s-]\d\d\d\d[\s-]\d\d\z", RegexOptions.IgnoreCase))
            {
                var possibleTitleDate =
                    Regex.Match(toReturn.Title, @".*[\s-](?<possibleDate>\d\d\d\d[\s-]\d\d)\z", RegexOptions.IgnoreCase)
                    .Groups["possibleDate"].Value;
                if (!string.IsNullOrWhiteSpace(possibleTitleDate))
                {
                    try
                    {
                        var tempDate = new DateTime(int.Parse(possibleTitleDate.Substring(0, 4)),
                                                    int.Parse(possibleTitleDate.Substring(5, 2)), 1);

                        toReturn.Summary =
                            $"{toReturn.Title.Substring(0, toReturn.Title.Length - possibleTitleDate.Length)}";
                        toReturn.Title =
                            $"{tempDate:yyyy} {tempDate:MMMM} {toReturn.Title.Substring(0, toReturn.Title.Length - possibleTitleDate.Length)}";

                        progress?.Report("Title updated based on 2yyy MM end pattern for file name");
                    }
                    catch
                    {
                        progress?.Report("Did not successfully parse 2yyy MM end pattern for file name");
                    }
                }
            }
            else
            {
                toReturn.Title = string.IsNullOrWhiteSpace(toReturn.Title)
                    ? toReturn.PhotoCreatedOn.ToString("yyyy MMMM dd h-mm-ss tt")
                    : $"{toReturn.PhotoCreatedOn:yyyy} {toReturn.PhotoCreatedOn:MMMM} {toReturn.Title}";
            }

            //Order is important here - the title supplies the summary in the code above - but overwrite that if there is a
            //description.
            var description = exifDirectory?.GetDescription(ExifDirectoryBase.TagImageDescription) ?? string.Empty;

            if (!string.IsNullOrWhiteSpace(description))
            {
                toReturn.Summary = description;
            }

            //Add a trailing . to the summary if it doesn't end with ! ? .
            if (!toReturn.Summary.EndsWith(".") && !toReturn.Summary.EndsWith("!") && !toReturn.Summary.EndsWith("?"))
            {
                toReturn.Summary = $"{toReturn.Summary}.";
            }

            //Remove multi space from title and summary
            if (!string.IsNullOrWhiteSpace(toReturn.Title))
            {
                toReturn.Title = Regex.Replace(toReturn.Title, @"\s+", " ").TrimNullToEmpty();
            }

            if (!string.IsNullOrWhiteSpace(toReturn.Summary))
            {
                toReturn.Summary = Regex.Replace(toReturn.Summary, @"\s+", " ").TrimNullToEmpty();
            }

            var xmpSubjectKeywordList = new List <string>();

            var xmpSubjectArrayItemCount = xmpDirectory?.XmpMeta?.CountArrayItems(XmpConstants.NsDC, "subject");

            if (xmpSubjectArrayItemCount != null)
            {
                for (var i = 1; i <= xmpSubjectArrayItemCount; i++)
                {
                    var subjectArrayItem = xmpDirectory?.XmpMeta?.GetArrayItem(XmpConstants.NsDC, "subject", i);
                    if (subjectArrayItem == null || string.IsNullOrWhiteSpace(subjectArrayItem.Value))
                    {
                        continue;
                    }
                    xmpSubjectKeywordList.AddRange(subjectArrayItem.Value.Replace(";", ",").Split(",")
                                                   .Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()));
                }
            }

            xmpSubjectKeywordList = xmpSubjectKeywordList.Distinct().ToList();

            var keywordTagList = new List <string>();

            var keywordValue = iptcDirectory?.GetDescription(IptcDirectory.TagKeywords)?.Replace(";", ",") ??
                               string.Empty;

            if (!string.IsNullOrWhiteSpace(keywordValue))
            {
                keywordTagList.AddRange(keywordValue.Replace(";", ",").Split(",")
                                        .Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()));
            }

            if (xmpSubjectKeywordList.Count == 0 && keywordTagList.Count == 0)
            {
                toReturn.Tags = string.Empty;
            }
            else if (xmpSubjectKeywordList.Count >= keywordTagList.Count)
            {
                toReturn.Tags = string.Join(",", xmpSubjectKeywordList);
            }
            else
            {
                toReturn.Tags = string.Join(",", keywordTagList);
            }

            if (!string.IsNullOrWhiteSpace(toReturn.Tags))
            {
                toReturn.Tags = Db.TagListJoin(Db.TagListParse(toReturn.Tags));
            }

            return(await GenerationReturn.Success($"Parsed Photo Metadata for {selectedFile.FullName} without error"),
                   toReturn);
        }
 public ItalyPhoto(PhotoMetadata metadata, IDecoder <PhotoMetadata> decoder) => Photo = new Photo(metadata, decoder);