2016-05-26 13 views
4

私は、画像へのフィルタのリアルタイム適用が必要なアプリケーションを作成しています。 UIImageCIImageに変換してフィルタを適用するのは非常に高速ですが、作成したCIImageCGImageRefに変換するには時間がかかりすぎて、画像を表示するには時間がかかります(1/5秒、リアルタイムである必要があります)。CIImageを描画するのが遅すぎる

画像は、約2500の問題現在

の一部で、最も可能性が高い大2500個のピクセル、によって、私は私がEAGLContext.drawImage()

context.drawImage(image, inRect: destinationRect, fromRect: image.extent) 
を使用してに見てきた

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) 

を使用していています

しかし、これがどのように行われたか、それがもっと速くなるかについての確かな文書は見つかりません。

CIImageを画面に表示する方法はありますか(UIImageView、または直接CALayer)?私は画質をあまり低下させないようにしたいと思います。これはユーザーには目立つかもしれないからです。

答えて

3

私はcontext.drawImage(image, inRect: destinationRect, fromRect: image.extent)を使用して終了:

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

: 'VARの画像:CIImage? { did { self.setNeedsDisplay() } } } setRenderImageメソッド全体が廃止されました。 –

+0

これをストーリーボードから操作するには、init?(coder :)でコンテキストを設定する必要があります(init(フレーム)で行ったように)。 –

0

これはかなり大きなイメージなので、間違いなくその一部です。単一の画像フィルタを行うには、GPUImageをご覧ください。 CoreImageを使用してスキップすることができます。

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

あなたはGlkViewを使用して、context.drawImage()と言ったようにレンダリングすることができます:画像をレンダリング

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

あなたの処理の後方法。ここで私はまた助けCIImageにそれを変換する前に、単に

画面サイズにUIImageのサイズを変更する
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) 

を、それを使用するために、次に

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 
      ) 
     } 
    } 
} 

を作成した画像ビュークラスです。高品質の画像の場合は、物事をスピードアップします。実際に保存するときはフルサイズのイメージを使用してください。

それはそれです!そして、画像を更新するために、ビュー

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

には、金属を考慮し、MTKViewで表示する価値があるかもしれません。

MTLCreateSystemDefaultDevice()で作成できるメタルデバイスが必要です。これは、コマンドキューとコアイメージコンテキストを作成するために使用されます。これらのオブジェクトは永続的とインスタンス化するのは非常に高価であるため、理想的に一度に作成する必要があります両方:

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を持っています。

希望に役立ちます!

サイモン

あなたがイメージプロパティにdidSetを使用することができ
+0

私はこれが最も実績のある解決策であることを発見しました。私のコードではMTKViewをサブクラス化していないので、 'commit()'を呼び出した直後に 'MTKView'で' draw() 'を呼び出さなければならず、ビューの' device'を作成しなければなりませんその 'framebufferOnly'を' false'にします。 – Ash

+0

また、 'CIRenderDestination'と' CIContext'の 'startTask'メソッドを見る価値があります。 –

+0

私は現在、アドレス指定に取り組んでいるこの機能に悩まされています。それは、ビューのサイズが変更された場合、バッキングドロワブルのテクスチャサイズは常に新しいサイズの1ステップと思われます。強制的に強制的に更新します。現状では、ストレッチを使用してこの問題を否定する独自のビューがありますが、高品質の出力が必要なグラフィカルアプリケーションを作成しています。画像を含むビューのサイズで画像をレンダリングします。 – Ash