diff options
Diffstat (limited to 'camera/OMXCameraAdapter/OMXFD.cpp')
-rw-r--r-- | camera/OMXCameraAdapter/OMXFD.cpp | 504 |
1 files changed, 504 insertions, 0 deletions
diff --git a/camera/OMXCameraAdapter/OMXFD.cpp b/camera/OMXCameraAdapter/OMXFD.cpp new file mode 100644 index 0000000..1a482b2 --- /dev/null +++ b/camera/OMXCameraAdapter/OMXFD.cpp @@ -0,0 +1,504 @@ +/* + * Copyright (C) Texas Instruments - http://www.ti.com/ + * + * 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. + */ + +/** +* @file OMXFD.cpp +* +* This file contains functionality for handling face detection. +* +*/ + +#include "CameraHal.h" +#include "OMXCameraAdapter.h" + +namespace Ti { +namespace Camera { + +const uint32_t OMXCameraAdapter::FACE_DETECTION_THRESHOLD = 80; + +status_t OMXCameraAdapter::setParametersFD(const android::CameraParameters ¶ms, + BaseCameraAdapter::AdapterState state) +{ + status_t ret = NO_ERROR; + + LOG_FUNCTION_NAME; + + LOG_FUNCTION_NAME_EXIT; + + return ret; +} + +status_t OMXCameraAdapter::startFaceDetection() +{ + status_t ret = NO_ERROR; + + android::AutoMutex lock(mFaceDetectionLock); + + ret = setFaceDetection(true, mFaceOrientation); + if (ret != NO_ERROR) { + goto out; + } + + if ( mFaceDetectionRunning ) { + mFDSwitchAlgoPriority = true; + } + + // Note: White balance will not be face prioritized, since + // the algorithm needs full frame statistics, and not face + // regions alone. + + faceDetectionNumFacesLastOutput = 0; + out: + return ret; +} + +status_t OMXCameraAdapter::stopFaceDetection() +{ + status_t ret = NO_ERROR; + const char *str = NULL; + BaseCameraAdapter::AdapterState state; + BaseCameraAdapter::getState(state); + + android::AutoMutex lock(mFaceDetectionLock); + + ret = setFaceDetection(false, mFaceOrientation); + if (ret != NO_ERROR) { + goto out; + } + + if ( mFaceDetectionRunning ) { + //Enable region priority and disable face priority for AF + setAlgoPriority(REGION_PRIORITY, FOCUS_ALGO, true); + setAlgoPriority(FACE_PRIORITY, FOCUS_ALGO , false); + + //Enable Region priority and disable Face priority + setAlgoPriority(REGION_PRIORITY, EXPOSURE_ALGO, true); + setAlgoPriority(FACE_PRIORITY, EXPOSURE_ALGO, false); + } + + if (mPending3Asettings) { + apply3Asettings(mParameters3A); + } + + faceDetectionNumFacesLastOutput = 0; + out: + return ret; +} + +void OMXCameraAdapter::pauseFaceDetection(bool pause) +{ + android::AutoMutex lock(mFaceDetectionLock); + // pausing will only take affect if fd is already running + if (mFaceDetectionRunning) { + mFaceDetectionPaused = pause; + faceDetectionNumFacesLastOutput = 0; + } +} + +status_t OMXCameraAdapter::setFaceDetectionOrientation(OMX_U32 orientation) +{ + status_t ret = NO_ERROR; + + android::AutoMutex lock(mFaceDetectionLock); + + mFaceOrientation = orientation; + + if (mFaceDetectionRunning) { + // restart face detection with new rotation + setFaceDetection(true, orientation); + } + + return ret; +} + +status_t OMXCameraAdapter::setFaceDetection(bool enable, OMX_U32 orientation) +{ + status_t ret = NO_ERROR; + OMX_ERRORTYPE eError = OMX_ErrorNone; + OMX_CONFIG_OBJDETECTIONTYPE objDetection; + + LOG_FUNCTION_NAME; + + if ( OMX_StateInvalid == mComponentState ) + { + CAMHAL_LOGEA("OMX component is in invalid state"); + ret = -EINVAL; + } + + if ( NO_ERROR == ret ) + { + if ( orientation > 270 ) { + orientation = 0; + } + + OMX_INIT_STRUCT_PTR (&objDetection, OMX_CONFIG_OBJDETECTIONTYPE); + objDetection.nPortIndex = mCameraAdapterParameters.mPrevPortIndex; + objDetection.nDeviceOrientation = orientation; + if ( enable ) + { + objDetection.bEnable = OMX_TRUE; + } + else + { + objDetection.bEnable = OMX_FALSE; + } + + eError = OMX_SetConfig(mCameraAdapterParameters.mHandleComp, + ( OMX_INDEXTYPE ) OMX_IndexConfigImageFaceDetection, + &objDetection); + if ( OMX_ErrorNone != eError ) + { + CAMHAL_LOGEB("Error while configuring face detection 0x%x", eError); + ret = -1; + } + else + { + CAMHAL_LOGDA("Face detection configured successfully"); + } + } + + if ( NO_ERROR == ret ) + { + // TODO(XXX): Should enable/disable FD extra data separately + // on each port. + ret = setExtraData(enable, OMX_ALL, OMX_FaceDetection); + + if ( NO_ERROR != ret ) + { + CAMHAL_LOGEA("Error while configuring face detection extra data"); + } + else + { + CAMHAL_LOGDA("Face detection extra data configured successfully"); + } + } + + if ( NO_ERROR == ret ) + { + mFaceDetectionRunning = enable; + mFaceDetectionPaused = !enable; + } + + LOG_FUNCTION_NAME_EXIT; + + return ret; +} + +status_t OMXCameraAdapter::createPreviewMetadata(OMX_BUFFERHEADERTYPE* pBuffHeader, + android::sp<CameraMetadataResult> &result, + size_t previewWidth, + size_t previewHeight) +{ + status_t ret = NO_ERROR; + status_t faceRet = NO_ERROR; + status_t metaRet = NO_ERROR; + OMX_FACEDETECTIONTYPE *faceData = NULL; + + LOG_FUNCTION_NAME; + + if ( OMX_StateExecuting != mComponentState ) { + CAMHAL_LOGEA("OMX component is not in executing state"); + return NO_INIT; + } + + if ( NULL == pBuffHeader ) { + CAMHAL_LOGEA("Invalid Buffer header"); + return-EINVAL; + } + + if ( mFaceDetectionRunning && !mFaceDetectionPaused ) { + OMX_OTHER_EXTRADATATYPE *extraData; + + extraData = getExtradata(pBuffHeader->pPlatformPrivate, + (OMX_EXTRADATATYPE)OMX_FaceDetection); + + if ( NULL != extraData ) { + CAMHAL_LOGVB("Size = %d, sizeof = %d, eType = 0x%x, nDataSize= %d, nPortIndex = 0x%x, nVersion = 0x%x", + extraData->nSize, + sizeof(OMX_OTHER_EXTRADATATYPE), + extraData->eType, + extraData->nDataSize, + extraData->nPortIndex, + extraData->nVersion); + } else { + CAMHAL_LOGD("FD extra data not found!"); + return -EINVAL; + } + + faceData = ( OMX_FACEDETECTIONTYPE * ) extraData->data; + if ( NULL != faceData ) { + if ( sizeof(OMX_FACEDETECTIONTYPE) == faceData->nSize ) { + CAMHAL_LOGVB("Faces detected %d", + faceData->ulFaceCount, + faceData->nSize, + sizeof(OMX_FACEDETECTIONTYPE), + faceData->eCameraView, + faceData->nPortIndex, + faceData->nVersion); + } else { + CAMHAL_LOGEB("OMX_FACEDETECTIONTYPE size mismatch: expected = %d, received = %d", + ( unsigned int ) sizeof(OMX_FACEDETECTIONTYPE), + ( unsigned int ) faceData->nSize); + return -EINVAL; + } + } else { + CAMHAL_LOGEA("Invalid OMX_FACEDETECTIONTYPE"); + return -EINVAL; + } + } + + result = new (std::nothrow) CameraMetadataResult; + if(NULL == result.get()) { + ret = NO_MEMORY; + return ret; + } + + //Encode face coordinates + faceRet = encodeFaceCoordinates(faceData, result->getMetadataResult() + , previewWidth, previewHeight); + if ((NO_ERROR == faceRet) || (NOT_ENOUGH_DATA == faceRet)) { + // Ignore harmless errors (no error and no update) and go ahead and encode + // the preview meta data + metaRet = encodePreviewMetadata(result->getMetadataResult() + , pBuffHeader->pPlatformPrivate); + if ( (NO_ERROR != metaRet) && (NOT_ENOUGH_DATA != metaRet) ) { + // Some 'real' error occurred during preview meta data encod, clear metadata + // result and return correct error code + result.clear(); + ret = metaRet; + } + } else { + //Some real error occurred during face encoding, clear metadata result + // and return correct error code + result.clear(); + ret = faceRet; + } + + if((NOT_ENOUGH_DATA == faceRet) && (NOT_ENOUGH_DATA == metaRet)) { + //No point sending the callback if nothing is changed + result.clear(); + ret = faceRet; + } + + LOG_FUNCTION_NAME_EXIT; + + return ret; +} + +status_t OMXCameraAdapter::encodeFaceCoordinates(const OMX_FACEDETECTIONTYPE *faceData, + camera_frame_metadata_t *metadataResult, + size_t previewWidth, + size_t previewHeight) +{ + status_t ret = NO_ERROR; + camera_face_t *faces; + size_t hRange, vRange; + double tmp; + bool faceArrayChanged = false; + + LOG_FUNCTION_NAME; + + hRange = CameraMetadataResult::RIGHT - CameraMetadataResult::LEFT; + vRange = CameraMetadataResult::BOTTOM - CameraMetadataResult::TOP; + + android::AutoMutex lock(mFaceDetectionLock); + + // Avoid memory leak if called twice on same CameraMetadataResult + if ( (0 < metadataResult->number_of_faces) && (NULL != metadataResult->faces) ) { + free(metadataResult->faces); + metadataResult->number_of_faces = 0; + metadataResult->faces = NULL; + } + + if ( (NULL != faceData) && (0 < faceData->ulFaceCount) ) { + int orient_mult; + int trans_left, trans_top, trans_right, trans_bot; + + faces = ( camera_face_t * ) malloc(sizeof(camera_face_t)*faceData->ulFaceCount); + if ( NULL == faces ) { + ret = NO_MEMORY; + goto out; + } + + /** + / * When device is 180 degrees oriented to the sensor, need to translate + / * the output from Ducati to what Android expects + / * Ducati always gives face coordinates in this form, irrespective of + / * rotation, i.e (l,t) always represents the point towards the left eye + / * and top of hair. + / * (l, t) + / * --------------- + / * - ,,,,,,, - + / * - | | - + / * - |<a <a| - + / * - (| ^ |) - + / * - | -=- | - + / * - \_____/ - + / * --------------- + / * (r, b) + / * + / * However, Android expects the coords to be in respect with what the + / * sensor is viewing, i.e Android expects sensor to see this with (l,t) + / * and (r,b) like so: + / * (l, t) + / * --------------- + / * - _____ - + / * - / \ - + / * - | -=- | - + / * - (| ^ |) - + / * - |a> a>| - + / * - | | - + / * - ,,,,,,, - + / * --------------- + / * (r, b) + */ + + if (mFaceOrientation == 180) { + orient_mult = -1; + trans_left = 2; // right is now left + trans_top = 3; // bottom is now top + trans_right = 0; // left is now right + trans_bot = 1; // top is not bottom + } else { + orient_mult = 1; + trans_left = 0; // left + trans_top = 1; // top + trans_right = 2; // right + trans_bot = 3; // bottom + } + + int j = 0, i = 0; + for ( ; j < faceData->ulFaceCount ; j++) + { + OMX_S32 nLeft = 0; + OMX_S32 nTop = 0; + //Face filtering + //For real faces, it is seen that the h/w passes a score >=80 + //For false faces, we seem to get even a score of 70 sometimes. + //In order to avoid any issue at application level, we filter + //<=70 score here. + if(faceData->tFacePosition[j].nScore <= FACE_DETECTION_THRESHOLD) + continue; + + if (mFaceOrientation == 180) { + // from sensor pov, the left pos is the right corner of the face in pov of frame + nLeft = faceData->tFacePosition[j].nLeft + faceData->tFacePosition[j].nWidth; + nTop = faceData->tFacePosition[j].nTop + faceData->tFacePosition[j].nHeight; + } else { + nLeft = faceData->tFacePosition[j].nLeft; + nTop = faceData->tFacePosition[j].nTop; + } + + tmp = ( double ) nLeft / ( double ) previewWidth; + tmp *= hRange; + tmp -= hRange/2; + faces[i].rect[trans_left] = tmp; + + tmp = ( double ) nTop / ( double )previewHeight; + tmp *= vRange; + tmp -= vRange/2; + faces[i].rect[trans_top] = tmp; + + tmp = ( double ) faceData->tFacePosition[j].nWidth / ( double ) previewWidth; + tmp *= hRange; + tmp *= orient_mult; + faces[i].rect[trans_right] = faces[i].rect[trans_left] + tmp; + + tmp = ( double ) faceData->tFacePosition[j].nHeight / ( double ) previewHeight; + tmp *= vRange; + tmp *= orient_mult; + faces[i].rect[trans_bot] = faces[i].rect[trans_top] + tmp; + + faces[i].score = faceData->tFacePosition[j].nScore; + faces[i].id = 0; + faces[i].left_eye[0] = CameraMetadataResult::INVALID_DATA; + faces[i].left_eye[1] = CameraMetadataResult::INVALID_DATA; + faces[i].right_eye[0] = CameraMetadataResult::INVALID_DATA; + faces[i].right_eye[1] = CameraMetadataResult::INVALID_DATA; + faces[i].mouth[0] = CameraMetadataResult::INVALID_DATA; + faces[i].mouth[1] = CameraMetadataResult::INVALID_DATA; + i++; + } + + metadataResult->number_of_faces = i; + metadataResult->faces = faces; + + for (int i = 0; i < metadataResult->number_of_faces; i++) + { + bool faceChanged = true; + int centerX = (faces[i].rect[trans_left] + faces[i].rect[trans_right] ) / 2; + int centerY = (faces[i].rect[trans_top] + faces[i].rect[trans_bot] ) / 2; + + int sizeX = (faces[i].rect[trans_right] - faces[i].rect[trans_left] ) ; + int sizeY = (faces[i].rect[trans_bot] - faces[i].rect[trans_top] ) ; + + for (int j = 0; j < faceDetectionNumFacesLastOutput; j++) + { + int tempCenterX = (faceDetectionLastOutput[j].rect[trans_left] + + faceDetectionLastOutput[j].rect[trans_right] ) / 2; + int tempCenterY = (faceDetectionLastOutput[j].rect[trans_top] + + faceDetectionLastOutput[j].rect[trans_bot] ) / 2; + int tempSizeX = (faceDetectionLastOutput[j].rect[trans_right] - + faceDetectionLastOutput[j].rect[trans_left] ) ; + int tempSizeY = (faceDetectionLastOutput[j].rect[trans_bot] - + faceDetectionLastOutput[j].rect[trans_top] ) ; + + if ( ( tempCenterX == centerX) && + ( tempCenterY == centerY) ) { + // Found Face. + // Now check size of rectangle + // compare to last output. + if ( ( tempSizeX == sizeX ) && + ( tempSizeY == sizeY ) ) { + faceChanged = false; + } + } + } + // Send face detection data after some face coordinate changes + if (faceChanged) { + faceArrayChanged = true; + } + } + + // Save this output for next iteration + for (int i = 0; i < metadataResult->number_of_faces; i++) + { + faceDetectionLastOutput[i] = faces[i]; + } + } else { + metadataResult->number_of_faces = 0; + metadataResult->faces = NULL; + } + + // Send face detection data after face count changes + if (faceDetectionNumFacesLastOutput != metadataResult->number_of_faces) { + faceArrayChanged = true; + } + faceDetectionNumFacesLastOutput = metadataResult->number_of_faces; + + if ( !faceArrayChanged ) { + ret = NOT_ENOUGH_DATA; + } + + LOG_FUNCTION_NAME_EXIT; + +out: + + return ret; +} + +} // namespace Camera +} // namespace Ti |