Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2f5bb33c authored by Igor Murashkin's avatar Igor Murashkin Committed by Android Git Automerger
Browse files

am 89b3f042: Merge "camera2: (LEGACY) Add face detection support and vstab modes" into lmp-dev

* commit '89b3f042a6e82635e60038e5441b685f715e56a5':
  camera2: (LEGACY) Add face detection support and vstab modes
parents 1081fa6a cd3a994e
Loading
Loading
Loading
Loading
+80 −6
Original line number Original line Diff line number Diff line
@@ -67,6 +67,7 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashMap;
import java.util.List;


/**
/**
 * Implementation of camera metadata marshal/unmarshal across Binder to
 * Implementation of camera metadata marshal/unmarshal across Binder to
@@ -227,6 +228,7 @@ public class CameraMetadataNative implements Parcelable {


    private static final String CELLID_PROCESS = "CELLID";
    private static final String CELLID_PROCESS = "CELLID";
    private static final String GPS_PROCESS = "GPS";
    private static final String GPS_PROCESS = "GPS";
    private static final int FACE_LANDMARK_SIZE = 6;


    private static String translateLocationProviderToProcess(final String provider) {
    private static String translateLocationProviderToProcess(final String provider) {
        if (provider == null) {
        if (provider == null) {
@@ -347,7 +349,7 @@ public class CameraMetadataNative implements Parcelable {
        // Check if key has been overridden to use a wrapper class on the java side.
        // Check if key has been overridden to use a wrapper class on the java side.
        GetCommand g = sGetCommandMap.get(key);
        GetCommand g = sGetCommandMap.get(key);
        if (g != null) {
        if (g != null) {
            return (T) g.getValue(this, key);
            return g.getValue(this, key);
        }
        }
        return getBase(key);
        return getBase(key);
    }
    }
@@ -587,9 +589,71 @@ public class CameraMetadataNative implements Parcelable {
        return availableFormats;
        return availableFormats;
    }
    }


    private Face[] getFaces() {
    private boolean setFaces(Face[] faces) {
        final int FACE_LANDMARK_SIZE = 6;
        if (faces == null) {
            return false;
        }

        int numFaces = faces.length;

        // Detect if all faces are SIMPLE or not; count # of valid faces
        boolean fullMode = true;
        for (Face face : faces) {
            if (face == null) {
                numFaces--;
                Log.w(TAG, "setFaces - null face detected, skipping");
                continue;
            }

            if (face.getId() == Face.ID_UNSUPPORTED) {
                fullMode = false;
            }
        }

        Rect[] faceRectangles = new Rect[numFaces];
        byte[] faceScores = new byte[numFaces];
        int[] faceIds = null;
        int[] faceLandmarks = null;

        if (fullMode) {
            faceIds = new int[numFaces];
            faceLandmarks = new int[numFaces * FACE_LANDMARK_SIZE];
        }

        int i = 0;
        for (Face face : faces) {
            if (face == null) {
                continue;
            }

            faceRectangles[i] = face.getBounds();
            faceScores[i] = (byte)face.getScore();

            if (fullMode) {
                faceIds[i] = face.getId();


                int j = 0;

                faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getLeftEyePosition().x;
                faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getLeftEyePosition().y;
                faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getRightEyePosition().x;
                faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getRightEyePosition().y;
                faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getMouthPosition().x;
                faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getMouthPosition().y;
            }

            i++;
        }

        set(CaptureResult.STATISTICS_FACE_RECTANGLES, faceRectangles);
        set(CaptureResult.STATISTICS_FACE_IDS, faceIds);
        set(CaptureResult.STATISTICS_FACE_LANDMARKS, faceLandmarks);
        set(CaptureResult.STATISTICS_FACE_SCORES, faceScores);

        return true;
    }

    private Face[] getFaces() {
        Integer faceDetectMode = get(CaptureResult.STATISTICS_FACE_DETECT_MODE);
        Integer faceDetectMode = get(CaptureResult.STATISTICS_FACE_DETECT_MODE);
        if (faceDetectMode == null) {
        if (faceDetectMode == null) {
            Log.w(TAG, "Face detect mode metadata is null, assuming the mode is SIMPLE");
            Log.w(TAG, "Face detect mode metadata is null, assuming the mode is SIMPLE");
@@ -653,9 +717,12 @@ public class CameraMetadataNative implements Parcelable {
                if (faceScores[i] <= Face.SCORE_MAX &&
                if (faceScores[i] <= Face.SCORE_MAX &&
                        faceScores[i] >= Face.SCORE_MIN &&
                        faceScores[i] >= Face.SCORE_MIN &&
                        faceIds[i] >= 0) {
                        faceIds[i] >= 0) {
                    Point leftEye = new Point(faceLandmarks[i*6], faceLandmarks[i*6+1]);
                    Point leftEye = new Point(faceLandmarks[i*FACE_LANDMARK_SIZE],
                    Point rightEye = new Point(faceLandmarks[i*6+2], faceLandmarks[i*6+3]);
                            faceLandmarks[i*FACE_LANDMARK_SIZE+1]);
                    Point mouth = new Point(faceLandmarks[i*6+4], faceLandmarks[i*6+5]);
                    Point rightEye = new Point(faceLandmarks[i*FACE_LANDMARK_SIZE+2],
                            faceLandmarks[i*FACE_LANDMARK_SIZE+3]);
                    Point mouth = new Point(faceLandmarks[i*FACE_LANDMARK_SIZE+4],
                            faceLandmarks[i*FACE_LANDMARK_SIZE+5]);
                    Face face = new Face(faceRectangles[i], faceScores[i], faceIds[i],
                    Face face = new Face(faceRectangles[i], faceScores[i], faceIds[i],
                            leftEye, rightEye, mouth);
                            leftEye, rightEye, mouth);
                    faceList.add(face);
                    faceList.add(face);
@@ -865,6 +932,13 @@ public class CameraMetadataNative implements Parcelable {
                metadata.setFaceRectangles((Rect[]) value);
                metadata.setFaceRectangles((Rect[]) value);
            }
            }
        });
        });
        sSetCommandMap.put(CaptureResult.STATISTICS_FACES.getNativeKey(),
                new SetCommand() {
            @Override
            public <T> void setValue(CameraMetadataNative metadata, T value) {
                metadata.setFaces((Face[])value);
            }
        });
        sSetCommandMap.put(CaptureRequest.TONEMAP_CURVE.getNativeKey(), new SetCommand() {
        sSetCommandMap.put(CaptureRequest.TONEMAP_CURVE.getNativeKey(), new SetCommand() {
            @Override
            @Override
            public <T> void setValue(CameraMetadataNative metadata, T value) {
            public <T> void setValue(CameraMetadataNative metadata, T value) {
+231 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.hardware.camera2.legacy;

import android.graphics.Rect;
import android.hardware.Camera;
import android.hardware.Camera.FaceDetectionListener;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.legacy.ParameterUtils.ZoomData;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.params.Face;
import android.hardware.camera2.utils.ListUtils;
import android.hardware.camera2.utils.ParamsUtils;
import android.util.Log;
import android.util.Size;

import com.android.internal.util.ArrayUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static android.hardware.camera2.CaptureRequest.*;
import static com.android.internal.util.Preconditions.*;

/**
 * Map legacy face detect callbacks into face detection results.
 */
