不使用UIImagePngRepresentation或UIImageJpegRepresentation,将UIImage转换为NSData

23
我需要将UIImage转换为NSData,但不能使用UIImagePngRepresentationUIImageJpegRepresentation方法。对于来自photolib的图像,可以使用assetlib方法(参见Using ALAssetsLibrary and ALAsset take out Image as NSData),但是对于捕获的图像,没有asset URL,因此在这种情况下,我需要直接将UIImage转换为带有exif数据的字节,请问该如何实现?请帮忙。

为什么?多平台的原因吗?这是我能想到的唯一的原因...如果是这样的话,那就试试使用LodePNG http://lodev.org/lodepng/。 - borrrden
2
通过使用UIImage的PNG和JPEG表示,图像的大小会有所不同,即便在1.0压缩质量下,JPEG的图像质量也会降低。我需要一种直接将UIImage转换为字节的方法。 - H Bastan
当然,图像的大小会有所不同...这是压缩的本质。否则,所有图片都将具有巨大的文件大小(wxhx4字节)。此外,您的大小将取决于文件的尺寸。我认为您需要比目前更好地描述您的问题。 - borrrden
存储在photolib中的文件已经是压缩格式了,现在当我们使用uipngrepresentation时,1.需要解压缩文件并重新压缩它(如果我可以直接将存储在photolib中的压缩图像转换为数据,则可以避免此过程)2.由于使用png表示法,EXIF数据丢失,导致图像显示为90度旋转。 - H Bastan
1
你在谈论UIImage。如果你有一个UIImage,那么你已经解压缩了数据。故事结束。但是看起来你不想使用UIImage。你只是想更改现有PNG的EXIF数据? - borrrden
显示剩余2条评论
3个回答

22

我有一个类似的问题,但出于安全原因,我不想像Wes建议的那样将数据写入默认文件系统。我还希望能够向我的图像添加元数据。我的解决方法如下:

- (NSData *)dataFromImage:(UIImage *)image metadata:(NSDictionary *)metadata mimetype:(NSString *)mimetype
{
    NSMutableData *imageData = [NSMutableData data];
    CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef)mimetype, NULL);
    CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, uti, 1, NULL);

    if (imageDestination == NULL)
    {
        NSLog(@"Failed to create image destination");
        imageData = nil;
    }
    else
    {
        CGImageDestinationAddImage(imageDestination, image.CGImage, (__bridge CFDictionaryRef)metadata);

        if (CGImageDestinationFinalize(imageDestination) == NO)
        {
            NSLog(@"Failed to finalise");
            imageData = nil;
        }
        CFRelease(imageDestination);
    }

    CFRelease(uti);

    return imageData;
}

要使用它,您需要执行以下操作:

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
    NSDictionary *metadata = [info objectForKey:UIImagePickerControllerMediaMetadata];
    NSString *mimeType = @"image/jpeg"; // ideally this would be dynamically set. Not defaulted to a jpeg!

    // you could add to the metadata dictionary (after converting it to a mutable copy) if you needed to.

    // convert to NSData
    NSData *imageData = [self dataFromImage:image metadata:metadata mimetype:mimeType];

    // ...
}

执行时间比使用UIImageJpegRepresentation快了约5%,但它使用的内存大致相同。但至少它只为非常短的时间保留该内存。 - hasan

18

H Bastan,

根据您的评论:

...我想通过UIImagepicker委托方法将由设备相机捕获的图像保存到文档目录中...

看起来您的真正目标是将带有EXIF数据的图像保存到文档目录中,而不仅仅是获取带有EXIF数据的NSData对象的UIImage。在这种情况下,您可以在实现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);

从我的测试来看,执行时间与执行以下操作的时间相同或更快:

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

...而且您可以保留EXIF数据!

如果您想确保界面响应,还可以将其调度到后台队列中:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{

    // Do the image saving here...
});

重要提示:您需要将ImageIO.frameworkMobileCoreServices.framework添加到目标的构建阶段,并在执行图片保存的类中导入它们。

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

其他注意事项 据我所知,在此情况下,按照示例保存图像时不会导致世代损失。我们使用UIImage的CGImage属性,并且正如文档所述:

底层Quartz图像数据


谢谢,这真的很有帮助。如果我不设置压缩质量,它会以原始图像的压缩质量保存,这对我来说很好,而且图像数据大小也与photolib相同,因此这是完美的方法,谢谢。 - H Bastan
抱歉,这些评论是针对下面的解决方案的,我没有尝试过你的。 - hasan
我已经卡了相当长的一段时间了。但是由于您的解决方案,我现在可以继续前进了。谢谢。 - Asbar

0
你可以尝试这样做。不确定是否是最佳方法,但值得一试。如果图像URL可用,可以尝试使用initWithContentsOfUrl
NSData *data = [[NSData alloc]initWithContentsOfFile:@"/Users/test/isajf/isajf/test.jpg"];

嗨,谢谢,但我正在使用uiimagepicker来捕获图像,在这里它只返回uiimage而不是图像的url,因此上述方法将无用。 - H Bastan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接