Camera2实现预览及算法处理

本文介绍了将车辆安全驾驶检测项目的相机接口从Camera1.0升级到Camera2.0的过程。详细讲解了Camera2.0的学习,包括Google官方Demo和流程。在实践中,讨论了权限申请、Camera2.0工具类的创建以及数据回调处理。在添加算法处理后,发现帧率降低导致画面卡顿,尤其在低端手机上表现明显。最后展示了预览效果,包括竖屏和横屏下的画面状态。
摘要由CSDN通过智能技术生成

一、应用背景

小白公司业务为车辆安全驾驶检测等,需要开启相机将图像传递给算法进行处理以达到行车预警的功能。由于项目需求,需要将开启相机这一块由原来的Camera1.0更新为Camera2.0,以下是小白开发中所遇到的问题与解决方案,仅供参考。

二、Camera2.0学习

1、Google官方demo

官方demo地址:android-Camera2Basic

2、流程

在这里插入图片描述
这里引用了管道的概念将安卓设备和摄像头之间联通起来,系统向摄像头发送 Capture 请求,而摄像头会返回 CameraMetadata。这一切建立在一个叫作 CameraCaptureSession 的会话中。

三、实践

1、申请权限

<uses-permission android:name="android.permission.CAMERA" />

Android 6.0以上需要进行权限的动态申请。

2、Camera2.0工具类

public class Camera2Proxy {

    private static final String TAG = "Camera2Proxy";

    private Activity mActivity;

    private int mCameraId = CameraCharacteristics.LENS_FACING_FRONT; // 要打开的摄像头ID
    private Size mPreviewSize; // 预览大小
    private CameraManager mCameraManager; // 相机管理者
    private CameraCharacteristics mCameraCharacteristics; // 相机属性
    private CameraDevice mCameraDevice; // 相机对象
    private CameraCaptureSession mCaptureSession;
    private CaptureRequest.Builder mPreviewRequestBuilder; // 相机预览请求的构造器
    private Semaphore mCameraOpenCloseLock = new Semaphore(1);//一个信号量以防止应用程序在关闭相机之前退出。
    private CaptureRequest mPreviewRequest;
    private Handler mBackgroundHandler;
    private HandlerThread mBackgroundThread;
    private ImageReader mImageReader;
    private Surface mPreviewSurface;
    private SurfaceTexture mPreviewSurfaceTexture;
    private OrientationEventListener mOrientationEventListener;

    private int mDisplayRotate = 0;
    private int mDeviceOrientation = 0; // 设备方向,由相机传感器获取
    private int mZoom = 1; // 缩放
    public static int CameraFPS = 0; // 帧率
    private int frameCount = 0;
    private long frameTime = SystemClock.elapsedRealtime();
    public long mLastPreviewFrameTime = SystemClock.elapsedRealtime(); // 最后一帧数据的时间
    private byte[] data = null;
    private boolean mStop = true;
    private final Object lock = new Object();
    private YuvCroper yuvCroper;
    private int cropH = 0;  //剪裁图像后的高度
    private DataSendThread mDataSendThread;
    private MediaRecorder mediaRecorder;
    private long startRecordTime = 0;// 录像开启时间
    private static final int SENSOR_ORIENTATION_DEFAULT_DEGREES = 90;
    private static final int SENSOR_ORIENTATION_INVERSE_DEGREES = 270;
    public static final int MEDIA_QUALITY_HIGH = 30 * 100000;
    private int rotation;
    private boolean isRecording = false;
    private String filePath;
    private static final SparseIntArray DEFAULT_ORIENTATIONS = new SparseIntArray();
    private static final SparseIntArray INVERSE_ORIENTATIONS = new SparseIntArray();

    static {
        DEFAULT_ORIENTATIONS.append(Surface.ROTATION_0, 90);
        DEFAULT_ORIENTATIONS.append(Surface.ROTATION_90, 0);
        DEFAULT_ORIENTATIONS.append(Surface.ROTATION_180, 270);
        DEFAULT_ORIENTATIONS.append(Surface.ROTATION_270, 180);
    }

    static {
        INVERSE_ORIENTATIONS.append(Surface.ROTATION_0, 270);
        INVERSE_ORIENTATIONS.append(Surface.ROTATION_90, 180);
        INVERSE_ORIENTATIONS.append(Surface.ROTATION_180, 90);
        INVERSE_ORIENTATIONS.append(Surface.ROTATION_270, 0);
    }


    /**
     * 打开摄像头的回调
     */
    private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(@NonNull CameraDevice camera) {
            Log.d(TAG, "onOpened");
            mCameraOpenCloseLock.release();
            mCameraDevice = camera;
            int recordState = (int) SPUtils.get(mActivity, SPUtils.SP_RECORD_STATE, 0);
            if (recordState == 1) {
                initRecordPreviewRequest();
            } else {
                initPreviewRequest();
            }
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice camera) {
            Log.d(TAG, "onDisconnected");
            mCameraOpenCloseLock.release();
            releaseCamera();
        }

