2012-10-13 30 views
6

SOの答え+ Googleの結果が何度も何度も試してふるいすぎると、iOSのEXIFで作業することはとてもイライラします。変更されたEXIFデータが適切に保存されない

以下はその結果を示す作業コードです。

この出力
[[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:videoConnection 
    completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) 
    { 
     NSData *imageNSData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; 

     CGImageSourceRef imgSource = CGImageSourceCreateWithData((__bridge_retained CFDataRef)imageNSData, NULL); 

     //get all the metadata in the image 
     NSDictionary *metadata = (__bridge NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imgSource, 0, NULL); 

     NSLog(@"original metadata Info: %@",metadata); 

     //make the metadata dictionary mutable so we can add properties to it 
     NSMutableDictionary *metadataAsMutable = [metadata mutableCopy]; 

     NSMutableDictionary *EXIFDictionary = [[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyExifDictionary]mutableCopy]; 
     NSMutableDictionary *GPSDictionary = [[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyGPSDictionary]mutableCopy]; 
     NSMutableDictionary *RAWDictionary = [[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyRawDictionary]mutableCopy]; 

     if(!EXIFDictionary) 
      EXIFDictionary = [[NSMutableDictionary dictionary] init]; 

     if(!GPSDictionary) 
      GPSDictionary = [[NSMutableDictionary dictionary] init]; 

     if(!RAWDictionary) 
      RAWDictionary = [[NSMutableDictionary dictionary] init]; 


     [GPSDictionary setObject:@"camera coord Latitude" 
         forKey:(NSString*)kCGImagePropertyGPSLatitude]; 
     [GPSDictionary setObject:@"camera coord Longitude" 
         forKey:(NSString*)kCGImagePropertyGPSLongitude]; 
     [GPSDictionary setObject:@"camera GPS Date Stamp" 
         forKey:(NSString*)kCGImagePropertyGPSDateStamp]; 
     [GPSDictionary setObject:@"camera direction (heading) in degrees" 
         forKey:(NSString*)kCGImagePropertyGPSImgDirection]; 

     [GPSDictionary setObject:@"subject coord Latitude" 
         forKey:(NSString*)kCGImagePropertyGPSDestLatitude]; 
     [GPSDictionary setObject:@"subject coord Longitude" 
         forKey:(NSString*)kCGImagePropertyGPSDestLongitude]; 

     [EXIFDictionary setObject:@"[S.D.] kCGImagePropertyExifUserComment" 
          forKey:(NSString *)kCGImagePropertyExifUserComment]; 

     [EXIFDictionary setValue:@"69 m" forKey:(NSString *)kCGImagePropertyExifSubjectDistance]; 


     //Add the modified Data back into the image’s metadata 
     [metadataAsMutable setObject:EXIFDictionary forKey:(NSString *)kCGImagePropertyExifDictionary]; 
     [metadataAsMutable setObject:GPSDictionary forKey:(NSString *)kCGImagePropertyGPSDictionary]; 
     [metadataAsMutable setObject:RAWDictionary forKey:(NSString *)kCGImagePropertyRawDictionary]; 


     NSLog(@"metadataAsMutable Info: %@",metadataAsMutable); 

     CFStringRef UTI = CGImageSourceGetType(imgSource); //this is the type of image (e.g., public.jpeg) 

     //this will be the data CGImageDestinationRef will write into 
     NSMutableData *newImageData = [NSMutableData data]; 

     CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)newImageData, UTI, 1, NULL); 

     if(!destination) 
      NSLog(@"***Could not create image destination ***"); 

     //add the image contained in the image source to the destination, overidding the old metadata with our modified metadata 
     CGImageDestinationAddImageFromSource(destination, imgSource, 0, (__bridge CFDictionaryRef) metadataAsMutable); 

     //tell the destination to write the image data and metadata into our data object. 
     //It will return false if something goes wrong 
     BOOL success = NO; 
     success = CGImageDestinationFinalize(destination); 

     if(!success) 
      NSLog(@"***Could not create data from image destination ***"); 

     CIImage *testImage = [CIImage imageWithData:newImageData]; 
     NSDictionary *propDict = [testImage properties]; 
     NSLog(@"Properties %@", propDict); 

    }]; 

