Android Camera 2: изображения ImageReader не имеют значения шага

Я пытался направить изображения с камеры на ImageReader, чтобы я мог управлять изображениями напрямую, используя Camera2 API. Когда у меня есть поток сеанса захвата в SurfaceView, поток работает нормально. Когда я затем устанавливаю поток сеанса захвата для своего ImageReader, я замечаю, что изображения каким-то образом недействительны.

В моей функции обратного вызова OnImageAvailable ImageReader я вытаскиваю следующее доступное изображение и пытаюсь его прочитать. Вот где у меня проблема. Изображение не пустое, и плоскости есть, но буферы плоскостей сначала равны нулю. Когда я пытаюсь захватить буферы, они внезапно перестают быть нулевыми, но попытка чтения из них приводит к сбою приложения без трассировки стека. Кроме того, шаг пикселей и строк в плоскостях устанавливается равным 0. Однако ширина и высота изображения задаются правильно.

Поэтому я думаю, что я неправильно настраиваю свой ImageReader. Тогда возникает вопрос, что я делаю неправильно?

Код:

public class CompatibleCamera {
    private static final int CAMERA2_API_LEVEL = 23;
    public static final int FORMAT_RAW = ImageFormat.RAW_SENSOR;
    public static final int FORMAT_JPEG = ImageFormat.JPEG;
    private static final int MAX_IMAGES = 2;
    // Interface for the user to use. User supplies the function to manipulate the image
    public interface ImageTransform
    {
        void doTransform(Image image);
    }

    //***********Camera 2 API Members***********

    // The camera2 API CameraManager. Used to access the camera device
    private CameraManager mCamera2Manager;
    // The information used by the device to reference the camera. Not a camera object itself
    private CameraDevice mCamera2Device;
    private String mCamera2DeviceID = "";
    // The class that allows us to get the camera's image

    private ImageReader mImageReader;
    // This listener is where we have the programmer deal with the image. Just edit the interface
    private ImageReader.OnImageAvailableListener mListener;
    // This is the thread for the handler. It keeps it off the UI thread so we don't block the GUI

    private HandlerThread mCameraCaptureHandlerThread;
    // This runs in the background and handles the camera feed, activating the OnImageAvailableListener
    private Handler mCameraCaptureHandler;

    private HandlerThread mImageAvailableHandlerThread;
    // This runs in the background and handles the camera feed, activating the OnImageAvailableListener
    private Handler mImageAvailableHandler;

    // This object is the camera feed, essentially. We store it so we can properly close it later
    private CameraCaptureSession cameraCaptureSession;

    // DEBUG
    private boolean TEST_SURFACE_VIEW = false;
    private Surface dbSurface;

    // Mutex lock. Locks and unlocks when the ImageReader is pulling and processing an image
    private Semaphore imageReaderLock = new Semaphore(1);

    //***********Common Members***********

    // The context of the activity holding this object
    private Context mContext;

    // Our ImageTransform implementation to alter the image as it comes in
    private ImageTransform mTransform;

    private int iImageFormat= FORMAT_RAW;

    //==========Methods==========

