0
点赞
收藏
分享

微信扫一扫

Android Studio 中CameraX的实现

官方使用的是Kotlin代码,这里用Java。


导入依赖

在app的build.gradle中加入

// Use the most recent version of CameraX, currently that is alpha04
def camerax_version = "1.0.0-alpha05"
//noinspection GradleDependency
implementation "androidx.camera:camera-core:${camerax_version}"
//noinspection GradleDependency
implementation "androidx.camera:camera-camera2:${camerax_version}"

布局文件

布局一般是加一个TextureView来预览,因为用了不同的布局(ConstraintLayout),要到build.gradle中再添加implementation 'androidx.constraintlayout:constraintlayout:1.1.3'依赖。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center|center_horizontal|center_vertical"
    android:orientation="vertical"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    >

    <TextureView
        android:id="@+id/view_finder"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

权限

为了后续方便,加入了照相机,存储读、写权限,录音权限是防止之后使用录像功能忘记打开声音而出错。

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

启动照相

在本个activity中的onCreat中加入:

        // viewFinder 在前面定义
        viewFinder = findViewById(R.id.view_finder);
        viewFinder.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5, int i6, int i7) {
                updateTransform();
            }
        });

        viewFinder.post(new Runnable() {
            @Override
            public void run() {
                startCamera();
            }
        });

其中两个方法分别是updateTransform、startCamera:

    private void updateTransform() {
        Matrix matrix = new Matrix();
        // Compute the center of the view finder
        float centerX = viewFinder.getWidth() / 2f;
        float centerY = viewFinder.getHeight() / 2f;

        float[] rotations = {0,90,180,270};
        // Correct preview output to account for display rotation
        float rotationDegrees = rotations[viewFinder.getDisplay().getRotation()];

        matrix.postRotate(-rotationDegrees, centerX, centerY);

        // Finally, apply transformations to our TextureView
        viewFinder.setTransform(matrix);
    }

更新相机预览:主要是给TextureView设置一个旋转的矩阵变化,防止预览方向不对

private void startCamera() {
        CameraX.unbindAll();
        // 1. preview
        PreviewConfig previewConfig = new PreviewConfig.Builder()
                .setLensFacing(CameraX.LensFacing.BACK)
//                .setTargetAspectRatio(new Rational(1, 1))
//                .setTargetResolution(new Size(640, 640))
                .build();

        Preview preview = new Preview(previewConfig);
        preview.setOnPreviewOutputUpdateListener(new Preview.OnPreviewOutputUpdateListener() {
            @Override
            public void onUpdated(Preview.PreviewOutput output) {
                ViewGroup parent = (ViewGroup) viewFinder.getParent();
                parent.removeView(viewFinder);
                parent.addView(viewFinder, 0);

                viewFinder.setSurfaceTexture(output.getSurfaceTexture());
                updateTransform();
            }
        });
        CameraX.bindToLifecycle(this, preview);
}

启动相机:创建PreviewConfig和Preview这两个对象,可以设置预览图像的尺寸和比例,在OnPreviewOutputUpdateListener回调中用setSurfaceTexture方法,将相机图像输出到TextureView。最后用CameraX.bindToLifecycle方法将相机与当前页面的生命周期绑定。

bug记录:
在CameraX.bindToLifecycle(this, preview);出现错误
解决:activity继承类改为AppCompatActivity,而且在AndroidManifest.xml增加android:theme="@style/Theme.AppCompat.Light.NoActionBar"

拍照

拍照创建ImageCaptureConfig和ImageCapture这两个对象,放储存路径。

        // 2. capture
        ImageCaptureConfig imageCaptureConfig = new ImageCaptureConfig.Builder()
                .setTargetAspectRatio(new Rational(1,1))
                .setCaptureMode(ImageCapture.CaptureMode.MIN_LATENCY)
                .build();
        final ImageCapture imageCapture = new ImageCapture(imageCaptureConfig);
        viewFinder.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                File externalFilesDir = getExternalFilesDir(Environment.DIRECTORY_DCIM);
                String storePath = externalFilesDir + "/" + System.currentTimeMillis() + ".jpg";
                File photo = new File(storePath);
//                /storage/emulated/0/Android/data/com.example.android_ncnn_yolov4_tiny/cache/1623981023712.jpg
                imageCapture.takePicture(photo, new ImageCapture.OnImageSavedListener() {
                    @Override
                    public void onImageSaved(@NonNull File file) {
                        // showToast("saved " + file.getAbsolutePath());
                        System.out.println(file.getAbsolutePath());
                    }
                    @Override
                    public void onError(@NonNull  ImageCapture.ImageCaptureError imageCaptureError, @NonNull  String message, @Nullable  Throwable cause) {
                        // showToast("error " + message);
                        cause.printStackTrace();
                    }
                });
                return true;
            }
        });
        CameraX.bindToLifecycle(this, preview, imageCapture);
public void showToast(String msg) {
        Toast.makeText(LocationActivity.this, msg, Toast.LENGTH_SHORT).show();
    }

bug记录:
安卓存储空间分内部(手机)和外部(卡),很难找到路径,在测试时候使用的手机没有安卡,存放位置有一点点问题。

处理

实时获取相机的图像数据,做想要的分析处理。

// 3. analyze
        HandlerThread handlerThread = new HandlerThread("Analyze-thread");
        handlerThread.start();

        ImageAnalysisConfig imageAnalysisConfig = new ImageAnalysisConfig.Builder()
                .setCallbackHandler(new Handler(handlerThread.getLooper()))
                .setImageReaderMode(ImageAnalysis.ImageReaderMode.ACQUIRE_LATEST_IMAGE)
                .setTargetAspectRatio(new Rational(2, 3))
//                .setTargetResolution(new Size(600, 600))
                .build();

        ImageAnalysis imageAnalysis = new ImageAnalysis(imageAnalysisConfig);
        imageAnalysis.setAnalyzer(new MyAnalyzer());

        CameraX.bindToLifecycle(this, preview, imageCapture, imageAnalysis);

分析处理的内容是新建了一个MyAnalzer()的类

private class MyAnalyzer implements ImageAnalysis.Analyzer {
        @Override
        public void analyze(ImageProxy imageProxy, int rotationDegrees) {
            final Image image = imageProxy.getImage();
            if(image != null) {
                Log.d("尺寸", image.getWidth() + "," + image.getHeight());
                // 这里写自己的处理
            }
        }
    }
举报

相关推荐

0 条评论