CameraX Image Analysis (with OUTPUT_IMAGE_FORMAT_RGBA_8888) : maxImages (4) 已经获取,在获取更多之前调用#close

问题描述 投票:0回答:2

我正在尝试实现一个非常基本的 CameraX 图像分析用例,我会将分析器接收到的图像发送到 tflite 分类器模型。该模型只接受位图作为输入我想我会在构建 ImageAmalysis 时设置 OUTPUT_IMAGE_FORMAT_RGBA_8888。与标准 YUV_420_888 相比,生成的媒体图像更容易转换为位图。问题是我得到一个错误:“java.lang.IllegalStateException: maxImages (4) has already been acquired, call #close before acquiring more.”通常这意味着我忘记关闭 imageProxy 但事实并非如此,所以我不知道我的代码有什么问题。见下文:

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Bitmap;

import android.media.Image;
import android.os.Bundle;
import android.util.Size;

import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ExperimentalGetImage;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.ImageProxy;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner;

import com.google.common.util.concurrent.ListenableFuture;

import java.nio.ByteBuffer;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;


public class MainActivity extends AppCompatActivity {

    private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;

    private Executor analysisExecutor;

    private PreviewView previewView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        analysisExecutor = Executors.newSingleThreadExecutor();

        setContentView(R.layout.activity_main);
        previewView = findViewById(R.id.viewFinder);

        cameraProviderFuture = ProcessCameraProvider.getInstance(this);       

        cameraProviderFuture.addListener(() -> {
            try {
                ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
                cameraProvider.unbindAll();
                bindUseCases(cameraProvider);
            } catch (ExecutionException | InterruptedException e) {
                // No errors need to be handled for this Future.
                // This should never be reached.
            }
        }, ContextCompat.getMainExecutor(this));

    }

    void bindUseCases(@NonNull ProcessCameraProvider cameraProvider) {

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

        //Preview
        Preview preview = new Preview.Builder()
                .build();
        preview.setSurfaceProvider(previewView.getSurfaceProvider());

        //ImageAnalysis
        ImageAnalysis imageAnalysis =
                new ImageAnalysis.Builder()
                        .setTargetResolution(new Size(1280, 720))
                        .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                        .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
                        .build();
        imageAnalysis.setAnalyzer(analysisExecutor, new ImageAnalysis.Analyzer() {
            @Override
            @ExperimentalGetImage
            public void analyze(@NonNull ImageProxy imageProxy) {
                //my code to turn media image into a bitmap
                Image img = imageProxy.getImage();
                Image.Plane[] planes = img.getPlanes();
                ByteBuffer buffer = planes[0].getBuffer();
                int pixelStride = planes[0].getPixelStride();
                int rowStride = planes[0].getRowStride();
                int rowPadding = rowStride - pixelStride * img.getWidth();
                Bitmap bitmap = Bitmap.createBitmap(img.getWidth()+rowPadding/pixelStride, img.getHeight(), Bitmap.Config.ARGB_8888);
                bitmap.copyPixelsFromBuffer(buffer);
                //close img and imageProxy
                img.close();
                imageProxy.close();
            }
        });

        Camera camera = cameraProvider.bindToLifecycle((LifecycleOwner)this, cameraSelector, imageAnalysis, preview);
    }

}

完整的错误信息:

E/ImageAnalysisAnalyzer: 获取图像失败。 java.lang.IllegalStateException: maxImages (4) 已经获取,获取更多之前调用#close。 在 android.media.ImageReader.acquireNextImage(ImageReader.java:527) 在 android.media.ImageReader.acquireLatestImage(ImageReader.java:411) 在 androidx.camera.core.AndroidImageReaderProxy.acquireLatestImage(AndroidImageReaderProxy.java:56) 在 androidx.camera.core.SafeCloseImageReaderProxy.acquireLatestImage(SafeCloseImageReaderProxy.java:67) 在 androidx.camera.core.ImageYuvToRgbConverter.convertYUVToRGB(ImageYuvToRgbConverter.java:83) 在 androidx.camera.core.ImageAnalysisAbstractAnalyzer.analyzeImage(ImageAnalysisAbstractAnalyzer.java:125) 在 androidx.camera.core.ImageAnalysisNonBlockingAnalyzer.onValidImageAvailable(ImageAnalysisNonBlockingAnalyzer.java:103) 在 androidx.camera.core.ImageAnalysisAbstractAnalyzer.onImageAvailable(ImageAnalysisAbstractAnalyzer.java:69) 在 androidx.camera.core.SafeCloseImageReaderProxy.lambda$setOnImageAvailableListener$1$SafeCloseImageReaderProxy(SafeCloseImageReaderProxy.java:170) 在 androidx.camera.core.-$$Lambda$SafeCloseImageReaderProxy$vlVuGMKvMVqmwbJFm3dTgGgUzu4.onImageAvailable(未知 资料来源:4) 在 androidx.camera.core.AndroidImageReaderProxy.lambda$setOnImageAvailableListener$0$AndroidImageReaderProxy(AndroidImageReaderProxy.java:139) 在 androidx.camera.core.-$$Lambda$AndroidImageReaderProxy$ydxkGVJ03P0ZMYkq3dfSV-hzi3E.run(未知 资料来源:4) 在 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 在 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 在 java.lang.Thread.run(Thread.java:923)

java android-camerax rgba
2个回答
1
投票

你应该像这样从图像中获得所有需要的数据后立即关闭图像:

public void analyze(@NonNull ImageProxy imageProxy) {
    //my code to turn media image into a bitmap
    Image img = imageProxy.getImage();
    Image.Plane[] planes = img.getPlanes();
    ByteBuffer buffer = planes[0].getBuffer();
    int pixelStride = planes[0].getPixelStride();
    int rowStride = planes[0].getRowStride();
    int imgWidth = img.getWidth();
    int imgHeight = img.getHeight();
    //close imageProxy
    imageProxy.close();
    int rowPadding = rowStride - pixelStride * img.getWidth();
    Bitmap bitmap = Bitmap.createBitmap(imgWidth + rowPadding / pixelStride, imgHeight, Bitmap.Config.ARGB_8888);
    bitmap.copyPixelsFromBuffer(buffer);
}

0
投票

我认为

imageProxy.close();
在分析返回新的 ImageProxy 之前为时已晚。也许在
imageProxy.close()
的下方调用
imageProxy.getImage();

© www.soinside.com 2019 - 2024. All rights reserved.