2012-10-12 23:17:45.415 Waypointer[3120:907] original metadata Info: { 
ColorModel = RGB; 
DPIHeight = 72; 
DPIWidth = 72; 
Depth = 8; 
Orientation = 1; 
PixelHeight = 2448; 
PixelWidth = 3264; 
"{Exif}" =  { 
    ApertureValue = "2.526069"; 
    BrightnessValue = "-4.410617"; 
    ColorSpace = 1; 
    ComponentsConfiguration =   (
     1, 
     2, 
     3, 
     0 
    ); 
    ExifVersion =   (
     2, 
     2, 
     1 
    ); 
    ExposureMode = 0; 
    ExposureProgram = 2; 
    ExposureTime = "0.06666667"; 
    FNumber = "2.4"; 
    Flash = 16; 
    FlashPixVersion =   (
     1, 
     0 
    ); 
    FocalLenIn35mmFilm = 35; 
    FocalLength = "4.28"; 
    ISOSpeedRatings =   (
     800 
    ); 
    MeteringMode = 5; 
    PixelXDimension = 3264; 
    PixelYDimension = 2448; 
    SceneCaptureType = 0; 
    SensingMethod = 2; 
    ShutterSpeedValue = "3.906905"; 
    SubjectArea =   (
     1631, 
     1223, 
     881, 
     881 
    ); 
    WhiteBalance = 0; 
}; 
"{TIFF}" =  { 
    Orientation = 1; 
    ResolutionUnit = 2; 
    XResolution = 72; 
    YResolution = 72; 
    "_YCbCrPositioning" = 1; 
}; 
} 

そして、この:それはすべて完了だ後

2012-10-12 23:17:45.421 Waypointer[3120:907] metadataAsMutable Info: { 
ColorModel = RGB; 
DPIHeight = 72; 
DPIWidth = 72; 
Depth = 8; 
Orientation = 1; 
PixelHeight = 2448; 
PixelWidth = 3264; 
"{Exif}" =  { 
    ApertureValue = "2.526069"; 
    BrightnessValue = "-4.410617"; 
    ColorSpace = 1; 
    ComponentsConfiguration =   (
     1, 
     2, 
     3, 
     0 
    ); 
    ExifVersion =   (
     2, 
     2, 
     1 
    ); 
    ExposureMode = 0; 
    ExposureProgram = 2; 
    ExposureTime = "0.06666667"; 
    FNumber = "2.4"; 
    Flash = 16; 
    FlashPixVersion =   (
     1, 
     0 
    ); 
    FocalLenIn35mmFilm = 35; 
    FocalLength = "4.28"; 
    ISOSpeedRatings =   (
     800 
    ); 
    MeteringMode = 5; 
    PixelXDimension = 3264; 
    PixelYDimension = 2448; 
    SceneCaptureType = 0; 
    SensingMethod = 2; 
    ShutterSpeedValue = "3.906905"; 
    SubjectArea =   (
     1631, 
     1223, 
     881, 
     881 
    ); 
    SubjectDistance = "69 m"; 
    UserComment = "[S.D.] kCGImagePropertyExifUserComment"; 
    WhiteBalance = 0; 
}; 
"{GPS}" =  { 
    DateStamp = "camera GPS Date Stamp"; 
    DestLatitude = "subject coord Latitude"; 
    DestLongitude = "subject coord Longitude"; 
    ImgDirection = "camera direction (heading) in degrees"; 
    Latitude = "camera coord Latitude"; 
    Longitude = "camera coord Longitude"; 
}; 
"{Raw}" =  { 
}; 
"{TIFF}" =  { 
    Orientation = 1; 
    ResolutionUnit = 2; 
    XResolution = 72; 
    YResolution = 72; 
    "_YCbCrPositioning" = 1; 
}; 
} 

そして、この:例が示すように

2012-10-12 23:17:47.131 Waypointer[3120:907] Properties { 
ColorModel = RGB; 
DPIHeight = 72; 
DPIWidth = 72; 
Depth = 8; 
Orientation = 1; 
PixelHeight = 2448; 
PixelWidth = 3264; 
"{Exif}" =  { 
    ApertureValue = "2.526069"; 
    BrightnessValue = "-4.410617"; 
    ColorSpace = 1; 
    ComponentsConfiguration =   (
     0, 
     0, 
     0, 
     1 
    ); 
    ExifVersion =   (
     2, 
     2, 
     1 
    ); 
    ExposureMode = 0; 
    ExposureProgram = 2; 
    ExposureTime = "0.06666667"; 
    FNumber = "2.4"; 
    Flash = 16; 
    FlashPixVersion =   (
     1, 
     0 
    ); 
    FocalLenIn35mmFilm = 35; 
    FocalLength = "4.28"; 
    ISOSpeedRatings =   (
     800 
    ); 
    MeteringMode = 5; 
    PixelXDimension = 3264; 
    PixelYDimension = 2448; 
    SceneCaptureType = 0; 
    SensingMethod = 2; 
    ShutterSpeedValue = "3.906905"; 
    SubjectArea =   (
     1631, 
     1223, 
     881, 
     881 
    ); 
    UserComment = "[S.D.] kCGImagePropertyExifUserComment"; 
    WhiteBalance = 0; 
}; 
"{JFIF}" =  { 
    DensityUnit = 1; 
    JFIFVersion =   (
     1, 
     1 
    ); 
    XDensity = 72; 
    YDensity = 72; 
}; 
"{TIFF}" =  { 
    Orientation = 1; 
    ResolutionUnit = 2; 
    XResolution = 72; 
    YResolution = 72; 
    "_YCbCrPositioning" = 1; 
}; 
} 

