Camera1, Camera2, CameraView, CameraX, Camera2, CameraView, CameraX, Camera2, CameraView, CameraX, Camera2, CameraView, CameraX, CameraView Mainly camera preview, size, orientation, and image data processing.

size

The size here is mainly the preview size, the photo size and the View size to display the preview picture.

Preview size

How do I get preview sizes? We can get from the source of CameraView, divided into Camera1 and Camera2.

Camera1

mCameraParameters = mCamera.getParameters();
// Supported preview sizes
mPreviewSizes.clear();
for (Camera.Size size : mCameraParameters.getSupportedPreviewSizes()) {
    Log.d("DEBUG"."###### SupportedPreviewSizes: width=" + size.width + ", height="
            + size.height);
    mPreviewSizes.add(new Size(size.width, size.height));
}
Copy the code

Camera2

mPreviewSizes.clear();
for (android.util.Size size : map.getOutputSizes(mPreview.getOutputClass())) {
    int width = size.getWidth();
    int height = size.getHeight();
    if (width <= MAX_PREVIEW_WIDTH && height <= MAX_PREVIEW_HEIGHT) {
        mPreviewSizes.add(newSize(width, height)); }}Copy the code

The preview sizes supported by different manufacturers and systems are different. Here are all the preview sizes supported by Redmi Note 5A:

SupportedPreviewSizes: width=1280, height=720
SupportedPreviewSizes: width=960, height=720
SupportedPreviewSizes: width=864, height=480
SupportedPreviewSizes: width=800, height=480
SupportedPreviewSizes: width=768, height=432
SupportedPreviewSizes: width=720, height=480
SupportedPreviewSizes: width=640, height=640
SupportedPreviewSizes: width=640, height=480
SupportedPreviewSizes: width=480, height=640
SupportedPreviewSizes: width=640, height=360
SupportedPreviewSizes: width=576, height=432
SupportedPreviewSizes: width=480, height=360
SupportedPreviewSizes: width=480, height=320
SupportedPreviewSizes: width=384, height=288
SupportedPreviewSizes: width=352, height=288
SupportedPreviewSizes: width=320, height=240
SupportedPreviewSizes: width=240, height=320
SupportedPreviewSizes: width=240, height=160
SupportedPreviewSizes: width=176, height=144
SupportedPreviewSizes: width=144, height=176
SupportedPreviewSizes: width=160, height=120
Copy the code

Here the size of the proportion is generally 4:3, 16:9, the other proportion is cut out on this basis

Select preview size

Under the same aspect to height ratio, select the width and height closest to View to avoid excessive preview size, resulting in performance loss, resulting in preview lag. AspectRatio DEFAULT_ASPECT_RATIO = AspectRatio. Of (4, 3)

Camera1

private Size chooseOptimalSize(SortedSet<Size> sizes) {
    if(! mPreview.isReady()) {// Not yet laid out
        return sizes.first(); // Return the smallest size
    }
    int desiredWidth;
    int desiredHeight;
    final int surfaceWidth = mPreview.getWidth();
    final int surfaceHeight = mPreview.getHeight();
    if (isLandscape(mDisplayOrientation)) {
        desiredWidth = surfaceHeight;
        desiredHeight = surfaceWidth;
    } else {
        desiredWidth = surfaceWidth;
        desiredHeight = surfaceHeight;
    }
    Size result = null;
    for (Size size : sizes) { // Iterate from small to large
        if (desiredWidth <= size.getWidth() && desiredHeight <= size.getHeight()) {
            return size;

        }
        result = size;
    }
    return result;
}
Copy the code

The horizontal and vertical screens are distinguished, and then the dimensions of the width and height are equal to or greater than the width and height of the View.

Camera2

private Size chooseOptimalSize(a) {
    int surfaceLonger, surfaceShorter;
    final int surfaceWidth = mPreview.getWidth();
    final int surfaceHeight = mPreview.getHeight();
    if (surfaceWidth < surfaceHeight) {
        surfaceLonger = surfaceHeight;
        surfaceShorter = surfaceWidth;
    } else {
        surfaceLonger = surfaceWidth;
        surfaceShorter = surfaceHeight;
    }
    SortedSet<Size> candidates = mPreviewSizes.sizes(mAspectRatio);

    // Pick the smallest of those big enough
    for (Size size : candidates) {
        if (size.getWidth() >= surfaceLonger && size.getHeight() >= surfaceShorter) {
            returnsize; }}// If no size is big enough, pick the largest one.
    return candidates.last();
}
Copy the code

