Как сохранить фотографию с неповрежденными данными GPS в iOS 13.4.1 с помощью Xamarin? - PullRequest
0 голосов
/ 25 апреля 2020

Похоже, что что-то изменилось в последних версиях iOS, поскольку это связано с сохранением данных GPS на фотографиях и после ошибки в библиотеке Xam.Plugin.Media , я пытаюсь выяснить, как сделать фотографию и сохранить ее в Фотогалерее с неповрежденными данными GPS, используя iOS 13.4.1 и Xamarin. iOS 13.16.0.13. Я посмотрел на внутреннюю часть Xam.Plugin.Media и, используя это в качестве отправной точки, попытался объединить что-то на догадке, потому что Xam.Plugin.Media использует ALAssetsLibrary для сохранения в Фотогалерее, возможно, именно поэтому GPS данные не сохраняются вместе с фотографией.

Сейчас я нахожусь в точке, где я думал, что смогу сделать снимок, объединить данные GPS в файл и сохранить вывод JPG во временную папку в папку приложения (то есть я еще даже не сохранил фотографию в галерее). Но когда я просматриваю этот временный файл с помощью приложения «Просмотр» в MacOS или Photoshop для просмотра метаданных GPS, данных там нет.

Мой обработчик для UIImagePickerController.FinishedPickingMedia таков:

