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

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

相关推荐
sun00770019 小时前
android ndk编译valgrind
android
AI视觉网奇20 小时前
android studio 断点无效
android·ide·android studio
jiaxi的天空20 小时前
android studio gradle 访问不了
android·ide·android studio
No Silver Bullet21 小时前
android组包时会把从maven私服获取的包下载到本地吗
android
catchadmin21 小时前
PHP serialize 序列化完全指南
android·开发语言·php
tangweiguo030519871 天前
Kable使用指南:Android BLE开发的现代化解决方案
android·kotlin
00后程序员张1 天前
iOS App 混淆与资源保护:iOS配置文件加密、ipa文件安全、代码与多媒体资源防护全流程指南
android·安全·ios·小程序·uni-app·cocoa·iphone
柳岸风1 天前
Android Studio Meerkat | 2024.3.1 Gradle Tasks不展示
android·ide·android studio
编程乐学1 天前
安卓原创--基于 Android 开发的菜单管理系统
android
whatever who cares1 天前
android中ViewModel 和 onSaveInstanceState 的最佳使用方法
android