给定一个 CIImage,将图像数据写入磁盘的最快方法是什么?
Given a CIImage, what is the fastest way to write image data to disk?
我正在使用 PhotoKit 并实现了过滤器,用户可以将其应用于照片库中的照片。我目前正在获取图像,应用过滤器,将编辑后的版本返回为 CIImage
,然后我使用 UIImageJPEGRepresentation
将 CIImage
转换为 NSData
,这样我就可以把它写出来到磁盘。虽然效果很好,但当用户尝试编辑非常大(如 30 MB)的照片时,可能需要 30 秒以上才能完成,其中 98% 的时间花在 UIImageJPEGRepresentation
上(从 Instruments 获得的统计数据)。
我正在寻找一种更有效的方法来将这张编辑后的照片保存到磁盘,而不会影响质量(如果可能的话)。
我知道 UIImagePNGRepresentation
可能会提高质量,但这比 JPEG 表示法还要慢。
这就是我目前在默认优先级线程 (qos_class_utility
) 上所做的事情:
func jpegRepresentationOfImage(image: CIImage) -> NSData {
let eaglContext = EAGLContext(API: .OpenGLES2)
let ciContext = CIContext(EAGLContext: eaglContext)
let outputImageRef = ciContext.createCGImage(image, fromRect: image.extent())
let uiImage = UIImage(CGImage: outputImageRef, scale: 1.0, orientation: UIImageOrientation.Up)
return UIImageJPEGRepresentation(uiImage, 0.9) //this takes upwards of 20-30 seconds with large photos!
}
//writing out to disk:
var error: NSError?
let success = jpegData.writeToURL(contentEditingOutput.renderedContentURL, options: NSDataWritingOptions.AtomicWrite, error: &error)
我建议使用 CGImageDestination 将 CGImage 直接传递给 ImageIO。您可以将字典传递给 CGImageDestinationAddImage 以指示压缩质量、图像方向等。
CFDataRef save_cgimage_to_jpeg (CGImageRef image)
{
CFMutableDataRef cfdata = CFDataCreateMutable(nil,0);
CGImageDestinationRef dest = CGImageDestinationCreateWithData(data, CFSTR("public.jpeg"), 1, NULL);
CGImageDestinationAddImage(dest, image, NULL);
if(!CGImageDestinationFinalize(dest))
; // error
CFRelease(dest);
return cfdata
}
尝试使用 CIContext 方法 writeJPEGRepresentation(可用 iOS 10)如下,以避开 UIImage 并使写入速度更快。
extension CIImage {
@objc func saveJPEG(_ name:String, inDirectoryURL:URL? = nil, quality:CGFloat = 1.0) -> String? {
var destinationURL = inDirectoryURL
if destinationURL == nil {
destinationURL = try? FileManager.default.url(for:.documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
}
if var destinationURL = destinationURL {
destinationURL = destinationURL.appendingPathComponent(name)
if let colorSpace = CGColorSpace(name: CGColorSpace.sRGB) {
do {
let context = CIContext()
try context.writeJPEGRepresentation(of: self, to: destinationURL, colorSpace: colorSpace, options: [kCGImageDestinationLossyCompressionQuality as CIImageRepresentationOption : quality])
return destinationURL.path
} catch {
return nil
}
}
}
return nil
}
}
@objc 关键字使您能够调用 Objective-C 中的方法:
NSString* path = [image saveJPEG:@"image.jpg" inDirectoryURL:url quality:1.0];
对于 PNG 有一个类似的方法 writePNGRepresentation for iOS 11:
if #available(iOS 11.0, *) {
if let colorSpace = CGColorSpace(name: CGColorSpace.sRGB) {
do {
let format = CIFormat.RGBA8
try context.writePNGRepresentation(of: self, to: destinationURL, format: format, colorSpace: colorSpace)
return destinationURL.path
} catch {
return nil
}
}
}
我正在使用 PhotoKit 并实现了过滤器,用户可以将其应用于照片库中的照片。我目前正在获取图像,应用过滤器,将编辑后的版本返回为 CIImage
,然后我使用 UIImageJPEGRepresentation
将 CIImage
转换为 NSData
,这样我就可以把它写出来到磁盘。虽然效果很好,但当用户尝试编辑非常大(如 30 MB)的照片时,可能需要 30 秒以上才能完成,其中 98% 的时间花在 UIImageJPEGRepresentation
上(从 Instruments 获得的统计数据)。
我正在寻找一种更有效的方法来将这张编辑后的照片保存到磁盘,而不会影响质量(如果可能的话)。
我知道 UIImagePNGRepresentation
可能会提高质量,但这比 JPEG 表示法还要慢。
这就是我目前在默认优先级线程 (qos_class_utility
) 上所做的事情:
func jpegRepresentationOfImage(image: CIImage) -> NSData {
let eaglContext = EAGLContext(API: .OpenGLES2)
let ciContext = CIContext(EAGLContext: eaglContext)
let outputImageRef = ciContext.createCGImage(image, fromRect: image.extent())
let uiImage = UIImage(CGImage: outputImageRef, scale: 1.0, orientation: UIImageOrientation.Up)
return UIImageJPEGRepresentation(uiImage, 0.9) //this takes upwards of 20-30 seconds with large photos!
}
//writing out to disk:
var error: NSError?
let success = jpegData.writeToURL(contentEditingOutput.renderedContentURL, options: NSDataWritingOptions.AtomicWrite, error: &error)
我建议使用 CGImageDestination 将 CGImage 直接传递给 ImageIO。您可以将字典传递给 CGImageDestinationAddImage 以指示压缩质量、图像方向等。
CFDataRef save_cgimage_to_jpeg (CGImageRef image)
{
CFMutableDataRef cfdata = CFDataCreateMutable(nil,0);
CGImageDestinationRef dest = CGImageDestinationCreateWithData(data, CFSTR("public.jpeg"), 1, NULL);
CGImageDestinationAddImage(dest, image, NULL);
if(!CGImageDestinationFinalize(dest))
; // error
CFRelease(dest);
return cfdata
}
尝试使用 CIContext 方法 writeJPEGRepresentation(可用 iOS 10)如下,以避开 UIImage 并使写入速度更快。
extension CIImage {
@objc func saveJPEG(_ name:String, inDirectoryURL:URL? = nil, quality:CGFloat = 1.0) -> String? {
var destinationURL = inDirectoryURL
if destinationURL == nil {
destinationURL = try? FileManager.default.url(for:.documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
}
if var destinationURL = destinationURL {
destinationURL = destinationURL.appendingPathComponent(name)
if let colorSpace = CGColorSpace(name: CGColorSpace.sRGB) {
do {
let context = CIContext()
try context.writeJPEGRepresentation(of: self, to: destinationURL, colorSpace: colorSpace, options: [kCGImageDestinationLossyCompressionQuality as CIImageRepresentationOption : quality])
return destinationURL.path
} catch {
return nil
}
}
}
return nil
}
}
@objc 关键字使您能够调用 Objective-C 中的方法:
NSString* path = [image saveJPEG:@"image.jpg" inDirectoryURL:url quality:1.0];
对于 PNG 有一个类似的方法 writePNGRepresentation for iOS 11:
if #available(iOS 11.0, *) {
if let colorSpace = CGColorSpace(name: CGColorSpace.sRGB) {
do {
let format = CIFormat.RGBA8
try context.writePNGRepresentation(of: self, to: destinationURL, format: format, colorSpace: colorSpace)
return destinationURL.path
} catch {
return nil
}
}
}