    public CompatibleCamera(Context context, ImageTransform transform, int imageFormat)
    {
        mContext = context;
        mTransform = transform;

        mListener = new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader imageReader) {
                try {
                    imageReaderLock.acquire();
                    Image image = imageReader.acquireNextImage();

                    //<--------------Problem With Image is Here-------------->
                    mTransform.doTransform(image);
                    image.close();
                    imageReaderLock.release();
                }
                catch(InterruptedException ex)
                {
                    ex.printStackTrace();
                }
            }
        };
    }

    private boolean camera2GetManager()
    {
        //----First, get the CameraManager and a Camera Device----
        mCamera2Manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
        if (mCamera2Manager == null) {
            System.out.println("    DEBUG: Manager is null");
            return false;
        }
        else {
            System.out.println("    DEBUG: Camera Manager obtained");

            try {
                String[] cameraIDs = mCamera2Manager.getCameraIdList();

                for (String cameraID : cameraIDs) {
                    CameraCharacteristics cameraCharacteristics = mCamera2Manager.getCameraCharacteristics(cameraID);
                    if (cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) ==
                            CameraCharacteristics.LENS_FACING_BACK) {
                        mCamera2DeviceID = cameraID;
                        break;
                    }
                }
                if (mCamera2DeviceID.equals("")) {
                    System.out.println("No back camera, exiting");
                    return false;
                }
                System.out.println("    DEBUG: Camera Device obtained");

                // Open the Camera Device
            } catch (Exception ex) {
                ex.printStackTrace();
                return false;
            }
            return camera2OpenCamera();
        }
    }

    private boolean camera2SetupImageReader()
    {
        // Get the largest image size available

        CameraCharacteristics cameraCharacteristics;
        try {
            cameraCharacteristics= mCamera2Manager.getCameraCharacteristics(mCamera2DeviceID);
        } catch(Exception e) {
            e.printStackTrace();
            return false;
        }

        StreamConfigurationMap map = cameraCharacteristics.get(
                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
        Size largestSize = Collections.max(
                Arrays.asList(map.getOutputSizes(iImageFormat)),
                new CompareSizesByArea());


        // Set up the handler
        mCameraCaptureHandlerThread = new HandlerThread("cameraCaptureHandlerThread");
        mCameraCaptureHandlerThread.start();
        mCameraCaptureHandler = new Handler(mCameraCaptureHandlerThread.getLooper());


        mImageAvailableHandlerThread = new HandlerThread("imageReaderHandlerThread");
        mImageAvailableHandlerThread.start();
        mImageAvailableHandler = new Handler(mImageAvailableHandlerThread.getLooper());

        mImageReader = ImageReader.newInstance( largestSize.getWidth(),
                largestSize.getHeight(),
                iImageFormat,
                MAX_IMAGES);
        mImageReader.setOnImageAvailableListener(mListener, mImageAvailableHandler);

        // This callback is used to asynchronously set up the capture session on our end
        final CameraCaptureSession.StateCallback captureStateCallback = new CameraCaptureSession.StateCallback() {
            // When configured, set the target surface
            @Override
            public void onConfigured(@NonNull CameraCaptureSession session) {
                try
                {
                    CaptureRequest.Builder requestBuilder = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
                    if (TEST_SURFACE_VIEW)
                        requestBuilder.addTarget(dbSurface);
                    else
                        requestBuilder.addTarget(mImageReader.getSurface());
                    //set to null - image data will be produced but will not receive metadata
                    session.setRepeatingRequest(requestBuilder.build(), null, mCameraCaptureHandler);
                    cameraCaptureSession = session;
                }
                catch (Exception ex)
                {
                    ex.printStackTrace();
                }
            }

            @Override
            public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
                System.out.println("Failed to configure the capture session :(");
            }
        };

        ArrayList<Surface> surfaces = new ArrayList<>();
        if (TEST_SURFACE_VIEW)
            surfaces.add(dbSurface);
        else
            surfaces.add(mImageReader.getSurface());

        try
        {
            mCamera2Device.createCaptureSession(surfaces, captureStateCallback, mCameraCaptureHandler);
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
        return true;
    }
}

person Abahu    schedule 08.07.2018    source источник


Ответы (1)


RAW_SENSOR - особый зверь форматов.

Общий формат необработанного изображения сенсора камеры, обычно представляющий одноканальное мозаичное изображение Байера. Каждый образец цвета пикселя хранится с точностью до 16 бит.

Макет цветной мозаики, максимальные и минимальные значения кодирования необработанных данных пикселей, цветовое пространство изображения и вся другая необходимая информация для интерпретации необработанного изображения датчика должны быть запрошены из android.hardware.camera2.CameraDevice, создавший изображение.

Вы не должны пытаться использовать информацию о шаге напрямую, как если бы это был кадр YUV.

person Alex Cohn    schedule 08.07.2018