Android:如何在已经显示Camara预览版的Surfaceview上绘图
作者:互联网
我正在尝试编写一个应用程序,该应用程序必须在移动电话的屏幕上显示前置摄像头正在拍摄的内容[该应用程序不会记录/保存手机内存中的任何内容].同样在面部被拍摄(并被检测到)的情况下,它必须显示为矩形.
为此,我正在使用:
> Surfaceview显示前置摄像头拍摄的内容.
> FaceDetectionListener,用于检测摄像机输入中的面部.
到目前为止,该应用程序正确显示前置摄像头正在拍摄的内容.它还可以正确检测脸部.但是我无法在检测到的脸部周围绘制边界矩形.
以下是一些片段,以说明我是如何尝试解决任务的.
活动:
@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课程:
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我明白错误是锁定表面.但我不明白为什么.
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
我的解决方案基于以下问题的公认答案:
Android drawing on surfaceview and canvas
但似乎我错了.谁能告诉我应该改变什么?
解决方法:
你不能. Surface是生产者 – 消费者对的生产者一方,一次只能有一个生产者.您可以提供相机框架,使用Canvas在软件中渲染,或使用OpenGL在硬件中渲染,但不能将它们混合在一个Surface上.
你有几个选择.一种方法是使用与第一个重叠的第二个SurfaceView(通过FrameLayout).为此,您必须为第二个Surface指定Z顺序 – 如果不这样做,系统将看到您有两个Surfaces试图占用相同的空间,只有一个将获胜.使用setZOrderMediaOverlay()方法将新Surface放置在相机预览的前面,但在View UI后面.您还需要将Surface的颜色格式从默认的RGB565更改为支持透明度的格式; setColorFormat(TRANSLUCENT)
或RGB8888将起作用.确保使用适当的颜色传输模式将曲面擦除为透明黑色.
另一种方法是使用custom View.这样更有效,因为它不涉及创建单独的Surface(您在View UI层上绘图),Canvas渲染可以是硬件加速的.最方便的View是SurfaceView的一部分 – 不需要对布局进行任何更改.如果你的代码是SurfaceView的子类,你已经在那里了.
有关多个重叠SurfaceView的示例,请参见Grafika’s“多表面测试”活动.如果您想了解有关Android图形系统的更多信息,请参阅architecture doc.
标签:android,surfaceview,face-detection,android-canvas 来源: https://codeday.me/bug/20190829/1761869.html