3

I am Importing Photos from the iPhone Album to the documents folder of my application. This is my code.

for (int j=0; j<[assetArray count]; j++) {

    ALAsset *assest = [assetArray objectAtIndex:j];
    CGImageRef imageRef = assest.defaultRepresentation.fullResolutionImage;
    UIImage *image = [UIImage imageWithCGImage:imageRef];
    NSData *imageData = UIImageJPEGRepresentation(image);
    [imageData writeToFile:documentsPath atomically:YES];
}

Its working fine but when I try to Import higher resolution Images it takes more time. What is the correct way to Import with least time? BTW: It takes more time when I convert the image into NSData.

Aravindhan
  • 15,608
  • 10
  • 56
  • 71
  • i had a same problem but i solved it by saving asset urls in database and when i need to store images in documents directory i get asset one by one and store images in documents directory. One reason can be that you are saving assets in the array save asset url and one more thing run this for loop in autorelease pool that will reduce memory issues. – Leena Apr 02 '12 at 11:28

2 Answers2

23

It's dangerous to use the fullResolutionImage for this task for several reasons. Some remarks:

  1. For large images memory problems could occur. when using the fullResolutionImage method. Please note, that the Photo-Library (on the iPad at least) can also contain RAW-images.
  2. The performance is suboptimal as internally from the imagefile ImageIO creates first a CGImageRef that is then converted to a JPEG. This takes time.
  3. The AssetLibrary can also contain videos. In such a case fullResolutionImage only returns a previewImage of the video, but not the actual video.
  4. It's no problem to store the actual Asset-Objects as these are small in memory.

A far better approach to write out the images to the documents directory, would be to use the getBytes method of ALAssetsRepresentation. This should be way faster and more efficient memory wise. It also gives you the original image file (incl. metadata) and also works for videos.

Your example code rewritten then would look like that:

//reading out the orginal images
    for (int j=0; j<[assetArray count]; j++) {

    ALAssetRepresentation *representation = [[assetArray objectAtIndex:j] defaultRepresentation];
    NSString* filename = [documentPath stringByAppendingPathComponent:[representation filename]];

    [[NSFileManager defaultManager] createFileAtPath:filename contents:nil attributes:nil];
    NSOutputStream *outPutStream = [NSOutputStream outputStreamToFileAtPath:filename append:YES];
    [outPutStream open];

    long long offset = 0;
    long long bytesRead = 0;

    NSError *error;
    uint8_t * buffer = malloc(131072);
    while (offset<[representation size] && [outPutStream hasSpaceAvailable]) {
        bytesRead = [representation getBytes:buffer fromOffset:offset length:131072 error:&error];
        [outPutStream write:buffer maxLength:bytesRead];
        offset = offset+bytesRead;
    }
    [outPutStream close];
    free(buffer);
}


//reading out the fullScreenImages and thumbnails
for (int j=0; j<[assetArray count]; j++) 
{
    @autoreleasepool
    {

        ALAsset *asset = [assetArray objectAtIndex:j];

         NSString *orgFilename = [representation filename];
         NSString *filenameFullScreen = [NSString stringWithFormat:@"%@_fullscreen.png",[orgFilename stringByDeletingPathExtension]]
         NSString* pathFullScreen = [documentPath stringByAppendingPathComponent:filenameFullScreen];

         CGImageRef imageRefFullScreen = [[asset defaultRepresentation] fullScreenImage];
         UIImage *imageFullScreen = [UIImage imageWithCGImage:imageRefFullScreen];
         NSData *imageDataFullScreen = UIImagePNGRepresentation(imageFullScreen);
         [imageDataFullScreen writeToFile:pathFullScreen atomically:YES];

         NSString *filenameThumb = [NSString stringWithFormat:@"%@_thumb.png",[orgFilename stringByDeletingPathExtension]]
         NSString* pathThumb = [documentPath stringByAppendingPathComponent:filenameThumb];

         CGImageRef imageRefThumb = [asset thumbnail];
         UIImage *imageThumb = [UIImage imageWithCGImage:imageRefThumb];
         NSData *imageDataThumb = UIImagePNGRepresentation(imageThumb);
         [imageDataThumb writeToFile:pathThumb atomically:YES];

    }
}
holtmann
  • 6,043
  • 32
  • 44
  • thank you its working very nicely.. But how can I copy the thumbnail image and fullscreen image to the documents folder ? or is there is anyother way for getting these images from the ALAssetsRepresentation in the documents folder ? – Aravindhan Apr 09 '12 at 06:39
  • 1
    I amended the source code to include reading out the fullScreenImage and thumbnail. For these 2 images you can use the approach you had before-as they are limited in size and consume only a predictable amount of memory in contrast to the fullResolutionImage. Depending on what you want to do you might consider saving the fullScreenImage as JPEG instead of PNG, but this is only a minor code change. – holtmann Apr 10 '12 at 09:48
  • Thank you. Our app kept crashing, but now it doesn't. – Dan Abramov Apr 15 '13 at 15:51
  • what is documentPath here – Nilesh Kumar Apr 24 '14 at 09:39
5

I was using ELCImagePicker and was facing same problem while importing multiple photos at a time from photos library using asselts. We can not reduce time taken to import but crash issue will be resolved.

for (int j=0; j<[assetArray count]; j++) 
{
    @autoreleasepool // This is compiler level feature so will only work on xcode 4.1 or above
    {
         ALAsset *assest = [assetArray objectAtIndex:j];
         CGImageRef imageRef = assest.defaultRepresentation.fullResolutionImage;
         UIImage *image = [UIImage imageWithCGImage:imageRef];
         NSData *imageData = UIImagePNGRepresentation(image);
         [imageData writeToFile:documentsPath atomically:YES];
    }
}

If possible try to store only AssetURL in assetArray instead of whole ALAsset object and create ALAsset at a time from url so may be it helps to reduce memory consumption. In such case you will need to use blocks as

ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
{
    CGImageRef iref = [[myasset defaultRepresentation] fullResolutionImage];
    if (iref) //You have image so use it 
    {
    }
};

ALAssetsLibraryAccessFailureBlock failureblock  = ^(NSError *myerror)
{
    NSLog(@"Can't get image - %@",[myerror localizedDescription]);
};

ALAssetsLibrary* assetslibrary = [[[ALAssetsLibrary alloc] init] autorelease];
[assetslibrary assetForURL:imageURL resultBlock:resultblock failureBlock:failureblock];
Janak Nirmal
  • 22,706
  • 18
  • 63
  • 99
  • We can able to reduce the time. I saw this app http://itunes.apple.com/us/app/lock-photo-video-note-audio/id448033053?mt=8 its importing very fastly. – Aravindhan Apr 02 '12 at 09:09
  • What time it takes in your code to import your photos in device not simulator ? Can you please add more detail like number of photos resolution ? – Janak Nirmal Apr 02 '12 at 09:26
  • autoreleasepool fixed the crash... thanks.. but I don't know how to reduce the time.... – Aravindhan Apr 03 '12 at 06:36
  • I have tested for import time taken with above code it took me around 2 to 2.3 seconds for importing 1.7 MB Size file. and max 4 seconds for 2 files with different size of 1.7 MB and 0.8 MB – Janak Nirmal Apr 03 '12 at 07:05