@SuppressWarnings("deprecation")
public class LegacyFaceDetectMapper {
    private static String TAG = "LegacyFaceDetectMapper";
    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);

    private final Camera mCamera;
    private final boolean mFaceDetectSupported;
    private boolean mFaceDetectEnabled = false;

    private final Object mLock = new Object();
    private Camera.Face[] mFaces;
    private Camera.Face[] mFacesPrev;
    /**
     * Instantiate a new face detect mapper.
     *
     * @param camera a non-{@code null} camera1 device
     * @param characteristics a  non-{@code null} camera characteristics for that camera1
     *
     * @throws NullPointerException if any of the args were {@code null}
     */
    public LegacyFaceDetectMapper(Camera camera, CameraCharacteristics characteristics) {
        mCamera = checkNotNull(camera, "camera must not be null");
        checkNotNull(characteristics, "characteristics must not be null");

        mFaceDetectSupported = ArrayUtils.contains(
                characteristics.get(
                        CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES),
                STATISTICS_FACE_DETECT_MODE_SIMPLE);

        if (!mFaceDetectSupported) {
            return;
        }

       mCamera.setFaceDetectionListener(new FaceDetectionListener() {

        @Override
        public void onFaceDetection(Camera.Face[] faces, Camera camera) {
            int lengthFaces = faces == null ? 0 : faces.length;
            synchronized (mLock) {
                if (mFaceDetectEnabled) {
                    mFaces = faces;
                } else if (lengthFaces > 0) {
                    // stopFaceDetectMode could race against the requests, print a debug log
                    Log.d(TAG,
                            "onFaceDetection - Ignored some incoming faces since" +
                            "face detection was disabled");
                }
            }

            if (VERBOSE) {
                Log.v(TAG, "onFaceDetection - read " + lengthFaces + " faces");
            }
        }
       });
    }

    /**
     * Process the face detect mode from the capture request into an api1 face detect toggle.
     *
     * <p>This method should be called after the parameters are {@link LegacyRequestMapper mapped}
     * with the request.</p>
     *
     * <p>Callbacks are processed in the background, and the next call to {@link #mapResultTriggers}
     * will have the latest faces detected as reflected by the camera1 callbacks.</p>
     *
     * <p>None of the arguments will be mutated.</p>
     *
     * @param captureRequest a non-{@code null} request
     * @param parameters a non-{@code null} parameters corresponding to this request (read-only)
     */
    public void processFaceDetectMode(CaptureRequest captureRequest,
            Camera.Parameters parameters) {
        checkNotNull(captureRequest, "captureRequest must not be null");

        /*
         * statistics.faceDetectMode
         */
        int fdMode = ParamsUtils.getOrDefault(captureRequest, STATISTICS_FACE_DETECT_MODE,
                STATISTICS_FACE_DETECT_MODE_OFF);

        if (fdMode != STATISTICS_FACE_DETECT_MODE_OFF && !mFaceDetectSupported) {
            Log.w(TAG,
                    "processFaceDetectMode - Ignoring statistics.faceDetectMode; " +
                    "face detection is not available");
            return;
        }

        // Print some warnings out in case the values were wrong
        switch (fdMode) {
            case STATISTICS_FACE_DETECT_MODE_OFF:
            case STATISTICS_FACE_DETECT_MODE_SIMPLE:
                break;
            case STATISTICS_FACE_DETECT_MODE_FULL:
                Log.w(TAG,
                        "processFaceDetectMode - statistics.faceDetectMode == FULL unsupported, " +
                        "downgrading to SIMPLE");
                break;
            default:
                Log.w(TAG, "processFaceDetectMode - ignoring unknown statistics.faceDetectMode = "
                        + fdMode);
                return;
        }

        boolean enableFaceDetect = fdMode != STATISTICS_FACE_DETECT_MODE_OFF;
        synchronized (mLock) {
            // Enable/disable face detection if it's changed since last time
            if (enableFaceDetect != mFaceDetectEnabled) {
                if (enableFaceDetect) {
                    mCamera.startFaceDetection();

                    if (VERBOSE) {
                        Log.v(TAG, "processFaceDetectMode - start face detection");
                    }
                } else {
                    mCamera.stopFaceDetection();

                    if (VERBOSE) {
                        Log.v(TAG, "processFaceDetectMode - stop face detection");
                    }

                    mFaces = null;
                }

                mFaceDetectEnabled = enableFaceDetect;
            }
        }
    }

    /**
     * Update the {@code result} camera metadata map with the new value for the
     * {@code statistics.faces} and {@code statistics.faceDetectMode}.
     *
     * <p>Face detect callbacks are processed in the background, and each call to
     * {@link #mapResultFaces} will have the latest faces as reflected by the camera1 callbacks.</p>
     *
     * @param result a non-{@code null} result
     * @param legacyRequest a non-{@code null} request (read-only)
     */
    public void mapResultFaces(CameraMetadataNative result, LegacyRequest legacyRequest) {
        checkNotNull(result, "result must not be null");
        checkNotNull(legacyRequest, "legacyRequest must not be null");

        Camera.Face[] faces, previousFaces;
        int fdMode;
        synchronized (mLock) {
            fdMode = mFaceDetectEnabled ?
                            STATISTICS_FACE_DETECT_MODE_SIMPLE : STATISTICS_FACE_DETECT_MODE_OFF;

            if (mFaceDetectEnabled) {
                faces = mFaces;
            } else {
                faces = null;
            }

            previousFaces = mFacesPrev;
            mFacesPrev = faces;
        }

        CameraCharacteristics characteristics = legacyRequest.characteristics;
        CaptureRequest request = legacyRequest.captureRequest;
        Size previewSize = legacyRequest.previewSize;
        Camera.Parameters params = legacyRequest.parameters;

        Rect activeArray = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
        ZoomData zoomData = ParameterUtils.convertScalerCropRegion(activeArray,
                request.get(CaptureRequest.SCALER_CROP_REGION), previewSize, params);

        List<Face> convertedFaces = new ArrayList<>();
        if (faces != null) {
            for (Camera.Face face : faces) {
                if (face != null) {
                    convertedFaces.add(
                            ParameterUtils.convertFaceFromLegacy(face, activeArray, zoomData));
                } else {
                    Log.w(TAG, "mapResultFaces - read NULL face from camera1 device");
                }
            }
        }

        if (VERBOSE && previousFaces != faces) { // Log only in verbose and IF the faces changed
            Log.v(TAG, "mapResultFaces - changed to " + ListUtils.listToString(convertedFaces));
        }

        result.set(CaptureResult.STATISTICS_FACES, convertedFaces.toArray(new Face[0]));
        result.set(CaptureResult.STATISTICS_FACE_DETECT_MODE, fdMode);
    }
}
+2 −1
Original line number Original line Diff line number Diff line
@@ -247,7 +247,8 @@ public class LegacyFocusStateMapper {
                // No action necessary. The callbacks will handle transitions.
                // No action necessary. The callbacks will handle transitions.
                break;
                break;
            default:
            default:
                Log.w(TAG, "mapTriggers - ignoring unknown control.afTrigger = " + afTrigger);
                Log.w(TAG, "processRequestTriggers - ignoring unknown control.afTrigger = "
                        + afTrigger);
        }
        }
    }
    }