を、あなたイメージの元のmetaData、そのmoそして最終的な出力を計算します。

最終的な出力は、私が何をしても、変更された値をスティックすることができないためです。

いくつかの非常に具体的なフォーマットがありますか? iOSで変更内容を削除するのはなぜですか? これらの追加値を追加するには何が必要ですか?彼らは.headerにリストされており、簡単に受け入れられるべきだと考えられました。

+0

「メタデータ」では、ARCが「__bridge_transfer」(または「CFBridgingRelease」)ではなく「__bridge」を使用する必要があります。君は。 –

+0

さて、完了です。しかし、私はまだ同じ結果を得る。 –

+0

私があなたに持っている唯一のもう一つのことは、辞書を間違って作成していることです。 'dictionary'のような便利なコンストラクタはすでに初期化されたオブジェクトを返すので、' init'をそのようなオブジェクトに送る必要はありません。 '[[... alloc] init]'、 '[... new]'、 '[NSMutableDictionary dictionary]'のいずれかを使います。私はこれが問題を解決するとは思わない、出力が正しく見えるので、それは私が見ることができる唯一の残りのバグだ。 –

答えて

9

スコット、アップルの開発者、私のインシデントレポートに戻り、問題を修正:

以上前のコード文字列はGPS の値について値書いている - これはを動作しません。 はNS/CFNumbersである必要があります( はEXIFの浮動小数値)。

私はアップルにドキュメントに関するバグレポートを提出する予定です。

この回答を得るまでに1週間かかりましたが、Appleが開発者に提供するサポートに本当に感謝しています。

Final properties info { 
    ColorModel = RGB; 
    DPIHeight = 72; 
    DPIWidth = 72; 
    Depth = 8; 
    Orientation = 6; 
    PixelHeight = 2448; 
    PixelWidth = 3264; 
    "{Exif}" =  { 
     ApertureValue = "2.526069"; 
     BrightnessValue = "0.547474"; 
     ColorSpace = 1; 
     ComponentsConfiguration =   (
      0, 
      0, 
      0, 
      1 
     ); 
     ExifVersion =   (
      2, 
      2, 
      1 
     ); 
     ExposureMode = 0; 
     ExposureProgram = 2; 
     ExposureTime = "0.05"; 
     FNumber = "2.4"; 
     Flash = 16; 
     FlashPixVersion =   (
      1, 
      0 
     ); 
     FocalLenIn35mmFilm = 35; 
     FocalLength = "4.28"; 
     ISOSpeedRatings =   (
      320 
     ); 
     MeteringMode = 5; 
     PixelXDimension = 3264; 
     PixelYDimension = 2448; 
     SceneCaptureType = 0; 
     SensingMethod = 2; 
     ShutterSpeedValue = "4.321929"; 
     SubjectArea =   (
      1631, 
      1223, 
      881, 
      881 
     ); 
     SubjectDistance = "69.999"; 
     UserComment = "[S.D.] kCGImagePropertyExifUserComment"; 
     WhiteBalance = 0; 
    }; 
    "{GPS}" =  { 
     DateStamp = "2012:10:18"; 
     DestLatitude = "37.795"; 
     DestLatitudeRef = N; 
     DestLongitude = "122.41"; 
     DestLongitudeRef = W; 
     ImgDirection = 300; 
     Latitude = "37.795"; 
     LatitudeRef = N; 
     Longitude = "122.41"; 
     LongitudeRef = W; 
    }; 
    "{JFIF}" =  { 
     DensityUnit = 1; 
     JFIFVersion =   (
      1, 
      1 
     ); 
     XDensity = 72; 
     YDensity = 72; 
    }; 
    "{TIFF}" =  { 
     Orientation = 6; 
     ResolutionUnit = 2; 
     XResolution = 72; 
     YResolution = 72; 
     "_YCbCrPositioning" = 1; 
    }; 
} 

あなたが見ることができるように、すべての値は、次のとおりです。この出力