First judge the width and height of the View, distinguish between the larger value and the smaller value, and then get the size of the width and height of the View is greater than or equal to the larger value and smaller value of the size.

Photo size

The code was also taken from CameraView

Camera1

mPictureSizes.clear();
for (Camera.Size size : mCameraParameters.getSupportedPictureSizes()) {
    Log.d("DEBUG"."###### SupportedPictureSizes: width=" + size.width + ", height="
            + size.height);
    mPictureSizes.add(new Size(size.width, size.height));
}
Copy the code

Camera2

protected void collectPictureSizes(SizeMap sizes, StreamConfigurationMap map) {
    for (android.util.Size size : map.getOutputSizes(ImageFormat.JPEG)) {
        mPictureSizes.add(newSize(size.getWidth(), size.getHeight())); }}Copy the code

Camera sizes supported on Redmi Note 5A:

SupportedPictureSizes: width=4160, height=3120
SupportedPictureSizes: width=4160, height=2340
SupportedPictureSizes: width=4096, height=3072
SupportedPictureSizes: width=4096, height=2304
SupportedPictureSizes: width=4000, height=3000
SupportedPictureSizes: width=3840, height=2160
SupportedPictureSizes: width=3264, height=2448
SupportedPictureSizes: width=3200, height=2400
SupportedPictureSizes: width=2976, height=2976
SupportedPictureSizes: width=2592, height=1944
SupportedPictureSizes: width=2592, height=1458
SupportedPictureSizes: width=2688, height=1512
SupportedPictureSizes: width=2304, height=1728
SupportedPictureSizes: width=2048, height=1536
SupportedPictureSizes: width=2336, height=1314
SupportedPictureSizes: width=1920, height=1080
SupportedPictureSizes: width=1600, height=1200
SupportedPictureSizes: width=1440, height=1080
SupportedPictureSizes: width=1280, height=960
SupportedPictureSizes: width=1280, height=768
SupportedPictureSizes: width=1280, height=720
SupportedPictureSizes: width=1200, height=1200
SupportedPictureSizes: width=1024, height=768
SupportedPictureSizes: width=800, height=600
SupportedPictureSizes: width=864, height=480
SupportedPictureSizes: width=800, height=480
SupportedPictureSizes: width=720, height=480
SupportedPictureSizes: width=640, height=480
SupportedPictureSizes: width=640, height=360
SupportedPictureSizes: width=480, height=640
SupportedPictureSizes: width=480, height=360
SupportedPictureSizes: width=480, height=320
SupportedPictureSizes: width=352, height=288
SupportedPictureSizes: width=320, height=240
SupportedPictureSizes: width=240, height=320
Copy the code

Here the size ratio is generally 4:3, 16:9

Select camera size

Both Camaer1 and Camera2 follow the same logic, selecting the maximum size in a fixed aspect ratio so that the picture will be the clearest.

Size largest = mPictureSizes.sizes(mAspectRatio).last();
Copy the code

The direction of

There are two Settings: image preview orientation and photo orientation. Before we get there, a few concepts need to be introduced:

  • Screen coordinate direction
  • Natural direction of equipment
  • Camera Sensor Direction
  • Camera preview direction

Screen coordinate direction

In the Android system, the top left corner of the screen is the origin (0,0) coordinate system. The coordinate system is fixed and will not change due to the change of device direction.

Natural screen orientation

Every device has a natural orientation, mobile phones and tablets have different natural orientation, as shown in the picture, here is a picture stolen:

OrientationEventListener

abstract public void onOrientationChanged(int orientation);
Copy the code

OnOrientationChanged returns an Angle from 0 to 359, where 0 represents the natural direction.

Camera Sensor Direction

Camera preview direction

The orientation of the image captured by the camera sensor and displayed on the screen is the camera preview orientation. By default, the orientation is the same as that of the Camera sensor, which can be changed through the Camera API. Camaer1 can set the preview orientation using setDisplayOrientation, and Camera2 can set the preview orientation using TextureView. Orientation is different for different camera positions. Orientation is the Angle that the camera sensor rotates clockwise to the natural orientation of the screen.

The rear

The rear orientation90


Pre –

