1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 
18 package android.hardware.camera2.params;
19 
20 import android.graphics.Point;
21 import android.graphics.Rect;
22 import android.hardware.camera2.CameraCharacteristics;
23 import android.hardware.camera2.CameraMetadata;
24 import android.hardware.camera2.CaptureResult;
25 
26 /**
27  * Describes a face detected in an image.
28  */
29 public final class Face {
30 
31     /**
32      * The ID is {@code -1} when the optional set of fields is unsupported.
33      *
34      * @see #getId()
35      */
36     public static final int ID_UNSUPPORTED = -1;
37 
38     /**
39      * The minimum possible value for the confidence level.
40      *
41      * @see #getScore()
42      */
43     public static final int SCORE_MIN = 1;
44 
45     /**
46      * The maximum possible value for the confidence level.
47      *
48      * @see #getScore()
49      */
50     public static final int SCORE_MAX = 100;
51 
52     private final Rect mBounds;
53     private final int mScore;
54     private final int mId;
55     private final Point mLeftEye;
56     private final Point mRightEye;
57     private final Point mMouth;
58 
59     /**
60      * Create a new face with all fields set.
61      *
62      * <p>The id, leftEyePosition, rightEyePosition, and mouthPosition are considered optional.
63      * They are only required when the {@link CaptureResult} reports that the value of key
64      * {@link CaptureResult#STATISTICS_FACE_DETECT_MODE} is
65      * {@link CameraMetadata#STATISTICS_FACE_DETECT_MODE_FULL}.
66      * If the id is {@value #ID_UNSUPPORTED} then the leftEyePosition, rightEyePosition, and
67      * mouthPositions are guaranteed to be {@code null}. Otherwise, each of leftEyePosition,
68      * rightEyePosition, and mouthPosition may be independently null or not-null.</p>
69      *
70      * @param bounds Bounds of the face.
71      * @param score Confidence level between {@value #SCORE_MIN}-{@value #SCORE_MAX}.
72      * @param id A unique ID per face visible to the tracker.
73      * @param leftEyePosition The position of the left eye.
74      * @param rightEyePosition The position of the right eye.
75      * @param mouthPosition The position of the mouth.
76      *
77      * @throws IllegalArgumentException
78      *             if bounds is {@code null},
79      *             or if the confidence is not in the range of
80      *             {@value #SCORE_MIN}-{@value #SCORE_MAX},
81      *             or if id is {@value #ID_UNSUPPORTED} and
82      *               leftEyePosition/rightEyePosition/mouthPosition aren't all null,
83      *             or else if id is negative.
84      *
85      * @hide
86      */
Face(Rect bounds, int score, int id, Point leftEyePosition, Point rightEyePosition, Point mouthPosition)87     public Face(Rect bounds, int score, int id,
88             Point leftEyePosition, Point rightEyePosition, Point mouthPosition) {
89         checkNotNull("bounds", bounds);
90         if (score < SCORE_MIN || score > SCORE_MAX) {
91             throw new IllegalArgumentException("Confidence out of range");
92         } else if (id < 0 && id != ID_UNSUPPORTED) {
93             throw new IllegalArgumentException("Id out of range");
94         }
95         if (id == ID_UNSUPPORTED) {
96             checkNull("leftEyePosition", leftEyePosition);
97             checkNull("rightEyePosition", rightEyePosition);
98             checkNull("mouthPosition", mouthPosition);
99         }
100 
101         mBounds = bounds;
102         mScore = score;
103         mId = id;
104         mLeftEye = leftEyePosition;
105         mRightEye = rightEyePosition;
106         mMouth = mouthPosition;
107     }
108 
109     /**
110      * Create a new face without the optional fields.
111      *
112      * <p>The id, leftEyePosition, rightEyePosition, and mouthPosition are considered optional.
113      * If the id is {@value #ID_UNSUPPORTED} then the leftEyePosition, rightEyePosition, and
114      * mouthPositions are guaranteed to be {@code null}. Otherwise, each of leftEyePosition,
115      * rightEyePosition, and mouthPosition may be independently null or not-null. When devices
116      * report the value of key {@link CaptureResult#STATISTICS_FACE_DETECT_MODE} as
117      * {@link CameraMetadata#STATISTICS_FACE_DETECT_MODE_SIMPLE} in {@link CaptureResult},
118      * the face id of each face is expected to be {@value #ID_UNSUPPORTED}, the leftEyePosition,
119      * rightEyePosition, and mouthPositions are expected to be {@code null} for each face.</p>
120      *
121      * @param bounds Bounds of the face.
122      * @param score Confidence level between {@value #SCORE_MIN}-{@value #SCORE_MAX}.
123      *
124      * @throws IllegalArgumentException
125      *             if bounds is {@code null},
126      *             or if the confidence is not in the range of
127      *             {@value #SCORE_MIN}-{@value #SCORE_MAX}.
128      *
129      * @hide
130      */
Face(Rect bounds, int score)131     public Face(Rect bounds, int score) {
132         this(bounds, score, ID_UNSUPPORTED,
133                 /*leftEyePosition*/null, /*rightEyePosition*/null, /*mouthPosition*/null);
134     }
135 
136     /**
137      * Bounds of the face.
138      *
139      * <p>A rectangle relative to the sensor's
140      * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE}, with (0,0)
141      * representing the top-left corner of the active array rectangle.</p>
142      *
143      * <p>There is no constraints on the the Rectangle value other than it
144      * is not-{@code null}.</p>
145      */
getBounds()146     public Rect getBounds() {
147         return mBounds;
148     }
149 
150     /**
151      * The confidence level for the detection of the face.
152      *
153      * <p>The range is {@value #SCORE_MIN} to {@value #SCORE_MAX}.
154      * {@value #SCORE_MAX} is the highest confidence.</p>
155      *
156      * <p>Depending on the device, even very low-confidence faces may be
157      * listed, so applications should filter out faces with low confidence,
158      * depending on the use case. For a typical point-and-shoot camera
159      * application that wishes to display rectangles around detected faces,
160      * filtering out faces with confidence less than half of {@value #SCORE_MAX}
161      * is recommended.</p>
162      *
163      * @see #SCORE_MAX
164      * @see #SCORE_MIN
165      */
getScore()166     public int getScore() {
167         return mScore;
168     }
169 
170     /**
171      * An unique id per face while the face is visible to the tracker.
172      *
173      * <p>
174      * If the face leaves the field-of-view and comes back, it will get a new
175      * id.</p>
176      *
177      * <p>This is an optional field, may not be supported on all devices.
178      * If the id is {@value #ID_UNSUPPORTED} then the leftEyePosition, rightEyePosition, and
179      * mouthPositions are guaranteed to be {@code null}. Otherwise, each of leftEyePosition,
180      * rightEyePosition, and mouthPosition may be independently null or not-null. When devices
181      * report the value of key {@link CaptureResult#STATISTICS_FACE_DETECT_MODE} as
182      * {@link CameraMetadata#STATISTICS_FACE_DETECT_MODE_SIMPLE} in {@link CaptureResult},
183      * the face id of each face is expected to be {@value #ID_UNSUPPORTED}.</p>
184      *
185      * <p>This value will either be {@value #ID_UNSUPPORTED} or
186      * otherwise greater than {@code 0}.</p>
187      *
188      * @see #ID_UNSUPPORTED
189      */
getId()190     public int getId() {
191         return mId;
192     }
193 
194     /**
195      * The coordinates of the center of the left eye.
196      *
197      * <p>The coordinates are in
198      * the same space as the ones for {@link #getBounds}. This is an
199      * optional field, may not be supported on all devices. If not
200      * supported, the value will always be set to null.
201      * This value will  always be null only if {@link #getId()} returns
202      * {@value #ID_UNSUPPORTED}.</p>
203      *
204      * @return The left eye position, or {@code null} if unknown.
205      */
getLeftEyePosition()206     public Point getLeftEyePosition() {
207         return mLeftEye;
208     }
209 
210     /**
211      * The coordinates of the center of the right eye.
212      *
213      * <p>The coordinates are
214      * in the same space as the ones for {@link #getBounds}.This is an
215      * optional field, may not be supported on all devices. If not
216      * supported, the value will always be set to null.
217      * This value will  always be null only if {@link #getId()} returns
218      * {@value #ID_UNSUPPORTED}.</p>
219      *
220      * @return The right eye position, or {@code null} if unknown.
221      */
getRightEyePosition()222     public Point getRightEyePosition() {
223         return mRightEye;
224     }
225 
226     /**
227      * The coordinates of the center of the mouth.
228      *
229      * <p>The coordinates are in
230      * the same space as the ones for {@link #getBounds}. This is an optional
231      * field, may not be supported on all devices. If not
232      * supported, the value will always be set to null.
233      * This value will  always be null only if {@link #getId()} returns
234      * {@value #ID_UNSUPPORTED}.</p>
235      * </p>
236      *
237      * @return The mouth position, or {@code null} if unknown.
238      */
getMouthPosition()239     public Point getMouthPosition() {
240         return mMouth;
241     }
242 
243     /**
244      * Represent the Face as a string for debugging purposes.
245      */
246     @Override
toString()247     public String toString() {
248         return String.format("{ bounds: %s, score: %s, id: %d, " +
249                 "leftEyePosition: %s, rightEyePosition: %s, mouthPosition: %s }",
250                 mBounds, mScore, mId, mLeftEye, mRightEye, mMouth);
251     }
252 
checkNotNull(String name, Object obj)253     private static void checkNotNull(String name, Object obj) {
254         if (obj == null) {
255             throw new IllegalArgumentException(name + " was required, but it was null");
256         }
257     }
258 
checkNull(String name, Object obj)259     private static void checkNull(String name, Object obj) {
260         if (obj != null) {
261             throw new IllegalArgumentException(name + " was required to be null, but it wasn't");
262         }
263     }
264 }
265