Android 在相机预览上绘制
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12252530/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Android draw on camera preview
提问by Solenoid
I'm making a virtual reality application where the camera should detect faces, locate them and show their location on the camera preview.
我正在制作一个虚拟现实应用程序,其中相机应该检测人脸,定位它们并在相机预览中显示它们的位置。
I know of 3 ways to do it, I'd like to use GLSurfaceView to be as fast as possible (according to this post), but currently I'm trying to draw on the sameSurfaceView where the camera is using for its preview. My callback to draw on it would be onFaceDetection
like so:
我知道有 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);
}
}
With this code I keep getting this error:
使用此代码,我不断收到此错误:
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.internalLockCanvas(SurfaceView.java:744)
09-03 19:35:42.743: E/SurfaceHolder(19394): at android.view.SurfaceView.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.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)
Is there a problem with the camera trying to draw its preview on the same SurfaceView the face detection callback? How can I do this without layering SurfaceViews?
尝试在面部检测回调的同一 SurfaceView 上绘制预览的相机是否有问题?如何在不分层 SurfaceViews 的情况下执行此操作?
采纳答案by James Burnstone
You can't lock and draw on a SurfaceView
which has Type.PUSH_BUFFERS
, (the one you're displaying frames to). You have to create another view above your original one in the Z direction and draw on a SurfaceView
in that View
.
您无法锁定和绘制SurfaceView
具有Type.PUSH_BUFFERS
, (您要向其显示帧的那个)。您必须在 Z 方向上的原始视图上方创建另一个视图,并SurfaceView
在其中绘制一个View
。
So in your main.xml
create a custom view below your original view in a FrameLayout
.
因此,在您main.xml
在FrameLayout
.
Create and handle a SurfaceView
in your Activity View. Add this view to the Camera preview display. Start your custom view passing the SurfaceHolder
. In this view you can lock and draw on a canvas.
SurfaceView
在您的活动视图中创建和处理 a 。将此视图添加到相机预览显示。通过SurfaceHolder
. 在此视图中,您可以锁定画布并在画布上绘图。
回答by Igor
As James pointed out you need to create custom surface which extends SurfaceView (I usually implement SurfaceHolder.Callback also):
正如 James 指出的,您需要创建扩展 SurfaceView 的自定义表面(我通常也实现 SurfaceHolder.Callback):
public class CameraSurfacePreview extends SurfaceView implements SurfaceHolder.Callback
public class CameraSurfacePreview extends SurfaceView implements SurfaceHolder.Callback
Constructor will be something like:
构造函数将类似于:
public CameraSurfacePreview(Context context) {
super(context);
...
mHolder = getHolder();
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
...
You need to bind camera with your surface after camera open call (if you implement implements SurfaceHolder.Callback put this somewhere inside overridden surfaceCreated):
您需要在相机打开调用后将相机与您的表面绑定(如果您实现了实现 SurfaceHolder.Callback 将它放在覆盖的surfaceCreated 中):
mCamera = Camera.open();
mCamera.setPreviewDisplay(mHolder);
Finally you need to add instance of your custom surface somehere in activity content view:
最后,您需要在活动内容视图中的某处添加自定义表面的实例:
CameraSurfacePreview cameraSurfacePreview = new CameraSurfacePreview(this);
//camera surface preview is first child!
((ViewGroup)findViewById(R.id.cameraLayout)).addView(cameraSurfacePreview, 0);
In my example layout for activity looks something like(I am showing camera preview in main frame layout):
在我的示例布局中,活动看起来像(我在主框架布局中显示相机预览):
<?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">
回答by Sebastian Breit
The lockcanvas/unlockcanvasandpost approach is not appropriate when using openGL as the openGL code is controlling and locking the surface. If you want to use the standard 2d APIs, don't use OpenGL.
当使用 openGL 时,lockcanvas/unlockcanvasandpost 方法不合适,因为 openGL 代码正在控制和锁定表面。如果您想使用标准的 2d API,请不要使用 OpenGL。
回答by Sruit A.Suk
I could said,
我可以说,
It's not about a Thread.
这与线程无关。
It not because of this line that make error either.
也不是因为这条线出错。
canvas.drawPoint(leftEye.x, leftEye.y, red);
It because of the canvas still using and can't lock it
因为画布还在使用,无法锁定
If you carefully check, you will see this canvas you get is == null
如果你仔细检查,你会看到你得到的这个画布是 == null
canvas = canvasHolder.lockCanvas();
if (canvas == null) Log.i("Error", "Canvas == null!");
You might have question then where is it already use?
您可能会有疑问,那么它已经在哪里使用了?
It already use to display to show what's going on to you! That is
它已经用于显示向您展示正在发生的事情!那是
camera.setPreviewDisplay(previewHolder);
So, I suggest, If you want to draw Point over your eye, you might need to have another SurfaceView / SurfaceHolder over you SurfaceView for preview camera :]
所以,我建议,如果你想在你的眼睛上画点,你可能需要在你的 SurfaceView 上有另一个 SurfaceView/SurfaceHolder 作为预览相机:]