如何根据预览显示上的矩形裁剪 CameraX 捕获的图像

of3*_*inc 6 android android-studio android-camerax

你好,我是android开发新手,谁能帮我根据PreviewView上的矩形裁剪CameraX捕获的图像?

这是activity_main.xml:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/frameRoot"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.camera.view.PreviewView
            android:id="@+id/viewFinder"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <View
            android:id="@+id/viewOverlay_center"
            android:background="@drawable/card_rectangle"
            android:layout_width="270dp"
            android:layout_height="400dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toTopOf="@id/ll_bottom"/>

        <LinearLayout
            android:id="@+id/ll_bottom"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:background="@color/black"
            android:gravity="center"
            android:orientation="horizontal"
            android:padding="16dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent">

            <ImageView
                android:id="@+id/image_capture_button"
                android:layout_width="60dp"
                android:layout_height="60dp"
                android:background="?android:selectableItemBackground"
                android:src="@drawable/shutter_camera" />
        </LinearLayout>
        
    </androidx.constraintlayout.widget.ConstraintLayout>

</FrameLayout>
Run Code Online (Sandbox Code Playgroud)

这是我的MainActivity.java:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        viewBinding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(viewBinding.getRoot());

        viewBinding.imageCaptureButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                capturePhoto();
            }
        });
    }

    private void capturePhoto(){
        if (imageCapture == null) {
            return;
        }

        String filename = "Card-" + mDateFormat.format(System.currentTimeMillis());


        ContentValues contentValues = new ContentValues();
        contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "Card-" + mDateFormat.format(System.currentTimeMillis()));
        contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");


        Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);

        imageCapture.takePicture(ContextCompat.getMainExecutor(this), new ImageCapture.OnImageCapturedCallback() {
            @Override
            public void onCaptureSuccess(@NonNull ImageProxy imageProxy) {
                super.onCaptureSuccess(imageProxy);

                ImageInfo imageInfo = imageProxy.getImageInfo();

                Bitmap image = rotateBitmapIfNeeded(imageProxyToBitmap(imageProxy),imageInfo);
                saveBitmap(cropBitmapToCard(image,viewBinding.frameRoot,viewBinding.viewOverlayCenter),uri);

                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setClass(MainActivity.this,  DecoderActivity.class);
                intent.putExtra("KEY", uri.toString());
                startActivity(intent);

                imageProxy.close();
            }

            @Override
            public void onError(@NonNull ImageCaptureException exception) {
                super.onError(exception);
                Log.e("MainActivity","Error on takePicture",exception);
            }
        });

    }

    private void startCamera() {
        cameraProviderFuture = ProcessCameraProvider.getInstance(this);
        cameraProviderFuture.addListener(new Runnable() {
            @Override
            public void run() {
                try {
                    ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
                    startCameraX(cameraProvider);
                } catch (ExecutionException | InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, ContextCompat.getMainExecutor(this));
    }

    void startCameraX(@NonNull ProcessCameraProvider cameraProvider) {
        cameraProvider.unbindAll();

        CameraSelector cameraSelector = new CameraSelector.Builder()
                .requireLensFacing(CameraSelector.LENS_FACING_BACK)
                .build();

        // preview use case
        Preview preview = new Preview.Builder().build();
        preview.setSurfaceProvider(viewBinding.viewFinder.getSurfaceProvider());

        // image capture use case
        imageCapture = new ImageCapture.Builder()
                .setTargetRotation(this.getWindowManager().getDefaultDisplay().getRotation())
                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                .build();

        // bind to lifecycle
        cameraProvider.bindToLifecycle((LifecycleOwner) this, cameraSelector, preview, imageCapture);
    }

    void saveBitmap(Bitmap target, Uri uri){
        try {
            OutputStream output = getContentResolver().openOutputStream(uri);
            target.compress(Bitmap.CompressFormat.JPEG, 100, output);
        }
        catch (Exception e) {
            Log.d("onBtnSavePng", e.toString()); // java.io.IOException: Operation not permitted
        }
    }

    Bitmap cropBitmapToCard(Bitmap source,View frame, View cardPlaceHolder){
        float scaleX = source.getWidth() / (float)frame.getWidth();
        float scaleY = source.getHeight() / (float) frame.getHeight();

        int x =(int)((cardPlaceHolder.getLeft()) * scaleX);
        int y = (int)((cardPlaceHolder.getTop()) * scaleY);

        Log.v("MainActivity-Crop","leftPos: " + cardPlaceHolder.getLeft() + " width: " + cardPlaceHolder.getWidth());

        int width = (int)(cardPlaceHolder.getWidth() * scaleX);
        int height = (int)(cardPlaceHolder.getHeight() * scaleY);

        return Bitmap.createBitmap(source,x,y,width,height);
    }

    private Bitmap imageProxyToBitmap(ImageProxy image)
    {
        ImageProxy.PlaneProxy planeProxy = image.getPlanes()[0];
        ByteBuffer buffer = planeProxy.getBuffer();
        byte[] bytes = new byte[buffer.remaining()];
        buffer.get(bytes);

        return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
    }

    private Bitmap rotateBitmapIfNeeded(Bitmap source, ImageInfo info){
        int angle = info.getRotationDegrees();
        Matrix mat = new Matrix();
        mat.postRotate(angle);
        return Bitmap.createBitmap(source,0,0,source.getWidth(),source.getHeight(),mat,true);
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是我的应用程序的样子(预览模式),我想提取矩形内位图的 ROI

在此输入图像描述

我得到了这个,你可以看到我的裁剪仍然不正确。

在此输入图像描述

y 轴似乎存在映射错误。有人可以帮助我如何正确裁剪图像吗?因为我已经搜索和研究如何做到这一点,但仍然感到困惑并且不适合我

Xi *_*ang 1

请查看 androidx.camera.view.transform下的 API 。这是一些未经测试的示例代码:

OutputTransform source = previewView.getOutputTransform()
// imageProxy is the output of the ImageCapture.
OutputTransform target = ImageProxyTransformFactory().getOutputTransform(imageProxy);;

// Build the transform from ImageAnalysis to PreviewView
CoordinateTransform coordinateTransform = new CoordinateTransform(source, target);

// The variable box here is your bounding box in PreviewView
coordinateTransform.mapRect(box);

// At this point, the variable box will contain the value you need.
Run Code Online (Sandbox Code Playgroud)