Programing

UIImagePickerController 및 기존 사진에서 EXIF ​​데이터 추출

lottogame 2020. 12. 31. 07:50
반응형

UIImagePickerController 및 기존 사진에서 EXIF ​​데이터 추출


UIImagePickerController가 선택 후 사진의 메타 데이터를 반환하지 않는다는 것은 잘 알려져 있습니다. 그러나 앱 스토어에있는 몇 가지 앱 (모바일 사진, PixelPipe)은 원본 파일과 그 안에 저장된 EXIF ​​데이터를 읽을 수있어 앱이 선택한 사진에서 지리 데이터를 추출 할 수 있습니다.

/ private / var / mobile / Media / DCIM / 100APPLE / 폴더 에서 원본 파일을 읽고 EXIF 라이브러리를 통해 실행하는 것으로 보입니다 .

그러나 UIImagePickerController에서 반환 된 사진을 디스크의 파일에 일치시키는 방법을 찾을 수 없습니다. 파일 크기를 살펴 봤지만 원본 파일은 JPEG이고 반환 된 이미지는 원시 UIImage이므로 선택한 이미지의 파일 크기를 알 수 없습니다.

해시 테이블을 만들고 각 이미지의 첫 x 픽셀과 일치시키는 것을 고려하고 있습니다. 이것은 정상을 넘어선 것처럼 보이며 아마도 상당히 느릴 것입니다.

어떤 제안?


이 exif iPhone 라이브러리를 보셨습니까?

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

내 편에서 시도해 볼게요. UIImagePickerController로 찍은 사진에서 GPS (지오 태그) 좌표를 얻고 싶습니다.

좀 더 자세히 살펴보면이 라이브러리는 NSData 정보를 입력으로 받아들이고 UIImagePickerController는 스냅 샷을 찍은 후 UIImage를 반환합니다. 이론적으로 UIImage에 대해 UIkit 범주에서 선택한 것을 사용하면

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

그런 다음 UIImage를 NSData 인스턴스로 변환 한 다음 iPhone exif 라이브러리와 함께 사용할 수 있습니다.

업데이트 :
위에서 언급 한 라이브러리에 테스트를했는데 작동하는 것 같습니다. 그러나 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]];
....
....

