face_detector.cpp
Go to the documentation of this file.
00001 
00061 #ifdef __LINUX__
00062         #include "cob_people_detection/face_detector.h"
00063         #include "cob_vision_utils/GlobalDefines.h"
00064 #else
00065 #include "cob_vision/cob_people_detection/common/include/cob_people_detection/PeopleDetector.h"
00066 #include "cob_common/cob_vision_utils/common/include/cob_vision_utils/GlobalDefines.h"
00067 #endif
00068 
00069 #include <opencv2/opencv.hpp>
00070 
00071 using namespace ipa_PeopleDetector;
00072 
00073 FaceDetector::FaceDetector(void)
00074 {       
00075         m_face_cascade = 0;
00076         m_initialized = false;
00077 }
00078 
00079 unsigned long FaceDetector::init(std::string directory, double faces_increase_search_scale, int faces_drop_groups, int faces_min_search_scale_x, int faces_min_search_scale_y,
00080                 bool reason_about_3dface_size, double face_size_max_m, double face_size_min_m, double max_face_z_m, bool debug)
00081 {
00082         // parameters
00083         m_faces_increase_search_scale = faces_increase_search_scale;
00084         m_faces_drop_groups = faces_drop_groups;
00085         m_faces_min_search_scale_x = faces_min_search_scale_x;
00086         m_faces_min_search_scale_y = faces_min_search_scale_y;
00087         m_reason_about_3dface_size = reason_about_3dface_size;
00088         m_face_size_max_m = face_size_max_m;
00089         m_face_size_min_m = face_size_min_m;
00090         m_max_face_z_m = max_face_z_m;
00091         m_debug = debug;
00092 
00093         // load Haar-Classifier for frontal face detection
00094         std::string faceCascadePath = directory + "haarcascades/haarcascade_frontalface_alt2.xml";
00095         m_face_cascade = (CvHaarClassifierCascade*)cvLoad(faceCascadePath.c_str(), 0, 0, 0 );   //"ConfigurationFiles/haarcascades/haarcascade_frontalface_alt2.xml", 0, 0, 0 );
00096 
00097         // Create Memory
00098         m_storage = cvCreateMemStorage(0);
00099 
00100         m_initialized = true;
00101 
00102         return ipa_Utils::RET_OK;
00103 }
00104 
00105 FaceDetector::~FaceDetector(void)
00106 {
00107         // Release Classifiers and memory
00108         cvReleaseHaarClassifierCascade(&m_face_cascade);
00109         cvReleaseMemStorage(&m_storage);
00110 }
00111 
00112 unsigned long FaceDetector::detectColorFaces(std::vector<cv::Mat>& heads_color_images, const std::vector<cv::Mat>& heads_depth_images, std::vector<std::vector<cv::Rect> >& face_coordinates)
00113 {
00114         if (m_initialized == false)
00115         {
00116                 std::cout << "Error: FaceDetector::DetectColorFaces: init() must be called first." << std::endl;
00117                 return ipa_Utils::RET_FAILED;
00118         }
00119 
00120         face_coordinates.clear();
00121         face_coordinates.resize(heads_color_images.size());
00122 
00123         // iterate over head regions
00124         for(unsigned int head=0; head<heads_color_images.size(); head++)
00125         {
00126                 // detect faces in color image in proposed region
00127                 IplImage imgPtr = (IplImage)heads_color_images[head];
00128                 CvSeq* faces = cvHaarDetectObjects(&imgPtr,     m_face_cascade, m_storage, m_faces_increase_search_scale, m_faces_drop_groups, CV_HAAR_DO_CANNY_PRUNING, cvSize(m_faces_min_search_scale_x, m_faces_min_search_scale_y));
00129 
00130                 cv::Size parentSize;
00131                 cv::Point roiOffset;
00132                 for(int i=0; i<faces->total; i++)
00133                 {
00134                         cv::Rect* face = (cv::Rect*)cvGetSeqElem(faces, i);
00135 //                      heads_color_images[head].locateROI(parentSize, roiOffset);
00136 //                      face->x += roiOffset.x;         // todo: check what happens if the original matrix is used without roi
00137 //                      face->y += roiOffset.y;
00138                         // exclude faces that are too small for the head bounding box
00139                         if (face->width > 0.4*heads_color_images[head].cols && face->height > 0.4*heads_color_images[head].rows)
00140                                 face_coordinates[head].push_back(*face);
00141                 }
00142         }
00143 
00144         if (m_reason_about_3dface_size==true)
00145         {
00146                 // check whether the color faces have a reasonable 3D size
00147                 for (uint head_index=0; head_index<face_coordinates.size(); head_index++)
00148                 {
00149                         double avg_depth_value = 0.0;
00150                         int last_accepted_face_index = -1;
00151                         double last_accepted_face_area = 0.0;
00152                         for (uint face_index = 0; face_index < face_coordinates[head_index].size(); face_index++)
00153                         {
00154                                 //std::cout << face_index << ": face_coordinates[head_index].size()=" << face_coordinates[head_index].size() << std::endl;
00155 
00156                                 cv::Rect& face = face_coordinates[head_index][face_index];
00157 
00158                                 // Get the median disparity in the middle half of the bounding box.
00159                                 int uStart = floor(0.25*face.width);
00160                                 int uEnd = floor(0.75*face.width) + 1;
00161                                 int vStart = floor(0.25*face.height);
00162                                 int vEnd = floor(0.75*face.height) + 1;
00163                                 int du = abs(uEnd-uStart);
00164 
00165                                 // generate vector of all depth values in the face region, NaNs are converted to -1
00166                                 cv::Mat face_region = heads_depth_images[head_index];
00167         // EDIT
00168                                 cv::Mat tmat(1, du*abs(vEnd-vStart), CV_32FC1);
00169                                 float* tmatPtr = (float*)tmat.data;
00170                                 for (int v=vStart; v<vEnd; v++)
00171                                 {
00172                                         float* zPtr = (float*)face_region.row(v).data;
00173                                         zPtr += 2+3*uStart;
00174                                         for (int u=uStart; u<uEnd; u++)
00175                                         {
00176                                                 float depthval = *zPtr;
00177                                                 if (!isnan(depthval)) *tmatPtr = depthval;
00178                                                 else *tmatPtr = -1.0;
00179                                                 tmatPtr++;
00180                                                 zPtr += 3;
00181                                         }
00182                                 }
00183 
00184                                 // median
00185                                 cv::Mat tmat_sorted;
00186                                 cv::sort(tmat, tmat_sorted, CV_SORT_EVERY_ROW+CV_SORT_DESCENDING);
00187                                 double avg_depth = tmat_sorted.at<float>(floor(cv::countNonZero(tmat_sorted>=0.0)*0.5)); // Get the middle valid disparity (-1 disparities are invalid)
00188 
00189                                 // If the median disparity was valid and the face is a reasonable size, the face status is "good".
00190                                 // If the median disparity was valid but the face isn't a reasonable size, the face status is "bad".
00191                                 // Otherwise, the face status is "unknown".
00192                                 // Only bad faces are removed
00193                                 bool remove_face = false;
00194                                 if (avg_depth > 0)
00195                                 {
00196                                         double radiusX, radiusY, radius3d=1e20;
00197                                         cv::Vec3f a, b;
00198                                         // vertical line regularly lies completely on the head whereas this does not hold very often for the horizontal line crossing the bounding box of the face
00199                                         // rectangle in the middle
00200                                         a = heads_depth_images[head_index].at<cv::Vec3f>((int)(face.y+face.height*0.25), (int)(face.x+0.5*face.width));
00201                                         b = heads_depth_images[head_index].at<cv::Vec3f>((int)(face.y+face.height*0.75), (int)(face.x+0.5*face.width));
00202                                         if (m_debug) std::cout << "a: " << a.val[0] << " " << a.val[1] << " " << a.val[2] << "   b: " << " " << b.val[0] << " " << b.val[1] << " " << b.val[2] << "\n";
00203                                         if (isnan(a.val[0]) || isnan(b.val[0])) radiusY = 0.0;
00204                                         else radiusY = cv::norm(b-a);
00205                                         radius3d = radiusY;
00206 
00207                                         // for radius estimation with the horizontal line through the face rectangle use points which typically still lie on the face and not in the background
00208                                         a = heads_depth_images[head_index].at<cv::Vec3f>((int)(face.y+face.height*0.5), (int)(face.x+face.width*0.25));
00209                                         b = heads_depth_images[head_index].at<cv::Vec3f>((int)(face.y+face.height*0.5), (int)(face.x+face.width*0.75));
00210                                         if (m_debug) std::cout << "a: " << a.val[0] << " " << a.val[1] << " " << a.val[2] << "   b: " << " " << b.val[0] << " " << b.val[1] << " " << b.val[2] << "\n";
00211                                         if (isnan(a.val[0]) || isnan(b.val[0])) radiusX = 0.0;
00212                                         else
00213                                         {
00214                                                 radiusX = cv::norm(b-a);
00215                                                 if (radiusY != 0.0) radius3d = (radiusX+radiusY)*0.5;
00216                                                 else radius3d = radiusX;
00217                                         }
00218 
00219 //                                      cv::Point pup(face.x+0.5*face.width, face.y+face.height*0.25);
00220 //                                      cv::Point plo(face.x+0.5*face.width, face.y+face.height*0.75);
00221 //                                      cv::Point ple(face.x+face.width*0.25, face.y+face.height*0.5);
00222 //                                      cv::Point pri(face.x+face.width*0.75, face.y+face.height*0.5);
00223 //                                      cv::line(xyz_image_8U3, pup, plo, CV_RGB(255, 255, 255), 2);
00224 //                                      cv::line(xyz_image_8U3, ple, pri, CV_RGB(255, 255, 255), 2);
00225 
00226                                         if (m_debug)
00227                                         {
00228                                                 std::cout << "radiusX: " << radiusX << "  radiusY: " << radiusY << "\n";
00229                                                 std::cout << "avg_depth: " << avg_depth << " > max_face_z_m: " << m_max_face_z_m << " ?  2*radius3d: " << 2.0*radius3d << " < face_size_min_m: " << m_face_size_min_m << " ?  2radius3d: " << 2.0*radius3d << " > face_size_max_m:" << m_face_size_max_m << "?\n";
00230                                         }
00231                                         if (radius3d > 0.0 && (avg_depth > m_max_face_z_m || 2.0*radius3d < m_face_size_min_m || 2.0*radius3d > m_face_size_max_m))
00232                                         {
00233                                                 remove_face = true;
00234                                         }
00235                                 }
00236 
00237                                 if (remove_face==true)
00238                                 {
00239                                         // face does not match normal human appearance -> remove from list
00240                                         face_coordinates[head_index].erase(face_coordinates[head_index].begin()+face_index);
00241                                         face_index--;
00242                                 }
00243                                 else
00244                                 {
00245                                         // only one face can be detected per head -> check which has the bigger face area in the image
00246                                         double current_area = face_coordinates[head_index][face_index].height * face_coordinates[head_index][face_index].width;
00247                                         if (last_accepted_face_index == -1)
00248                                         {
00249                                                 last_accepted_face_index = (int)face_index;
00250                                                 last_accepted_face_area = current_area;
00251                                                 avg_depth_value = avg_depth;
00252                                         }
00253                                         else
00254                                         {
00255                                                 if (current_area > last_accepted_face_area)
00256                                                 {
00257                                                         // delete old selection
00258                                                         face_coordinates[head_index].erase(face_coordinates[head_index].begin()+last_accepted_face_index);
00259                                                         face_index--;
00260 
00261                                                         last_accepted_face_index = face_index;
00262                                                         last_accepted_face_area = current_area;
00263                                                         avg_depth_value = avg_depth;
00264                                                 }
00265                                                 else
00266                                                 {
00267                                                         // delete current face
00268                                                         face_coordinates[head_index].erase(face_coordinates[head_index].begin()+face_index);
00269                                                         face_index--;
00270                                                 }
00271                                         }
00272                                 }
00273                         }
00274                         assert(face_coordinates[head_index].size()==0 || face_coordinates[head_index].size()==1);
00275 
00276                         // clear image background
00277                         if (avg_depth_value > 0)
00278                                 for (int v=0; v<heads_depth_images[head_index].rows; v++)
00279                                         for (int u=0; u<heads_depth_images[head_index].cols; u++)
00280                                         {
00281                                                 float val = heads_depth_images[head_index].at<cv::Vec3f>(v,u)[2];
00282                                                 if (val==0.f || fabs(heads_depth_images[head_index].at<cv::Vec3f>(v,u)[2]-avg_depth_value) > 0.18 || val!=val)
00283                                                         heads_color_images[head_index].at<cv::Vec3b>(v,u) = cv::Vec3b(255, 255, 255);
00284                                         }
00285 //                      std::cout << "avg_depth_value=" << avg_depth_value << std::endl;
00286 //                      cv::imshow("rgb image", heads_color_images[head_index]);
00287 //                      cv::waitKey(10);
00288 //                      cv::imshow("xyz image", xyz_image_8U3);
00289 //                      cv::waitKey(10);
00290                 }
00291         }
00292 
00293         return ipa_Utils::RET_OK;
00294 }
00295 


cob_people_detection
Author(s): Richard Bormann , Thomas Zwölfer
autogenerated on Mon May 6 2019 02:32:06