2011-12-03 5 views
15

カメラ出力の1つのフレームにカスタムフィルタを適用して表示する方法を教えてください。私がこれまで試したどのようなカメラ出力にカスタムフィルタを適用する

  • その名は「グリーン」私は実際にちょうど何とか値を変更したいが含まれていますが

    mCamera.setPreviewCallback(new CameraGreenFilter()); 
    
    public class CameraGreenFilter implements PreviewCallback { 
    
        @Override 
        public void onPreviewFrame(byte[] data, Camera camera) { 
         final int len = data.length; 
         for(int i=0; i<len; ++i){ 
          data[i] *= 2; 
         } 
        } 
    } 
    
    は(この場合には、色が少し強化されるだろう) 。長い話が短い、それは動作しません。

  • 私はバイト配列 'data'がカメラ出力のコピーであることを知りました。しかし、私は本当のバッファが必要なので、これは本当に助けにはなりません。

  • これをopenGLで実装できると聞いたことがあります。それは非常に複雑に思えます。

もっと簡単な方法はありますか?そうでなければ、このopenGL-surfaceViewマッピングはどのように機能しますか?

答えて

36

OK、これを行うにはいくつかの方法があります。しかし、パフォーマンスには大きな問題があります。カメラからのバイト[]はYUV formatにあります。表示したい場合は、RGB形式の何らかの種類に変換する必要があります。この変換は非常に高価な動作であり、出力fpsを大幅に低下させます。

カメラのプレビューで実際に何をしたいかによって異なります。最良の解決策は、コールバックなしでカメラのプレビューを描画し、カメラのプレビューに影響を与えることです。これは、議論された賛同の物をする通常の方法です。

実際に出力を手動で表示する必要がある場合は、いくつかの方法があります。あなたの例は、いくつかの理由で機能しません。まず、イメージを表示していません。

mCamera.setPreviewCallback(new CameraGreenFilter()); 
mCamera.setPreviewDisplay(null); 

カメラがプレビューを表示しない場合よりも、手動で表示する必要があります。また、onPreviewFrameメソッドで高価な操作を行うことはできません。データの有効期間が限られているため、次のフレームで上書きされます。 1つのヒント、setPreviewCallbackWithBufferを使用すると、バッファが再利用され、各フレームに新しいメモリを割り当てる必要がないため、高速です。

は、だから、このような何かをする必要があります:

private byte[] cameraFrame; 
private byte[] buffer; 
@Override 
public void onPreviewFrame(byte[] data, Camera camera) { 
    cameraFrame = data; 
    addCallbackBuffer(data); //actually, addCallbackBuffer(buffer) has to be called once sowhere before you call mCamera.startPreview(); 
} 


private ByteOutputStream baos; 
private YuvImage yuvimage; 
private byte[] jdata; 
private Bitmap bmp; 
private Paint paint; 

@Override //from SurfaceView 
public void onDraw(Canvas canvas) { 
    baos = new ByteOutputStream(); 
    yuvimage=new YuvImage(cameraFrame, ImageFormat.NV21, prevX, prevY, null); 

    yuvimage.compressToJpeg(new Rect(0, 0, width, height), 80, baos); //width and height of the screen 
    jdata = baos.toByteArray(); 

    bmp = BitmapFactory.decodeByteArray(jdata, 0, jdata.length); 

    canvas.drawBitmap(bmp , 0, 0, paint); 
    invalidate(); //to call ondraw again 
} 

は、この作業を行うには、あなたはクラスのコンストラクタまたはどこかにsetWillNotDraw(false)を呼び出す必要があります。

色を変更する場合は、たとえば、paint.setColorFilter(filter)を適用できます。あなたが望むなら、私はそれのいくつかの例を投稿することができます。

これで動作しますが、パフォーマンスは低くなります(8fps未満)。BitmapFactory.decodeByteArrayが遅いためです。ネイティブコードとアンドロイドNDKを使ってYUVからRGBへデータを変換しようとすることはできますが、それはかなり複雑です。

他のオプションは、OpenGL ESを使用することです。カメラフレームをテクスチャとしてバインドするGLSurfaceViewが必要です(GLSurfaceViewではCamera.previewCallbackを実装しているため、onPreviewFrameは通常のサーフェスと同じように使用します)。しかし、同じ問題がある、あなたはYUVデータを変換する必要があります。YUVのバイト配列の前半は色のない輝度データのみであるため、プレビュー(グレースケール画像)からの輝度データだけを非常に高速に表示できます。だから、あなたは、配列の最初の半分をコピーするためのarraycopyを使用して、あなたがこのようなテクスチャをバインドするよりも、onPreviewFrame上:

gl.glGenTextures(1, cameraTexture, 0); 
int tex = cameraTexture[0]; 
gl.glBindTexture(GL10.GL_TEXTURE_2D, tex); 
gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_LUMINANCE, 
    this.prevX, this.prevY, 0, GL10.GL_LUMINANCE, 
    GL10.GL_UNSIGNED_BYTE, ByteBuffer.wrap(this.cameraFrame)); //cameraFrame is the first half od byte[] from onPreviewFrame 

gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); 

は、あなたが得るカントについて16-18この方法をFPS、あなたはいくつかのフィルタを作るためにOpenGLを使用することができます。もしあなたが望むなら、私はあなたにこれ以上のコードを送ることができますが、ここに入れるのは時間がかかりすぎます...

さらに詳しい情報は、私のsimillar questionを見ることができます。

+0

YUVデータを表示可能にするには、RGBデータに変換する必要があります。これはわずか8fps程度になります。そのような高い表示レートを得るためにアンドロイドフレームワークは何をしていますか(フィルタは適用されません)?あなたは、ほぼ20 fpsの周りを回るために、OpenGL ESにすべてをラッピングしていると思いますか? :) – poitroae

+0

おそらく、Android APIを使用していない可能性があります:)ネイティブコードなどで書かれているかもしれません(それは単なる推測です)...しかし、Androidはオープンソースなので、作品: –

+0

Jaa-c、あなたが上に書いたコード(OpenGLではない)を使って例を投稿することが可能であるかどうか疑問に思っていました。私はそれをすべて一緒に正しく動作させるために追加する方法を理解することはできません。これは古い質問ですが、私はこれについてGoogleで多く見つけることはできません。または、これを実装するためにどのようなクラスを作成するかについてのミニチュートリアルを作成できますか? –

関連する問題