+42 −0
Original line number Original line Diff line number Diff line
@@ -203,6 +203,11 @@ public class LegacyMetadataMapper {
         */
         */
        mapSensor(m, p);
        mapSensor(m, p);


        /*
         * statistics.*
         */
        mapStatistics(m, p);

        /*
        /*
         * sync.*
         * sync.*
         */
         */
@@ -486,6 +491,18 @@ public class LegacyMetadataMapper {
    }
    }


    private static void mapControlOther(CameraMetadataNative m, Camera.Parameters p) {
    private static void mapControlOther(CameraMetadataNative m, Camera.Parameters p) {
        /*
         * android.control.availableVideoStabilizationModes
         */
        {
            int stabModes[] = p.isVideoStabilizationSupported() ?
                    new int[] { CONTROL_VIDEO_STABILIZATION_MODE_OFF,
                                CONTROL_VIDEO_STABILIZATION_MODE_ON } :
                    new int[] { CONTROL_VIDEO_STABILIZATION_MODE_OFF };

            m.set(CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES, stabModes);
        }

        /*
        /*
         * android.control.maxRegions
         * android.control.maxRegions
         */
         */
@@ -742,6 +759,31 @@ public class LegacyMetadataMapper {
        m.set(SENSOR_INFO_PIXEL_ARRAY_SIZE, largestJpegSize);
        m.set(SENSOR_INFO_PIXEL_ARRAY_SIZE, largestJpegSize);
    }
    }


    private static void mapStatistics(CameraMetadataNative m, Parameters p) {
        /*
         * statistics.info.availableFaceDetectModes
         */
        int[] fdModes;

        if (p.getMaxNumDetectedFaces() > 0) {
            fdModes = new int[] {
                STATISTICS_FACE_DETECT_MODE_OFF,
                STATISTICS_FACE_DETECT_MODE_SIMPLE
                // FULL is never-listed, since we have no way to query it statically
            };
        } else {
            fdModes = new int[] {
                STATISTICS_FACE_DETECT_MODE_OFF
            };
        }
        m.set(STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES, fdModes);

        /*
         * statistics.info.maxFaceCount
         */
        m.set(STATISTICS_INFO_MAX_FACE_COUNT, p.getMaxNumDetectedFaces());
    }

    private static void mapSync(CameraMetadataNative m, Parameters p) {
    private static void mapSync(CameraMetadataNative m, Parameters p) {
        /*
        /*
         * sync.maxLatency
         * sync.maxLatency
+12 −2
Original line number Original line Diff line number Diff line
@@ -150,10 +150,8 @@ public class LegacyRequestMapper {
            if (supported) {
            if (supported) {
                params.setPreviewFpsRange(legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
                params.setPreviewFpsRange(legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
                        legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
                        legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
                params.setRecordingHint(false);
            } else {
            } else {
                Log.w(TAG, "Unsupported FPS range set [" + legacyFps[0] + "," + legacyFps[1] + "]");
                Log.w(TAG, "Unsupported FPS range set [" + legacyFps[0] + "," + legacyFps[1] + "]");
                params.setRecordingHint(true);
            }
            }
        }
        }


@@ -248,6 +246,18 @@ public class LegacyRequestMapper {
         // TODO: Don't add control.awbLock to availableRequestKeys if it's not supported
         // TODO: Don't add control.awbLock to availableRequestKeys if it's not supported
        }
        }


        // control.videoStabilizationMode
        {
            Integer stabMode = getIfSupported(request, CONTROL_VIDEO_STABILIZATION_MODE,
                    /*defaultValue*/CONTROL_VIDEO_STABILIZATION_MODE_OFF,
                    params.isVideoStabilizationSupported(),
                    /*allowedValue*/CONTROL_VIDEO_STABILIZATION_MODE_OFF);

            if (stabMode != null) {
                params.setVideoStabilization(stabMode == CONTROL_VIDEO_STABILIZATION_MODE_ON);
            }
        }

        // lens.focusDistance
        // lens.focusDistance
        {
        {
            boolean infinityFocusSupported =
            boolean infinityFocusSupported =
Loading