如何使用SurfaceView android处理来自相机的帧

时间:2015-12-22 16:17:40

标签: android bitmap android-camera surfaceview

我在从Android相机捕获帧fps的项目中工作。在Bitmap中转换每个帧以使用带有opencv SDK的本机库之后,在ImageView中显示此位图。这个过程消耗堆内存并影响app的性能。我在manifest.xml中添加下一个标记以增加堆内存:

<application
    android:name="app"
    android:largeHeap="true" />

有了这个,app在调试模式下生成apk版本时工作正常,但是当在发布模式下生成apk版本时,应用程序非常慢并且无法正常工作,性能存在问题。

我使用SurfaceView组件使用逻辑从相机获取帧,我使用SurfaceView因为我需要在应用程序内部的背景中使用,我想问你,我如何解决这个问题,依靠你的帮助

我正在使用这个逻辑来获取帧:

public synchronized void onPreviewFrame(final byte[] data, final Camera camera) {
        executorService.execute(new Runnable() {
            public void run() {

      Camera.Parameters parameters = camera.getParameters();
      orientation = getOrientationDevice().getOrientation();
      orientation = normalize(orientation);

      //converter yuv to bitmap
      int width = parameters.getPreviewSize().width;
      int height = parameters.getPreviewSize().height;
      YuvImage yuv = new YuvImage(data,PreviewFormat,width, height, null);
      ByteArrayOutputStream out = new ByteArrayOutputStream();
      yuv.compressToJpeg(new Rect(0, 0, width, height), 80, out);
      byte[] bytes = out.toByteArray();
      Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

               // rotate image 
             if (cameraId == Camera.CameraInfo.CAMERA_FACING_FRONT){
                    matrix.setScale(-1, 1);
                    matrix.postTranslate(bitmap.getWidth(), 0);
                    matrix.postRotate(rotation + 90);
                } else {
                    matrix.postRotate(rotation + 90);
                }                        

               // process bitmap from method jni
                ....

               // to pass bitmap a ImageView to show
               mCameraFrame.onFrame(bitmap.copy(bitmap.getConfig(), false));


            if (bitmap != null && !bitmap.isRecycled()) {
                bitmap.recycle();
                bitmap = null;
            }


            camera.addCallbackBuffer(data);

            return;

            });

        }

    };

在UI中,回调接收要在Imageview中显示的位图

@Override
    public void onFrame(final Bitmap mBitmap) {

        this.bitmapWeakReference = new WeakReference<Bitmap>(mBitmap);

        if (imageViewReference != null && bitmapWeakReference.get() != null) {
            final ImageView imageView = imageViewReference.get();
            if (imageView != null) {

                imageView.post(new Runnable() {
                    @Override
                    public void run() {
                        imageView.setImageBitmap(bitmapWeakReference.get());
                    }
                });

            }
        }

1 个答案:

答案 0 :(得分:1)

以高帧速率使用相机API需要将相机回调推离主UI线程,您需要separate Handler thread。摆脱onPreviewFrame()回调中的所有不必要的处理。您可以预先计算相机参数。避免,即使对于Rect:垃圾收集可能会导致性能下降。

您可以通过JNI将YUV帧提供给基于OpenCV的本机JNI,并且可以更快地执行所有转换和转换。

如果不对每个位图使用OpenCV,则可以将YUV转换为RGB而不通过JPEG。通过这种转换可以一步完成90°的旋转。

如果您在多核设备上运行,可以使用newFixedThreadPool()启动executorService,但是然后释放相机缓冲区(调用{{1}})可能需要精细管理,特别是如果您需要框架率要统一。

如果您只想显示预览流的两个副本,则可以使用OpenGL,CPU根本不会参与旋转和YUV到RGB转换。您甚至可以对通过OpenGL显示的纹理应用一些高级图像处理。