I am currently trying to implement computer vision algorithms on my android device. My idea is to have a camera preview running and draw on top of the preview (in a separate view) edges and other geometric figures. To command the camera, I use the camera2 API.
I start a capture session with two requests with the following targets:
- Request number 1: target/surface -> an ImageReader for image processing
- Request number 2: target/surface -> a SurfaceView for camera preview
I burst both requests with setRepeatingBurst. Now a problem occurs when I process the image in the ImageReader callback (onImageAvailable). When I process for let's say 500 ms then the image preview drops to 1 fps. I think the problem is because the image.close() gets delayed and that the second request is somehow connected to that image.
I thought that if I have two requests then there are sent two individual images to each surface. But apparently that's not the case. Well, I could make a quick copy in the ImageReader callback and close the image afterwards. And then start processing the copy. But the thing is that I don't want to copy.
Here is the code:
HandlerThread imageProcessingThread = new HandlerThread("imageProcessingThread");
imageProcessingThread.start();
Handler imageProcessingHandler = new Handler(imageProcessingThread.getLooper());
ImageReader imageReader = ImageReader.newInstance(width, height, format, 1);
imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
Image image = reader.acquireNextImage();
if(image == null) return;
// simulating processing
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// processing end
image.close();
}
}, imageProcessingHandler);
try {
HandlerThread imageProcessingThread3 = new HandlerThread("imageProcessingThread3");
imageProcessingThread3.start();
Handler imageProcessingHandler3 = new Handler(imageProcessingThread3.getLooper());
camManager.openCamera(cam.second, new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice cameraDevice) {
try {
List<Surface> surfaces = new ArrayList<>();
surfaces.add(binding.svPreview.getHolder().getSurface());
surfaces.add(imageReader.getSurface());
cameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
try {
CaptureRequest.Builder requestBuilder1 = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
requestBuilder1.addTarget(imageReader.getSurface());
requestBuilder1.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, new Range<>(30, 30));
CaptureRequest.Builder requestBuilder2 = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
requestBuilder2.addTarget(binding.svPreview.getHolder().getSurface());
requestBuilder2.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, new Range<>(30, 30));
HandlerThread imageProcessingThread2 = new HandlerThread("imageProcessingThread2");
imageProcessingThread2.start();
Handler imageProcessingHandler2 = new Handler(imageProcessingThread2.getLooper());
List<CaptureRequest> requests = new ArrayList<>();
requests.add(requestBuilder1.build());
requests.add(requestBuilder2.build());
session.setRepeatingBurst(requests, new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
}
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
}
}, imageProcessingHandler2);
} catch (Exception ex) {
fatalError(ex.getMessage());
return;
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
fatalError("onConfigureFailed");
return;
}
}, null);
} catch (Exception ex) {
fatalError(ex.getMessage());
return;
}
}
@Override
public void onDisconnected(@NonNull CameraDevice cameraDevice) {
}
@Override
public void onError(@NonNull CameraDevice cameraDevice, int i) {
}
}, imageProcessingHandler3);
} catch (Exception ex) {
fatalError(ex.getMessage());
return;
}