2014-08-31 10 views
6

Jak usunąć wszystkie dane exif w UIImage za pomocą target-c? I udało się uzyskać dane EXIF ​​przy użyciu następujących:Pozbycie się wszystkich danych Exif w Objective-C

NSData* pngData = UIImagePNGRepresentation(image); 
CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)pngData, NULL); 

NSDictionary* dic = nil; 
if (NULL == imageSource) 
{ 
    #ifdef _DEBUG 
    CGImageSourceStatus status = CGImageSourceGetStatus (source); 
    NSLog (@"Error: file name : %@ - Status: %d", file, status); 
    #endif 
} 
else 
{ 
    CFDictionaryRef propertyRef = CGImageSourceCopyPropertiesAtIndex (imageSource, 0, NULL); 
    CGImageMetadataRef metadataRef = CGImageSourceCopyMetadataAtIndex (imageSource, 0, NULL); 
    // CFDictionaryRef metadataRef = CFDictionaryGetValue(imageProperties, kCGImagePropertyExifDictionary); 
    if (metadataRef) { 
     NSDictionary* immutableMetadata = (NSDictionary *)metadataRef; 
     if (immutableMetadata) { 
      dic = [ NSDictionary dictionaryWithDictionary : (NSDictionary *)metadataRef ]; 
     } 
     CFRelease (metadataRef); 
    } 
    CFRelease(imageSource); 
    imageSource = nil; 
} 


return dic; 

Odpowiedz

11

Kilka myśli:

  1. Generalnie proces ładowania obrazu z NSData lub z zawartością pliku w UIImage, ponownego wydobywania danych z UIImagePNGRepresentation i oszczędności, które z powrotem do NSData usunie metadane z obrazu.

    Ta prosta technika ma jednak swoje wady. Warto zauważyć, że proces wymuszania jej na reprezentacji PNG może dramatycznie wpłynąć na rozmiar wyjściowy NSData. Na przykład, jeśli oryginalny obraz był skompresowanym plikiem JPEG (takim jak przechwycony przez aparat), wynikowy plik PNG może w rzeczywistości być większy niż oryginalny plik JPEG.

    Generalnie wolałbym dostać oryginalne dane (jeżeli plik w dokumentach lub wiązki, obciążenie, które bezpośrednio do NSData; jeśli jest to coś pobrać z ALAssetsLibrary, chciałbym odzyskać ALAsset, iz tym, ALAssetRepresentation , a następnie użyłbym getBytes, aby uzyskać oryginalną binarną reprezentację tego oryginalnego zasobu). W ten sposób unika się rundowego przełączania go przez UIImage (w szczególności, jeśli później używa się UIImagePNGRepresentation lub UIImageJEPGRepresentation).

  2. Jeśli chcesz rozebrać danych EXIF, można:

    • Utwórz źródło obrazu z oryginalnego NSData;

    • Utwórz miejsce docelowe obrazu, używając tej samej "liczby zdjęć" (prawie zawsze 1) i "typu";

    • Podczas kopiowania obrazów od źródła do miejsca przeznaczenia, powiedzieć to, że odpowiednie klawisze (kCGImagePropertyExifDictionary i kCGImagePropertyGPSDictionary powinien być ustawiony na kCFNull), które zgodnie z dokumentacją CGImageDestinationAddImageFromSource sposób można określić, że te powinny być usunięte w miejsce docelowe, jeśli pojawiły się w źródle).

    Tak więc, może to wyglądać tak:

    - (NSData *)dataByRemovingExif:(NSData *)data 
    { 
        CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)data, NULL); 
        NSMutableData *mutableData = nil; 
    
        if (source) { 
         CFStringRef type = CGImageSourceGetType(source); 
         size_t count = CGImageSourceGetCount(source); 
         mutableData = [NSMutableData data]; 
    
         CGImageDestinationRef destination = CGImageDestinationCreateWithData((CFMutableDataRef)mutableData, type, count, NULL); 
    
         NSDictionary *removeExifProperties = @{(id)kCGImagePropertyExifDictionary: (id)kCFNull, 
                   (id)kCGImagePropertyGPSDictionary : (id)kCFNull}; 
    
         if (destination) { 
          for (size_t index = 0; index < count; index++) { 
           CGImageDestinationAddImageFromSource(destination, source, index, (__bridge CFDictionaryRef)removeExifProperties); 
          } 
    
          if (!CGImageDestinationFinalize(destination)) { 
           NSLog(@"CGImageDestinationFinalize failed"); 
          } 
    
          CFRelease(destination); 
         } 
    
         CFRelease(source); 
        } 
    
        return mutableData; 
    } 
    

    uwagi, informacje GPS nie jest technicznie dane EXIF, ale zakładam, że chcesz usunąć to też. Jeśli chcesz zachować dane GPS, usuń wpis kCGImagePropertyGPSDictionary ze słownika .

  3. BTW, w twoim kodzie do wyodrębniania metadanych, wydajesz się, że odlewasz CGImageMetadataRef jako NSDictionary. Jeśli technika działa, to w porządku, ale myślę, że CGImageMetadataRef jest uważany za nieprzejrzysty typ danych, a to naprawdę należy używać CGImageMetadataCopyTags wyodrębnić szereg znaczników:

    - (NSArray *)metadataForData:(NSData *)data 
    { 
        NSArray *metadataArray = nil; 
        CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)data, NULL); 
    
        if (source) { 
         CGImageMetadataRef metadata = CGImageSourceCopyMetadataAtIndex(source, 0, NULL); 
         if (metadata) { 
          metadataArray = CFBridgingRelease(CGImageMetadataCopyTags(metadata)); 
          CFRelease(metadata); 
         } 
         CFRelease(source); 
        } 
    
        return metadataArray; 
    } 
    

    Dla kompletności, w wersjach iOS przed 7.0, można wyodrębnić dane z właściwości:

    - (NSDictionary *)metadataForData:(NSData *)data 
    { 
        NSDictionary *properties = nil; 
        CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)data, NULL); 
    
        if (source) { 
         properties = CFBridgingRelease(CGImageSourceCopyPropertiesAtIndex(source, 0, NULL)); 
         CFRelease(source); 
        } 
    
        return properties; 
    } 
    
