0
votes

Issue:- Camera Preview different from Image Captured using Camera2 Api. And the problem occurs only in landscape mode.

Requirement:- My requirement is to capture an image in landscape mode using camera2 api.Camera Preview should be in full screen.

I have followed the following github sample :- https://github.com/googlesamples/android-Camera2Basic

This sample works fine in portrait mode as well as in landscape mode if Texture View is wrap_content as the aspect ratio is maintained.

But to display the camera preview in full screen, i changed TextureView to match_parent. By doing that the output got changed. Now preview of camera is different from the image captured.

Please check the images attached here.

1.Camera Preview:-Screenshot of camera preview

2.Image Captured:-On Tapping Picture button

Following is my code snippet:-

fragment_camera2_basic.xml

<RelativeLayout 
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<com.example.android.camera2basic.AutoFitTextureView
    android:id="@+id/texture"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_alignParentStart="true"
    android:layout_alignParentTop="true" />

<FrameLayout
    android:id="@+id/control"
    android:layout_width="match_parent"
    android:layout_height="112dp"
    android:layout_alignParentBottom="true"
    android:layout_alignParentStart="true"
    android:background="@color/control_background">

    <Button
        android:id="@+id/picture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="@string/picture" />

    <ImageButton
        android:id="@+id/info"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical|right"
        android:padding="20dp"
        android:src="@drawable/ic_action_info" />

</FrameLayout>

</RelativeLayout>

Following is the code to capture image:-

 /**
 * Initiate a still image capture.
 */
private void takePicture() {
    lockFocus();
}

/**
 * Lock the focus as the first step for a still image capture.
 */
private void lockFocus() {
    try {
        // This is how to tell the camera to lock focus.
        mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
                CameraMetadata.CONTROL_AF_TRIGGER_START);
        // Tell #mCaptureCallback to wait for the lock.
        mState = STATE_WAITING_LOCK;
        mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
                mBackgroundHandler);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

/**
 * Run the precapture sequence for capturing a still image. This method should be called when
 * we get a response in {@link #mCaptureCallback} from {@link #lockFocus()}.
 */
private void runPrecaptureSequence() {
    try {
        // This is how to tell the camera to trigger.
        mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
                CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
        // Tell #mCaptureCallback to wait for the precapture sequence to be set.
        mState = STATE_WAITING_PRECAPTURE;
        mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
                mBackgroundHandler);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

/**
 * Capture a still picture. This method should be called when we get a response in
 * {@link #mCaptureCallback} from both {@link #lockFocus()}.
 */
private void captureStillPicture() {
    try {
        final Activity activity = getActivity();
        if (null == activity || null == mCameraDevice) {
            return;
        }
        // This is the CaptureRequest.Builder that we use to take a picture.
        final CaptureRequest.Builder captureBuilder =
                mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
        captureBuilder.addTarget(mImageReader.getSurface());

        // Use the same AE and AF modes as the preview.
        captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
        setAutoFlash(captureBuilder);

        // Orientation
        int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
        captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation));

        CameraCaptureSession.CaptureCallback CaptureCallback
                = new CameraCaptureSession.CaptureCallback() {

            @Override
            public void onCaptureCompleted(@NonNull CameraCaptureSession session,
                                           @NonNull CaptureRequest request,
                                           @NonNull TotalCaptureResult result) {
                showToast("Saved: " + mFile);
                Log.d(TAG, mFile.toString());
                unlockFocus();
            }
        };

        mCaptureSession.stopRepeating();
        mCaptureSession.abortCaptures();
        mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

/**
 * Retrieves the JPEG orientation from the specified screen rotation.
 *
 * @param rotation The screen rotation.
 * @return The JPEG orientation (one of 0, 90, 270, and 360)
 */
private int getOrientation(int rotation) {
    // Sensor orientation is 90 for most devices, or 270 for some devices (eg. Nexus 5X)
    // We have to take that into account and rotate JPEG properly.
    // For devices with orientation of 90, we simply return our mapping from ORIENTATIONS.
    // For devices with orientation of 270, we need to rotate the JPEG 180 degrees.
    return (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360;
}

/**
 * Unlock the focus. This method should be called when still image capture sequence is
 * finished.
 */
private void unlockFocus() {
    try {
        // Reset the auto-focus trigger
        mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
                CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
        setAutoFlash(mPreviewRequestBuilder);
        mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
                mBackgroundHandler);
        // After this, the camera will go back to the normal state of preview.
        mState = STATE_PREVIEW;
        mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback,
                mBackgroundHandler);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

I tried to set CaptureRequest.SCALER_CROP_REGION in camera preview builder and image reader as well but it did not work as expected.

1

1 Answers

0
votes

The TextureView is probably partially outside the display, in terms of the layout.
So it's being cut off.

You may want to confirm that with Android Studio's layout tools

In general, you can't get identical images unless you match aspect ratios, so if you want full-screen preview, you'll have to select a JPEG aspect ratio that matches the screen.

That may not available directly from the camera, so you may need to crop the JPEG yourself. But most likely you can at least get 16:9 preview and still capture relatively easily, which will have relatively small black bars, compared to the 4:3 maximum still capture size.