Android:如何在已经显示Camara预览的Surfaceview上进行绘制 [英] Android: How to draw on a Surfaceview which already is displaying a Camara Preview

查看:88
本文介绍了Android:如何在已经显示Camara预览的Surfaceview上进行绘制的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一个应用程序,该应用程序必须在手机屏幕上显示前置摄像头正在拍摄的内容[该应用程序未在手机的存储器中记录/保存任何内容].同样,如果要拍摄(检测到)面部,则必须用矩形使它显得发酸.

I am trying to program an application which has to display on the mobile phone's screen what is being filmed by the front camera [The application is not recording/saving anything in the memory of the phone]. Also in case a face is filmed (and detected), it has to appear sourrended by a rectangle.

为此,我正在使用:

  1. 表面视图,以显示前置摄像头正在拍摄的内容.
  2. FaceDetectionListener ,用于检测摄像头输入中的人脸.
  1. A Surfaceview to display what is being filmed by the front camera.
  2. A FaceDetectionListener to detect faces in the camera input.

到目前为止,该应用程序可以正确显示前置摄像头正在拍摄的内容.它还可以正确检测脸部.但是我无法在检测到的人脸周围绘制边界矩形.

So far the application displays properly what is being filmed by the front camera. It also detects correctly faces. But I'm not able to draw the boundary rectangle around the detected face.

以下是一些片段,展示了我如何解决任务.

Here are some snippets to show how I'm trying to solve the task.

活动:

@SuppressLint("InflateParams")
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)

public class FaceDetectorTutorial extends Activity {

   MySurface mMySurface;
   private SurfaceView surfaceView;

   public void onCreate(Bundle savedInstanceState) {

      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_face_detector_tutorial);
      setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

      surfaceView = (SurfaceView)findViewById(R.id.camPreview);

      mMySurface = new MySurface(this, surfaceView);

  }     

}    

MySurface类:

MySurface class:

class MySurface extends SurfaceView implements SurfaceHolder.Callback {  

  Paint paint = new Paint();

  public Camera camera;

  String Tag = "Log: ";


  private SurfaceHolder surfaceHolder;
  boolean preview = false;

 ////////// CLASS CONSTRUCTOR   //////////
 MySurface(Context context, SurfaceView msurfaceView) {
      super(context);

      paint.setColor(Color.RED);
      paint.setStrokeWidth(3);

      surfaceHolder = msurfaceView.getHolder();
      surfaceHolder.addCallback(this);  
      surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

 }


 /////////  FACE DETECTION LISTENER /////////
 @SuppressLint("NewApi")
   FaceDetectionListener faceDetectionListener = new FaceDetectionListener(){

        @Override
        public void onFaceDetection(Face[] faces, Camera camera) {

            if (faces.length > 0){

                //Just for the first one detected
                Rect Boundary = faces[0].rect;

                System.out.println(Boundary);

                tryDrawing(Boundary);

            }


 }};//End of FaceDetectionListener

 ////////// SURFACE METHODS ////////
 @Override
 public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {
         // TODO Auto-generated method stub
        if(preview){
               camera.stopFaceDetection();
               camera.stopPreview();
               preview = false;
         }

       if (camera != null){
               try {                
               camera.setPreviewDisplay(surfaceHolder);
               camera.startPreview();            
               camera.startFaceDetection();
               preview = true;
               } catch (IOException e) {
                   // TODO Auto-generated catch block
                  e.printStackTrace();
               }
           }

  }

  @Override
  public void surfaceCreated(SurfaceHolder holder) {
           // TODO Auto-generated method stub

        int cameraId = -1;      
        int numberOfCameras = camera.getNumberOfCameras();

        for (int i = 0; i < numberOfCameras; i++) {
            CameraInfo info = new CameraInfo();
            Camera.getCameraInfo(i, info);

            if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
                    cameraId = i;
                    break;
            }
        }

        camera = Camera.open(cameraId);
        camera.setFaceDetectionListener(faceDetectionListener);

  }

  @Override
  public void surfaceDestroyed(SurfaceHolder holder) {
           // TODO Auto-generated method stub
           camera.stopFaceDetection();
           camera.stopPreview();
           camera.release();
           camera = null;
           preview = false;
 }

 private void tryDrawing(Rect Boundary) {
      Log.i(Tag, "Trying to draw...");

      Canvas canvas = surfaceHolder.lockCanvas();

      if (canvas == null) {

          Log.e(Tag, "Cannot draw onto the canvas as it's null");

      } else {

          drawMyStuff(canvas,Boundary);
          surfaceHolder.unlockCanvasAndPost(canvas);
      }
  }

  private void drawMyStuff(final Canvas canvas, Rect Boundary) {

      canvas.drawRect(Boundary.left, Boundary.top, Boundary.right, Boundary.bottom, paint);

      Log.i(Tag, "Drawing...");

  }

}  

布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <SurfaceView
        android:id="@+id/camPreview"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

从logcat上我了解到错误正在锁定表面.但是我不明白为什么.

From the logcat I understand the error is locking the surface. But I don't understand why.

