UIImagePickerController и извлечение EXIF-данных из существующих фотографий

Хорошо известно, что UIImagePickerController не возвращает метаданные фотографии после выбора. Тем не менее, несколько приложений в магазине приложений (Mobile Fotos, PixelPipe), похоже, могут читать исходные файлы и данные EXIF, хранящиеся в них, что позволяет приложению извлекать геоданные с выбранной фотографии.

Они, похоже, делают это, читая исходный файл из папки / private / var / mobile / Media / DCIM / 100APPLE / и запуская его через библиотеку EXIF.

Тем не менее, я не могу найти способ сопоставления фотографии, возвращенной из UIImagePickerController, в файл на диске. Я изучил размеры файлов, но исходный файл является JPEG, в то время как возвращаемое изображение является сырым UIImage, что делает невозможным узнать размер файла выбранного изображения.

Я рассматриваю возможность создания таблицы hashей и сопоставления с первыми x пикселями каждого изображения. Это немного похоже на верх, хотя, возможно, довольно медленно.

Какие-либо предложения?

Вы взглянули на эту библиотеку exif iPhone?

http://code.google.com/p/iphone-exif/

Собираюсь попробовать это на моей стороне. Я хотел бы получить координаты GPS (геотаг) с изображения, которое было сделано с помощью UIImagePickerController: /

После более глубокого взгляда эта библиотека, кажется, берет информацию NSData как входной сигнал, а UIImagePickerController возвращает UIImage после моментального снимка. Теоретически, если мы используем выбранную из категории UIkit для UIImage

NSData * UIImageJPEGRepresentation ( UIImage *image, CGFloat compressionQuality ); 

Затем мы можем преобразовать UIImage в экземпляр NSData, а затем использовать его с библиотекой exif iPhone.

ОБНОВИТЬ:
Я дал тест в упомянутой выше библиотеке и, похоже, работает. Однако из-за моего ограниченного knwoledge о формате EXIF ​​и отсутствии API высокого уровня в библиотеке мне не удается получить значения для тегов EXIF. Вот мой код на случай, если кто-то из вас сможет продолжить:

 #import "EXFJpeg.h" - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)editingInfo { NSLog(@"image picked %@ with info %@", image, editingInfo); NSData* jpegData = UIImageJPEGRepresentation (image,0.5); EXFJpeg* jpegScanner = [[EXFJpeg alloc] init]; [jpegScanner scanImageData: jpegData]; EXFMetaData* exifData = jpegScanner.exifMetaData; EXFJFIF* jfif = jpegScanner.jfif; EXFTag* tagDefinition = [exifData tagDefinition: [NSNumber numberWithInt:EXIF_DateTime]]; //EXFTag* latitudeDef = [exifData tagDefinition: [NSNumber numberWithInt:EXIF_GPSLatitude]]; //EXFTag* longitudeDef = [exifData tagDefinition: [NSNumber numberWithInt:EXIF_GPSLongitude]]; id latitudeValue = [exifData tagValue:[NSNumber numberWithInt:EXIF_GPSLatitude]]; id longitudeValue = [exifData tagValue:[NSNumber numberWithInt:EXIF_GPSLongitude]]; id datetime = [exifData tagValue:[NSNumber numberWithInt:EXIF_DateTime]]; id t = [exifData tagValue:[NSNumber numberWithInt:EXIF_Model]]; .... .... 

Определение определения тегов в порядке, но все значения тегов возвращают nil 🙁

Если вы хотите попробовать в библиотеке, вам нужно определить глобальную переменную, чтобы запустить ее (как объясняется в документе, но hum ..: /)

BOOL gLogging = FALSE;

ОБНОВЛЕНИЕ 2
Ответ: iPhone – информация о местоположении доступа с фото . UIImage не инкапсулирует метаинформацию , поэтому мы застряли: наверняка, через этот интерфейс не будет предоставлена ​​информация EXIF.

ЗАКЛЮЧИТЕЛЬНОЕ ОБНОВЛЕНИЕ
Хорошо, мне удалось заставить его работать, по крайней мере, для правильной геотаг фотографии, возвращенной сборщиком.
Прежде чем запускать UIImagePickerController, вам нужно использовать CLLocationManager для извлечения текущего CLocation
После этого вы можете использовать этот метод, который использует библиотеку exif-iPhone для геоtagsрования UIImage из CLLocation:

