Android нарисовать на камере предварительный просмотр

Я делаю приложение виртуальной реальности, где камера должна обнаруживать лица, находить их и показывать их местоположение на предварительном просмотре камеры.

Я знаю 3 способа сделать это, я хотел бы использовать 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 обратном вызове обнаружения лица? Как я могу это сделать без слоев SurfaceViews?

Solutions Collecting From Web of "Android нарисовать на камере предварительный просмотр"

Вы не можете locK и нарисовать SurfaceView, у которого есть Type.PUSH_BUFFERS (тот, на котором вы показываете кадры). Вы должны создать другое представление над вашим оригинальным в направлении Z и нарисовать SurfaceView в этом представлении.

Таким образом, в вашем main.xml создайте собственное представление под вашим исходным представлением в FrameLayout.

Создайте и обработайте SurfaceView в представлении активности. Добавьте это представление в окно предварительного просмотра камеры. Начните свой пользовательский просмотр через SurfaceHolder. В этом представлении можно зафиксировать и нарисовать холст.

Как отметил Джеймс, вам нужно создать пользовательскую поверхность, которая расширяет 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, поместите это где-то внутри переопределенной поверхности).

 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"> 

Метод lockcanvas / unlockcanvasandpost не подходит при использовании openGL, поскольку код openGL контролирует и блокирует поверхность. Если вы хотите использовать стандартные 2d API, не используйте OpenGL.

Я мог сказать,

Речь идет не о теме.

Это не из-за этой строки, которая делает ошибку.

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

Это из-за того, что холст все еще использует и не может его заблокировать

Если вы тщательно проверите, вы увидите, что этот холст, который вы получаете, равен == null

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

У вас может возникнуть вопрос, где он уже используется?

Он уже используется для отображения, чтобы показать, что происходит с вами! То есть

 camera.setPreviewDisplay(previewHolder); 

Итак, я предлагаю: если вы хотите нарисовать точку над глазом, вам может понадобиться еще один SurfaceView / SurfaceHolder поверх SurfaceView для камеры предварительного просмотра:]