2012-10-13 77 views
6

Después de innumerables intentos y cribado a través de cada resultado de SO answer + google, me desconcierta que trabajar con EXIF ​​en iOS sea tan frustrante.Los datos EXIF ​​modificados no se guardan correctamente

A continuación se muestra el código de trabajo con sus resultados.

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

    }]; 

que da salida a esto:

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

Y esto:

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

Y, después de todo se hace, esto:

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

Como ilustra el ejemplo, se puede ver los metadatos originales de la imagen, su mo dificación, y luego su resultado final.

¡El resultado final es lo que me molesta porque no importa lo que haga, no puedo conseguir que mis valores modificados se queden!

¿Hay algún formato muy específico que me falta? ¿Por qué iOS está eliminando mis modificaciones? ¿Qué debo hacer para agregar estos valores adicionales? Se enumeran en la cabecera. Y pensaron que debería aceptarse fácilmente.

+0

En 'metadata', debe usar' __bridge_transfer' (o 'CFBridgingRelease'), no' __bridge', para que ARC suelte el diccionario Copiado tú. –

+0

Ok, hecho. Pero aún obtengo el mismo resultado. –

+0

Lo único que tengo para ti es que estás creando tus diccionarios incorrectamente. Los constructores de conveniencia, como 'dictionary', devuelven un objeto ya inicializado, por lo que no es necesario, y no debe, enviar' init' a dicho objeto. Use cualquiera '[[... alloc] init]', '[... new]', o '[NSMutableDictionary dictionary]'. No creo que esto resuelva el problema, ya que la salida se ve bien, pero es el único error restante que puedo ver. –

Respuesta

9

Scott, un desarrollador de Apple, volvió con mi informe de incidente y ha solucionado el problema:

El código anterior anterior está escribiendo cadena Los valores para los valores GPS - esto no funcionará , ellos deben ser NS/CFNumbers (extraemos un valor flotante para EXIF).

Voy a presentar un informe de error a Apple en su documentación.

Aunque tardé una semana en obtener esta respuesta, realmente aprecio el apoyo que Apple proporciona a sus desarrolladores. (Gracias a Scott!) ;-)

A continuación se muestra el código mejorado y su correcta salida:

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

    }]; 

que da salida a esto:

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

Como se puede ver, todos los valores son ahora correctamente integrado en el encabezado EXIF ​​y he probado que esto está escrito correctamente con el JPG al rollo de la cámara.

¡Disfrútalo! :)

+1

Mi entrada es un UIImage y también debería ser la salida. La información no se modifica, haga lo que haga ... Sin embargo, funciona para CIImage. Alguna idea ? – Mercurial

+2

Tu código funciona bien hasta que guardes los datos en el archivo. Cuando vuelvo a leer los datos del archivo, solo contiene los metadatos originales. Alguna idea ? – Amnysia

+0

@Sebastian Gracias me ayudó a leer y almacenar la imagen local, pero cuando lo cargo en google drive y luego lo descargué de Google Drive, no me muestra las coordenadas GPS integradas. –

Cuestiones relacionadas