2016-05-26 85 views
4

我正在創建一個應用程序,需要在圖像上實時應用濾鏡。將UIImage轉換爲CIImage,並且應用這些過濾器都是非常快的操作,但是將創建的CIImage轉換回CGImageRef並顯示圖像需要很長的時間(1/5秒,實際上如果編輯它實際上很多需要實時)。繪製CIImage太慢

的圖像是2500由2500像素的大,這是問題的最有可能的一部分

目前,我使用

let image: CIImage //CIImage with applied filters 
let eagl = EAGLContext(API: EAGLRenderingAPI.OpenGLES2) 
let context = CIContext(EAGLContext: eagl, options: [kCIContextWorkingColorSpace : NSNull()]) 

//this line takes too long for real-time processing 
let cg: CGImage = context.createCGImage(image, fromRect: image.extent) 

我已經研究過使用EAGLContext.drawImage()

context.drawImage(image, inRect: destinationRect, fromRect: image.extent) 

但是,我無法找到任何有關如何完成這項工作的可靠文件,或者它是否會更快更好

有沒有更快的方法在屏幕上顯示CIImage(在UIImageView中,或直接在CALayer上)?我想避免太多降低圖像質量,因爲這對用戶來說可能是顯而易見的。

回答

3

我最終使用的context.drawImage(image, inRect: destinationRect, fromRect: image.extent)方法。下面是我創建

import Foundation 
//GLKit must be linked and imported 
import GLKit 

class CIImageView: GLKView{ 
    var image: CIImage? 
    var ciContext: CIContext? 

    //initialize with the frame, and CIImage to be displayed 
    //(or nil, if the image will be set using .setRenderImage) 
    init(frame: CGRect, image: CIImage?){ 
     super.init(frame: frame, context: EAGLContext(API: EAGLRenderingAPI.OpenGLES2)) 

     self.image = image 
     //Set the current context to the EAGLContext created in the super.init call 
     EAGLContext.setCurrentContext(self.context) 
     //create a CIContext from the EAGLContext 
     self.ciContext = CIContext(EAGLContext: self.context) 
    } 

    //for usage in Storyboards 
    required init?(coder aDecoder: NSCoder){ 
     super.init(coder: aDecoder) 

     self.context = EAGLContext(API: EAGLRenderingAPI.OpenGLES2) 
     EAGLContext.setCurrentContext(self.context) 
     self.ciContext = CIContext(EAGLContext: self.context) 
    } 

    //set the current image to image 
    func setRenderImage(image: CIImage){ 
     self.image = image 

     //tell the processor that the view needs to be redrawn using drawRect() 
     self.setNeedsDisplay() 
    } 

    //called automatically when the view is drawn 
    override func drawRect(rect: CGRect){ 
     //unwrap the current CIImage 
     if let image = self.image{ 
      //multiply the frame by the screen's scale (ratio of points : pixels), 
      //because the following .drawImage() call uses pixels, not points 
      let scale = UIScreen.mainScreen().scale 
      let newFrame = CGRectMake(rect.minX, rect.minY, rect.width * scale, rect.height * scale) 

      //draw the image 
      self.ciContext?.drawImage(
       image, 
       inRect: newFrame, 
       fromRect: image.extent 
      ) 
     } 
    } 
} 

然後圖像視圖類,將其轉換爲一個CIImage也有助於之前使用它,只需

let myFrame: CGRect //frame in self.view where the image should be displayed 
let myImage: CIImage //CIImage with applied filters 

let imageView: CIImageView = CIImageView(frame: myFrame, image: myImage) 
self.view.addSubview(imageView) 

調整大小UIImage的屏幕大小。在高質量圖像的情況下,它可以加快速度。請確保在實際保存時使用全尺寸圖像。

那就是它!然後,在視圖

imageView.setRenderImage(newCIImage) 
//note that imageView.image = newCIImage won't work because 
//the view won't be redrawn 
+0

你可以在圖像屬性中使用didSet: 'VAR圖像:CIImage? { didSet {self.setNeedsDisplay() } }' 然後整個setRenderImage方法變得過時。 –

+0

您必須在init?(coder :)中設置上下文(就像您在init(frame)中所做的那樣),以便從故事板開始工作。 –

0

這是一個非常大的圖像,所以這絕對是它的一部分。我建議看看GPUImage做單圖像過濾器。您可以完全跳過使用CoreImage。

let inputImage:UIImage = //... some image 
let stillImageSource = GPUImagePicture(image: inputImage) 
let filter = GPUImageSepiaFilter() 
stillImageSource.addTarget(filter) 
filter.useNextFrameForImageCapture() 
stillImageSource.processImage() 
2

您可以使用GlkView並呈現爲您context.drawImage()說:渲染圖像

let glView = GLKView(frame: superview.bounds, context: EAGLContext(API: .OpenGLES2)) 
let context = CIContext(EAGLContext: glView.context) 

您處理後:

glView.bindDrawable() 
context.drawImage(image, inRect: destinationRect, fromRect: image.extent) 
glView.display() 
4

這可能是值得考慮的金屬,並用MTKView顯示更新的圖像。

您將需要一個金屬設備,可以使用MTLCreateSystemDefaultDevice()創建。這用於創建命令隊列和Core Image上下文。這兩個對象是持久的,挺貴的實例,因此,最好應創建一次:

lazy var commandQueue: MTLCommandQueue = 
{ 
    return self.device!.newCommandQueue() 
}() 

lazy var ciContext: CIContext = 
{ 
    return CIContext(MTLDevice: self.device!) 
}() 

您還需要一個色彩空間:

let colorSpace = CGColorSpaceCreateDeviceRGB()! 

當談到渲染CIImage,你「會需要創建一個短暫的命令緩衝區:

let commandBuffer = commandQueue.commandBuffer() 

你會想使您的CIImage(姑且稱之爲image)至MTKViewcurrentDrawable?.texture。如果綁定到targetTexture,渲染語法是:

ciContext.render(image, 
     toMTLTexture: targetTexture, 
     commandBuffer: commandBuffer, 
     bounds: image.extent, 
     colorSpace: colorSpace) 

    commandBuffer.presentDrawable(currentDrawable!) 

    commandBuffer.commit() 

我有一個工作版本here

希望幫助!

西蒙

+0

我發現這是最高性能的解決方案。請注意,在我的代碼中,因爲我沒有繼承MTKView,所以我必須在調用commit()後立即調用'MTKView'上的'draw()',並且必須將視圖的'device'設置爲創建的和它的'framebufferOnly'到'false'。 – Ash

+0

「CIContext」上的'CIRenderDestination'和'startTask'方法也值得一看。 –

+0

我注意到一個煩惱這個功能,我目前正在解決,這就是如果視圖調整大小,支持繪製的紋理尺寸似乎總是落後於新的大小一步,我似乎無法找到一種方法強制它正確更新。就目前而言,您自己的觀點使用拉伸抵消了這個問題,但是我創建了一個需要高質量輸出的圖形應用程序,我希望圖像能夠以包含視圖的大小完全呈現。 – Ash