iphone - UIImagePickerController واستخراج بيانات EXIF من الصور الموجودة




cocoa-touch gps (12)

من المعروف أن UIImagePickerController لا يعرض البيانات الوصفية للصورة بعد التحديد. ومع ذلك ، يبدو أن بعض التطبيقات في متجر التطبيقات (Mobile Fotos ، PixelPipe) تكون قادرة على قراءة الملفات الأصلية وبيانات EXIF ​​المخزنة في داخلها ، مما يمكّن التطبيق من استخراج البيانات الجغرافية من الصورة المحددة.

يبدو أنها تقوم بذلك عن طريق قراءة الملف الأصلي من المجلد / private / var / mobile / Media / DCIM / 100APPLE / وتشغيله من خلال مكتبة EXIF.

ومع ذلك ، لا يمكنني العمل على طريقة مطابقة صورة تم إرجاعها من UIImagePickerController إلى ملف موجود على القرص. لقد استكشفت أحجام الملفات ، ولكن الملف الأصلي هو JPEG ، في حين أن الصورة التي تم إرجاعها هي UIImage خام ، مما يجعل من المستحيل معرفة حجم الملف للصورة التي تم اختيارها.

أفكر في إنشاء جدول للتجزئة والمطابقة مقابل أول بكسل × لكل صورة. يبدو هذا قليلاً على القمة رغم ذلك ، وربما بطيء للغاية.

أي اقتراحات؟


بالنسبة إلى نظام التشغيل iOS 10 - Swift 3

يحتوي رد الاتصال في المنتقي على info حيث يوجد مفتاح به metadata : UIImagePickerControllerMediaMetadata


أضافت Apple إطار I / O Image في iOS4 والذي يمكن استخدامه لقراءة بيانات EXIF ​​من الصور. لا أعرف ما إذا كان UIImagePickerController بإرجاع صورة مع تضمين بيانات EXIF.

تحرير: في iOS4 يمكنك جلب بيانات EXIF ​​عن طريق الاستيلاء على قيمة مفتاح UIImagePickerControllerMediaMetadata في قاموس المعلومات الذي يتم تمريره إلى UIImagePickerControllerDelegate delegate.


استخدم مفتاح قاموس UIImagePickerControllerMediaURL للحصول على عنوان URL للملف إلى الملف الأصلي. على الرغم مما تنص عليه الوثائق ، يمكنك الحصول على عنوان URL الخاص بالصورة وليس فقط الأفلام.

 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)
            }
    }

الطريقة الشقية للقيام بذلك هي اجتياز عرض 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 ​​من اختيارك.

ولكن ، هذا يستدعي واجهة برمجة تطبيقات خاصة وسيتم الكشف عن أسماء هذه الطرق بواسطة Apple في حالة إرسال هذا التطبيق. لذلك لا تفعل هذا ، حسنا؟


قد تتمكن من تجزئة بيانات الصورة التي تم إرجاعها بواسطة UIImagePickerController وكل صورة في الدليل ومقارنتها.


قضيت بعض الوقت في العمل على هذا التطبيق الذي تم التعاقد عليه لبناءه. بشكل أساسي كما تقف API حاليًا ، هذا غير ممكن. المشكلة الأساسية هي الطبقة UIImage STRIPS جميع بيانات EXIF ​​باستثناء اتجاه خارج. أيضا وظيفة لحفظ شرائط الكاميرا لفة هذه البيانات بها. وبالتالي ، فإن الطريقة الوحيدة للاستيلاء على أي بيانات EXIF ​​إضافية والحفاظ عليها هي حفظها في "ألبوم الكاميرا" الخاص في تطبيقك. لقد قدمت هذا الخطأ بالتفاح أيضًا وأكدت على الحاجة إلى ممثلين مراجعي التطبيقات الذين كنا على اتصال بهم. نأمل في يوم من الأيام أنها سوف تضيفه في ... وإلا فإنه يجعل من علامات GEO غير مجدية تماما لأنها تعمل فقط في تطبيق الكاميرا "الأسهم".

ملاحظة تتداخل بعض التطبيقات على متجر التطبيقات حول هذا. من خلال ما وجدته ، الوصول مباشرةً إلى ألبوم الكاميرا وحفظ الصور مباشرة إليه لحفظ بيانات GEO. ومع ذلك ، لا يعمل هذا إلا مع صورة الكاميرا / الصور المحفوظة وليس بقية مكتبة الصور. تحتوي الصور "التي تمت مزامنتها" على هاتفك من جهاز الكمبيوتر على جميع بيانات EXIF ​​باستثناء التوجيه المرسوم.

ما زلت لا أستطيع أن أفهم لماذا تمت الموافقة على هذه التطبيقات (لا تزال حتى حذفها من قائمة الكاميرا) وتطبيقاتنا التي لا تزال لا يتم الاحتفاظ بها مرة أخرى.


للحصول على هذه البيانات الوصفية ، سيتعين عليك استخدام إطار المستوى الأدنى AVFoundation.

ألق نظرة على مثال Squarecam من Apple (http://developer.apple.com/library/ios/#samplecode/SquareCam/Introduction/Intro.html)

ابحث عن الطريقة أدناه وأضف السطر ، لقد أضفت إلى الشفرة. يحتوي قاموس البيانات التعريفية الذي تم إرجاعه أيضًا على كائن تشخيص NSDictionary.

-(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");
    }];
}

مجرد فكرة ، ولكن هل حاولت TTPhotoViewController في مشروع Three20 على جيثب؟

التي توفر منتقي الصور التي يمكن قراءتها من مصادر متعددة. قد تكون قادرًا على استخدامه كبديل لـ UIImagePickerController ، أو قد يعطيك المصدر فكرة عن كيفية الحصول على المعلومات التي تحتاجها.


هذا في Swift 3 إذا كنت لا تزال ترغب في دعم iOS 8:

- (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);

}

هذا يعمل مع iOS5 (بيتا 4) ولفة الكاميرا (تحتاج إلى نوع defs للكتل في .h):

#import <Photos/Photos.h>

- (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
}

هل هناك سبب محدد لرغبتك في استخراج بيانات الموقع من الصورة؟ قد يكون البديل هو الحصول على الموقع بشكل منفصل باستخدام إطار CoreLocation. إذا كانت البيانات الجغرافية هي التي تحتاجها فقط ، فقد يوفر لك هذا بعض الصداع.


هناك طريقة في iOS 8

دون استخدام أي مكتبة EXIF لجهة خارجية.

// 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




exif