AndroidX Jetpack CameraX使用介绍

AndroidX Jetpack CameraX使用介绍

CameraX介绍

CameraX 是一个 Jetpack 库,旨在帮助更简便的开发相机应用。如果你要开发新应用,建议您从 CameraX 开始。它提供了一个一致且易于使用的 API,该 API 适用于绝大多数

Android 设备,并向后兼容 Android 5.0(API 级别 21)

CameraX 结构

  • 预览:用于显示预览的 Surface,例如 PreviewView
  • 图片分析:提供 CPU 可访问的缓冲区
  • 图片拍摄:拍摄并保存照片
  • 视频拍摄:通过 VideoCapture 拍摄视频和音频(视频拍摄有兴趣的可以自己进行研究一下)

使用介绍

库引用

kotlin 复制代码
    dependencies{
        var camerax_version="1.3.0-alpha04"
        //可忽略,因为核心库是由camera-camera2间接包含
        implementation("androidx.camera:camera-core:${camerax_version}")
        //核心库是由camera-camera2
        implementation("androidx.camera:camera-camera2:${camerax_version}")
        //CameraX生命周期库
        implementation("androidx.camera:camera-lifecycle:${camerax_version}")
        //视频库
        implementation("androidx.camera:camera-video:${camerax_version}")
        //预览View库
        implementation("androidx.camera:camera-view:${camerax_version}")
        //CameraX视觉
        implementation("androidx.camera:camera-mlkit-vision:${camerax_version}")
        //CameraX扩展库
        implementation("androidx.camera:camera-extensions:${camerax_version}")
    }

在使用CameraX的时候与Camera一样需要权限

ini 复制代码
    //摄像头权限
    <uses-permission android:name="android.permission.CAMERA" />

预览实现

使用PreviewView进行预览,这是一种可以剪裁、缩放和旋转以确保正确显示的 View,当相机处于活动状态时,图片预览会流式传输到 PreviewView 中的 Surface

ini 复制代码
    <androidx.camera.view.PreviewView
        android:id="@+id/preview_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

布局创建完成后需获取ProcessCameraProvider,在创建View的时候需检查。

ini 复制代码
    public ListenableFuture<ProcessCameraProvider> getInstance(Context context) {
        if (instance == null) {
            instance = ProcessCameraProvider.getInstance(context);
        }
        return instance;
    }

我是在Application去创建的ProcessCameraProvider,在Activity中的View创建时获取ProcessCameraProvider对象,进行预览设置,生命周期绑定

scss 复制代码
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mActivityMainBinding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(mActivityMainBinding.getRoot());
        //预览View
        previewView = mActivityMainBinding.previewView;
        //获取ProcessCameraProvider对象
        instance = CameraUtils.getInstance().getInstance(this);
        instance.addListener(new Runnable() {
            @Override
            public void run() {
                //创建 Preview
                Preview preview = CameraUtils.getInstance().getPreview();
                try {
                    //解绑
                    instance.get().unbindAll();
                    //绑定生命周期、图片分析、拍照
                    instance.get().bindToLifecycle(MainActivity.this, CameraUtils.getInstance().getCameraSelector(), preview
                            , CameraUtils.getInstance().getImageCapture() /*创建 ImageCapture*/
                            , CameraUtils.getInstance().getImageAnalysis()/*创建 ImageAnalysis*/);
                } catch (ExecutionException e) {
                    throw new RuntimeException(e);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                //绑定预览
                preview.setSurfaceProvider(previewView.getSurfaceProvider());
            }
        }, ContextCompat.getMainExecutor(this));

    }

bindToLifecycle() 会返回一个 Camera 对象。如需详细了解如何控制相机输出(如变焦和曝光)可进行设置

PreviewView进行预览是还可实现渲染模式切换(默认模式PERFORMANCE)与视频缩放,具体可以去官方的实现预览API看看

图像分析

图像分析为应用提供 CPU 访问的图像,可以对这些图像执行图像处理、计算机视觉或机器学习推断。应用会实现对每个帧运行的 analyze() 方法

图像分析视频帧输出有两种模式:非阻塞(默认模式)与阻塞

非阻塞模式:在该模式下,执行器始终会将最新的图像缓存到图像缓冲区(与深度为 1 的队列相似),与此同时,应用会分析上一个图像

阻塞模式:在该模式下,内部执行器可以向内部图像队列添加多个图像,并仅在队列已满时才开始丢帧

图像分析实现

构建图像分析用例ImageAnalysis

scss 复制代码
    public ImageAnalysis getImageAnalysis() {
        if (mImageAnalysis == null) {
            mImageAnalysis = new ImageAnalysis.Builder()
//                    .setBackgroundExecutor(executor -> {
//
//                    })
                    .setBackpressureStrategy(STRATEGY_KEEP_ONLY_LATEST)//输出流策略,
                    .setImageQueueDepth(STRATEGY_BLOCK_PRODUCER)
                    .setOutputImageRotationEnabled(true) //允许输出流旋转
//                    .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_YUV_420_888) //输出流格式
//                    .setTargetAspectRatio(AspectRatio.RATIO_4_3)  //设置宽高比
//                    .setTargetName("" + System.currentTimeMillis()) //设置名称
                    .setTargetResolution(new Size(800, 1280)) //设置输出的分辨率
                    .setTargetRotation(ROTATION_270) //输出流成像方向
                    .build();
        }
        return mImageAnalysis;
    }

在构建ImageAnalysis的时候还可依据自己的需求进行相关配置,例如输出流成像方向,输出流格式等等

构建的ImageAnalysis绑定生命周期,同时创建ImageAnalysis.Analyzer,获取ImageProxy

