void FaceDetector::imageCallback(const sensor_msgs::Image::ConstPtr& image_ptr) { private_nh_.param("/qbo_face_tracking/send_to_recognizer", send_to_face_recognizer_, false); /* * Cretate a CvImage pointer to get cv::Mat representation of image */ cv_bridge::CvImagePtr cv_ptr; try { cv_ptr = cv_bridge::toCvCopy(image_ptr, sensor_msgs::image_encodings::RGB8); } catch (cv_bridge::Exception& e) { ROS_ERROR("cv_bridge exception: %s", e.what()); return; } cv::Mat image_received = (cv_ptr->image).clone(); image_size_ = cv_ptr->image.size(); /* * Creating structure to publish nose color */ qbo_arduqbo::Nose nose; nose.header.stamp = ros::Time::now(); nose.color=1; string detection_type = ""; if(face_detected_bool_) //If face was detected in previous iteration - use CAM shift { if(detectFaceCamShift(image_received) != 0) { face_detected_bool_ = false; //Reset cam_shift_detected cam_shift_detected_count_ = 0; cam_shift_undetected_count_++; } else { detection_type = "CAMSHIFT"; //Update cam_shift counter cam_shift_detected_count_++; cam_shift_undetected_count_ = 0; } } if(!face_detected_bool_) //If face was not detected - use Haar Cascade { vector<cv::Rect> faces_roi; detectFacesHaar(image_received, faces_roi); if(faces_roi.size() > 0) //If Haar cascade classifier found a face { face_detected_bool_ = true; track_object_ = false; //Changed detected_face_roi_ = faces_roi[0]; detected_face_ = cv_ptr->image(detected_face_roi_); //Adjust face ratio because Haar Cascade always returns a square face_ratio_ = 1.3*detected_face_roi_.height/detected_face_roi_.width; detection_type = "HAAR"; } } if(!face_detected_bool_ && exist_alternative_) //If face was not detected - use Haar Cascade { vector<cv::Rect> faces_roi; detectFacesAltHaar(image_received, faces_roi); if(faces_roi.size() > 0) //If Haar cascade classifier found a face { face_detected_bool_ = true; track_object_ = false; //Changed detected_face_roi_ = faces_roi[0]; detected_face_ = cv_ptr->image(detected_face_roi_); //Adjust face ratio because Haar Cascade always returns a square face_ratio_ = 1.3*detected_face_roi_.height/detected_face_roi_.width; detection_type = "HAAR"; } } /* * Kalman filter for Face pos estimation */ kalman_filter_.predict(); if(face_detected_bool_) //IF face detected, use measured position to update kalman filter { if(undetected_count_>=undetected_threshold_) { cv::Mat new_state(4,1, CV_32FC1); new_state.at<float>(0,0) = float(detected_face_roi_.x+detected_face_roi_.width/2.); new_state.at<float>(1,0) = float(detected_face_roi_.y+detected_face_roi_.height/2.); new_state.at<float>(2,0) = 0.; new_state.at<float>(3,0) = 0.; new_state.copyTo(kalman_filter_.statePre); new_state.copyTo(kalman_filter_.statePost); } else { float u_vel = float(detected_face_roi_.x+detected_face_roi_.width/2.)-kalman_filter_.statePost.at<float>(0,0); float v_vel = float(detected_face_roi_.y+detected_face_roi_.height/2.)-kalman_filter_.statePost.at<float>(1,0); cv::Mat measures(4,1, CV_32FC1); measures.at<float>(0,0) = float(detected_face_roi_.x+detected_face_roi_.width/2.); measures.at<float>(1,0) = float(detected_face_roi_.y+detected_face_roi_.height/2.); measures.at<float>(2,0) = u_vel; measures.at<float>(3,0) = v_vel; kalman_filter_.correct(measures); } } else //If face haven't been found, use only Kalman prediction { kalman_filter_.statePre.copyTo(kalman_filter_.statePost); kalman_filter_.errorCovPre.copyTo(kalman_filter_.errorCovPost); } /* * Compute head distance */ float head_distance; if(!face_detected_bool_) { if(head_distances_.size()!=0) head_distance=head_distances_[0];//100; else head_distance = 5; } else { head_distance=calcDistanceToHead(detected_face_, kalman_filter_); // ROS_ERROR("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxhead_distance:%f", head_distance); } if(head_distances_.size()==0) { for(int i=0;i<10;i++) head_distances_.push_back(head_distance); } else { head_distances_.pop_back(); head_distances_.insert(head_distances_.begin(),head_distance); } head_distance=0.0; //Reuse variable to compute mean head distance //Use mean distance of last measured head distances for(unsigned int i=0;i<head_distances_.size();i++) { //ROS_ERROR("xxxxxxxxxx0xxxxxhead_distance:%f, s_:%f\n", head_distance, head_distances_[i]); head_distance+=head_distances_[i]; } head_distance=head_distance/head_distances_.size(); //ROS_ERROR("xxxxxxxxxx2xxxxxhead_distance:%f", head_distance); //Update undetected count if(!face_detected_bool_) { undetected_count_++; } else { undetected_count_ = 0; } //Create Face Pos and Size message qbo_face_msgs::FacePosAndDist message; message.image_width=cv_ptr->image.cols; message.image_height=cv_ptr->image.rows; message.type_of_tracking = detection_type; if(undetected_count_<undetected_threshold_) //If head have been recently detected, use Kalman filter prediction { message.face_detected = true; message.u = kalman_filter_.statePost.at<float>(0,0) - cv_ptr->image.cols/2.; message.v = kalman_filter_.statePost.at<float>(1,0) - cv_ptr->image.rows/2.; message.distance_to_head = float(head_distance); } else //If head has not been recently detected, face detection have failed { message.u = default_pos_.x; message.v = default_pos_.y; message.face_detected = false; } if(face_detected_bool_) { //Publish head to topic cv_ptr->image = detected_face_; face_pub_.publish(cv_ptr->toImageMsg()); if(send_to_face_recognizer_) sendToRecognizer(); //Change nose color nose.color=4; //If face detected - Blue if(head_distance<distance_threshold_) { nose.color=2; //If face is near - Green } } //Publish nose color nose_color_pub_.publish(nose); /* * Draws for the Viewer * Velocity vector, face rectangle and face center */ int pred_x = int(message.u) + int(message.image_width)/2; int pred_y = int(message.v) + int(message.image_height)/2; cv::Point pred_kal = cv::Point(pred_x, pred_y); cv::Point tip_u_vel = cv::Point(pred_kal.x + (kalman_filter_.statePost.at<float>(2,0))/1., pred_kal.y); cv::Point tip_v_vel = cv::Point(pred_kal.x, pred_kal.y + (kalman_filter_.statePost.at<float>(3,0))/1.); //Draw face center and rectangle cv::circle(image_received, pred_kal,3,cv::Scalar(0,0,255), 3); cv::rectangle(image_received, detected_face_roi_, cv::Scalar(255,0,0), 2); if(tip_u_vel.x >= 0 && tip_u_vel.y >= 0 && tip_u_vel.x < image_received.cols && tip_u_vel.y < image_received.rows) { cv::line(image_received, pred_kal, tip_u_vel, cv::Scalar(0,255,0), 2); } if(tip_v_vel.x >= 0 && tip_v_vel.y >= 0 && tip_v_vel.x < image_received.cols && tip_v_vel.y < image_received.rows) { cv::line(image_received, pred_kal, tip_v_vel, cv::Scalar(255,0,0), 2); } //Draw name of person in image if(print_recognized_face_) cv::putText(image_received, name_detected_, cv::Point(40,40), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0,0,255), 3); /* * Publish Image viewer */ cv_bridge::CvImagePtr cv_ptr2; try { cv_ptr2 = cv_bridge::toCvCopy(image_ptr, sensor_msgs::image_encodings::RGB8); } catch (cv_bridge::Exception& e) { ROS_ERROR("cv_bridge exception: %s", e.what()); return; } cv_ptr2->image = image_received; viewer_image_pub_.publish(cv_ptr2->toImageMsg()); /* * Publish face position and size */ face_position_and_size_pub_.publish(message); imshow("1111",image_received); cv::waitKey(10); /* * Update Haar cascade use frequency for Dynamic Haar Cascade */ if(dynamic_check_haar_) { if(undetected_count_ > 10) { check_Haar_ = 2; cam_shift_detected_count_ = 0; cam_shift_undetected_count_ = 0; } else if(cam_shift_undetected_count_>3) { check_Haar_/=2; if(check_Haar_<2) check_Haar_ = 2; } if(cam_shift_detected_count_>10) { check_Haar_+=5; if(check_Haar_>100) check_Haar_ = 100; } track_object_= check_Haar_ ; } /* * ROS_INFO */ if(face_detected_bool_) { if(dynamic_check_haar_) ROS_INFO("FACE DETECTED -> Head Pos :(%d, %d), Head Distance: %lg, Dynamic check Haar: %u, Det type: %s, Alt: %s", (int)message.u, (int)message.v, head_distance, check_Haar_, detection_type.c_str(), (exist_alternative_)?"true":"false"); else ROS_INFO("FACE DETECTED -> Head Pos :(%d, %d), Head Distance: %lg, Check Haar: %u, Detection type: %s, Alt: %s", (int)message.u, (int)message.v, head_distance, check_Haar_, detection_type.c_str(), (exist_alternative_)?"true":"false"); } else { if(dynamic_check_haar_) ROS_INFO("NO FACE DETECTED. Using Haar Cascade Classifier to find one. Dynamic Check Haar: %u, Alt: %s", check_Haar_, (exist_alternative_)?"true":"false"); else ROS_INFO("NO FACE DETECTED. Using Haar Cascade Classifier to find one. Check Haar: %u, Alt: %s", check_Haar_, (exist_alternative_)?"true":"false"); } /* * Refresh face_detected bool to use Haar Cascade once in a while */ if(loop_counter_%check_Haar_==0) face_detected_bool_ = false; if(loop_counter_%check_track_obj_==0) track_object_ = false; loop_counter_++; }
void FaceDetector::imageCallback(const sensor_msgs::Image::ConstPtr& image_ptr) { #ifdef marco_debug ROS_INFO("imagecallback called"); #endif cv_bridge::CvImagePtr cv_ptr; try { cv_ptr = cv_bridge::toCvCopy(image_ptr, sensor_msgs::image_encodings::RGB8); } catch (cv_bridge::Exception& e) { ROS_ERROR("cv_bridge exception: %s", e.what()); return; } cv::Mat image_received = (cv_ptr->image).clone(); image_size_ = cv_ptr->image.size(); vector<cv::Rect> faces_roi; cv::Mat grayscale; cv::cvtColor(image_received,grayscale,CV_RGB2GRAY); // marco: converting to grayscale before haar detection detectFacesHaar(image_received, faces_roi,face_classifier_); if(faces_roi.size() > 0) //If Haar cascade classifier found a face { #ifdef marco_debug ROS_INFO("haar cascade found a face"); #endif vector<cv::Rect> eyes_roi; detectFacesHaar(grayscale(faces_roi[0]), eyes_roi,alternative_face_classifier_); if (eyes_roi.size()>0) { //ROS_INFO("HAAR CLASSIFIER FOUND A FACE"); face_detected_bool_ = true; track_object_ = false; //Changed detected_face_roi_ = faces_roi[0]; detected_face_ = cv_ptr->image(detected_face_roi_); } else face_detected_bool_=false; } else { face_detected_bool_=false; } // >>>>>>>> KALMAN double precTick = ticks_; ticks_ = (double) cv::getTickCount(); double dT = (ticks_ - precTick) / cv::getTickFrequency(); //seconds if (found_) { // >>>> Matrix A kalman_filter_.transitionMatrix.at<float>(2) = dT; kalman_filter_.transitionMatrix.at<float>(9) = dT; // <<<< Matrix A state_ = kalman_filter_.predict(); //cout << "State post:" << endl << state_ << endl; cv::Rect predRect; predRect.width = state_.at<float>(4); predRect.height = state_.at<float>(5); predRect.x = state_.at<float>(0) - predRect.width / 2; predRect.y = state_.at<float>(1) - predRect.height / 2; cv::Point center; center.x = state_.at<float>(0); center.y = state_.at<float>(1); cv::circle(image_received, center, 2, CV_RGB(255,0,0), -1); cv::rectangle(image_received, predRect, CV_RGB(255,0,0), 2); } if (!face_detected_bool_) { notFoundCount_++; //cout << "notFoundCount:" << notFoundCount_ << endl; if( notFoundCount_ >= 10 ) { found_ = false; } else { kalman_filter_.statePost = state_; } } else { notFoundCount_ = 0; meas_.at<float>(0) = (float)(detected_face_roi_.x + detected_face_roi_.width/2); meas_.at<float>(1) = (float)(detected_face_roi_.y + detected_face_roi_.height/2); meas_.at<float>(2) = (float)detected_face_roi_.width; meas_.at<float>(3) = (float)detected_face_roi_.height; if (!found_) // First detection! { // >>>> Initialization kalman_filter_.errorCovPre.at<float>(0) = 1; // px kalman_filter_.errorCovPre.at<float>(7) = 1; // px kalman_filter_.errorCovPre.at<float>(14) = 1; kalman_filter_.errorCovPre.at<float>(21) = 1; kalman_filter_.errorCovPre.at<float>(28) = 1; // px kalman_filter_.errorCovPre.at<float>(35) = 1; // px state_.at<float>(0) = meas_.at<float>(0); state_.at<float>(1) = meas_.at<float>(1); state_.at<float>(2) = 0; state_.at<float>(3) = 0; state_.at<float>(4) = meas_.at<float>(2); state_.at<float>(5) = meas_.at<float>(3); // <<<< Initialization found_ = true; } else kalman_filter_.correct(meas_); // Kalman Correction // cout << "Measure matrix:" << endl << meas_ << endl; } float head_distance; // ROS_INFO("Computing head distance"); if(!face_detected_bool_) { if(head_distances_.size()!=0) { head_distance=head_distances_[0];//100; } else head_distance = 3.0; } else { head_distance=calcDistanceToHead(detected_face_, kalman_filter_); #ifdef marco_debug ROS_ERROR ("COMPUTED HEAD DISTANCE: %f",head_distance); #endif } if(head_distances_.size()==0) { for(int i=0;i<10;i++) head_distances_.push_back(head_distance); } else { head_distances_.pop_back(); head_distances_.insert(head_distances_.begin(),head_distance); } #ifdef marco_debug ROS_INFO("head distance computed"); #endif head_distance=0; //Reuse variable to compute mean head distance //Use mean distance of last measured head distances for(unsigned int i=0;i<head_distances_.size();i++) head_distance+=head_distances_[i]; head_distance=head_distance/head_distances_.size(); //ROS_ERROR ("HEAD DISTANCE AFTER MEAN COMPUTING: %f",head_distance); #ifdef marco_debug ROS_INFO("updated undetected count"); #endif if(head_distance != head_distance) //not a number head_distance=3.0; //Create HeadDistance message std_msgs::Float32 head_distance_msgs; head_distance_msgs.data=head_distance; face_distance_pub_.publish(head_distance_msgs); //Create Face Pos and Size message sensor_msgs::JointState message; int servos_count=2; message.name.resize(servos_count); message.position.resize(servos_count); message.velocity.resize(servos_count); message.name[0]="head_pan_joint"; //izquierda-derecha message.name[1]="head_tilt_joint"; //arriba-abajo message.header.stamp = ros::Time::now(); #ifdef marco_debug ROS_INFO("message created"); #endif if( notFoundCount_ >= 10 ) { float rand_tilt = search_min_tilt_+((search_max_tilt_-search_min_tilt_) * double(rand()%20)/20.); float rand_pan = search_min_pan_+((search_max_pan_-search_min_pan_) * double(rand()%20)/20.); message.position[0]=rand_pan; message.position[1]=rand_tilt; message.velocity[0]=search_pan_vel_; message.velocity[1]=search_tilt_vel_; //ROS_ERROR("RANDOM MOVE: tilt: %f pan: %f",rand_tilt,rand_pan); random_=true; } else { if(face_detected_bool_) { float pan_pos=meas_.at<float>(0); // it was meas_ float tilt_pos=meas_.at<float>(1); pan_pos=scalePan(pan_pos); tilt_pos=scaleTilt(tilt_pos); message.position[0]=yaw_from_joint_ + pan_pos; message.position[1]=pitch_from_joint_ + tilt_pos; message.velocity[0]=abs(pan_pos*1.3) ; message.velocity[1]=abs(tilt_pos*1.5); } else { float pan_pos=state_.at<float>(0); // it was meas_ float tilt_pos=state_.at<float>(1); pan_pos=scalePan(pan_pos); tilt_pos=scaleTilt(tilt_pos); message.position[0]=yaw_from_joint_ + pan_pos; message.position[1]=pitch_from_joint_ + tilt_pos; message.velocity[0]=abs(pan_pos*1.3); message.velocity[1]=abs(tilt_pos*1.5); } //ROS_ERROR("KALMAN MOVE: pan: %f --- tilt: %f",message.position[0], message.position[1]); random_=false; } cv::imshow("Kalman predictor", image_received); cvWaitKey(1); /* * Publish joint state */ #ifdef marco_debug ROS_INFO("publishing: pan_pos: %lg --- tilt_pos: %lg",message.position[0],message.position[1]); #endif joint_pub_.publish(message); //ROS_ERROR("JOint state yaw_from_joint_: %f",yaw_from_joint_); sendBaseVelocity(head_distance); }