android - 将自定义过滤器应用于相机输出

标签 android opengl-es filter camera

如何将自定义滤镜应用到相机输出中的单个帧,并显示它们。

到目前为止我已经尝试过:

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;
        }
    }
}
  • 虽然它的名称包含“绿色”,但我实际上只是想以某种方式修改值(在这种情况下,颜色会被加强一点)。长话短说,它不起作用。

  • 我发现字节数组“数据”是相机输出的副本;但这并没有真正的帮助,因为我需要“真正的”缓冲区。

  • 我听说你可以用 openGL 来实现它。这听起来很复杂。

有没有更简单的方法?否则,这个 openGL-surfaceView 映射将如何工作?

最佳答案

好的,有几种方法可以做到这一点。但是性能方面存在重大问题。来自相机的 byte[] 位于 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 很慢。你可以尝试使用native code和android NDK将数据从YUV转换为RGB,但这相当复杂。

另一种选择是使用 openGL ES。您需要 GLSurfaceView,在其中将相机框架绑定(bind)为纹理(在 GLSurfaceView 中实现 Camera.previewCallback,因此您使用 onPreviewFrame 的方式与常规表面相同)。但是有同样的问题,需要转换 YUV 数据。有一次机会 - 您可以非常快速地仅显示预览(灰度图像)中的亮度数据,因为 YUV 中字节数组的前半部分只是没有颜色的亮度数据。因此,在 onPreviewFrame 上,您使用 arraycopy 复制数组的前半部分,然后像这样绑定(bind)纹理:

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 ,但也没有什么好的解决办法……

关于android - 将自定义过滤器应用于相机输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8371055/

相关文章:

android - 在 BackPressed 上恢复之前的 Activity

iphone - Opengl iPhone SDK : How to tell if you're touching an object on screen?

iphone - 开发 iPhone 游戏需要什么?

css - CSS3滤镜灰度和饱和有什么区别?

java - 如何保证我的 Android SurfaceView 是透明的而不是黑色的?

java - Android 更新每个操作的值总和

android - 机器人 : How to get button id from onClick method described in XML?

opengl-es - OpenGL ES (2.0) 着色语言 : How to input boolean into vertex shader and pass to fragment shader?

python - 按行过滤数据框

django按两级外键关系过滤