diff options
Diffstat (limited to 'camera/OMXCameraAdapter/OMXFD.cpp')
-rw-r--r-- | camera/OMXCameraAdapter/OMXFD.cpp | 490 |
1 files changed, 490 insertions, 0 deletions
diff --git a/camera/OMXCameraAdapter/OMXFD.cpp b/camera/OMXCameraAdapter/OMXFD.cpp new file mode 100644 index 0000000..15f8d05 --- /dev/null +++ b/camera/OMXCameraAdapter/OMXFD.cpp @@ -0,0 +1,490 @@ +/* + * 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. +* +*/ + +#undef LOG_TAG + +#define LOG_TAG "CameraHAL" + +#include "CameraHal.h" +#include "OMXCameraAdapter.h" + +#define FACE_DETECTION_THRESHOLD 80 + +// constants used for face smooth filtering +static const int HorizontalFilterThreshold = 40; +static const int VerticalFilterThreshold = 40; +static const int HorizontalFaceSizeThreshold = 30; +static const int VerticalFaceSizeThreshold = 30; + + +namespace android { + +status_t OMXCameraAdapter::setParametersFD(const 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; + + Mutex::Autolock lock(mFaceDetectionLock); + + ret = setFaceDetection(true, mDeviceOrientation); + 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); + + Mutex::Autolock lock(mFaceDetectionLock); + + ret = setFaceDetection(false, mDeviceOrientation); + if (ret != NO_ERROR) { + goto out; + } + + // Reset 3A settings + ret = setParameters3A(mParams, state); + if (ret != NO_ERROR) { + goto out; + } + + if (mPending3Asettings) { + apply3Asettings(mParameters3A); + } + + faceDetectionNumFacesLastOutput = 0; + out: + return ret; +} + +void OMXCameraAdapter::pauseFaceDetection(bool pause) +{ + Mutex::Autolock lock(mFaceDetectionLock); + // pausing will only take affect if fd is already running + if (mFaceDetectionRunning) { + mFaceDetectionPaused = pause; + faceDetectionNumFacesLastOutput = 0; + } +} + +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 ) + { + ret = setExtraData(enable, mCameraAdapterParameters.mPrevPortIndex, 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::detectFaces(OMX_BUFFERHEADERTYPE* pBuffHeader, + sp<CameraFDResult> &result, + size_t previewWidth, + size_t previewHeight) +{ + status_t ret = NO_ERROR; + OMX_ERRORTYPE eError = OMX_ErrorNone; + OMX_TI_FACERESULT *faceResult; + OMX_OTHER_EXTRADATATYPE *extraData; + OMX_FACEDETECTIONTYPE *faceData; + OMX_TI_PLATFORMPRIVATE *platformPrivate; + camera_frame_metadata_t *faces; + + 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; + } + + platformPrivate = (OMX_TI_PLATFORMPRIVATE *) (pBuffHeader->pPlatformPrivate); + if ( NULL != platformPrivate ) { + if ( sizeof(OMX_TI_PLATFORMPRIVATE) == platformPrivate->nSize ) { + CAMHAL_LOGVB("Size = %d, sizeof = %d, pAuxBuf = 0x%x, pAuxBufSize= %d, pMetaDataBufer = 0x%x, nMetaDataSize = %d", + platformPrivate->nSize, + sizeof(OMX_TI_PLATFORMPRIVATE), + platformPrivate->pAuxBuf1, + platformPrivate->pAuxBufSize1, + platformPrivate->pMetaDataBuffer, + platformPrivate->nMetaDataSize); + } else { + CAMHAL_LOGDB("OMX_TI_PLATFORMPRIVATE size mismatch: expected = %d, received = %d", + ( unsigned int ) sizeof(OMX_TI_PLATFORMPRIVATE), + ( unsigned int ) platformPrivate->nSize); + return -EINVAL; + } + } else { + CAMHAL_LOGDA("Invalid OMX_TI_PLATFORMPRIVATE"); + return-EINVAL; + } + + + if ( 0 >= platformPrivate->nMetaDataSize ) { + CAMHAL_LOGDB("OMX_TI_PLATFORMPRIVATE nMetaDataSize is size is %d", + ( unsigned int ) platformPrivate->nMetaDataSize); + return -EINVAL; + } + + extraData = getExtradata((OMX_OTHER_EXTRADATATYPE *) (platformPrivate->pMetaDataBuffer), + platformPrivate->nMetaDataSize, (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_LOGDA("Invalid OMX_OTHER_EXTRADATATYPE"); + 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_LOGDB("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; + } + + ret = encodeFaceCoordinates(faceData, &faces, previewWidth, previewHeight); + + if ( NO_ERROR == ret ) { + result = new CameraFDResult(faces); + } else { + result.clear(); + result = NULL; + } + + LOG_FUNCTION_NAME_EXIT; + + return ret; +} + +status_t OMXCameraAdapter::encodeFaceCoordinates(const OMX_FACEDETECTIONTYPE *faceData, + camera_frame_metadata_t **pFaces, + size_t previewWidth, + size_t previewHeight) +{ + status_t ret = NO_ERROR; + camera_face_t *faces; + camera_frame_metadata_t *faceResult; + size_t hRange, vRange; + double tmp; + + LOG_FUNCTION_NAME; + + if ( NULL == faceData ) { + CAMHAL_LOGEA("Invalid OMX_FACEDETECTIONTYPE parameter"); + return EINVAL; + } + + LOG_FUNCTION_NAME + + hRange = CameraFDResult::RIGHT - CameraFDResult::LEFT; + vRange = CameraFDResult::BOTTOM - CameraFDResult::TOP; + + faceResult = ( camera_frame_metadata_t * ) malloc(sizeof(camera_frame_metadata_t)); + if ( NULL == faceResult ) { + return -ENOMEM; + } + + if ( 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 ) { + return -ENOMEM; + } + + /** + / * 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 (mDeviceOrientation == 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 (mDeviceOrientation == 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] = CameraFDResult::INVALID_DATA; + faces[i].left_eye[1] = CameraFDResult::INVALID_DATA; + faces[i].right_eye[0] = CameraFDResult::INVALID_DATA; + faces[i].right_eye[1] = CameraFDResult::INVALID_DATA; + faces[i].mouth[0] = CameraFDResult::INVALID_DATA; + faces[i].mouth[1] = CameraFDResult::INVALID_DATA; + i++; + } + + faceResult->number_of_faces = i; + faceResult->faces = faces; + + for (int i = 0; i < faceResult->number_of_faces; i++) + { + 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 ( (abs(tempCenterX - centerX) < HorizontalFilterThreshold) && + (abs(tempCenterY - centerY) < VerticalFilterThreshold) ) + { + // Found Face. It did not move too far. + // Now check size of rectangle compare to last output + if ( (abs (tempSizeX -sizeX) < HorizontalFaceSizeThreshold) && + (abs (tempSizeY -sizeY) < VerticalFaceSizeThreshold) ) + { + // Rectangle is almost same as last time + // Output exactly what was done for this face last time. + faces[i] = faceDetectionLastOutput[j]; + } + else + { + // TODO(XXX): Rectangle size changed but position is same. + // Possibly we can apply just positional correctness. + } + } + } + } + + // Save this output for next iteration + for (int i = 0; i < faceResult->number_of_faces; i++) + { + faceDetectionLastOutput[i] = faces[i]; + } + faceDetectionNumFacesLastOutput = faceResult->number_of_faces; + } else { + faceResult->number_of_faces = 0; + faceResult->faces = NULL; + } + + *pFaces = faceResult; + + LOG_FUNCTION_NAME_EXIT; + + return ret; +} + +}; |