private void OnFinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs e)
{
    try
    {
        NSDictionary info = e.Info;
        NSDictionary meta = null;
        UIImage image = null;
        string savedImagePath = null;

        if (_imagePicker.SourceType == UIImagePickerControllerSourceType.Camera)
        {
            meta = (NSDictionary)info[UIImagePickerController.MediaMetadata];
            image = (UIImage)info[UIImagePickerController.OriginalImage];

            if (meta != null && meta.ContainsKey(ImageIO.CGImageProperties.Orientation))
            {
                var newMeta = new NSMutableDictionary();
                newMeta.SetValuesForKeysWithDictionary(meta);
                var newTiffDict = new NSMutableDictionary();
                newTiffDict.SetValuesForKeysWithDictionary(meta[ImageIO.CGImageProperties.TIFFDictionary] as NSDictionary);
                newTiffDict.SetValueForKey(meta[ImageIO.CGImageProperties.Orientation], ImageIO.CGImageProperties.TIFFOrientation);
                newMeta[ImageIO.CGImageProperties.TIFFDictionary] = newTiffDict;

                meta = newMeta;
            }

            if (_locationPermissionGranted)
            {
                meta = SetGpsLocation(meta);
            }

            savedImagePath = SaveImageWithMetadata(image, meta);
        }

        string aPath = null;
        if (_imagePicker.SourceType != UIImagePickerControllerSourceType.Camera)
        {

            // Try to get the album path's URL.
            var url = (NSUrl)info[UIImagePickerController.ReferenceUrl];
            aPath = url?.AbsoluteString;
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Unable to get metadata: {ex}");
    }
}

Какие вызовы:

private NSDictionary SetGpsLocation(NSDictionary meta)
{
    var newMeta = new NSMutableDictionary();
    newMeta.SetValuesForKeysWithDictionary(meta);
    var newGpsDict = new NSMutableDictionary();
    newGpsDict.SetValueForKey(new NSNumber(Math.Abs(_lat)), ImageIO.CGImageProperties.GPSLatitude);
    newGpsDict.SetValueForKey(new NSString(_lat > 0 ? "N" : "S"), ImageIO.CGImageProperties.GPSLatitudeRef);
    newGpsDict.SetValueForKey(new NSNumber(Math.Abs(_long)), ImageIO.CGImageProperties.GPSLongitude);
    newGpsDict.SetValueForKey(new NSString(_long > 0 ? "E" : "W"), ImageIO.CGImageProperties.GPSLongitudeRef);
    newGpsDict.SetValueForKey(new NSNumber(_altitude), ImageIO.CGImageProperties.GPSAltitude);
    newGpsDict.SetValueForKey(new NSNumber(0), ImageIO.CGImageProperties.GPSAltitudeRef);
    newGpsDict.SetValueForKey(new NSNumber(_speed), ImageIO.CGImageProperties.GPSSpeed);
    newGpsDict.SetValueForKey(new NSString("K"), ImageIO.CGImageProperties.GPSSpeedRef);
    newGpsDict.SetValueForKey(new NSNumber(_direction), ImageIO.CGImageProperties.GPSImgDirection);
    newGpsDict.SetValueForKey(new NSString("T"), ImageIO.CGImageProperties.GPSImgDirectionRef);
    newGpsDict.SetValueForKey(new NSString(_timestamp.ToString("hh:mm:ss")), ImageIO.CGImageProperties.GPSTimeStamp);
    newGpsDict.SetValueForKey(new NSString(_timestamp.ToString("yyyy:MM:dd")), ImageIO.CGImageProperties.GPSDateStamp);
    newMeta[ImageIO.CGImageProperties.GPSDictionary] = newGpsDict;
    return newMeta;
}

private string SaveImageWithMetadata(UIImage image, NSDictionary meta)
{
    string outputPath = null;

    try
    {
        var finalQuality = 1.0f;
        var imageData = image.AsJPEG(finalQuality);

        // Continue to move down quality, rare instances.
        while (imageData == null && finalQuality > 0)
        {
            finalQuality -= 0.05f;
            imageData = image.AsJPEG(finalQuality);
        }

        if (imageData == null)
        {
            throw new NullReferenceException("Unable to convert image to jpeg, please ensure file exists or " +
                "lower quality level");
        }

        var dataProvider = new CGDataProvider(imageData);
        var cgImageFromJpeg = CGImage.FromJPEG(dataProvider, null, false, CGColorRenderingIntent.Default);
        var imageWithExif = new NSMutableData();
        var destination = CGImageDestination.Create(imageWithExif, UTType.JPEG, 1);
        var cgImageMetadata = new CGMutableImageMetadata();
        var destinationOptions = new CGImageDestinationOptions();

        if (meta.ContainsKey(ImageIO.CGImageProperties.Orientation))
        {
            destinationOptions.Dictionary[ImageIO.CGImageProperties.Orientation] = 
                meta[ImageIO.CGImageProperties.Orientation];
        }

        if (meta.ContainsKey(ImageIO.CGImageProperties.DPIWidth))
        {
            destinationOptions.Dictionary[ImageIO.CGImageProperties.DPIWidth] = 
                meta[ImageIO.CGImageProperties.DPIWidth];
        }

        if (meta.ContainsKey(ImageIO.CGImageProperties.DPIHeight))
        {
            destinationOptions.Dictionary[ImageIO.CGImageProperties.DPIHeight] = 
                meta[ImageIO.CGImageProperties.DPIHeight];
        }

        if (meta.ContainsKey(ImageIO.CGImageProperties.ExifDictionary))
        {

            destinationOptions.ExifDictionary = 
                new CGImagePropertiesExif(meta[ImageIO.CGImageProperties.ExifDictionary] as NSDictionary);

        }

        if (meta.ContainsKey(ImageIO.CGImageProperties.TIFFDictionary))
        {
            destinationOptions.TiffDictionary = 
                new CGImagePropertiesTiff(meta[ImageIO.CGImageProperties.TIFFDictionary] as NSDictionary);

        }

        if (meta.ContainsKey(ImageIO.CGImageProperties.GPSDictionary))
        {
            destinationOptions.GpsDictionary =
                new CGImagePropertiesGps(meta[ImageIO.CGImageProperties.GPSDictionary] as NSDictionary);
        }

        if (meta.ContainsKey(ImageIO.CGImageProperties.JFIFDictionary))
        {
            destinationOptions.JfifDictionary =
                new CGImagePropertiesJfif(meta[ImageIO.CGImageProperties.JFIFDictionary] as NSDictionary);
        }

        if (meta.ContainsKey(ImageIO.CGImageProperties.IPTCDictionary))
        {
            destinationOptions.IptcDictionary =
                new CGImagePropertiesIptc(meta[ImageIO.CGImageProperties.IPTCDictionary] as NSDictionary);
        }

        destination.AddImageAndMetadata(cgImageFromJpeg, cgImageMetadata, destinationOptions);
        var success = destination.Close();

        if (success)
        {
            outputPath = GetOutputPath();
            imageWithExif.Save(outputPath, true);
        }
    }
    catch (Exception e)
    {
        Console.WriteLine($"Unable to save image with metadata: {e}");
    }

    return outputPath;
}

private static string GetOutputPath()
{
    var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "temp");
    Directory.CreateDirectory(path);

    var timestamp = DateTime.UtcNow.ToString("yyyMMdd_HHmmss", CultureInfo.InvariantCulture);
    var name = "IMG_" + timestamp + ".jpg";

    return Path.Combine(path, GetUniquePath(path, name));
}

private static string GetUniquePath(string path, string name)
{
    var ext = Path.GetExtension(name);
    name = Path.GetFileNameWithoutExtension(name);
    var fullName = name + ext;
    var i = 1;

    while (File.Exists(Path.Combine(path, fullName)))
    {
        fullName = name + "_" + (i++) + ext;
    }

    return Path.Combine(path, fullName);
}

Файл успешно сохранен, но без ожидаемых метаданных GPS. Это заставляет меня поверить, что проблема может заключаться в том, как я сохраняю временную фотографию или сохраняю метаданные GPS в ней в SaveImageWithMetadata или SetGpsLocation.

Если кто-то может предоставить некоторую информацию на том, что на самом деле работает сейчас с iOS 13.4.1 и сохранением данных GPS в фотографии, я был бы очень признателен.

...