        @Override
        public void onError(@NonNull CameraDevice camera, int error) {
            Log.e(TAG, "Camera Open failed, error: " + error);
            mCameraOpenCloseLock.release();
            releaseCamera();
        }
    };

    /**
     * 图像回调处理
     */
    private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
        @Override
        public void onImageAvailable(ImageReader reader) {
            Image image = reader.acquireLatestImage();
            if (image != null) {
                if (!mStop) {
                    synchronized (lock) {
                        data = ImageUtils.getBytesFromImageAsType(image, ImageUtils.YV12);
                    }
                }
                image.close();
            }
            frameCount++;
            mLastPreviewFrameTime = SystemClock.elapsedRealtime();
            //记录帧率
            if (mLastPreviewFrameTime - frameTime >= 1000) {
                CameraFPS = frameCount;
                Log.e(TAG, "fps = " + CameraFPS);
                frameCount = 0;
                frameTime = mLastPreviewFrameTime;
            }
        }
    };

    @TargetApi(Build.VERSION_CODES.M)
    public Camera2Proxy(Activity activity) {
        mActivity = activity;
        mCameraManager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
        try {
            //按照16:9的比例剪裁,剪裁后的图像尺寸不能是奇数,处理各个数据为4的倍数
            RectF rectF = new RectF(0, ((16.0f * MediaInfo.PRIVIEW_WIDTH - 9.0f * MediaInfo.PRIVIEW_HEIGHT) / (32.0f * MediaInfo.PRIVIEW_WIDTH)), 1, ((16.0f * MediaInfo.PRIVIEW_WIDTH + 9.0f * MediaInfo.PRIVIEW_HEIGHT) / (32.0f * MediaInfo.PRIVIEW_WIDTH)));
            Log.e("YuvCroper", "MediaInfo.PRIVIEW_WIDTH = " + MediaInfo.PRIVIEW_WIDTH + " MediaInfo.PRIVIEW_HEIGHT=" + MediaInfo.PRIVIEW_HEIGHT + " rectF=" + rectF.toString());
            yuvCroper = new YuvCroper(YuvCroper.YUV_420P, MediaInfo.PRIVIEW_HEIGHT, MediaInfo.PRIVIEW_WIDTH, rectF);
        } catch (Exception e) {
            e.printStackTrace();
        }
        mOrientationEventListener = new OrientationEventListener(mActivity) {
            @Override
            public void onOrientationChanged(int orientation) {
                mDeviceOrientation = orientation;
            }
        };
    }

    @SuppressLint("MissingPermission")
    public void openCamera(int width, int height) {
        Log.v(TAG, "openCamera");
        startBackgroundThread(); // 对应 releaseCamera() 方法中的 stopBackgroundThread()
        mOrientationEventListener.enable();
        try {
            if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
                throw new RuntimeException("Time out waiting to lock camera opening.");
            }
            try {
                mCameraCharacteristics = mCameraManager.getCameraCharacteristics(Integer.toString(0));
                StreamConfigurationMap map = mCameraCharacteristics.get(CameraCharacteristics
                        .SCALER_STREAM_CONFIGURATION_MAP);
                // 拍照大小,选择能支持的一个最大的图片大小
                mImageReader = ImageReader.newInstance(MediaInfo.PRIVIEW_WIDTH, MediaInfo.PRIVIEW_HEIGHT, ImageFormat.YUV_420_888, 2);
                mImageReader.setOnImageAvailableListener(   // 设置监听和后台线程处理器
                        mOnImageAvailableListener, null);
                // 预览大小,根据上面选择的拍照图片的长宽比,选择一个和控件长宽差不多的大小
                mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height, MediaInfo.PRIVIEW_WIDTH, MediaInfo.PRIVIEW_HEIGHT);
                Log.d(TAG, "preview size: " + mPreviewSize.getWidth() + "*" + mPreviewSize.getHeight());
                // 打开摄像头
                mCameraManager.openCamera(Integer.toString(0), mStateCallback, mBackgroundHandler);
                startSendDataThread();
            } catch (Exception e) {
                Log.e(TAG,"No current camera was found!");
                e.printStackTrace();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 关闭摄像头
     */
    public void releaseCamera() {
        Log.v(TAG, "releaseCamera");
        try {
            if (isRecording) {
                isRecording = false;
                if (mediaRecorder != null) {
                    mediaRecorder.stop();
                    mediaRecorder.reset();
                    mediaRecorder.release();
                    if (null != SDCardUtils.getInsSDCardPath()) {
                        long end = System.currentTimeMillis();
                        long duration = end - startRecordTime;
                        SQLiteFactory.getInstance().getFileDao().insertFileEntity(startRecordTime, end, 3, (int) duration, 0);
                    }
                }
                /*
                 * 生成录像对
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值