Orientation270 in front of orientation270, after the image is collected (without mirror processing), it must be displayed on the screen according to the coordinate system of the natural direction of the screen. It needs to rotate 270 degrees clockwise to be consistent with the natural direction of the device. When I preview, I mirror it, so I just need to rotate it 90 degrees clockwise to match the natural orientation. Camera1 and Camera2 specify the preview direction of the code from CameraView:

Camera1

private int calcDisplayOrientation(int screenOrientationDegrees) {
    if (mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
        return (360 - (mCameraInfo.orientation + screenOrientationDegrees) % 360) % 360;    // compensate the mirror
    } else {  // back-facing
        return (mCameraInfo.orientation - screenOrientationDegrees + 360) % 360; }}Copy the code

The code distinguishes between front and rear cameras.

  • The rear:(mCameraInfo.orientation - screenOrientationDegrees + 360) % 360To return to the natural direction, clockwise rotation is required, while the counterclockwise rotation of the screen just counterbalances the rotation of the camera. The difference between the two is +360.
  • Lead:(mCameraInfo.orientation + screenOrientationDegrees) % 360The vertical view of the screen is a mirror image, 360-(mCameraInfo.orientation + screenOrientationDegrees) % 360If you rotate the difference clockwise, it goes in the natural direction, except it’s a mirror image, flipped left and right

Camera2 uses TextureView’s setTransform for rotation and differentiates between vertical and horizontal screens.

/**
* Configures the transform matrix for TextureView based on {@link #mDisplayOrientation} and
* the surface size.
*/
void configureTransform(a) {
    Matrix matrix = new Matrix();
    if (mDisplayOrientation % 180= =90) {
        final int width = getWidth();
        final int height = getHeight();
        // Rotate the camera preview when the screen is landscape.
        matrix.setPolyToPoly(
                new float[] {0.f, 0.f, // top left
                        width, 0.f, // top right
                        0.f, height, // bottom left
                        width, height, // bottom right
                }, 0,
                mDisplayOrientation == 90 ?
                        // Clockwise
                        new float[] {0.f, height, // top left
                                0.f, 0.f, // top right
                                width, height, // bottom left
                                width, 0.f, // bottom right
                        } : // mDisplayOrientation == 270
                        // Counter-clockwise
                        new float[]{
                                width, 0.f, // top left
                                width, height, // top right
                                0.f, 0.f, // bottom left
                                0.f, height, // bottom right
                        }, 0.4);
    } else if (mDisplayOrientation == 180) {
        matrix.postRotate(180, getWidth() / 2, getHeight() / 2);
    }
    mTextureView.setTransform(matrix);
}
Copy the code

Photo direction

Setting the preview direction does not change the direction of the resulting photo. For the rear-mounted camera, the image captured by the camera is the same as the image previewed by the camera, only the orientation degree of the rear-mounted camera needs to be rotated. For front-facing cameras, the image previewed by the camera and the image captured by the camera are mirror images. Captured image: rotated 270 degrees clockwise, consistent with the natural orientation of the screen. Preview image: rotate 90 degrees clockwise to match the natural orientation of the screen. Finally, steal a picture to illustrate:

Camera1

Using mCameraParameters. SetRotation () set after the photo image direction:

mCameraParameters.setRotation(calcCameraRotation(displayOrientation)); ./**
* Calculate camera rotation
*
* This calculation is applied to the output JPEG either via Exif Orientation tag
* or by actually transforming the bitmap. (Determined by vendor camera API implementation)
*
* Note: This is not the same calculation as the display orientation
*
* @param screenOrientationDegrees Screen orientation in degrees
* @return Number of degrees to rotate image in order for it to view correctly.
*/
private int calcCameraRotation(int screenOrientationDegrees) {
    if (mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
        return (mCameraInfo.orientation + screenOrientationDegrees) % 360;
    } else {  // back-facing
        final int landscapeFlip = isLandscape(screenOrientationDegrees) ? 180 : 0;
        return (mCameraInfo.orientation + screenOrientationDegrees + landscapeFlip) % 360; }}Copy the code

The image collected by the camera only needs to rotate the orientation degree of the camera.

Camera2

According to CameraCharacteristics SENSOR_ORIENTATION, set up a JPEG image using captureRequest direction of rotation.

// Calculate JPEG orientation.
@SuppressWarnings("ConstantConditions")
int sensorOrientation = mCameraCharacteristics.get(
        CameraCharacteristics.SENSOR_ORIENTATION);
captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION,
        (sensorOrientation +
                mDisplayOrientation * (mFacing == Constants.FACING_FRONT ? 1 : -1) +
                360) % 360);
Copy the code

Image data

The default data format returned by Android Camera is NV21. Through Camera1 mParameters. SetPreviewFormat () Settings, Camera2 through ImageReader. NewInstance () setting. NV21 and YV12 are officially recommended formats.NV21 and YV12 belong to YUV formats, which can also be represented as YCbCr. Cb and Cr are the same as U and V.

YUV

YUV is a color coding method, and it is similar to RGB color coding method, mainly used in the field of TV systems and analog video. YUV represents three components, Y represents brightness, U and V represent chroma, which in turn defines two aspects of color: hue and saturation. By separating Y from UV, a complete image can be displayed without UV information, but only grayscale images can be displayed.

YUV sampling format

The main sampling methods of YUV images are as follows:

  • YUV 4:4:4 sampling: Each Y corresponds to a set of UV components
  • YUV 4:2:2 sampling: every two Y’s share a set of UV components
  • YUV 4:2:0 sampling: each of the four Y’s share a set of UV components

The black dot represents the Y component of the pixel sampled, and the hollow circle represents the UV component of the pixel

YUV storage format

There are two storage formats, planar and Packed.

  • Planar: stores the Y of all pixels successively, then the U of all pixels, then the V of all pixels
  • Packed: Y,U and V of each pixel are stored continuously and alternately

YUV Pixel formats vary according to sampling methods and storage formats. The most common YUV formats are:

Sample/format
YUV422 YUVY format UYVY format YUV422P format
YUV420 YUV420P

(YV12, YU12 format)
YUV420P

(NV12, NV21 format)

YUVY format

YUVY format belongs to Packed storage format, and two adjacent Y’s share two adjacent U and V’s

Y0 UO Y1 V0 Y2 U2 Y3 V2
Copy the code

Y0 and Y1 share U0 and V0. Y2 and Y3 share U2 and V2

UYVY format

UYVY format also belongs to Packed storage format. Different from YUYV format, UYVY format has a different order of UV

YUV422P format

YUV422P format belongs to planar storage format, which stores Y of all pixels successively first, then U of all pixels, and then V of all pixels

Format: YV12, YU12

YU12 and YV12 formats both belong to YUV420P format, YUV420P is planar storage format. First store all the Y’s, then store U and V’s. The difference between YU12 and YV12 is that YU12 is Y, then U, then V, and YV12 is Y, then V, then U.

NV12 or NV21

NV12, NV21 YUV420SP format, YUV420SP is planar storage format. Store all Y first, then store in alternating UV or VU order. The NV12 format stores Y first, then UV and then interchangeably. In NV21 format, Y is stored first, and THEN VU is stored alternately. Finally steal a summary of the data format:

YV21: YYYYYYYY UU VV => YUV420P   
YV12: YYYYYYYY VV UU => YUV420P   
NV12: YYYYYYYY UV UV => YUV420SP   
NV21: YYYYYYYY VU VU => YUV420SP
Copy the code

Android Camera default data format is NV21, directly set mParameters Camera1. SetPreviewFormat (ImageFormat. NV21), then take photos in the callback raw data is NV21 data back. Imagereader.newinstance () : imageformat.nv21: imageformat.nv21

if (format == ImageFormat.NV21) {
    throw new IllegalArgumentException(
            "NV21 format is not supported");
}
Copy the code

It is explained in the latest imageformat.nv21:

YCrCb format used for images, which uses the NV21 encoding format.   
This is the default format for android.hardware.Camera preview images,
when not otherwise set with android.hardware.Camera.Parameters.setPreviewFormat(int).
For the android.hardware.camera2 API, the YUV_420_888 format is recommended for YUV output instead.
Copy the code

It is suggested to use YUV_420_888 instead of Camera2, so data transformation is required to obtain NV21 data. For details, please refer to the Image class analysis (combined with YUV_420_888).

reference

  • Android camera development and encountered pits
  • Android Camera2 tutorial chapter 3 preview
  • Size and orientation issues in Android camera development
  • Android camera preview direction and adaptation exploration
  • Understand the sampling and format of YUV
  • YUV420 data format
  • Brief analysis of Image class (combined with YUV_420_888)