Marco Righini
Marco Righini

Reputation: 516

camera2 pictures taken are blurred

I'm experiencing a bad behavior when capturing a photo on a Samsung Galaxy S7 edge device.

I wait simultaneously for focus and exposure before taking the picture but at the end I have a kind of blurred image compared to the native camera app output.

The problem is especially visible on zoomed images but is present also when not zoomed. I've also tried to enable the optical image stabilization but the problem isn't fixed.

Below are linked sample images explaining the problem.

Native camera image

My camera image

Here is the code:

Method for picture capture step 1:

public void capturePicture() {
    CameraState state = getState();
    if (state != IDLE && state != CLOSING && state != TAKE_PICTURE) {
            boolean af = false;
            boolean ae = false;
            if (isAFEnabled()) {
                af = true;
            } else if (isAEEnabled()) {
                ae = true;
            }

            if (!af && !ae) {
                takePicture();
            } else {
                triggerFocusAndExposure(true);
            }
    }

    return;
}

Method for triggering focus and exposure:

private void triggerFocusAndExposure(boolean picture) {
    setState(WAIT_PRECAPTURE_PICTURE);

    if (isAFEnabled()) {
        previewBuilder.set(CONTROL_AF_TRIGGER, CONTROL_AF_TRIGGER_START);
    }

    if (isAEEnabled()) {
        previewBuilder.set(CONTROL_AE_PRECAPTURE_TRIGGER, CONTROL_AE_PRECAPTURE_TRIGGER_START);
    }

    try {
        cameraSession.capture(previewBuilder.build(), new SCameraCaptureSession.CaptureCallback() {
            @Override public void onCaptureCompleted(SCameraCaptureSession session, SCaptureRequest request, STotalCaptureResult result) {
                setState(PRECAPTURE_TRIGGERED_PICTURE);
            }
        }, backgroundHandler);
    } catch (CameraAccessException e) {
        return;
    }

    try {
        previewBuilder.set(CONTROL_AF_TRIGGER, CONTROL_AF_TRIGGER_IDLE);
        previewBuilder.set(CONTROL_AE_PRECAPTURE_TRIGGER, CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
        cameraSession.setRepeatingRequest(previewBuilder.build(), mSessionCaptureCallback, backgroundHandler);
    } catch (CameraAccessException e) {
    }
}

Methods for waiting focus and exposure:

private void waitPrecapture(STotalCaptureResult result, boolean picture) {
    // Check if AF/AE triggered and/or finished
    if ((!isAFTriggered(result) || isAfFinished(result)) && (!isAETriggered(result) || isAEFinished(result))) {
        takePicture();
    }
}

private boolean isAFTriggered(STotalCaptureResult result) {
    Integer afMode = result.get(SCaptureResult.CONTROL_AF_MODE);
    return afMode != CONTROL_AF_MODE_OFF &&
            afMode != CONTROL_AF_MODE_EDOF;
}

private boolean isAfFinished(STotalCaptureResult result) {
    int afState = result.get(SCaptureResult.CONTROL_AF_STATE);
    return afState == CONTROL_AF_STATE_FOCUSED_LOCKED || afState == CONTROL_AF_STATE_NOT_FOCUSED_LOCKED
            || afState == CONTROL_AF_STATE_PASSIVE_FOCUSED || afState == CONTROL_AF_STATE_PASSIVE_UNFOCUSED;
}

private boolean isAETriggered(STotalCaptureResult result) {
    boolean aeMode = result.get(SCaptureResult.CONTROL_AE_MODE) != SCaptureResult.CONTROL_AE_MODE_OFF;
    return aeMode && cameraCharacteristics.get(SCameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)
            != SCameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
}

private boolean isAEFinished(STotalCaptureResult result) {
    Integer aeState = result.get(SCaptureResult.CONTROL_AE_STATE);
    return aeState == null || aeState == SCaptureResult.CONTROL_AE_STATE_CONVERGED || aeState == SCaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED
            || aeState == SCaptureResult.CONTROL_AE_STATE_LOCKED;
}

Method for picture capture step 2:

public void takePicture(){
    imageReader.setOnImageAvailableListener(reader -> {
        Image image = reader.acquireLatestImage();
        ByteBuffer buffer = image.getPlanes()[0].getBuffer();
        byte[] bytes = new byte[buffer.remaining()];
        buffer.get(bytes);
        image.close();
        singleSubscriber.onSuccess(bytes);
    }, imageSavingHandler);

    try {
        cameraSession.capture(captureBuilder.build(), new SCameraCaptureSession.CaptureCallback() {
             @Override
             public void onCaptureCompleted(SCameraCaptureSession session, SCaptureRequest request, STotalCaptureResult result) {
                 if (getState() == CameraState.CLOSING) {
                     return;
                 }
                 cancelAF();
             }

             @Override
             public void onCaptureStarted(SCameraCaptureSession session, SCaptureRequest request, long timestamp, long frameNumber) {
                 super.onCaptureStarted(session, request, timestamp, frameNumber);
                 shutterCallback.call();
             }    

             @Override public void onCaptureFailed(SCameraCaptureSession session, SCaptureRequest request, SCaptureFailure failure) {
                  singleSubscriber.onError(new RuntimeException("Error taking picture, onCaptureFailed"));
                  if (getState() == CameraState.CLOSING) {
                      return;
                  }
                  cancelAF();
             }
         }, backgroundHandler);

         setState(CameraState.TAKE_PICTURE);
     } catch (CameraAccessException e) {
         singleSubscriber.onError(new RuntimeException("Error capturing image", e));
     }
}

Upvotes: 1

Views: 2685

Answers (1)

Marco Righini
Marco Righini

Reputation: 516

I've found the fix. captureBuilder automatically sets CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY and CaptureRequest.EDGE_MODE_HIGH_QUALITY but in my case the device has some problem handling these options. Setting CaptureRequest.NOISE_REDUCTION_MODE_FAST and CaptureRequest.EDGE_MODE_FAST fixes the problem.

Upvotes: 8

Related Questions