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());
                    }
                });
    }

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

相关推荐
百锦再10 分钟前
Android Studio开发 SharedPreferences 详解
android·ide·android studio
青春给了狗22 分钟前
Android 14 修改侧滑手势动画效果
android
CYRUS STUDIO29 分钟前
Android APP 热修复原理
android·app·frida·hotfix·热修复
火柴就是我1 小时前
首次使用Android Studio时,http proxy,gradle问题解决
android
limingade2 小时前
手机打电话时电脑坐席同时收听对方说话并插入IVR预录声音片段
android·智能手机·电脑·蓝牙电话·电脑打电话
浩浩测试一下2 小时前
计算机网络中的DHCP是什么呀? 详情解答
android·网络·计算机网络·安全·web安全·网络安全·安全架构
青春给了狗3 小时前
Android 14 系统统一修改app启动时图标大小和圆角
android
pengyu4 小时前
【Flutter 状态管理 - 柒】 | InheritedWidget:藏在组件树里的"魔法"✨
android·flutter·dart
居然是阿宋5 小时前
Kotlin高阶函数 vs Lambda表达式:关键区别与协作关系
android·开发语言·kotlin
凉、介6 小时前
PCI 总线学习笔记(五)
android·linux·笔记·学习·pcie·pci