2011-12-03 27 views
15

如何將自定義濾鏡應用於相機輸出中的單個幀並顯示它們。將自定義濾鏡應用到相機輸出

我試過到目前爲止:

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

好的,有幾種方法可以做到這一點。但是,性能存在重大問題。如果您想顯示它,則來自相機的字節[]位於YUV format,必須將其轉換爲某種RGB格式。這種轉換是相當昂貴的操作,並顯着降低輸出fps。

這取決於你實際上想用相機預覽做什麼。因爲最好的解決方案是在沒有回調的情況下繪製相機預覽,並對相機預覽產生一些效果。這是做出有爭議的重要性的常用方式。

但是,如果您確實需要手動顯示輸出,有幾種方法可以做到這一點。你的例子不起作用的原因有幾個。首先,你根本不顯示圖像。如果你這樣稱呼:

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

比你的相機根本不顯示預覽,你必須手動顯示它。並且你不能在onPreviewFrame方法中進行任何昂貴的操作,因爲數據的生命週期是有限的,它會覆蓋下一幀。一個提示,使用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)在類的構造函數或其他地方。

在onDraw中,例如,如果要修改顏色,則可以應用paint.setColorFilter(filter)。如果你願意,我可以發佈一些例子。

因此,這將工作,但性能會低(小於8fps),因爲BitmapFactory.decodeByteArray是緩慢的。您可以嘗試使用本地代碼和android NDK將YUV數據轉換爲RGB,但這非常複雜。

另一種選擇是使用openGL ES。您需要GLSurfaceView,將相機框架作爲紋理進行綁定(在GLSurfaceView中實現Camera.previewCallback,因此您在常規曲面中使用onPreviewFrame)。但也有同樣的問題,您需要轉換YUV數據。有一個機會 - 因爲YUV中的字節數組的前半部分只是沒有顏色的亮度數據,所以只能從預覽(灰度圖像)快速顯示亮度數據。因此,對onPreviewFrame您使用arraycopy到陣列的前半部分拷貝,比你綁定像這樣的質地:

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左右。什麼是Android框架如何獲得如此高的顯示速度(不應用過濾器)?你認爲他們正在包裝所有的東西來打開GL ES以獲得接近20幀/秒的速度嗎? :) – poitroae

+0

更可能的是,他們沒有使用Android API :)它可能是用本地代碼或類似的東西編寫的(這只是猜測)......但Android是開源的,因此您可以嘗試查找代碼的實際內容作品:) –

+0

Jaa-c,我想知道是否可以使用上面編寫的代碼發佈示例(不是openGL)。我無法弄清楚如何將它們全部加在一起才能正確工作,並且我知道這是一個較老的問題,但我在Google上找不到這件事。或者你可以製作一個迷你教程,說明爲實現這個目的需要製作哪些類? –