Учитывая CIImage, какой самый быстрый способ записать данные изображения на диск?

Я работаю с PhotoKit и внедрил фильтры, которые пользователи могут применять к фотографиям в своей библиотеке фотографий. В настоящее время я получаю изображение, применяю фильтр, возвращаю отредактированную версию как CIImage, затем я конвертирую CIImage в NSData с помощью UIImageJPEGRepresentation, чтобы записать это на диск. Хотя это прекрасно работает, когда пользователи пытаются редактировать действительно большие (например, 30 МБ) фотографии, это может занять более 30 секунд, при этом 98% времени тратится на UIImageJPEGRepresentation (статистика, полученная из инструментов).

Я ищу более эффективный способ сохранить это отредактированное фото на диск без ущерба для качества, если это возможно.

Я понимаю, что 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)

person Jordan H    schedule 12.01.2015    source источник
comment
Брэд заметил замедление, похоже, github.com/BradLarson/GPUImage   -  person Stephen J    schedule 27.08.2015


Ответы (2)


Я бы предложил передать CGImage напрямую в ImageIO, используя CGImageDestination. Вы можете передать словарь в 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
}
person David Hayward    schedule 01.02.2015
comment
Это кажется немного быстрее, по крайней мере, на 8 секунд. 22 секунды - это все еще много, но я полагаю, что этого следует ожидать, когда изображение большое. - person Jordan H; 01.02.2015
comment
Вы пробовали изменить приоритет потока? Полезность одна из самых низких. Вы можете попробовать использовать режимы .UserInitiated или .UserInteractive QoS в NSOperationQueue, если вашей целью является своевременное выполнение. - person johnnyclem; 19.11.2015

Попробуйте использовать метод 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 для 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
            }   
        }
    }
person Joe Pagliaro    schedule 28.12.2018