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
이렇게하는 나쁜 방법은 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
'Programing' 카테고리의 다른 글
"호출 대상에서 예외가 발생했습니다."오류 (mscorlib) (0) | 2020.12.31 |
---|---|
프로세스가 실행되는 코어를 제어하는 방법은 무엇입니까? (0) | 2020.12.31 |
매개 변수 사용에 JSP 포함 (0) | 2020.12.31 |
jQuery serializeArray가 클릭 한 제출 버튼을 포함하지 않습니다. (0) | 2020.12.31 |
REST를 사용해야하는 이유는 무엇입니까? (0) | 2020.12.31 |