Не мешайте ARCore с помощью SharedCamera - PullRequest
0 голосов
/ 12 марта 2019

Вопрос

Как я могу использовать )" rel="nofollow noreferrer"> SharedCamera в ARCore для получения изображений с камеры в ImageReader, но без вмешательства в процессы ARCore?

Состояние

ImageReader получает изображения, которые работают, но стандартный BaseArFragment, который я использую, всегда черный в моем приложении.

Я не вызываю setRepeatingRequest (CaptureRequest, CameraCaptureSession.CaptureCallback, Handler) , как указано в документации ARCore.

Так что, с моей точки зрения, ARCore вообще не должен менять свое поведение, и ImageReader получает отдельные изображения.

код

Я изменил пример google для совместно используемой камеры на github , так как я не хочу использовать вызовы OpenGl или рисовать, просто рисую на ArSceneView, как это всегда делает стандартный обычный ARCore. Вот мой текущий код:

/*****************************/
/**Camera*2*API*ARCore*Start**/
/*****************************/

/**Background handler thread, used to run camera callbacks without blocking the UI thread.*/
private HandlerThread backgroundThread;
/**Background handler, used to run camera callbacks as thread without blocking the UI thread.*/
private Handler backgroundHandler;
/**Image reader that continuously processes CPU images.*/
private ImageReader cpuImageReader;
/**Ensure native JNI face landmark detection calls only occur when a new image is available.*/
private boolean processNewImage = true;
/**True, if the {@link Session} is resumed, active, false if paused, inaktive.*/
private boolean sessionResumed = false;
/**Blocks on block() if close() was called before until open() is called. Needed on the UI thread wait for the {@link CameraDevice.StateCallback#onClosed(CameraDevice)} call on pause session.*/
private ConditionVariable cameraDeviceCanClose = new ConditionVariable();
/**The current capture session of the camera.*/
private CameraCaptureSession captureSession;
/**The current shared camera.*/
private SharedCamera sharedCamera;
/**The current shared camera device.*/
private CameraDevice cameraDevice;

/**Starts a background handler thread, used to run camera callbacks without blocking the UI thread.*/
public void startBackgroundThread() {
    if (backgroundThread == null) { // Only open if not already opend
        backgroundThread = new HandlerThread("sharedCameraBackground");
        backgroundThread.start();
        backgroundHandler = new Handler(backgroundThread.getLooper());

        configureImageReader();
    }
    pauseARCore();
}

/**Stops the background handler thread, used to run camera callbacks without blocking the UI thread.*/
public void stopBackgroundThread() {
    pauseARCore();

    if (backgroundThread != null) {
        backgroundThread.quitSafely();
        try {
            backgroundThread.join();
            backgroundThread = null;
            backgroundHandler = null;
        } catch (InterruptedException e) {
            Log.e(TAG, "Interrupted while trying to join background handler thread", e);
        }
    }
}

/**Resumes the {@link Session}. This method is necessary since {@link Session} has no isPaused method.*/
private void resumeARCore() {
    try {
        if(!sessionResumed) {
            sessionResumed = true;
            sharedSession.resume();
        }
    } catch (CameraNotAvailableException e) {
        e.printStackTrace();
    }
}

/**Pauses the {@link Session} and releases resources by closing capture, camera device, and the cpu image reader. This method is necessary since {@link Session} has no isPaused method.*/
private void pauseARCore(){
    if(sessionResumed){
        sessionResumed = false;
        sharedSession.pause();

        if(captureSession != null){
            captureSession.close(); // sets captureSession = null;
        }

        if(cameraDevice != null){
            cameraDeviceCanClose.close();
            cameraDevice.close(); // sets this.cameraDevice = null
            cameraDeviceCanClose.block();
        }

        if (cpuImageReader != null) {
            cpuImageReader.close();
            cpuImageReader = null;
        }
    }
}

/**Called to configure image retrieval from the shared camera.
 * @see <a href="https://github.com/google-ar/arcore-android-sdk/blob/master/samples/shared_camera_java/app/src/main/java/com/google/ar/core/examples/java/sharedcamera/SharedCameraActivity.java">Shared Camera GitHub Example by Google</a>*/