less 复制代码
    CameraUtils.getInstance().getImageAnalysis().setAnalyzer(CameraXExecutors.mainThreadExecutor(), new ImageAnalysis.Analyzer() {
        @Override
        public void analyze(@NonNull ImageProxy image) {
            f (image.getFormat() == YUV_420_888) {
                Log.d(TAG, "onCreate: YUV_420_888");
            }
            //创建位图
            Bitmap bgBitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
            //获取YuvToRgbConverter对象
            YuvToRgbConverter yuvToRgbConverter = new YuvToRgbConverter(MyApplication.mContext);
            //填充位图,获取Bitmap
            yuvToRgbConverter.yuvToRgb(Objects.requireNonNull(image.getImage()),bgBitmap);
            //释放当前帧
            image.close();
        }
    });

ImageProxy同时可转换出需要的数据量,下面是NV21的例子

ini 复制代码
    //CPU占用10%
    public static byte[] yuv420ToNv21(ImageProxy image) {
        ImageProxy.PlaneProxy[] planes = image.getPlanes();
        ByteBuffer yBuffer = planes[0].getBuffer();
        ByteBuffer uBuffer = planes[1].getBuffer();
        ByteBuffer vBuffer = planes[2].getBuffer();
        int ySize = yBuffer.remaining();
        int uSize = uBuffer.remaining();
        int vSize = vBuffer.remaining();
        int size = image.getWidth() * image.getHeight();
        byte[] nv21 = new byte[size * 3 / 2];
        yBuffer.get(nv21, 0, ySize);
        vBuffer.get(nv21, ySize, vSize);

        byte[] u = new byte[uSize];
        uBuffer.get(u);

        //每隔开一位替换V,达到VU交替
        int pos = ySize + 1;
        for (int i = 0; i < uSize; i++) {
            if (i % 2 == 0) {
                nv21[pos] = u[i];
                pos += 2;
            }
        }
        return nv21;
    }

图片拍摄

图片拍摄用例旨在拍摄高分辨率的优质照片,不仅提供简单的相机手动控制功能,还提供自动白平衡、自动曝光和自动对焦 (3A) 功能

  • takePicture(Executor, OnImageCapturedCallback):此方法为拍摄的图片提供内存缓冲区
  • takePicture(OutputFileOptions, Executor, OnImageSavedCallback):此方法将拍摄的图片保存到提供的文件位置

图片拍摄实现

构建ImageCapture用例

scss 复制代码
    public ImageCapture getImageCapture() {
        if (mImageCapture == null) {
            mImageCapture = new ImageCapture.Builder()
//                    .setCaptureMode(CAPTURE_MODE_MAXIMIZE_QUALITY) //设置拍照捕获模式
//                    .setFlashMode(FLASH_MODE_OFF)  //设置补光模式
//                    .setIoExecutor(command -> {
//
//                    })
                    .setJpegQuality(100)  //设置图像质量
//                    .setTargetAspectRatio(AspectRatio.RATIO_4_3)  //设置图像宽高比
//                    .setTargetName("" + System.currentTimeMillis()) //设置截图名称
                    .setTargetResolution(new Size(800, 1280)) //设置图像分辨率
                    .setTargetRotation(ROTATION_270) //设置图像旋转
                    .build();
        }
        return mImageCapture;
    }

在构建ImageCapture的时候还可依据自己的需求进行相关配置,例如图像旋转角度,图像质量等等

构建的ImageCapture需要绑定生命周期

less 复制代码
    public  void takePic() {
        File file = new File(VIDEOCALL_PICTURE_PATH, VIDEOCALL_PICTURE_NAME);
        ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(file).build();
        Uri uri = Uri.fromFile(file);
        CameraXOpen.getInstance().getImageCapture().takePicture(outputFileOptions,
                CameraXExecutors.mainThreadExecutor(),
                new ImageCapture.OnImageSavedCallback() {
                    @Override
                    public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
                        Log.d(TAG, "outputFileResults:"+ outputFileResults.getSavedUri() + "\nuri = " + uri);
                   }

                    @Override
                    public void onError(@NonNull ImageCaptureException exception) {
                        Log.d(TAG, "onError:" + exception.getImageCaptureError());
                    }
                });
    }

视频拍摄,我这边不做介绍,有兴趣的可以自行了解学习下

相关推荐
sunly_1 小时前
Flutter:InheritedWidget数据共享
android·javascript·flutter
Q8137574601 小时前
智能算法引领金融创新:正大科技的智能分析框架
android·开发语言·kotlin
张铁铁是个小胖子2 小时前
整合seata遇到的问题
android
一航jason4 小时前
Android Jetpack Compose 现有Java老项目集成使用compose开发
android·java·android jetpack
猿小蔡-Cool4 小时前
Android 中的 Zygote 和 Copy-on-Write 机制详解
android·zygote
顾北川_野4 小时前
android 默认关闭增强型4GLTE开关;去掉VT视频通话功能及菜单
android·开发语言
海绵波波1074 小时前
聊天服务器(7)数据模块
android·服务器·adb
软件聚导航5 小时前
uniapp 实现 ble蓝牙同时连接多台蓝牙设备,支持app、苹果(ios)和安卓手机,以及ios连接蓝牙后的一些坑
android·ios·uni-app
深海呐10 小时前
Android 最新的AndroidStudio引入依赖失败如何解决?如:Failed to resolve:xxxx
android·failed to res·failed to·failed to resol·failed to reso
解压专家66610 小时前
安卓解压软件推荐:高效处理压缩文件的实用工具
android·智能手机·winrar·7-zip