태그 정의의 가져 오는 모든 태그 값을 반환 OK,하지만 nil:(

라이브러리에 시도해보고 싶다면 전역 변수를 정의하여 실행해야합니다 (문서에 설명되어 있지만 hum .. : /).

BOOL gLogging = FALSE;

업데이트 2
답변 : 사진 에서 iPhone- 액세스 위치 정보 UIImage는 메타 정보를 캡슐화하지 않으므로 문제 가 있습니다. 확실히이 인터페이스를 통해 EXIF ​​정보가 제공되지 않습니다.

최종 업데이트
좋아, 적어도 피커가 반환 한 사진에 지오 태깅을 제대로 수행 할 수 있었다.
UIImagePickerController를 트리거하기 전에 CLLocationManager를 사용하여 현재 CLocation을 검색하는 것은
사용자에게 달려 있습니다. 일단 가져 오면 exif-iPhone 라이브러리를 사용하는이 메서드를 사용하여 CLLocation에서 UIImage를 지오 태깅 할 수 있습니다.


-(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 =@"N"; 
    [exifMetaData addTagValue: ref forKey:[NSNumber numberWithInt:EXIF_GPSLatitudeRef] ]; 
    if (imageLocation.coordinate.longitude <0.0)
        ref = @"W"; 
    else
        ref =@"E"; 
    [exifMetaData addTagValue: ref forKey:[NSNumber numberWithInt:EXIF_GPSLongitudeRef] ]; 
    NSMutableData* taggedJpegData = [[NSMutableData alloc] init];
    [jpegScanner populateImageData:taggedJpegData];
    [jpegScanner release];
    return [taggedJpegData autorelease];
}

// Helper methods for location conversion -(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 - minutes; 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; [fract release]; [EXFUtils convertRationalToFraction:&arrPtr :[locArray objectAtIndex:1]]; fract = [[EXFraction alloc] initWith:numDenumArray[0] :numDenumArray[1]]; gpsLoc.minutes = fract; [fract release]; [EXFUtils convertRationalToFraction:&arrPtr :[locArray objectAtIndex:2]]; fract = [[EXFraction alloc] initWith:numDenumArray[0] :numDenumArray[1]]; gpsLoc.seconds = fract; [fract release]; }


이것은 iOS5 (베타 4) 및 카메라 롤에서 작동합니다 (.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

없이 어떤 제 3 자 사용하여 EXIF라이브러리를.

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

Apple은 사진에서 EXIF ​​데이터를 읽는 데 사용할 수있는 이미지 I / O 프레임 워크를 iOS4에 추가했습니다. UIImagePickerController그래도 EXIF ​​데이터가 포함 된 그림 반환 되는지는 모르겠습니다 .

편집 : iOS4에서는 델리게이트 UIImagePickerControllerMediaMetadata에게 전달되는 정보 사전 값을 가져 와서 EXIF ​​데이터를 가져올 수 있습니다 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=<UIImage: 0x7fb38e00e870> size {1280, 850} orientation 0 scale 1.000000
editedImage=<UIImage: 0x7fb38e09e1e0> 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는 불가능합니다. 기본적인 문제는 UIImage 클래스가 방향 아웃을 제외한 모든 EXIF ​​데이터를 STRIPS한다는 것입니다. 또한 카메라 롤에 저장하는 기능은이 데이터를 제거합니다. 따라서 기본적으로 추가 EXIF ​​데이터를 수집하고 유지하는 유일한 방법은 응용 프로그램의 개인 "카메라 롤"에 저장하는 것입니다. 나는이 버그를 애플에도 신고했고 우리가 접촉 한 앱 리뷰어 담당자의 필요성을 강조했다. 바라건대 언젠가 그들은 그것을 추가 할 것입니다. 그렇지 않으면 "stock"카메라 응용 프로그램에서만 작동하기 때문에 GEO 태그를 완전히 쓸모 없게 만듭니다.

참고 앱 스토어의 일부 응용 프로그램 해킹 이 주위를. 내가 찾은 것은 GEO 데이터를 저장하기 위해 카메라 롤에 직접 액세스하고 사진을 바로 저장하는 것입니다. 그러나 이것은 카메라 롤 / 저장된 사진에서만 작동하며 나머지 사진 라이브러리에서는 작동하지 않습니다. 컴퓨터에서 휴대 전화로 "동기화 된"사진에는 방향 제거를 제외한 모든 EXIF ​​데이터가 있습니다.

나는 여전히 그 응용 프로그램이 승인 된 이유를 이해할 수 없으며 (카메라 롤에서 삭제조차도 함) 그 어느 것도 수행하지 않는 우리 응용 프로그램은 여전히 ​​보류 중입니다.


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

이것은 공용 API가 제공하지 않지만 많은 사람들에게 유용 할 수 있습니다. 주요 수단은 필요한 사항을 설명 하는 버그를 Apple 에 신고하는 것입니다 (필요한 이유를 설명하는 것도 도움이 될 수 있음). 귀하의 요청이 향후 릴리스에 포함될 수 있기를 바랍니다.

버그를 신고 한 후 iPhone 개발자 프로그램 멤버십과 함께 제공된 개발자 기술 지원 (DTS) 사건 중 하나를 사용할 수도 있습니다. 이를위한 공개적인 방법이 있다면 Apple 엔지니어가 알게 될 것입니다. 그렇지 않으면 적어도 모선 내에서 당신의 곤경에 조금 더 많은 관심을 기울이는 데 도움이 될 수 있습니다. 행운을 빕니다!


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];
    }
}

UIImagePickerController가 반환 한 이미지 데이터와 디렉터리의 각 이미지를 해시하고 비교할 수 있습니다.


그냥 생각해 보셨지만 GitHub의 Three20 프로젝트에서 TTPhotoViewController를 사용해 보셨습니까?

이는 여러 소스에서 읽을 수있는 이미지 선택기를 제공합니다. 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 메타 정보가 포함되어 있으면 분명히 작동합니다.

도움이되기를 바랍니다.


iOS 8에 대한 지원이 필요한 경우 Swift 3에 있습니다.

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 가있는 키 가있는 사전이 있습니다 .UIImagePickerControllerMediaMetadata

Image picker metadata example


이렇게하는 나쁜 방법은 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);

}

That will give you the path to the full size image, which you can then open with an EXIF library of your choice.

But, this calls a Private API and these method names will be detected by Apple if you submit this app. So don't do this, OK?

ReferenceURL : https://stackoverflow.com/questions/1238838/uiimagepickercontroller-and-extracting-exif-data-from-existing-photos

반응형