安卓相机预览上绘制图像

11

我正在开发一个虚拟现实应用程序,其中相机应该检测人脸,定位它们并在相机预览上显示它们的位置。

我知道三种方法可以实现它,我想尽可能快地使用GLSurfaceView(根据这篇帖子),但目前我正在尝试在相机用于预览的同一个 SurfaceView 上进行绘制。我的回调函数以此为onFaceDetection

public class MyActivity extends Activity implements SurfaceHolder.Callback, Camera.FaceDetectionListener {
    Camera camera;
    SurfaceView svPreview;
    SurfaceHolder previewHolder;
    TextView tvInfo;
    Paint red;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        svPreview = (SurfaceView) findViewById(R.id.svPreview);
        tvInfo = (TextView) findViewById(R.id.tvInfo);

        red = new Paint();
        red.setStyle(Paint.Style.STROKE);
        red.setStrokeWidth(3);

        previewHolder = svPreview.getHolder();
        previewHolder.addCallback(this);
        previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder arg0) {
        camera = Camera.open();
        try {
            camera.setDisplayOrientation(90);
            camera.setFaceDetectionListener(this);
            camera.setPreviewDisplay(previewHolder);
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        // . . .
        camera.startPreview();
        camera.autoFocus(null);
        camera.startFaceDetection();
    }

    public void surfaceDestroyed(SurfaceHolder arg0) {
        camera.stopFaceDetection();
        camera.cancelAutoFocus();
        camera.stopPreview();
        camera.release();
        camera = null;
    }

    public void onFaceDetection(Face[] faces, Camera camera) {  
        tvInfo.setText("Faces: " + String.valueOf(faces.length));

        Canvas canvas = previewHolder.lockCanvas();
        for(int i=0; i < faces.length; i++) {
            Point leftEye = faces[i].leftEye;
            Point rightEye = faces[i].rightEye;
            // this is not working
            canvas.drawPoint(leftEye.x, leftEye.y, red);
        }
        previewHolder.unlockCanvasAndPost(canvas);
    }
}

使用这段代码时,我一直收到这个错误:

09-03 19:35:42.743: E/SurfaceHolder(19394): Exception locking surface
09-03 19:35:42.743: E/SurfaceHolder(19394): java.lang.IllegalArgumentException
09-03 19:35:42.743: E/SurfaceHolder(19394):     at android.view.Surface.lockCanvasNative(Native Method)
09-03 19:35:42.743: E/SurfaceHolder(19394):     at android.view.Surface.lockCanvas(Surface.java:76)
09-03 19:35:42.743: E/SurfaceHolder(19394):     at android.view.SurfaceView$4.internalLockCanvas(SurfaceView.java:744)
09-03 19:35:42.743: E/SurfaceHolder(19394):     at android.view.SurfaceView$4.lockCanvas(SurfaceView.java:720)
09-03 19:35:42.743: E/SurfaceHolder(19394):     at com.bluetooth.activities.MyActivity.onFaceDetection(MyActivity.java:90)
09-03 19:35:42.743: E/SurfaceHolder(19394):     at android.hardware.Camera$EventHandler.handleMessage(Camera.java:729)
09-03 19:35:42.743: E/SurfaceHolder(19394):     at android.os.Handler.dispatchMessage(Handler.java:99)
09-03 19:35:42.743: E/SurfaceHolder(19394):     at android.os.Looper.loop(Looper.java:137)
09-03 19:35:42.743: E/SurfaceHolder(19394):     at android.app.ActivityThread.main(ActivityThread.java:4424)
09-03 19:35:42.743: E/SurfaceHolder(19394):     at java.lang.reflect.Method.invokeNative(Native Method)
09-03 19:35:42.743: E/SurfaceHolder(19394):     at java.lang.reflect.Method.invoke(Method.java:511)
09-03 19:35:42.743: E/SurfaceHolder(19394):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
09-03 19:35:42.743: E/SurfaceHolder(19394):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
09-03 19:35:42.743: E/SurfaceHolder(19394):     at dalvik.system.NativeStart.main(Native Method)
09-03 19:35:42.743: W/dalvikvm(19394): threadid=1: thread exiting with uncaught exception (group=0x40a561f8)
09-03 19:35:42.766: E/AndroidRuntime(19394): FATAL EXCEPTION: main
09-03 19:35:42.766: E/AndroidRuntime(19394): java.lang.IllegalArgumentException
09-03 19:35:42.766: E/AndroidRuntime(19394):    at android.view.Surface.unlockCanvasAndPost(Native Method)
09-03 19:35:42.766: E/AndroidRuntime(19394):    at android.view.SurfaceView$4.unlockCanvasAndPost(SurfaceView.java:775)
09-03 19:35:42.766: E/AndroidRuntime(19394):    at com.bluetooth.activities.MyActivity.onFaceDetection(MyActivity.java:99)
09-03 19:35:42.766: E/AndroidRuntime(19394):    at android.hardware.Camera$EventHandler.handleMessage(Camera.java:729)
09-03 19:35:42.766: E/AndroidRuntime(19394):    at android.os.Handler.dispatchMessage(Handler.java:99)
09-03 19:35:42.766: E/AndroidRuntime(19394):    at android.os.Looper.loop(Looper.java:137)
09-03 19:35:42.766: E/AndroidRuntime(19394):    at android.app.ActivityThread.main(ActivityThread.java:4424)
09-03 19:35:42.766: E/AndroidRuntime(19394):    at java.lang.reflect.Method.invokeNative(Native Method)
09-03 19:35:42.766: E/AndroidRuntime(19394):    at java.lang.reflect.Method.invoke(Method.java:511)
09-03 19:35:42.766: E/AndroidRuntime(19394):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
09-03 19:35:42.766: E/AndroidRuntime(19394):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
09-03 19:35:42.766: E/AndroidRuntime(19394):    at dalvik.system.NativeStart.main(Native Method)

