Android,操作相机预览帧

6

我想开发一个Android应用程序,利用相机对预览帧进行图像处理滤镜。

package alex.filter;

import java.io.IOException;

import android.content.Context;
import android.graphics.Canvas;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

class Preview extends SurfaceView implements SurfaceHolder.Callback {

    SurfaceHolder mHolder;
    public Camera camera;

    Preview(Context context) {
        super(context);
        mHolder = getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {        
        camera = Camera.open();        

        try {
            camera.setPreviewDisplay(holder);

            camera.setPreviewCallback(new PreviewCallback() {
                public void onPreviewFrame(byte[] data, Camera arg1) {                    
                    for( int i = 0 ; i < data.length ; i ++ ){
                        data[ i] = 0; // or some sirius filter
                    }                                                            
                    Preview.this.invalidate();
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
        }

    public void surfaceDestroyed(SurfaceHolder holder) {        
        camera.stopPreview();        
        camera = null;
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {        
        Camera.Parameters parameters = camera.getParameters();
        parameters.setPreviewSize(w, h);
        camera.setParameters(parameters);
        camera.startPreview();
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);        
    }
}

然而,无论我在onPreviewFrame方法中做什么,模拟器都没有任何变化。


1
+1,我也在想是否可以直接操作预览帧。 - Matheus Moreira
2个回答

4
另一个选择是使用OpenCV框架,它有一个Android版本:http://opencv.willowgarage.com/wiki/Android2.3.0。这是开源计算机视觉项目的NDK端口,它可以获取原始预览帧并允许在显示在SurfaceView之前使用OpenCV进行处理。因为它操作了帧,所以它的帧率不如直接连接硬件优化预览,但由于它的很多内容都是本地的,所以它做得相当不错。该版本中有一个OpenCV_Sample应用程序,编译成演示应用程序,可以完成您正在寻找的大部分功能。它具有菜单选项,可以启用反向、模糊图像或在预览区域上执行边缘检测。即使它不完全符合您的要求,源代码中也有一些很棒的示例可供学习。

1

这是因为在回调中获取的预览缓冲区只是预览缓冲区的副本,因此您所做的任何修改都不会显示,因为您获得的缓冲区是您自己的副本。在Android SDK 这里中提到了这一点。

我不确定如何做到这一点,但我已经考虑了一些关于如何处理这个问题的想法,以下是我认为应该采取的措施 -

  • 注册预览缓冲区
  • 禁用默认预览显示
    • 如果您未将预览显示设置为表面,则不应获取任何显示(但我不确定是否有效 - 在某些论坛中读取过,无法回忆起来源)
    • 如果上述方法不起作用,则必须隐藏视图(我知道这很低效,但我想不到其他的方法..)
  • 降低预览的帧率,以便我们不会被缓冲区淹没
  • 现在,要显示我们的缓冲区,我们可以使用默认的位图绘制功能或使用OpenGL来显示缓冲区。但是,我迄今为止都没有使用过任何一种,也不知道是否可行,对此有什么想法吗?

更新
重新查看SDK文档,我找到了这个API - setPreviewTexture此API允许我们-“从图像流中捕获带有OpenGL ES纹理的帧”。一旦您具有应用纹理的图像,就可以使用OpenGL来显示您的帧。(请参阅@Stephan发布的答案,了解如何执行此操作。)

注意 - setPreviewTexture 仅适用于 API 级别 11 及以上!SDK 链接


我想我能看出你的意图。有没有一种方法可以在不使用SurfaceView的情况下使用SurfaceHolder?如果我们使用它来获取预览数据,并使用优化的GLSurfaceView来绘制处理后的帧,也许我们可以实现合理的帧率。 - Matheus Moreira
2
我想我可能已经解决了这个问题 - 再次查看相机文档时,发现了setPreviewTexture,它允许我们“从图像流中捕获帧作为OpenGL ES纹理”,我认为这些OpenGL ES纹理是正确的选择。一旦完成,我们可以再次使用OpenGL来显示帧。SurfaceTexture - bluefalcon
不错!那个东西是从API 11级开始的,所以我在文档中没有看到它。我正在寻找适用于API 8级的东西,但还是请在您的答案中包含它,因为如果没有替代方案,我将授予您奖励。 - Matheus Moreira
1
我还没有尝试过,所以不知道它是否也适用于这个,但是可以尝试并查看兼容性包。也许它能帮到你! :) - Stephan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接