11-23 17:13:51.791: I/System.out(12515): Rect(-187, -495 - 328, 196)
11-23 17:13:51.791: I/Log:(12515): Trying to draw...
11-23 17:13:51.791: E/SurfaceHolder(12515): Exception locking surface
11-23 17:13:51.791: E/SurfaceHolder(12515): java.lang.IllegalArgumentException
11-23 17:13:51.791: E/SurfaceHolder(12515):     at android.view.Surface.nativeLockCanvas(Native Method)
11-23 17:13:51.791: E/SurfaceHolder(12515):     at android.view.Surface.lockCanvas(Surface.java:452)
11-23 17:13:51.791: E/SurfaceHolder(12515):     at android.view.SurfaceView$4.internalLockCanvas(SurfaceView.java:781)
11-23 17:13:51.791: E/SurfaceHolder(12515):     at android.view.SurfaceView$4.lockCanvas(SurfaceView.java:757)
11-23 17:13:51.791: E/SurfaceHolder(12515):     at com.example.facedetectiontutorial.MySurface.tryDrawing(FaceDetectorTutorial.java:175)
11-23 17:13:51.791: E/SurfaceHolder(12515):     at com.example.facedetectiontutorial.MySurface.access$0(FaceDetectorTutorial.java:172)
11-23 17:13:51.791: E/SurfaceHolder(12515):     at com.example.facedetectiontutorial.MySurface$1.onFaceDetection(FaceDetectorTutorial.java:109)
11-23 17:13:51.791: E/SurfaceHolder(12515):     at android.hardware.Camera$EventHandler.handleMessage(Camera.java:815)
11-23 17:13:51.791: E/SurfaceHolder(12515):     at android.os.Handler.dispatchMessage(Handler.java:99)
11-23 17:13:51.791: E/SurfaceHolder(12515):     at android.os.Looper.loop(Looper.java:137)
11-23 17:13:51.791: E/SurfaceHolder(12515):     at android.app.ActivityThread.main(ActivityThread.java:5041)
11-23 17:13:51.791: E/SurfaceHolder(12515):     at java.lang.reflect.Method.invokeNative(Native Method)
11-23 17:13:51.791: E/SurfaceHolder(12515):     at java.lang.reflect.Method.invoke(Method.java:511)
11-23 17:13:51.791: E/SurfaceHolder(12515):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
11-23 17:13:51.791: E/SurfaceHolder(12515):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
11-23 17:13:51.791: E/SurfaceHolder(12515):     at dalvik.system.NativeStart.main(Native Method)
11-23 17:13:51.893: E/Log:(12515): Cannot draw onto the canvas as it's null

我的解决方案基于以下问题的公认答案:

I based my solution in the accepted answer of the following question:

Android在Surfaceview和画布上绘制

但是似乎我将其应用错误.谁能告诉我该改变什么?

But it seems I'm applying it wrong. Could anyone tell me what should I change?

推荐答案

您不能. Surface是生产者-消费者对的生产者方,一次只能有一个生产者.您可以提供相机框架,可以通过Canvas在软件中进行渲染,也可以通过OpenGL在硬件中进行渲染,但是不能将它们混合在单个Surface上.

You can't. A Surface is the producer side of a producer-consumer pair, and there can only be one producer at a time. You can provide camera frames, render in software with Canvas, or render in hardware with OpenGL, but you can't mix them up on a single Surface.

您有一些选择.一种方法是使用第二个SurfaceView与第二个SurfaceView重叠(通过FrameLayout).为此,必须为第二个Surface指定Z顺序-否则,系统将看到您有两个Surface试图占据相同的空间,并且只有一个获胜.使用 setZOrderMediaOverlay()方法放置新在相机预览的前面但在View UI后面显示曲面.您还需要将Surface的颜色格式从默认的RGB565更改为支持透明性的颜色. setColorFormat(TRANSLUCENT) RGB8888都可以.确保使用适当的颜色传输模式将Surface擦除为透明黑色.

You have a few options. One approach is to use a second SurfaceView that overlaps the first (via a FrameLayout). For this to work, you must specify the Z order for the second Surface -- if you don't, the system will see that you have two Surfaces attempting to occupy the same space, and only one will win. Use the setZOrderMediaOverlay() method to place the new Surface in front of the camera preview but behind the View UI. You also need to change the Surface's color format from the default RGB565 to one that supports transparency; setColorFormat(TRANSLUCENT) or RGB8888 will work. Make sure you erase the Surface to transparent black with an appropriate color transfer mode.

另一种方法是使用自定义视图.这是更有效的方法,因为它不需要创建单独的Surface(在View UI层上进行绘制),并且可以通过硬件来加速Canvas渲染.最方便使用的视图是SurfaceView的一部分-无需更改布局.如果您的代码将SurfaceView子类化,那么您已经到了一半.

Another approach is to use a custom View. This is more efficient, as it doesn't involve creating a separate Surface (you're drawing on the View UI layer), and Canvas rendering can be hardware-accelerated. The most convenient View to use is the one that's part of SurfaceView -- no change to your layout is required. If your code is sub-classing SurfaceView you're halfway there already.

有关多个重叠SurfaceView的示例,请参见 Grafika的多表面测试"活动.如果您想了解有关Android图形系统的更多信息,请参见建筑文档.

For an example of multiple overlapping SurfaceViews, see Grafika's "multi-surface test" Activity. If you want to understand more about Android's graphics system, see the architecture doc.

这篇关于Android:如何在已经显示Camara预览的Surfaceview上进行绘制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