Saving / retrieving JPEG from user gallery without recompression

I am trying to find a way to read and write JPEG images to the user's gallery (camera view) without recompressing their iOS. UIImage seems to be the bottleneck here. The only way to save the user gallery I found is UIImageWriteToSavedPhotosAlbum (). Is there any way around this?

Now my procedure looks like

-Ask UIImagePickerController for photography. And when he did FinishPickingMediaWithInfo, do:

NSData *imgdata = [NSData dataWithData:UIImageJPEGRepresentation([info objectForKey:@"UIImagePickerControllerOriginalImage"], 1)]; [imgdata writeToFile:filePath atomically:NO]; 

-Jpeg lossless process on disk.

-After saving:

 UIImageWriteToSavedPhotosAlbum([UIImage imageWithContentsOfFile:[self getImagePath]], self, @selector(image:didFinishSavingWithError:contextInfo:), nil); 

Here's a tiny animation of how quality deteriorates after 3 passes:

JPEG quality degradation

Obviously, every time I do this, it gets worse, but I could not automate the image selection part to fully test it for 50/100/1000 cycles.

+6
source share
1 answer

UIImage decodes image data, so it can be edited and displayed, therefore

 UIImageWriteToSavedPhotosAlbum([UIImage imageWithContentsOfFile:[NSData dataWithContentsOfFile:[self getImagePath]]], self, @selector(image:didFinishSavingWithError:contextInfo:), nil); 

First decodes the image, and then encodes it using the UIImageWriteToSavedPhotosAlbum method.

Instead, you should use ALAssetsLibrary / writeImageDataToSavedPhotosAlbum: metadata: completionBlock:, something like this:

 ALAssetsLibrary *assetLib = [[[ALAssetsLibrary alloc] init] autorelease]; [assetLib writeImageDataToSavedPhotosAlbum:[self getImagePath] metadata:nil completionBlock:nil]; 

You can also transfer metadata and a call termination unit.

EDIT:

To get an image:

[info objectForKey:@"UIImagePickerControllerOriginalImage"] contains a decoded UIImage selected from UIImagePickerController . You should use instead

 NSURL *assetURL = [info objectForKey:UIImagePickerControllerReferenceURL]; 

Using assetURL , you can get an ALAsset for it using the ALAssetsLibrary / assetForURL: resultBlock: failureBlock: method :

 ALAssetsLibrary *assetLib = [[[ALAssetsLibrary alloc] init] autorelease]; [assetLib assetForURL:assetURL resultBlock:resultBlock failureBlock:failureBlock]; 

Now you can get the unchanged NSData of this image:

 ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *asset){ ALAssetRepresentation *assetRep = [asset defaultRepresentation]; long long imageDataSize = [assetRepresentation size]; uint8_t* imageDataBytes = malloc(imageDataSize); [assetRepresentation getBytes:imageDataBytes fromOffset:0 length:imageDataSize error:nil]; NSData *imageData = [NSData dataWithBytesNoCopy:imageDataBytes length:imageDataSize freeWhenDone:YES]; // you could for instance read data in smaller buffers and append them to your file instead of reading it all at once // save it [imgdata writeToFile:filePath atomically:NO]; }; ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror){ NSLog(@"Cannot get image - %@",[myerror localizedDescription]); // }; 

I may have made some mistakes in the code, but the steps are listed above. If something doesn’t work correctly or if you want to make it a little more efficient, there are many examples of things like reading NSData from ALAsset on stackoverflow or other sites.

+10
source