-(NSData*) geotagImage:(UIImage*)image withLocation:(CLLocation*)imageLocation { NSData* jpegData = UIImageJPEGRepresentation(image, 0.8); EXFJpeg* jpegScanner = [[EXFJpeg alloc] init]; [jpegScanner scanImageData: jpegData]; EXFMetaData* exifMetaData = jpegScanner.exifMetaData; // end of helper methods // adding GPS data to the Exif object NSMutableArray* locArray = [self createLocArray:imageLocation.coordinate.latitude]; EXFGPSLoc* gpsLoc = [[EXFGPSLoc alloc] init]; [self populateGPS: gpsLoc :locArray]; [exifMetaData addTagValue:gpsLoc forKey:[NSNumber numberWithInt:EXIF_GPSLatitude] ]; [gpsLoc release]; [locArray release]; locArray = [self createLocArray:imageLocation.coordinate.longitude]; gpsLoc = [[EXFGPSLoc alloc] init]; [self populateGPS: gpsLoc :locArray]; [exifMetaData addTagValue:gpsLoc forKey:[NSNumber numberWithInt:EXIF_GPSLongitude] ]; [gpsLoc release]; [locArray release]; NSString* ref; if (imageLocation.coordinate.latitude <0.0) ref = @"S"; else ref [email protected]"N"; [exifMetaData addTagValue: ref forKey:[NSNumber numberWithInt:EXIF_GPSLatitudeRef] ]; if (imageLocation.coordinate.longitude <0.0) ref = @"W"; else ref [email protected]"E"; [exifMetaData addTagValue: ref forKey:[NSNumber numberWithInt:EXIF_GPSLongitudeRef] ]; NSMutableData* taggedJpegData = [[NSMutableData alloc] init]; [jpegScanner populateImageData:taggedJpegData]; [jpegScanner release]; return [taggedJpegData autorelease]; } 

// Вспомогательные методы для преобразования местоположения - (NSMutableArray *) createLocArray: (double) val {val = fabs (val); NSMutableArray * array = [[NSMutableArray alloc] init]; double deg = (int) val; [array addObject: [NSNumber numberWithDouble: deg]]; val = val - deg; val = val * 60; double minutes = (int) val; [array addObject: [NSNumber numberWithDouble: minutes]]; val = val - минуты; val = val * 60; double seconds = val; [array addObject: [NSNumber numberWithDouble: seconds]]; return array; } - (void) populateGPS: (EXFGPSLoc *) gpsLoc: (NSArray *) locArray {long numDenumArray [2]; long * arrPtr = numDenumArray; [EXFUtils convertRationalToFraction: & arrPtr: [locArray objectAtIndex: 0]]; EXFraction * fract = [[EXFraction alloc] initWith: numDenumArray [0]: numDenumArray [1]]; gpsLoc.degrees = fract; [фракционный выброс]; [EXFUtils convertRationalToFraction: & arrPtr: [locArray objectAtIndex: 1]]; fract = [[EXFraction alloc] initWith: numDenumArray [0]: numDenumArray [1]]; gpsLoc.minutes = fract; [фракционный выброс]; [EXFUtils convertRationalToFraction: & arrPtr: [locArray objectAtIndex: 2]]; fract = [[EXFraction alloc] initWith: numDenumArray [0]: numDenumArray [1]]; gpsLoc.seconds = fract; [фракционный выброс]; }

Это работает с iOS5 (бета-версия 4) и рулон камеры (вам нужно ввести defs для блоков в .h):

 -(void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType]; if ([mediaType isEqualToString:(NSString*)kUTTypeImage]) { NSURL *url = [info objectForKey:UIImagePickerControllerReferenceURL]; if (url) { ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset) { CLLocation *location = [myasset valueForProperty:ALAssetPropertyLocation]; // location contains lat/long, timestamp, etc // extracting the image is more tricky and 5.x beta ALAssetRepresentation has bugs! }; ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror) { NSLog(@"cant get image - %@", [myerror localizedDescription]); }; ALAssetsLibrary *assetsLib = [[ALAssetsLibrary alloc] init]; [assetsLib assetForURL:url resultBlock:resultblock failureBlock:failureblock]; } } 

В iOS 8 есть способ

Без использования сторонней библиотеки EXIF .

 #import  - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { NSURL *url = [info objectForKey:UIImagePickerControllerReferenceURL]; PHFetchResult *fetchResult = [PHAsset fetchAssetsWithALAssetURLs:@[url] options:nil]; PHAsset *asset = fetchResult.firstObject; //All you need is //asset.location.coordinate.latitude //asset.location.coordinate.longitude //Other useful properties of PHAsset //asset.favorite //asset.modificationDate //asset.creationDate } 

Apple добавила Image I / O Framework в iOS4, которая может использоваться для чтения EXIF-данных из изображений. Я не знаю, возвращает ли UIImagePickerController изображение с данными EXIF, встроенными.

Изменить. В iOS4 вы можете получить данные EXIF, захватив значение ключа UIImagePickerControllerMediaMetadata в информационном словаре, который передается UIImagePickerControllerDelegate .

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

 // Inside whatever implements UIImagePickerControllerDelegate @import AssetsLibrary; // ... your other code here ... @implementation MYImagePickerDelegate - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { NSString *mediaType = info[UIImagePickerControllerMediaType]; UIImage *originalImage = info[UIImagePickerControllerOriginalImage]; UIImage *editedImage = info[UIImagePickerControllerEditedImage]; NSValue *cropRect = info[UIImagePickerControllerCropRect]; NSURL *mediaUrl = info[UIImagePickerControllerMediaURL]; NSURL *referenceUrl = info[UIImagePickerControllerReferenceURL]; NSDictionary *mediaMetadata = info[UIImagePickerControllerMediaMetadata]; NSLog(@"mediaType=%@", mediaType); NSLog(@"originalImage=%@", originalImage); NSLog(@"editedImage=%@", editedImage); NSLog(@"cropRect=%@", cropRect); NSLog(@"mediaUrl=%@", mediaUrl); NSLog(@"referenceUrl=%@", referenceUrl); NSLog(@"mediaMetadata=%@", mediaMetadata); if (!referenceUrl) { NSLog(@"Media did not have reference URL."); } else { ALAssetsLibrary *assetsLib = [[ALAssetsLibrary alloc] init]; [assetsLib assetForURL:referenceUrl resultBlock:^(ALAsset *asset) { NSString *type = [asset valueForProperty:ALAssetPropertyType]; CLLocation *location = [asset valueForProperty:ALAssetPropertyLocation]; NSNumber *duration = [asset valueForProperty:ALAssetPropertyDuration]; NSNumber *orientation = [asset valueForProperty:ALAssetPropertyOrientation]; NSDate *date = [asset valueForProperty:ALAssetPropertyDate]; NSArray *representations = [asset valueForProperty:ALAssetPropertyRepresentations]; NSDictionary *urls = [asset valueForProperty:ALAssetPropertyURLs]; NSURL *assetUrl = [asset valueForProperty:ALAssetPropertyAssetURL]; NSLog(@"type=%@", type); NSLog(@"location=%@", location); NSLog(@"duration=%@", duration); NSLog(@"assetUrl=%@", assetUrl); NSLog(@"orientation=%@", orientation); NSLog(@"date=%@", date); NSLog(@"representations=%@", representations); NSLog(@"urls=%@", urls); } failureBlock:^(NSError *error) { NSLog(@"Failed to get asset: %@", error); }]; } [picker dismissViewControllerAnimated:YES completion:nil]; } @end 

Поэтому, когда вы выбираете изображение, вы получаете результат, который выглядит так (включая дату!):

 mediaType=public.image originalImage= size {1280, 850} orientation 0 scale 1.000000 editedImage= size {640, 424} orientation 0 scale 1.000000 cropRect=NSRect: {{0, 0}, {1280, 848}} mediaUrl=(null) referenceUrl=assets-library://asset/asset.JPG?id=AC072879-DA36-4A56-8A04-4D467C878877&ext=JPG mediaMetadata=(null) type=ALAssetTypePhoto location=(null) duration=ALErrorInvalidProperty assetUrl=assets-library://asset/asset.JPG?id=AC072879-DA36-4A56-8A04-4D467C878877&ext=JPG orientation=0 date=2014-07-14 04:28:18 +0000 representations=( "public.jpeg" ) urls={ "public.jpeg" = "assets-library://asset/asset.JPG?id=AC072879-DA36-4A56-8A04-4D467C878877&ext=JPG"; } 

В любом случае, мы надеемся, что это поможет кому-то еще некоторое время.

Я провожу некоторое время, работая над этим, а также для приложения, которое было заключено контрактом на сборку. В принципе, поскольку API в настоящее время стоит, это невозможно. Основная проблема – это class UIImage STRIPS для всех данных EXIF, за исключением ориентации. Кроме того, функция сохранения в рулон камеры блокирует данные. Таким образом, в основном единственный способ захватить и поддерживать любые дополнительные данные EXIF ​​- это сохранить его в приватном «ролике камеры» в вашем приложении. Я также подал эту ошибку с яблоком и подчеркнул необходимость повторных обращений к разработчикам приложений, с которыми мы связывались. Надеемся, что когда-нибудь они добавят его. Иначе это приведет к тому, что маркировка GEO будет полностью бесполезной, поскольку она работает только в приложении «запаса».

ПРИМЕЧАНИЕ. Некоторые приложения в магазине приложений взломают это. К тому, что я нашел, напрямую обращаюсь к рулону камеры и сохраняю фотографии прямо на нее, чтобы сохранить данные GEO. Однако это работает только с фотокамерой / сохраненными фотографиями, а НЕ с остальной библиотекой фотографий. Фотографии, синхронизированные с вашим телефоном с вашего компьютера, имеют все данные EXIF, за исключением ориентации.

Я все еще не могу понять, почему эти приложения были одобрены (черт возьми, они даже УДАЛИТЬ из рулона камеры), и наше приложение, которое никоим образом не сдерживается.

Это то, что публичный API не предоставляет, но может быть полезен для многих людей. Ваш основной ресурс заключается в том, чтобы сообщить об ошибке с Apple, которая описывает, что вам нужно (и может быть полезно объяснить, зачем вам это нужно). Надеюсь, ваш запрос может быть выпущен в будущем выпуске.

После подачи ошибки вы также можете использовать один из инцидентов технической поддержки разработчиков (DTS), которые появились с вашим членством в Программе разработчиков iPhone. Если есть общеansible способ сделать это, инженер Apple будет знать. В противном случае это может по крайней мере помочь получить ваше положение немного больше внимания в Mothership. Удачи!

Используйте UIImagePickerControllerMediaURL словаря UIImagePickerControllerMediaURL чтобы получить URL-адрес файла в исходный файл. Несмотря на то, что говорит документация, вы можете получить URL-адрес файла для фотографий, а не только фильмы.

 - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { // Try to get the original file. NSURL *originalFile = [info objectForKey:UIImagePickerControllerMediaURL]; if (originalFile) { NSData *fileData = [NSData dataWithContentsOfURL:originalFile]; } } 

Для iOS 8 и более поздних версий вы можете использовать Photos Framework.

  func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { let url = info[UIImagePickerControllerReferenceURL] as? URL if url != nil { let fetchResult = PHAsset.fetchAssets(withALAssetURLs: [url!], options: nil) let asset = fetchResult.firstObject print(asset?.location?.coordinate.latitude) print(asset?.creationDate) } } 

Возможно, вы сможете hash-данные изображения, возвращаемые UIImagePickerController, и каждое из изображений в каталоге и сравнить их.

Просто мысль, но вы пробовали TTPhotoViewController в проекте Three20 на GitHub?

Это обеспечивает выборщик изображений, который может считываться из нескольких источников. Вы можете использовать его в качестве альтернативы UIImagePickerController, или источник может дать вам понять, как определить, как получить необходимую информацию.

Есть ли конкретная причина, по которой вы хотите извлечь данные местоположения из изображения? Альтернативой могло бы быть получение местоположения отдельно с использованием инфраструктуры CoreLocation. Если вам нужны только геоданные, это может сэкономить вам некоторые головные боли.

кажется, что фотография, полученная UIImagePickerControllerMediaURL, вообще не имеет тегов exif

Чтобы получить эти метаданные, вам придется использовать структуру AVFoundation нижнего уровня.

Взгляните на пример Apple Squarecam (http://developer.apple.com/library/ios/#samplecode/SquareCam/Introduction/Intro.html)

Найдите метод ниже и добавьте строку, добавленную в код. В словаре метаданных также содержится диагностический объект NSDictionary.

 - (BOOL)writeCGImageToCameraRoll:(CGImageRef)cgImage withMetadata:(NSDictionary *)metadata { NSDictionary *Exif = [metadata objectForKey:@"Exif"]; // Add this line } 

Я использую это для изображений ролика камеры

 -(CLLocation*)locationFromAsset:(ALAsset*)asset { if (!asset) return nil; NSDictionary* pickedImageMetadata = [[asset defaultRepresentation] metadata]; NSDictionary* gpsInfo = [pickedImageMetadata objectForKey:(__bridge NSString *)kCGImagePropertyGPSDictionary]; if (gpsInfo){ NSNumber* nLat = [gpsInfo objectForKey:(__bridge NSString *)kCGImagePropertyGPSLatitude]; NSNumber* nLng = [gpsInfo objectForKey:(__bridge NSString *)kCGImagePropertyGPSLongitude]; if (nLat && nLng) return [[CLLocation alloc]initWithLatitude:[nLat doubleValue] longitude:[nLng doubleValue]]; } return nil; } -(void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { //UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage]; NSURL *assetURL = [info objectForKey:UIImagePickerControllerReferenceURL]; // create the asset library in the init method of your custom object or view controller //self.library = [[ALAssetsLibrary alloc] init]; // [self.library assetForURL:assetURL resultBlock:^(ALAsset *asset) { // try to retrieve gps metadata coordinates CLLocation* myLocation = [self locationFromAsset:asset]; // Do your stuff.... } failureBlock:^(NSError *error) { NSLog(@"Failed to get asset from library"); }]; } 

Он работает, очевидно, если изображение содержит метаинформацию gps

Надеюсь, поможет

Это в Swift 3, если вы все еще хотите поддержку iOS 8:

 import AssetsLibrary func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { if picker.sourceType == UIImagePickerControllerSourceType.photoLibrary, let url = info[UIImagePickerControllerReferenceURL] as? URL { let assetLibrary = ALAssetsLibrary() assetLibrary.asset(for: url, resultBlock: { (asset) in if let asset = asset { let assetRep: ALAssetRepresentation = asset.defaultRepresentation() let metaData: NSDictionary = assetRep.metadata() as NSDictionary print(metaData) } }, failureBlock: { (error) in print(error!) }) } } 

Для iOS 10 – Swift 3

Обратный вызов сборщика имеет info dict, где есть ключ с метаданными : UIImagePickerControllerMediaMetadata

Пример метаданных сборщика изображений

Непослушный способ сделать это – пересечь представления UIImagePickerViewController и выделить выбранное изображение в обратном вызове делегата.

 - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { id thumbnailView = [[[[[[[[[[picker.view subviews] objectAtIndex:0] subviews] objectAtIndex:0] subviews] objectAtIndex:0] subviews] objectAtIndex:0] subviews] objectAtIndex:0]; NSString *fullSizePath = [[[thumbnailView selectedPhoto] fileGroup] pathForFullSizeImage]; NSString *thumbnailPath = [[[thumbnailView selectedPhoto] fileGroup] pathForThumbnailFile]; NSLog(@"%@ and %@", fullSizePath, thumbnailPath); } 

Это даст вам путь к полноразмерному изображению, которое затем можно открыть с помощью библиотеки EXIF ​​по вашему выбору.

Но это вызывает Private API, и эти имена методов будут обнаружены Apple, если вы отправите это приложение. Так что не делай этого, хорошо?

  • Сохранение выбранного изображения в CoreData
  • Interesting Posts

    Загрузка не выполняется, когда пользователь перетаскивает и удаляет вложение с почтового клиента

    Последствия предупреждения Windows Genuine Advantage?

    Нет сетевого подключения после обновления до Windows 10

    Как использовать временную метку сервера Firebase для создания даты?

    Выполнение локального скрипта / команды на удаленном сервере

    ng2 – динамическое создание компонента на основе шаблона

    Компиляция в JRE с Eclipse

    Speedbit угнал мою домашнюю страницу Firefox

    AppDelegate, rootViewController и presentViewController

    Почему Windows 7 всегда автоматически меняет язык ввода или клавиатуры?

    Инкрементное (дельта) резервное копирование зашифрованных данных

    Почему несколько экземпляров Tk обескуражены?

    Как центрировать ячейки в JTable

    Проблема с проблемой загрузки Windows 7 – с использованием BCD возможно

    Определение текущего приложения переднего плана из фоновой задачи или службы

    Давайте будем гением компьютера.