Вопрос
Как я могу использовать )" 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();
}