From b779ac1e2e3e463aa49442ac801dde410f22dc73 Mon Sep 17 00:00:00 2001 From: Igor Murashkin Date: Thu, 5 Sep 2013 12:40:19 -0700 Subject: [PATCH] camera2: Update Face class. - Move Face to be outer class. Enables Parcelable-izing it later. - Add static public constants for field values. - Add @hide constructors. - Rename methods returning Point to have Position suffix. - Add new key android.statistics.faces (CaptureResult#STATISTICS_FACES) Bug: 10360518 Bug: 10549293 Change-Id: I067f06f0426114b2c3a3266ca7e00e6cb1d89046 --- api/current.txt | 13 +- .../hardware/camera2/CaptureResult.java | 98 ++----- core/java/android/hardware/camera2/Face.java | 252 ++++++++++++++++++ 3 files changed, 275 insertions(+), 88 deletions(-) create mode 100644 core/java/android/hardware/camera2/Face.java diff --git a/api/current.txt b/api/current.txt index 1a9b77c877d40..dbca96eacf809 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11129,6 +11129,7 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_SENSITIVITY; field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEMPERATURE; field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TIMESTAMP; + field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_FACES; field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_FACE_DETECT_MODE; field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_FACE_IDS; field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_FACE_LANDMARKS; @@ -11144,14 +11145,16 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_MODE; } - public static class CaptureResult.Face { - ctor public CaptureResult.Face(); + public final class Face { method public android.graphics.Rect getBounds(); method public int getId(); - method public android.graphics.Point getLeftEye(); - method public android.graphics.Point getMouth(); - method public android.graphics.Point getRightEye(); + method public android.graphics.Point getLeftEyePosition(); + method public android.graphics.Point getMouthPosition(); + method public android.graphics.Point getRightEyePosition(); method public int getScore(); + field public static final int ID_UNSUPPORTED = -1; // 0xffffffff + field public static final int SCORE_MAX = 100; // 0x64 + field public static final int SCORE_MIN = 1; // 0x1 } public final class Rational { diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 3fcd2f9c147c1..377e78aa54776 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -51,89 +51,6 @@ public final class CaptureResult extends CameraMetadata { return mResults.get(key); } - /** - * Describes a face detected in an image. - */ - public static class Face { - - /** - *

Bounds of the face. A rectangle relative to the sensor's - * {@link CameraProperties#SENSOR_INFO_ACTIVE_ARRAY_SIZE}, with (0,0) - * representing the top-left corner of the active array rectangle.

- */ - public Rect getBounds() { - return mBounds; - } - - /**

The confidence level for the detection of the face. The range is 1 to - * 100. 100 is the highest confidence.

- * - *

Depending on the device, even very low-confidence faces may be - * listed, so applications should filter out faces with low confidence, - * depending on the use case. For a typical point-and-shoot camera - * application that wishes to display rectangles around detected faces, - * filtering out faces with confidence less than 50 is recommended.

- * - */ - public int getScore() { - return mScore; - } - - /** - * An unique id per face while the face is visible to the tracker. If - * the face leaves the field-of-view and comes back, it will get a new - * id. This is an optional field, may not be supported on all devices. - * If not supported, id will always be set to -1. The optional fields - * are supported as a set. Either they are all valid, or none of them - * are. - */ - public int getId() { - return mId; - } - - /** - * The coordinates of the center of the left eye. The coordinates are in - * the same space as the ones for {@link #getBounds}. This is an - * optional field, may not be supported on all devices. If not - * supported, the value will always be set to null. The optional fields - * are supported as a set. Either they are all valid, or none of them - * are. - */ - public Point getLeftEye() { - return mLeftEye; - } - - /** - * The coordinates of the center of the right eye. The coordinates are - * in the same space as the ones for {@link #getBounds}.This is an - * optional field, may not be supported on all devices. If not - * supported, the value will always be set to null. The optional fields - * are supported as a set. Either they are all valid, or none of them - * are. - */ - public Point getRightEye() { - return mRightEye; - } - - /** - * The coordinates of the center of the mouth. The coordinates are in - * the same space as the ones for {@link #getBounds}. This is an optional - * field, may not be supported on all devices. If not supported, the - * value will always be set to null. The optional fields are supported - * as a set. Either they are all valid, or none of them are. - */ - public Point getMouth() { - return mMouth; - } - - private Rect mBounds; - private int mScore; - private int mId; - private Point mLeftEye; - private Point mRightEye; - private Point mMouth; - } - /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ * The key entries below this point are generated from metadata * definitions in /system/media/camera/docs. Do not modify by hand or @@ -1003,4 +920,19 @@ public final class CaptureResult extends CameraMetadata { /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ * End generated code *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/ + + /** + *

+ * List of the {@link Face Faces} detected through camera face detection + * in this result. + *

+ *

+ * Only available if {@link #STATISTICS_FACE_DETECT_MODE} {@code !=} + * {@link CameraMetadata#STATISTICS_FACE_DETECT_MODE_OFF OFF}. + *

+ * + * @see Face + */ + public static final Key STATISTICS_FACES = + new Key("android.statistics.faces", Face[].class); } diff --git a/core/java/android/hardware/camera2/Face.java b/core/java/android/hardware/camera2/Face.java new file mode 100644 index 0000000000000..6bfc5354359a9 --- /dev/null +++ b/core/java/android/hardware/camera2/Face.java @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2013 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; + +import android.graphics.Point; +import android.graphics.Rect; + +/** + * Describes a face detected in an image. + */ +public final class Face { + + /** + * The ID is {@code -1} when the optional set of fields is unsupported. + * + * @see Face#Face(Rect, int) + * @see #getId() + */ + public static final int ID_UNSUPPORTED = -1; + + /** + * The minimum possible value for the confidence level. + * + * @see #getScore() + */ + public static final int SCORE_MIN = 1; + + /** + * The maximum possible value for the confidence level. + * + * @see #getScore() + */ + public static final int SCORE_MAX = 100; + + private final Rect mBounds; + private final int mScore; + private final int mId; + private final Point mLeftEye; + private final Point mRightEye; + private final Point mMouth; + + /** + * Create a new face with all fields set. + * + *

The id, leftEyePosition, rightEyePosition, and mouthPosition are considered optional. + * If the id is {@value #ID_UNSUPPORTED} then the leftEyePosition, rightEyePosition, and + * mouthPositions are guaranteed to be {@code null}. Otherwise, each of leftEyePosition, + * rightEyePosition, and mouthPosition may be independently null or not-null.

+ * + * @param bounds Bounds of the face. + * @param score Confidence level between {@value #SCORE_MIN}-{@value #SCORE_MAX}. + * @param id A unique ID per face visible to the tracker. + * @param leftEyePosition The position of the left eye. + * @param rightEyePosition The position of the right eye. + * @param mouthPosition The position of the mouth. + * + * @throws IllegalArgumentException + * if bounds is {@code null}, + * or if the confidence is not in the range of + * {@value #SCORE_MIN}-{@value #SCORE_MAX}, + * or if id is {@value #ID_UNSUPPORTED} and + * leftEyePosition/rightEyePosition/mouthPosition aren't all null, + * or else if id is negative. + * + * @hide + */ + public Face(Rect bounds, int score, int id, + Point leftEyePosition, Point rightEyePosition, Point mouthPosition) { + checkNotNull("bounds", bounds); + if (score < SCORE_MIN || score > SCORE_MAX) { + throw new IllegalArgumentException("Confidence out of range"); + } else if (id < 0 && id != ID_UNSUPPORTED) { + throw new IllegalArgumentException("Id out of range"); + } + if (id == ID_UNSUPPORTED) { + checkNull("leftEyePosition", leftEyePosition); + checkNull("rightEyePosition", rightEyePosition); + checkNull("mouthPosition", mouthPosition); + } + + mBounds = bounds; + mScore = score; + mId = id; + mLeftEye = leftEyePosition; + mRightEye = rightEyePosition; + mMouth = mouthPosition; + } + + /** + * Create a new face without the optional fields. + * + *

The id, leftEyePosition, rightEyePosition, and mouthPosition are considered optional. + * If the id is {@value #ID_UNSUPPORTED} then the leftEyePosition, rightEyePosition, and + * mouthPositions are guaranteed to be {@code null}. Otherwise, each of leftEyePosition, + * rightEyePosition, and mouthPosition may be independently null or not-null.

+ * + * @param bounds Bounds of the face. + * @param score Confidence level between {@value #SCORE_MIN}-{@value #SCORE_MAX}. + * + * @throws IllegalArgumentException + * if bounds is {@code null}, + * or if the confidence is not in the range of + * {@value #SCORE_MIN}-{@value #SCORE_MAX}. + * + * @hide + */ + public Face(Rect bounds, int score) { + this(bounds, score, ID_UNSUPPORTED, + /*leftEyePosition*/null, /*rightEyePosition*/null, /*mouthPosition*/null); + } + + /** + * Bounds of the face. + * + *

A rectangle relative to the sensor's + * {@link CameraProperties#SENSOR_INFO_ACTIVE_ARRAY_SIZE}, with (0,0) + * representing the top-left corner of the active array rectangle.

+ * + *

There is no constraints on the the Rectangle value other than it + * is not-{@code null}.

+ */ + public Rect getBounds() { + return mBounds; + } + + /** + * The confidence level for the detection of the face. + * + *

The range is {@value #SCORE_MIN} to {@value #SCORE_MAX}. + * {@value #SCORE_MAX} is the highest confidence.

+ * + *

Depending on the device, even very low-confidence faces may be + * listed, so applications should filter out faces with low confidence, + * depending on the use case. For a typical point-and-shoot camera + * application that wishes to display rectangles around detected faces, + * filtering out faces with confidence less than half of {@value #SCORE_MAX} + * is recommended.

+ * + * @see #SCORE_MAX + * @see #SCORE_MIN + */ + public int getScore() { + return mScore; + } + + /** + * An unique id per face while the face is visible to the tracker. + * + *

+ * If the face leaves the field-of-view and comes back, it will get a new + * id.

+ * + *

This is an optional field, may not be supported on all devices. + * If the id is {@value #ID_UNSUPPORTED} then the leftEyePosition, rightEyePosition, and + * mouthPositions are guaranteed to be {@code null}. Otherwise, each of leftEyePosition, + * rightEyePosition, and mouthPosition may be independently null or not-null.

+ * + *

This value will either be {@value #ID_UNSUPPORTED} or + * otherwise greater than {@code 0}.

+ * + * @see #ID_UNSUPPORTED + */ + public int getId() { + return mId; + } + + /** + * The coordinates of the center of the left eye. + * + *

The coordinates are in + * the same space as the ones for {@link #getBounds}. This is an + * optional field, may not be supported on all devices. If not + * supported, the value will always be set to null. + * This value will always be null only if {@link #getId()} returns + * {@value #ID_UNSUPPORTED}.

+ * + * @return The left eye position, or {@code null} if unknown. + */ + public Point getLeftEyePosition() { + return mLeftEye; + } + + /** + * The coordinates of the center of the right eye. + * + *

The coordinates are + * in the same space as the ones for {@link #getBounds}.This is an + * optional field, may not be supported on all devices. If not + * supported, the value will always be set to null. + * This value will always be null only if {@link #getId()} returns + * {@value #ID_UNSUPPORTED}.

+ * + * @return The right eye position, or {@code null} if unknown. + */ + public Point getRightEyePosition() { + return mRightEye; + } + + /** + * The coordinates of the center of the mouth. + * + *

The coordinates are in + * the same space as the ones for {@link #getBounds}. This is an optional + * field, may not be supported on all devices. If not + * supported, the value will always be set to null. + * This value will always be null only if {@link #getId()} returns + * {@value #ID_UNSUPPORTED}.

them are. + *

+ * + * @return The mouth position, or {@code null} if unknown. + */ + public Point getMouthPosition() { + return mMouth; + } + + /** + * Represent the Face as a string for debugging purposes. + */ + @Override + public String toString() { + return String.format("{ bounds: %s, score: %s, id: %d, " + + "leftEyePosition: %s, rightEyePosition: %s, mouthPosition: %s }", + mBounds, mScore, mId, mLeftEye, mRightEye, mMouth); + } + + private static void checkNotNull(String name, Object obj) { + if (obj == null) { + throw new IllegalArgumentException(name + " was required, but it was null"); + } + } + + private static void checkNull(String name, Object obj) { + if (obj != null) { + throw new IllegalArgumentException(name + " was required to be null, but it wasn't"); + } + } +}