1 /*
2 * Copyright (C) Texas Instruments - http://www.ti.com/
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 * @file OMXFD.cpp
19 *
20 * This file contains functionality for handling face detection.
21 *
22 */
23
24 #include "CameraHal.h"
25 #include "OMXCameraAdapter.h"
26
27 namespace Ti {
28 namespace Camera {
29
30 const uint32_t OMXCameraAdapter::FACE_DETECTION_THRESHOLD = 80;
31
setParametersFD(const android::CameraParameters & params,BaseCameraAdapter::AdapterState state)32 status_t OMXCameraAdapter::setParametersFD(const android::CameraParameters ¶ms,
33 BaseCameraAdapter::AdapterState state)
34 {
35 status_t ret = NO_ERROR;
36
37 LOG_FUNCTION_NAME;
38
39 LOG_FUNCTION_NAME_EXIT;
40
41 return ret;
42 }
43
startFaceDetection()44 status_t OMXCameraAdapter::startFaceDetection()
45 {
46 status_t ret = NO_ERROR;
47
48 android::AutoMutex lock(mFaceDetectionLock);
49
50 ret = setFaceDetection(true, mFaceOrientation);
51 if (ret != NO_ERROR) {
52 goto out;
53 }
54
55 if ( mFaceDetectionRunning ) {
56 mFDSwitchAlgoPriority = true;
57 }
58
59 // Note: White balance will not be face prioritized, since
60 // the algorithm needs full frame statistics, and not face
61 // regions alone.
62
63 faceDetectionNumFacesLastOutput = 0;
64 out:
65 return ret;
66 }
67
stopFaceDetection()68 status_t OMXCameraAdapter::stopFaceDetection()
69 {
70 status_t ret = NO_ERROR;
71 const char *str = NULL;
72 BaseCameraAdapter::AdapterState state;
73 BaseCameraAdapter::getState(state);
74
75 android::AutoMutex lock(mFaceDetectionLock);
76
77 ret = setFaceDetection(false, mFaceOrientation);
78 if (ret != NO_ERROR) {
79 goto out;
80 }
81
82 if ( mFaceDetectionRunning ) {
83 //Enable region priority and disable face priority for AF
84 setAlgoPriority(REGION_PRIORITY, FOCUS_ALGO, true);
85 setAlgoPriority(FACE_PRIORITY, FOCUS_ALGO , false);
86
87 //Enable Region priority and disable Face priority
88 setAlgoPriority(REGION_PRIORITY, EXPOSURE_ALGO, true);
89 setAlgoPriority(FACE_PRIORITY, EXPOSURE_ALGO, false);
90 }
91
92 if (mPending3Asettings) {
93 apply3Asettings(mParameters3A);
94 }
95
96 faceDetectionNumFacesLastOutput = 0;
97 out:
98 return ret;
99 }
100
pauseFaceDetection(bool pause)101 void OMXCameraAdapter::pauseFaceDetection(bool pause)
102 {
103 android::AutoMutex lock(mFaceDetectionLock);
104 // pausing will only take affect if fd is already running
105 if (mFaceDetectionRunning) {
106 mFaceDetectionPaused = pause;
107 faceDetectionNumFacesLastOutput = 0;
108 }
109 }
110
setFaceDetectionOrientation(OMX_U32 orientation)111 status_t OMXCameraAdapter::setFaceDetectionOrientation(OMX_U32 orientation)
112 {
113 status_t ret = NO_ERROR;
114
115 android::AutoMutex lock(mFaceDetectionLock);
116
117 mFaceOrientation = orientation;
118
119 if (mFaceDetectionRunning) {
120 // restart face detection with new rotation
121 setFaceDetection(true, orientation);
122 }
123
124 return ret;
125 }
126
setFaceDetection(bool enable,OMX_U32 orientation)127 status_t OMXCameraAdapter::setFaceDetection(bool enable, OMX_U32 orientation)
128 {
129 status_t ret = NO_ERROR;
130 OMX_ERRORTYPE eError = OMX_ErrorNone;
131 OMX_CONFIG_OBJDETECTIONTYPE objDetection;
132
133 LOG_FUNCTION_NAME;
134
135 if ( OMX_StateInvalid == mComponentState )
136 {
137 CAMHAL_LOGEA("OMX component is in invalid state");
138 ret = -EINVAL;
139 }
140
141 if ( NO_ERROR == ret )
142 {
143 if ( orientation > 270 ) {
144 orientation = 0;
145 }
146
147 OMX_INIT_STRUCT_PTR (&objDetection, OMX_CONFIG_OBJDETECTIONTYPE);
148 objDetection.nPortIndex = mCameraAdapterParameters.mPrevPortIndex;
149 objDetection.nDeviceOrientation = orientation;
150 if ( enable )
151 {
152 objDetection.bEnable = OMX_TRUE;
153 }
154 else
155 {
156 objDetection.bEnable = OMX_FALSE;
157 }
158
159 eError = OMX_SetConfig(mCameraAdapterParameters.mHandleComp,
160 ( OMX_INDEXTYPE ) OMX_IndexConfigImageFaceDetection,
161 &objDetection);
162 if ( OMX_ErrorNone != eError )
163 {
164 CAMHAL_LOGEB("Error while configuring face detection 0x%x", eError);
165 ret = -1;
166 }
167 else
168 {
169 CAMHAL_LOGDA("Face detection configured successfully");
170 }
171 }
172
173 if ( NO_ERROR == ret )
174 {
175 // TODO(XXX): Should enable/disable FD extra data separately
176 // on each port.
177 ret = setExtraData(enable, OMX_ALL, OMX_FaceDetection);
178
179 if ( NO_ERROR != ret )
180 {
181 CAMHAL_LOGEA("Error while configuring face detection extra data");
182 }
183 else
184 {
185 CAMHAL_LOGDA("Face detection extra data configured successfully");
186 }
187 }
188
189 if ( NO_ERROR == ret )
190 {
191 mFaceDetectionRunning = enable;
192 mFaceDetectionPaused = !enable;
193 }
194
195 LOG_FUNCTION_NAME_EXIT;
196
197 return ret;
198 }
199
createPreviewMetadata(OMX_BUFFERHEADERTYPE * pBuffHeader,android::sp<CameraMetadataResult> & result,size_t previewWidth,size_t previewHeight)200 status_t OMXCameraAdapter::createPreviewMetadata(OMX_BUFFERHEADERTYPE* pBuffHeader,
201 android::sp<CameraMetadataResult> &result,
202 size_t previewWidth,
203 size_t previewHeight)
204 {
205 status_t ret = NO_ERROR;
206 status_t faceRet = NO_ERROR;
207 status_t metaRet = NO_ERROR;
208 OMX_FACEDETECTIONTYPE *faceData = NULL;
209
210 LOG_FUNCTION_NAME;
211
212 if ( OMX_StateExecuting != mComponentState ) {
213 CAMHAL_LOGEA("OMX component is not in executing state");
214 return NO_INIT;
215 }
216
217 if ( NULL == pBuffHeader ) {
218 CAMHAL_LOGEA("Invalid Buffer header");
219 return-EINVAL;
220 }
221
222 if ( mFaceDetectionRunning && !mFaceDetectionPaused ) {
223 OMX_OTHER_EXTRADATATYPE *extraData;
224
225 extraData = getExtradata(pBuffHeader->pPlatformPrivate,
226 (OMX_EXTRADATATYPE)OMX_FaceDetection);
227
228 if ( NULL != extraData ) {
229 CAMHAL_LOGVB("Size = %d, sizeof = %d, eType = 0x%x, nDataSize= %d, nPortIndex = 0x%x, nVersion = 0x%x",
230 extraData->nSize,
231 sizeof(OMX_OTHER_EXTRADATATYPE),
232 extraData->eType,
233 extraData->nDataSize,
234 extraData->nPortIndex,
235 extraData->nVersion);
236 } else {
237 CAMHAL_LOGD("FD extra data not found!");
238 return -EINVAL;
239 }
240
241 faceData = ( OMX_FACEDETECTIONTYPE * ) extraData->data;
242 if ( NULL != faceData ) {
243 if ( sizeof(OMX_FACEDETECTIONTYPE) == faceData->nSize ) {
244 CAMHAL_LOGVB("Faces detected %d",
245 faceData->ulFaceCount,
246 faceData->nSize,
247 sizeof(OMX_FACEDETECTIONTYPE),
248 faceData->eCameraView,
249 faceData->nPortIndex,
250 faceData->nVersion);
251 } else {
252 CAMHAL_LOGEB("OMX_FACEDETECTIONTYPE size mismatch: expected = %d, received = %d",
253 ( unsigned int ) sizeof(OMX_FACEDETECTIONTYPE),
254 ( unsigned int ) faceData->nSize);
255 return -EINVAL;
256 }
257 } else {
258 CAMHAL_LOGEA("Invalid OMX_FACEDETECTIONTYPE");
259 return -EINVAL;
260 }
261 }
262
263 result = new (std::nothrow) CameraMetadataResult;
264 if(NULL == result.get()) {
265 ret = NO_MEMORY;
266 return ret;
267 }
268
269 //Encode face coordinates
270 faceRet = encodeFaceCoordinates(faceData, result->getMetadataResult()
271 , previewWidth, previewHeight);
272 if ((NO_ERROR == faceRet) || (NOT_ENOUGH_DATA == faceRet)) {
273 // Ignore harmless errors (no error and no update) and go ahead and encode
274 // the preview meta data
275 metaRet = encodePreviewMetadata(result->getMetadataResult()
276 , pBuffHeader->pPlatformPrivate);
277 if ( (NO_ERROR != metaRet) && (NOT_ENOUGH_DATA != metaRet) ) {
278 // Some 'real' error occurred during preview meta data encod, clear metadata
279 // result and return correct error code
280 result.clear();
281 ret = metaRet;
282 }
283 } else {
284 //Some real error occurred during face encoding, clear metadata result
285 // and return correct error code
286 result.clear();
287 ret = faceRet;
288 }
289
290 if((NOT_ENOUGH_DATA == faceRet) && (NOT_ENOUGH_DATA == metaRet)) {
291 //No point sending the callback if nothing is changed
292 result.clear();
293 ret = faceRet;
294 }
295
296 LOG_FUNCTION_NAME_EXIT;
297
298 return ret;
299 }
300
encodeFaceCoordinates(const OMX_FACEDETECTIONTYPE * faceData,camera_frame_metadata_t * metadataResult,size_t previewWidth,size_t previewHeight)301 status_t OMXCameraAdapter::encodeFaceCoordinates(const OMX_FACEDETECTIONTYPE *faceData,
302 camera_frame_metadata_t *metadataResult,
303 size_t previewWidth,
304 size_t previewHeight)
305 {
306 status_t ret = NO_ERROR;
307 camera_face_t *faces;
308 size_t hRange, vRange;
309 double tmp;
310 bool faceArrayChanged = false;
311
312 LOG_FUNCTION_NAME;
313
314 hRange = CameraMetadataResult::RIGHT - CameraMetadataResult::LEFT;
315 vRange = CameraMetadataResult::BOTTOM - CameraMetadataResult::TOP;
316
317 android::AutoMutex lock(mFaceDetectionLock);
318
319 // Avoid memory leak if called twice on same CameraMetadataResult
320 if ( (0 < metadataResult->number_of_faces) && (NULL != metadataResult->faces) ) {
321 free(metadataResult->faces);
322 metadataResult->number_of_faces = 0;
323 metadataResult->faces = NULL;
324 }
325
326 if ( (NULL != faceData) && (0 < faceData->ulFaceCount) ) {
327 int orient_mult;
328 int trans_left, trans_top, trans_right, trans_bot;
329
330 faces = ( camera_face_t * ) malloc(sizeof(camera_face_t)*faceData->ulFaceCount);
331 if ( NULL == faces ) {
332 ret = NO_MEMORY;
333 goto out;
334 }
335
336 /**
337 / * When device is 180 degrees oriented to the sensor, need to translate
338 / * the output from Ducati to what Android expects
339 / * Ducati always gives face coordinates in this form, irrespective of
340 / * rotation, i.e (l,t) always represents the point towards the left eye
341 / * and top of hair.
342 / * (l, t)
343 / * ---------------
344 / * - ,,,,,,, -
345 / * - | | -
346 / * - |<a <a| -
347 / * - (| ^ |) -
348 / * - | -=- | -
349 / * - \_____/ -
350 / * ---------------
351 / * (r, b)
352 / *
353 / * However, Android expects the coords to be in respect with what the
354 / * sensor is viewing, i.e Android expects sensor to see this with (l,t)
355 / * and (r,b) like so:
356 / * (l, t)
357 / * ---------------
358 / * - _____ -
359 / * - / \ -
360 / * - | -=- | -
361 / * - (| ^ |) -
362 / * - |a> a>| -
363 / * - | | -
364 / * - ,,,,,,, -
365 / * ---------------
366 / * (r, b)
367 */
368
369 if (mFaceOrientation == 180) {
370 orient_mult = -1;
371 trans_left = 2; // right is now left
372 trans_top = 3; // bottom is now top
373 trans_right = 0; // left is now right
374 trans_bot = 1; // top is not bottom
375 } else {
376 orient_mult = 1;
377 trans_left = 0; // left
378 trans_top = 1; // top
379 trans_right = 2; // right
380 trans_bot = 3; // bottom
381 }
382
383 int j = 0, i = 0;
384 for ( ; j < faceData->ulFaceCount ; j++)
385 {
386 OMX_S32 nLeft = 0;
387 OMX_S32 nTop = 0;
388 //Face filtering
389 //For real faces, it is seen that the h/w passes a score >=80
390 //For false faces, we seem to get even a score of 70 sometimes.
391 //In order to avoid any issue at application level, we filter
392 //<=70 score here.
393 if(faceData->tFacePosition[j].nScore <= FACE_DETECTION_THRESHOLD)
394 continue;
395
396 if (mFaceOrientation == 180) {
397 // from sensor pov, the left pos is the right corner of the face in pov of frame
398 nLeft = faceData->tFacePosition[j].nLeft + faceData->tFacePosition[j].nWidth;
399 nTop = faceData->tFacePosition[j].nTop + faceData->tFacePosition[j].nHeight;
400 } else {
401 nLeft = faceData->tFacePosition[j].nLeft;
402 nTop = faceData->tFacePosition[j].nTop;
403 }
404
405 tmp = ( double ) nLeft / ( double ) previewWidth;
406 tmp *= hRange;
407 tmp -= hRange/2;
408 faces[i].rect[trans_left] = tmp;
409
410 tmp = ( double ) nTop / ( double )previewHeight;
411 tmp *= vRange;
412 tmp -= vRange/2;
413 faces[i].rect[trans_top] = tmp;
414
415 tmp = ( double ) faceData->tFacePosition[j].nWidth / ( double ) previewWidth;
416 tmp *= hRange;
417 tmp *= orient_mult;
418 faces[i].rect[trans_right] = faces[i].rect[trans_left] + tmp;
419
420 tmp = ( double ) faceData->tFacePosition[j].nHeight / ( double ) previewHeight;
421 tmp *= vRange;
422 tmp *= orient_mult;
423 faces[i].rect[trans_bot] = faces[i].rect[trans_top] + tmp;
424
425 faces[i].score = faceData->tFacePosition[j].nScore;
426 faces[i].id = 0;
427 faces[i].left_eye[0] = CameraMetadataResult::INVALID_DATA;
428 faces[i].left_eye[1] = CameraMetadataResult::INVALID_DATA;
429 faces[i].right_eye[0] = CameraMetadataResult::INVALID_DATA;
430 faces[i].right_eye[1] = CameraMetadataResult::INVALID_DATA;
431 faces[i].mouth[0] = CameraMetadataResult::INVALID_DATA;
432 faces[i].mouth[1] = CameraMetadataResult::INVALID_DATA;
433 i++;
434 }
435
436 metadataResult->number_of_faces = i;
437 metadataResult->faces = faces;
438
439 for (int i = 0; i < metadataResult->number_of_faces; i++)
440 {
441 bool faceChanged = true;
442 int centerX = (faces[i].rect[trans_left] + faces[i].rect[trans_right] ) / 2;
443 int centerY = (faces[i].rect[trans_top] + faces[i].rect[trans_bot] ) / 2;
444
445 int sizeX = (faces[i].rect[trans_right] - faces[i].rect[trans_left] ) ;
446 int sizeY = (faces[i].rect[trans_bot] - faces[i].rect[trans_top] ) ;
447
448 for (int j = 0; j < faceDetectionNumFacesLastOutput; j++)
449 {
450 int tempCenterX = (faceDetectionLastOutput[j].rect[trans_left] +
451 faceDetectionLastOutput[j].rect[trans_right] ) / 2;
452 int tempCenterY = (faceDetectionLastOutput[j].rect[trans_top] +
453 faceDetectionLastOutput[j].rect[trans_bot] ) / 2;
454 int tempSizeX = (faceDetectionLastOutput[j].rect[trans_right] -
455 faceDetectionLastOutput[j].rect[trans_left] ) ;
456 int tempSizeY = (faceDetectionLastOutput[j].rect[trans_bot] -
457 faceDetectionLastOutput[j].rect[trans_top] ) ;
458
459 if ( ( tempCenterX == centerX) &&
460 ( tempCenterY == centerY) ) {
461 // Found Face.
462 // Now check size of rectangle
463 // compare to last output.
464 if ( ( tempSizeX == sizeX ) &&
465 ( tempSizeY == sizeY ) ) {
466 faceChanged = false;
467 }
468 }
469 }
470 // Send face detection data after some face coordinate changes
471 if (faceChanged) {
472 faceArrayChanged = true;
473 }
474 }
475
476 // Save this output for next iteration
477 for (int i = 0; i < metadataResult->number_of_faces; i++)
478 {
479 faceDetectionLastOutput[i] = faces[i];
480 }
481 } else {
482 metadataResult->number_of_faces = 0;
483 metadataResult->faces = NULL;
484 }
485
486 // Send face detection data after face count changes
487 if (faceDetectionNumFacesLastOutput != metadataResult->number_of_faces) {
488 faceArrayChanged = true;
489 }
490 faceDetectionNumFacesLastOutput = metadataResult->number_of_faces;
491
492 if ( !faceArrayChanged ) {
493 ret = NOT_ENOUGH_DATA;
494 }
495
496 LOG_FUNCTION_NAME_EXIT;
497
498 out:
499
500 return ret;
501 }
502
503 } // namespace Camera
504 } // namespace Ti
505