@SuppressLint("MissingPermission") // Is checked in BaseFragment#resume ... :/
private void configureImageReader() {
    sharedCamera = sharedSession.getSharedCamera(); // Store the ARCore shared camera reference, https://developers.google.com/ar/develop/java/camera-sharing, https://github.com/google-ar/arcore-android-sdk/blob/master/samples/shared_camera_java/app/src/main/java/com/google/ar/core/examples/java/sharedcamera/SharedCameraActivity.java
    CameraConfig cc = sharedSession.getCameraConfig();
    String cameraId = cc.getCameraId(); // Store the ID of the camera used by ARCore.
    Size s = cc.getImageSize();

    cpuImageReader = ImageReader.newInstance(s.getWidth(), s.getHeight(), ImageFormat.YUV_420_888, 1); // TODO ImageFormat.YUV_420_888?
    cpuImageReader.setOnImageAvailableListener(getImageAvailableListener(), backgroundHandler);
    sharedCamera.setAppSurfaces(cameraId, Arrays.asList(cpuImageReader.getSurface()));

    CameraDevice.StateCallback cameraDeviceCallback = sharedCamera.createARDeviceStateCallback(getCameraDeviceCallback(), backgroundHandler);
    CameraManager cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);

    try {
        cameraManager.openCamera(cameraId, cameraDeviceCallback, backgroundHandler);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

private int counter = 0;
/**CPU image reader callback, processes received images.
 * @return Callback interface for being notified that a new image is available. The onImageAvailable is called per image basis, that is, callback fires for every new frame available from ImageReader.*/
private ImageReader.OnImageAvailableListener getImageAvailableListener(){ 
    return imageReader -> {
        if(processNewImage) {
            processNewImage = false;

            Image image = imageReader.acquireLatestImage();

            if (image != null) { // THIS WORKS ALL 
                if(counter++ < 6) {
                    Bitmap b = bitmapFrom(image);
                    b.recycle();
                    processNewImage = true;
                }
                else
                    image.close();
            }
            // Skips null images
        }
    };
}

/**Callback for the {@link CameraDevice}, sets on open the {@link ImageReader#getSurface()} to receive images.*/
private CameraDevice.StateCallback getCameraDeviceCallback() {
   return new CameraDevice.StateCallback(){
        @Override
        public void onOpened(@NonNull CameraDevice cameraDevice) {
            PictureProcessing.this.cameraDevice = cameraDevice;
            List<Surface> s = sharedCamera.getArCoreSurfaces();

            s.add(cpuImageReader.getSurface());

            CameraCaptureSession.StateCallback camSessionCallback = sharedCamera.createARSessionStateCallback(getCameraCaptureSessionStateCallback(), backgroundHandler);

            try {
                cameraDevice.createCaptureSession(s, camSessionCallback, backgroundHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

       @Override
       public void onClosed(@NonNull CameraDevice camera) {
           cameraDeviceCanClose.open();
           PictureProcessing.this.cameraDevice = null;
       }

       @Override
        public void onDisconnected(@NonNull CameraDevice cameraDevice) {
            cameraDevice.close();
            PictureProcessing.this.cameraDevice = null;
        }

        @Override
        public void onError(@NonNull CameraDevice cameraDevice, int i) {
            cameraDevice.close();
            PictureProcessing.this.cameraDevice = null;
        }
    };
}

private CameraCaptureSession.StateCallback getCameraCaptureSessionStateCallback(){
    return new CameraCaptureSession.StateCallback() {
        @Override
        public void onActive(@NonNull CameraCaptureSession session) {
            PictureProcessing.this.captureSession = session;
            resumeARCore();
        }

        @Override
        public void onClosed(@NonNull CameraCaptureSession session) {
            PictureProcessing.this.captureSession = null;
        }

        @Override
        public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
            PictureProcessing.this.captureSession = cameraCaptureSession;
        }

        @Override
        public void onConfigureFailed(@NonNull CameraCaptureSession session) {
            Log.e(TAG, "Failed to configure camera capture session.");
        }
    };
}

/***************************/
/**Camera*2*API*ARCore*End**/
/***************************/

Методы start и stopBackgroundThread вызываются из расширенного BaseARFragment, код:

    @Override
public void onResume() {
    super.onResume();
    getPictureProcessing.startBackgroundThread();
}

@Override
public void onPause() {
    pictureProcessing.stopBackgroundThread();
    super.onPause();
}
...