在相机尝试在同一个SurfaceView上绘制预览和人脸检测回调时是否会出现问题?我如何在不分层使用SurfaceView的情况下完成此操作?


我为此问题添加了悬赏,因为从挖掘Android源代码来看,预览运行在与应用程序的其余部分不同的线程上,这意味着您的应用程序无法获取Canvas对象或锁定Surface,因为它们是不同的线程。话虽如此,我希望有人能够回答这个问题。 - David C. Sainte-Claire
在所有这些视图的顶部使用一个透明背景的FrameLayout如何! - Shafi
4个回答

8

如果你正在显示帧的SurfaceView使用了Type.PUSH_BUFFERS,你不能在其上锁定和绘制。你需要在Z方向上创建另一个视图,并在其中的SurfaceView上进行绘制。

因此,在main.xml中,创建一个自定义视图,并将其放置在FrameLayout中原始视图的下方。

在Activity View中创建并处理SurfaceView。将此视图添加到相机预览显示中。启动你的自定义视图并传递SurfaceHolder。在这个视图中,你可以锁定并绘制画布。


6

正如James所指出的,您需要创建一个扩展SurfaceView的自定义表面(通常我还会实现SurfaceHolder.Callback):

public class CameraSurfacePreview extends SurfaceView implements SurfaceHolder.Callback

构造函数将类似于:

public CameraSurfacePreview(Context context) {
     super(context);
     ...
     mHolder = getHolder();
     mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
     ...

在相机打开调用之后(如果您实现了SurfaceHolder.Callback,请将其放在覆盖的surfaceCreated中的某个位置),您需要将相机与您的表面绑定。
mCamera = Camera.open();
mCamera.setPreviewDisplay(mHolder);

最后,您需要在活动内容视图中的某个位置添加自定义表面的实例:
CameraSurfacePreview cameraSurfacePreview = new CameraSurfacePreview(this);
//camera surface preview is first child!
((ViewGroup)findViewById(R.id.cameraLayout)).addView(cameraSurfacePreview, 0); 

在我的示例布局中,活动的外观类似于(我在主框架布局中显示相机预览):
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
              android:layout_width="fill_parent"        
                      android:layout_height="fill_parent" 
              android:layout_gravity="top|left"
              android:id="@+id/cameraLayout">

0

我可以说,

这不是关于线程的问题。

也不是因为这行代码出错了。

canvas.drawPoint(leftEye.x, leftEye.y, red);

这是因为canvas仍在使用中,无法锁定它。

如果你仔细检查,你会发现你获取的canvas为null。

canvas = canvasHolder.lockCanvas();
    if (canvas == null)  Log.i("Error", "Canvas == null!"); 

你可能会问它已经在哪里被使用了?

它已经被用来展示给你正在发生的事情!就是这样

camera.setPreviewDisplay(previewHolder);

所以,我的建议是,如果你想在眼前绘制点,可能需要在你的 SurfaceView 上再添加一个 SurfaceView / SurfaceHolder 用于预览摄像头 :]

0

当使用OpenGL时,lockcanvas/unlockcanvasandpost方法不适用,因为OpenGL代码正在控制并锁定表面。如果您想要使用标准的2D API,请不要使用OpenGL。


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