2011-12-03 31 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上找不到这件事。或者你可以制作一个迷你教程,说明为实现这个目的需要制作哪些类? –