iosuiimagensdataassetsuiimagepngrepresentation

Convert UIImage to NSData without using UIImagePngrepresentation or UIImageJpegRepresentation


I Need to convert UIImage to NSData but without using UIImagePngRepresentation or UIImageJpegRepresentation, for images from photolib i can use assetlib method as mentioned here Using ALAssetsLibrary and ALAsset take out Image as NSData , but for captured image , assset url is not there hence in that case i need to convert UIImage directly to bytes with exif data , how can i accomplish this ? please help


Solution

  • H Bastan,

    Based on your comment:

    ...I want to save the captured image return by device camera, through UIImagepicker delegate method in document directory...

    It looks like your real goal is to save the image with EXIF data to the documents directory and not specifically getting the UIImage as an NSData object with the EXIF data. In that case, you can do the following in your implementation of imagePickerController:didFinishPickingMediaWithInfo:

    // Get your image.
    UIImage *capturedImage = [info objectForKey:UIImagePickerControllerOriginalImage];
    
    // Get your metadata (includes the EXIF data).
    NSDictionary *metadata = [info objectForKey:UIImagePickerControllerMediaMetadata];
    
    // Create your file URL.
    NSFileManager *defaultManager = [NSFileManager defaultManager];
    NSURL *docsURL = [[defaultManager URLsForDirectory:NSDocumentDirectory
                                             inDomains:NSUserDomainMask] lastObject];
    NSURL *outputURL = [docsURL URLByAppendingPathComponent:@"imageWithEXIFData.jpg"];
    
    // Set your compression quuality (0.0 to 1.0).
    NSMutableDictionary *mutableMetadata = [metadata mutableCopy];
    [mutableMetadata setObject:@(1.0) forKey:(__bridge NSString *)kCGImageDestinationLossyCompressionQuality];
    
    // Create an image destination.
    CGImageDestinationRef imageDestination = CGImageDestinationCreateWithURL((__bridge CFURLRef)outputURL, kUTTypeJPEG , 1, NULL);
    if (imageDestination == NULL ) {
    
        // Handle failure.
        NSLog(@"Error -> failed to create image destination.");
        return;
    }
    
    // Add your image to the destination.
    CGImageDestinationAddImage(imageDestination, capturedImage.CGImage, (__bridge CFDictionaryRef)mutableMetadata);
    
    // Finalize the destination.
    if (CGImageDestinationFinalize(imageDestination) == NO) {
    
        // Handle failure.
        NSLog(@"Error -> failed to finalize the image.");
    }
    
    CFRelease(imageDestination);
    

    From my tests the execution time was about the same or faster than doing:

    NSData *data = UIImageJPEGRepresentation(capturedImage, 1.0);
    [data writeToURL:outputURL atomically:YES];
    

    ...and you get to keep the EXIF data!

    You could also dispatch it to a background queue if you want to make sure your UI stays responsive:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
    
        // Do the image saving here...
    });
    

    Important Note: You will need to add the ImageIO.framework and the MobileCoreServices.framework to your target's build phases and import them in the class where you do the image saving:

    #import <ImageIO/ImageIO.h>
    #import <MobileCoreServices/MobileCoreServices.h>
    

    Other notes It is my understanding that in this case we're not creating a generation loss when saving the image as proposed in the example. We are using UIImage's CGImage property and as the documentation states:

    The underlying Quartz image data