2012-10-31 8 views
6

Próbuję znaleźć sposób na odczyt i zapis obrazów JPEG do galerii użytkownika (rolka z aparatu) bez ponownego kompresowania przez iOS. UIImage wydaje się być wąskim gardłem. Jedyną metodą zapisania do galerii użytkownika, którą znalazłem, jest UIImageWriteToSavedPhotosAlbum(). Czy istnieje sposób obejścia tego?Zapisywanie/pobieranie JPEG z galerii użytkownika bez rekompresji

Teraz moja rutyna wygląda to

-Ask UIImagePickerController o zdjęcie. A kiedy didFinishPickingMediaWithInfo, zrobić:

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

-Process bezstratnie JPEG na dysku.

-Potem zapisać go z powrotem:

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

Oto maleńka animacja co degradacja jakości wygląda po 3 podań:

JPEG quality degradation

To oczywiście pogarsza się za każdym razem to zrobić , ale nie mogłem zautomatyzować części do pobierania obrazów, aby w pełni przetestować ją pod kątem cykli 50/100/1000.

Odpowiedz

10

UIImage dekoduje dane obrazu, dzięki czemu można je edytować i wyświetlane, więc

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

najpierw dekodować obraz, a nie kodować go metodą UIImageWriteToSavedPhotosAlbum.

Zamiast tego należy użyć ALAssetsLibrary/writeImageDataToSavedPhotosAlbum:metadata:completionBlock:, coś jak to:

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

Można również przekazać metadane i blok zakończenia do rozmowy.

EDIT:

Dla uzyskania obrazu:

[info objectForKey:@"UIImagePickerControllerOriginalImage"] zawiera dekodowane UIImage wybrany spośród UIImagePickerController. Należy zamiast tego użyć

NSURL *assetURL = [info objectForKey:UIImagePickerControllerReferenceURL]; 

Używanie assetURL można teraz uzyskać ALAsset dla niego za pomocą metody ALAssetsLibrary/assetForURL:resultBlock:failureBlock::

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

Teraz można dostać niezmienioną NSData tego obrazka:

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]); 
    // 
}; 

Możliwe, że popełniłem błędy w kodzie, ale kroki są wymienione powyżej. W przypadku czegoś, co nie działa poprawnie, lub jeśli chcesz, aby było to trochę bardziej wydajne, istnieje wiele przykładów robienia rzeczy takich jak czytanie NSData z ALAsset na stackoverflow lub innych stronach.

+0

Tak, dziękuję, już dowiedziałem się o tej sztuczce z AssetsLibrary. Mam nadzieję, że twoja odpowiedź będzie pomocna dla innych. – Kai