+0

Dzięki za odpowiedź, to pomoże pełna. Po usunięciu EXIF ​​&& GPS z CGImage dane obrazu zostają skompresowane, ponieważ zapisuję UIImage przez "writeImageToSavedPhotosAlbum". Jeśli dodaję "kCGImageDestinationLossyCompressionQuality" jako 1.0 do "removeExifProperties", obraz wyjściowy będzie większy niż oryginalne dane. Czy masz jakieś sugestie, aby zachować dane obrazu jako oryginalne, ale usunąć niektóre metadane, takie jak EXIF ​​i GPS? –

2

Według Image I/O programming guide, można użyć CGImageDestinationSetProperties dodać CFDictionaryRef właściwości.

Ich przykładowy kod to:

float compression = 1.0; // Lossless compression if available. 
int orientation = 4; // Origin is at bottom, left. 
CFStringRef myKeys[3]; 
CFTypeRef myValues[3]; 
CFDictionaryRef myOptions = NULL; 
myKeys[0] = kCGImagePropertyOrientation; 
myValues[0] = CFNumberCreate(NULL, kCFNumberIntType, &orientation); 
myKeys[1] = kCGImagePropertyHasAlpha; 
myValues[1] = kCFBooleanTrue; 
myKeys[2] = kCGImageDestinationLossyCompressionQuality; 
myValues[2] = CFNumberCreate(NULL, kCFNumberFloatType, &compression); 
myOptions = CFDictionaryCreate(NULL, (const void **)myKeys, (const void **)myValues, 3, 
         &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 
// Release the CFNumber and CFDictionary objects when you no longer need them. 

A ja nie rozumiem, dlaczego nie wziąć go przez całą drogę. Zgaduję, co dalej będzie:

  • stworzyć CFImageDestinationRef
  • skopiować obraz do niego
  • ustawić żądaną metadanych na nim

Jak zrobić pierwszy krok nieprawdaż t bardzo jasne z dokumentów. Więc spojrzał na CGImageDestination reference, i wygląda na to, można to zrobić w ten sposób (NIE TESTOWANY):

NSMutableData* mutableData = [[NSMutableData alloc] initWithCapacity:[pngData length]]; 
CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge_transfer CFMutableDataRef)mutableData, <# Some UTI Type #>, 1, NULL); 
CGImageDestinationAddImage(imageDestination, image, yourProperties); 

CGImageDestinationFinalize(imageDestination); 

więc stworzyć swój właściwości słownika drogę Jabłko pokazuje w docs, a następnie utworzyć obrazu docelowego i napisz do niego wszystko, łącznie z twoimi właściwościami. Następnie możesz uzyskać dostęp do obiektu NSMutableData i odczytać dane obrazu.