[[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:videoConnection 
    completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) 
    { 
     NSData *imageNSData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; 

     CGImageSourceRef imgSource = CGImageSourceCreateWithData((__bridge_retained CFDataRef)imageNSData, NULL); 

     //get all the metadata in the image 
     NSDictionary *metadata = (__bridge NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imgSource, 0, NULL); 

     //make the metadata dictionary mutable so we can add properties to it 
     NSMutableDictionary *metadataAsMutable = [metadata mutableCopy]; 

     NSMutableDictionary *EXIFDictionary = [[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyExifDictionary]mutableCopy]; 
     NSMutableDictionary *GPSDictionary = [[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyGPSDictionary]mutableCopy]; 
     NSMutableDictionary *RAWDictionary = [[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyRawDictionary]mutableCopy]; 

     if(!EXIFDictionary) 
      EXIFDictionary = [[NSMutableDictionary dictionary] init]; 

     if(!GPSDictionary) 
      GPSDictionary = [[NSMutableDictionary dictionary] init]; 

     if(!RAWDictionary) 
      RAWDictionary = [[NSMutableDictionary dictionary] init]; 


     [GPSDictionary setObject:[NSNumber numberWithFloat:37.795] 
          forKey:(NSString*)kCGImagePropertyGPSLatitude]; 

     [GPSDictionary setObject:@"N" forKey:(NSString*)kCGImagePropertyGPSLatitudeRef]; 

     [GPSDictionary setObject:[NSNumber numberWithFloat:122.410] 
          forKey:(NSString*)kCGImagePropertyGPSLongitude]; 

     [GPSDictionary setObject:@"W" forKey:(NSString*)kCGImagePropertyGPSLongitudeRef]; 

     [GPSDictionary setObject:@"2012:10:18" 
          forKey:(NSString*)kCGImagePropertyGPSDateStamp]; 

     [GPSDictionary setObject:[NSNumber numberWithFloat:300] 
          forKey:(NSString*)kCGImagePropertyGPSImgDirection]; 

     [GPSDictionary setObject:[NSNumber numberWithFloat:37.795] 
          forKey:(NSString*)kCGImagePropertyGPSDestLatitude]; 

     [GPSDictionary setObject:@"N" forKey:(NSString*)kCGImagePropertyGPSDestLatitudeRef]; 

     [GPSDictionary setObject:[NSNumber numberWithFloat:122.410] 
          forKey:(NSString*)kCGImagePropertyGPSDestLongitude]; 

     [GPSDictionary setObject:@"W" forKey:(NSString*)kCGImagePropertyGPSDestLongitudeRef]; 

     [EXIFDictionary setObject:@"[S.D.] kCGImagePropertyExifUserComment" 
          forKey:(NSString *)kCGImagePropertyExifUserComment]; 

     [EXIFDictionary setObject:[NSNumber numberWithFloat:69.999] 
          forKey:(NSString*)kCGImagePropertyExifSubjectDistance]; 


     //Add the modified Data back into the image’s metadata 
     [metadataAsMutable setObject:EXIFDictionary forKey:(NSString *)kCGImagePropertyExifDictionary]; 
     [metadataAsMutable setObject:GPSDictionary forKey:(NSString *)kCGImagePropertyGPSDictionary]; 
     [metadataAsMutable setObject:RAWDictionary forKey:(NSString *)kCGImagePropertyRawDictionary]; 


     CFStringRef UTI = CGImageSourceGetType(imgSource); //this is the type of image (e.g., public.jpeg) 

     //this will be the data CGImageDestinationRef will write into 
     NSMutableData *newImageData = [NSMutableData data]; 

     CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)newImageData, UTI, 1, NULL); 

     if(!destination) 
      NSLog(@"***Could not create image destination ***"); 

     //add the image contained in the image source to the destination, overidding the old metadata with our modified metadata 
     CGImageDestinationAddImageFromSource(destination, imgSource, 0, (__bridge CFDictionaryRef) metadataAsMutable); 

     //tell the destination to write the image data and metadata into our data object. 
     //It will return false if something goes wrong 
     BOOL success = NO; 
     success = CGImageDestinationFinalize(destination); 

     if(!success) 
      NSLog(@"***Could not create data from image destination ***"); 

     CIImage *testImage = [CIImage imageWithData:newImageData]; 
     NSDictionary *propDict = [testImage properties]; 
     NSLog(@"Final properties %@", propDict); 

    }]; 

:(感謝スコット!);-)

以下の改善コードがあり、その正しい出力EXIFヘッダーに正しく埋め込まれており、これがJPGでカメラロールに正しく書き込まれていることをテストしました。

お楽しみください! :)

+1

私の入力はUIImageなので、出力も同じです。データは私が何をしても変更されずに残っています...それはCIImageでも機能します。何か案が ? – Mercurial

+2

データがファイルに保存されるまで、コードは正常に動作します。ファイルからデータを読み返すと、元のメタデータのみが含まれます。何か案が ? – Amnysia

+0

@Sebastian地元の画像を読み込んで保存するのに役立ちましたが、GoogleドライブにアップロードしてGoogleドライブからダウンロードすると、埋め込まれたGPS座標が表示されません。 –

関連する問題