Сохраните исходные данные изображения с измененными метаданными (без перекодирования) на iOS

Я хочу сохранить изображение с некоторыми изменениями метаданных во временной папке, без перекодирования фактических данных изображения.

Единственный метод, который я нашел способным сделать это, - это ALAssetsLibrary/writeImageDataToSavedPhotosAlbum:metadata:completeBlock: однако этот метод сохраняет изображение в библиотеке фотографий. Вместо этого я хочу сохранить изображение во временную папку (например, поделиться им по электронной почте, не заполняя библиотеку фотографий).

Я пытался использовать CGImageDestinationRef ( CGImageDestinationAddImageFromSource), но оно может быть создано только с использованием декодированного изображения, что означает, что оно перекодируется при сохранении (проверено, пиксельные байты выглядят по-разному).

Существуют ли другие методы / классы, доступные для iOS, которые могут сохранять данные изображения вместе с метаданными, кроме использования CGImageDestinationRef? Предложения по обходным путям также приветствуются.

3 ответа

Решение

Это неприятная проблема с iOS SDK. Во-первых, я рекомендую подать запрос на улучшение.

Теперь есть потенциальный обходной путь: ЕСЛИ ALAsset был создан вами (то есть, его editable свойство YES) затем вы можете в основном прочитать данные, записать с метаданными, снова прочитать, сохранить на диск, а затем записать с исходными метаданными.

Такой подход позволит избежать создания дублированного изображения.

Пожалуйста, прочитайте // комментарии, поскольку я пропустил некоторые вещи для краткости (например, создание словаря метаданных):

ALAsset* asset; //get your asset, don't use this empty one
if (asset.editable) {

    // get the source data
    ALAssetRepresentation *rep = [asset defaultRepresentation];
    Byte *buffer = (Byte*)malloc(rep.size);
    // add error checking here
    NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:rep.size error:nil];
    NSData *sourceData = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];

    // make your metadata whatever you want
    // you should use actual metadata, not a blank dictionary
    NSDictionary *metadataDictionary = [NSDictionary dictionary];

    // these are __weak to avoid creating an ARC retain cycle
    NSData __weak *originalData = sourceData;
    NSDictionary __weak *originalMetadata = [rep metadata];

    [asset setImageData:sourceData
               metadata:metadataDictionary
        completionBlock:^(NSURL *assetURL, NSError *error) {
            //now get your data and write it to file
            if (!error) {
                //get your data...
                NSString *assetPath = [assetURL path];
                NSData *targetData = [[NSFileManager defaultManager] contentsAtPath:assetPath];

                //...write to file...
                NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
                NSString *documentPath = [searchPaths lastObject];
                NSURL *fileURL = [NSURL fileURLWithPath:documentPath];
                [targetData writeToURL:fileURL atomically:YES];

                //...and put it back the way it was
                [asset setImageData:originalData metadata:originalMetadata completionBlock:nil];
            } else {
                // handle error on setting data
                NSLog(@"ERROR: %@", [error localizedDescription]);
            }
        }];
} else {
    // you'll need to make a new ALAsset which you have permission to edit and then try again

}

Как видите, если ALAsset не принадлежит вам, вам нужно будет создать его, который добавит фотографию в библиотеку пользователя, чего вы и хотели избежать. Однако, как вы уже догадались, вы не можете удалить ALAsset из библиотеки фотографий пользователя, даже если ваше приложение его создало. (Не стесняйтесь подать еще один запрос на улучшение.)

Так что, если фото / изображение было создано в вашем приложении, это будет работать для вас.

Но если нет, он создаст дополнительную копию, которую пользователь должен удалить.

Единственная альтернатива - проанализировать NSData самостоятельно, что было бы больно. Я не знаю библиотеки с открытым исходным кодом, чтобы заполнить этот пробел в iOS SDK.

Попробуй это:

- (void)saveImage: (UIImage*)image
{
    if (image != nil)
    {
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                             NSUserDomainMask, YES);
        NSString *documentsDirectory = [paths objectAtIndex:0];
        NSString* path = [documentsDirectory stringByAppendingPathComponent:
                          @"test.png" ];
        NSData* data = UIImagePNGRepresentation(image);
        [data writeToFile:path atomically:YES];
    }
}

- (UIImage*)loadImage
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                         NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString* path = [documentsDirectory stringByAppendingPathComponent:
                      @"test.png" ];
    UIImage* image = [UIImage imageWithContentsOfFile:path];
    [self sendAction:path];
    return image;
}

Наряду с iphone-exif и ответом Аарона, вы также можете посмотреть библиотеку libexif c.

см. также ответ HCO23 на
Как записать или изменить данные EXIF ​​для существующего изображения в файловой системе, не загружая изображение?

(@ HCO23 использовал libexif в проектах iOS).

Стоит также отметить, что многие "профессиональные" приложения для редактирования метаданных в магазине приложений, кажется, обходят эту проблему, создавая файлы sidecar/xmp.

Другие вопросы по тегам