00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041 #include "face_detector/faces.h"
00042 #include <cfloat>
00043 #include <algorithm>
00044
00045 namespace people {
00046
00047 Faces::Faces():
00048 list_(NULL),
00049 cam_model_(NULL) {
00050 }
00051
00052 Faces::~Faces() {
00053
00054 threads_.interrupt_all();
00055 threads_.join_all();
00056 delete face_go_mutex_;
00057 face_go_mutex_ = NULL;
00058
00059 for (int i=list_.size(); i>0; i--) {
00060 list_.pop_back();
00061 }
00062
00063 cam_model_ = 0;
00064
00065 }
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075 void Faces::initFaceDetectionDisparity(uint num_cascades, string haar_classifier_filename, double face_size_min_m, double face_size_max_m, double max_face_z_m, double face_sep_dist_m) {
00076 images_ready_ = 0;
00077
00078 face_size_min_m_ = face_size_min_m;
00079 face_size_max_m_ = face_size_max_m;
00080 max_face_z_m_ = max_face_z_m;
00081 face_sep_dist_m_ = face_sep_dist_m;
00082
00083 face_go_mutex_ = new boost::mutex();
00084 bool cascade_ok = cascade_.load(haar_classifier_filename);
00085
00086 if (!cascade_ok) {
00087 ROS_ERROR_STREAM("Cascade file " << haar_classifier_filename << " doesn't exist.");
00088 return;
00089 }
00090 threads_.create_thread(boost::bind(&Faces::faceDetectionThreadDisparity,this,0));
00091 }
00092
00094
00095 vector<Box2D3D> Faces::detectAllFacesDisparity(const cv::Mat &image, double threshold, const cv::Mat &disparity_image, image_geometry::StereoCameraModel *cam_model) {
00096
00097 faces_.clear();
00098
00099
00100
00101 if (image.channels() == 1) {
00102 cv_image_gray_.create(image.size(), CV_8UC1);
00103 image.copyTo(cv_image_gray_);
00104 }
00105 else if (image.channels() == 3) {
00106 cv_image_gray_.create(image.size(), CV_8UC1);
00107 cv::cvtColor(image, cv_image_gray_, CV_BGR2GRAY);
00108 }
00109 else {
00110 std::cerr << "Unknown image type"<<std::endl;
00111 return faces_;
00112 }
00113
00114 disparity_image_ = &disparity_image;
00115 cam_model_ = cam_model;
00116
00117
00118 num_threads_to_wait_for_ = threads_.size();
00119 boost::mutex::scoped_lock fgmlock(*(face_go_mutex_));
00120 images_ready_++;
00121 fgmlock.unlock();
00122
00123 face_detection_ready_cond_.notify_all();
00124
00125 boost::mutex::scoped_lock fdmlock(face_done_mutex_);
00126 while (num_threads_to_wait_for_ > 0) {
00127 face_detection_done_cond_.wait(fdmlock);
00128 }
00129
00130 return faces_;
00131 }
00132
00134
00135 void Faces::faceDetectionThreadDisparity(uint i) {
00136
00137 while (1) {
00138 boost::mutex::scoped_lock fgmlock(*(face_go_mutex_));
00139 boost::mutex::scoped_lock tlock(t_mutex_, boost::defer_lock);
00140 while (1) {
00141 tlock.lock();
00142 if (images_ready_) {
00143 --images_ready_;
00144 tlock.unlock();
00145 break;
00146 }
00147 tlock.unlock();
00148 face_detection_ready_cond_.wait(fgmlock);
00149 }
00150
00151
00152 cv::Point3d p3_1(0,0,max_face_z_m_);
00153 cv::Point3d p3_2(face_size_min_m_,0,max_face_z_m_);
00154 cv::Point2d p2_1 = (cam_model_->left()).project3dToPixel(p3_1);
00155 cv::Point2d p2_2 = (cam_model_->left()).project3dToPixel(p3_2);
00156 int this_min_face_size = (int)(floor(fabs(p2_2.x-p2_1.x)));
00157
00158 std::vector<cv::Rect> faces_vec;
00159 cascade_.detectMultiScale(cv_image_gray_, faces_vec, 1.2, 2, CV_HAAR_DO_CANNY_PRUNING, cv::Size(this_min_face_size,this_min_face_size));
00160
00161
00162 cv::Scalar color(0,255,0);
00163 Box2D3D one_face;
00164 double avg_disp = 0.0;
00165 cv::Mat uvd(1,3,CV_32FC1);
00166 cv::Mat xyz(1,3,CV_32FC1);
00167
00168 for (uint iface = 0; iface < faces_vec.size(); iface++) {
00169
00170 one_face.status = "good";
00171
00172 one_face.box2d = faces_vec[iface];
00173 one_face.id = i;
00174
00175
00176 cv::Mat disp_roi_shallow(*disparity_image_,cv::Rect(floor(one_face.box2d.x+0.25*one_face.box2d.width),
00177 floor(one_face.box2d.y+0.25*one_face.box2d.height),
00178 floor(one_face.box2d.x+0.75*one_face.box2d.width) - floor(one_face.box2d.x+0.25*one_face.box2d.width) + 1,
00179 floor(one_face.box2d.y+0.75*one_face.box2d.height) - floor(one_face.box2d.y+0.25*one_face.box2d.height) + 1));
00180 cv::Mat disp_roi = disp_roi_shallow.clone();
00181 cv::Mat tmat = disp_roi.reshape(1,disp_roi.rows*disp_roi.cols);
00182 cv::Mat tmat_sorted;
00183 cv::sort(tmat, tmat_sorted, CV_SORT_EVERY_COLUMN+CV_SORT_DESCENDING);
00184 avg_disp = tmat_sorted.at<float>(floor(cv::countNonZero(tmat_sorted>=0.0)/2.0));
00185
00186
00187 one_face.center2d = cv::Point2d(one_face.box2d.x+one_face.box2d.width/2.0,
00188 one_face.box2d.y+one_face.box2d.height/2.0);
00189 one_face.width2d = one_face.box2d.width;
00190
00191
00192
00193
00194 if (avg_disp > 0) {
00195 cam_model_->projectDisparityTo3d(cv::Point2d(0.0,0.0),avg_disp,p3_1);
00196 cam_model_->projectDisparityTo3d(cv::Point2d(one_face.box2d.width,0.0),avg_disp,p3_2);
00197 one_face.width3d = fabs(p3_2.x-p3_1.x);
00198 cam_model_->projectDisparityTo3d(one_face.center2d, avg_disp, one_face.center3d);
00199 if (one_face.center3d.z > max_face_z_m_ || one_face.width3d < face_size_min_m_ || one_face.width3d > face_size_max_m_) {
00200 one_face.status = "bad";
00201 }
00202 }
00203 else {
00204 one_face.width3d = 0.0;
00205 one_face.center3d = cv::Point3d(0.0,0.0,0.0);
00206 one_face.status = "unknown";
00207 }
00208
00209
00210
00211 boost::mutex::scoped_lock lock(face_mutex_);
00212 faces_.push_back(one_face);
00213 lock.unlock();
00214 }
00215
00216 boost::mutex::scoped_lock fdmlock(face_done_mutex_);
00217 num_threads_to_wait_for_--;
00218 fdmlock.unlock();
00219 face_detection_done_cond_.notify_one();
00220 }
00221 }
00222
00224
00225 void Faces::initFaceDetectionDepth(uint num_cascades, string haar_classifier_filename, double face_size_min_m, double face_size_max_m, double max_face_z_m, double face_sep_dist_m) {
00226
00227 images_ready_ = 0;
00228
00229 face_size_min_m_ = face_size_min_m;
00230 face_size_max_m_ = face_size_max_m;
00231 max_face_z_m_ = max_face_z_m;
00232 face_sep_dist_m_ = face_sep_dist_m;
00233
00234 face_go_mutex_ = new boost::mutex();
00235 bool cascade_ok = cascade_.load(haar_classifier_filename);
00236
00237 if (!cascade_ok) {
00238 ROS_ERROR_STREAM("Cascade file " << haar_classifier_filename << " doesn't exist.");
00239 return;
00240 }
00241 threads_.create_thread(boost::bind(&Faces::faceDetectionThreadDepth,this,0));
00242 }
00243
00244
00246
00247 vector<Box2D3D> Faces::detectAllFacesDepth(const cv::Mat &image, double threshold, const cv::Mat &dimage, image_geometry::StereoCameraModel *cam_model) {
00248
00249 faces_.clear();
00250
00251
00252
00253 if (image.channels() == 1) {
00254 cv_image_gray_.create(image.size(), CV_8UC1);
00255 image.copyTo(cv_image_gray_);
00256 }
00257 else if (image.channels() == 3) {
00258 cv_image_gray_.create(image.size(), CV_8UC1);
00259 cv::cvtColor(image, cv_image_gray_, CV_BGR2GRAY);
00260 }
00261 else {
00262 std::cerr << "Unknown image type"<<std::endl;
00263 return faces_;
00264 }
00265
00266 depth_image_ = &dimage;
00267 cam_model_ = cam_model;
00268
00269
00270 num_threads_to_wait_for_ = threads_.size();
00271 boost::mutex::scoped_lock fgmlock(*(face_go_mutex_));
00272 images_ready_++;
00273 fgmlock.unlock();
00274
00275 face_detection_ready_cond_.notify_all();
00276
00277 boost::mutex::scoped_lock fdmlock(face_done_mutex_);
00278 while (num_threads_to_wait_for_ > 0) {
00279 face_detection_done_cond_.wait(fdmlock);
00280 }
00281
00282 return faces_;
00283 }
00284
00286
00287 void Faces::faceDetectionThreadDepth(uint i) {
00288
00289 while (1) {
00290 boost::mutex::scoped_lock fgmlock(*(face_go_mutex_));
00291 boost::mutex::scoped_lock tlock(t_mutex_, boost::defer_lock);
00292 while (1) {
00293 tlock.lock();
00294 if (images_ready_) {
00295 --images_ready_;
00296 tlock.unlock();
00297 break;
00298 }
00299 tlock.unlock();
00300 face_detection_ready_cond_.wait(fgmlock);
00301 }
00302
00303
00304 cv::Point3d p3_1(0,0,max_face_z_m_);
00305 cv::Point3d p3_2(face_size_min_m_,0,max_face_z_m_);
00306 cv::Point2d p2_1 = (cam_model_->left()).project3dToPixel(p3_1);
00307 cv::Point2d p2_2 = (cam_model_->left()).project3dToPixel(p3_2);
00308 int this_min_face_size = (int)(floor(fabs(p2_2.x-p2_1.x)));
00309
00310 std::vector<cv::Rect> faces_vec;
00311 cascade_.detectMultiScale(cv_image_gray_, faces_vec, 1.2, 2, CV_HAAR_DO_CANNY_PRUNING, cv::Size(this_min_face_size,this_min_face_size));
00312
00313
00314 cv::Scalar color(0,255,0);
00315 Box2D3D one_face;
00316 double avg_d = 0.0;
00317 cv::Mat uvd(1,3,CV_32FC1);
00318 cv::Mat xyz(1,3,CV_32FC1);
00319
00320 for (uint iface = 0; iface < faces_vec.size(); iface++) {
00321
00322 one_face.status = "good";
00323
00324 one_face.box2d = faces_vec[iface];
00325 one_face.id = i;
00326
00327
00328 cv::Mat depth_roi_shallow(*depth_image_,cv::Rect(floor(one_face.box2d.x+0.25*one_face.box2d.width),
00329 floor(one_face.box2d.y+0.25*one_face.box2d.height),
00330 floor(one_face.box2d.x+0.75*one_face.box2d.width) - floor(one_face.box2d.x+0.25*one_face.box2d.width) + 1,
00331 floor(one_face.box2d.y+0.75*one_face.box2d.height) - floor(one_face.box2d.y+0.25*one_face.box2d.height) + 1));
00332
00333
00334 one_face.center2d = cv::Point2d(one_face.box2d.x + one_face.box2d.width/2.0,
00335 one_face.box2d.y + one_face.box2d.height/2.0);
00336 one_face.width2d = one_face.box2d.width;
00337
00338
00339
00340
00341
00342
00343 std::vector<float> depths;
00344
00345 for (int i=0; i < depth_roi_shallow.rows; i++) {
00346 const float* dptr = depth_roi_shallow.ptr<float>(i);
00347 for (int j=0; j<depth_roi_shallow.cols; j++) {
00348 if (dptr[j] == dptr[j]) {
00349 depths.push_back(dptr[j]);
00350 }
00351 }
00352 }
00353
00354 std::vector<float>::iterator dbegin = depths.begin();
00355 std::vector<float>::iterator dend = depths.end();
00356
00357 if (depths.size() > 0) {
00358 std::sort(dbegin, dend);
00359 avg_d = depths[floor(depths.size()/2.0)];
00360
00361 one_face.width3d = fabs((cam_model_->left()).getDeltaX(one_face.box2d.width, avg_d));
00362 one_face.center3d = (cam_model_->left()).projectPixelTo3dRay(one_face.center2d);
00363
00364 one_face.center3d = (avg_d/one_face.center3d.z) * one_face.center3d;
00365 if (one_face.center3d.z > max_face_z_m_ || one_face.width3d < face_size_min_m_ || one_face.width3d > face_size_max_m_) {
00366 one_face.status = "bad";
00367 }
00368 }
00369 else {
00370 one_face.width3d = 0.0;
00371 one_face.center3d = cv::Point3d(0.0,0.0,0.0);
00372 one_face.status = "unknown";
00373 }
00374
00375
00376
00377 boost::mutex::scoped_lock lock(face_mutex_);
00378 faces_.push_back(one_face);
00379 lock.unlock();
00380 }
00381
00382 boost::mutex::scoped_lock fdmlock(face_done_mutex_);
00383 num_threads_to_wait_for_--;
00384 fdmlock.unlock();
00385 face_detection_done_cond_.notify_one();
00386 }
00387
00388 }
00389
00390 };