github地址https://github.com/ngunauj/facedetection
熟悉ros環境。ubuntu16.04 + ros kinetic版本。使用筆記本自帶攝像頭,完成人臉的實時檢測。代碼可能會更改,具體以github上的代碼為主。
camera_subscribe.cpp 訂閱camera發出的圖片信息,然后對Mat 類型的圖片進行每一幀圖片的人臉檢測,人臉檢測代碼參考opencv的開源代碼。
/* *********************************************** Author :guanjunace@foxmail.com Created Time :2017年07月10日 星期一 10時43分26秒 File Name :camera_subscribe.cpp ************************************************ */ #include <iostream> #include <ros/ros.h> #include <image_transport/image_transport.h> #include <cv_bridge/cv_bridge.h> #include <sensor_msgs/image_encodings.h> #include <opencv2/highgui/highgui.hpp> #include "opencv2/objdetect.hpp" #include "opencv2/imgproc.hpp" using namespace std; using namespace cv; const string cascadeName = "/usr/share/opencv/haarcascades/haarcascade_frontalface_alt.xml"; const string nestedCascadeName = "/usr/share/opencv/haarcascades/haarcascade_eye.xml"; const double scale = 1.3; void detectFace(Mat& img, CascadeClassifier& cascade, CascadeClassifier& nestedCascade) { double t = 0; vector<Rect> faces; const static Scalar colors[] = { Scalar(255,0,0), Scalar(255,128,0), Scalar(255,255,0), Scalar(0,255,0), Scalar(0,128,255), Scalar(0,255,255), Scalar(0,0,255), Scalar(255,0,255) }; Mat gray, smallImg; cvtColor( img, gray, COLOR_BGR2GRAY ); double fx = 1 / scale; resize(gray, smallImg, Size(), fx, fx, INTER_LINEAR); equalizeHist(smallImg, smallImg); t = (double)getTickCount(); cascade.detectMultiScale(smallImg, faces, 1.1, 2, 0 //|CASCADE_FIND_BIGGEST_OBJECT //|CASCADE_DO_ROUGH_SEARCH |CASCADE_SCALE_IMAGE, Size(30, 30)); t = (double)getTickCount() - t; printf("detection time = %g ms\n", t*1000/getTickFrequency()); for (size_t i = 0; i < faces.size(); ++i) { Rect r = faces[i]; Mat smallImgROI; vector<Rect> nestedObjects; Point center; Scalar color = colors[i%8]; int radius; double aspect_ratio = (double)r.width/r.height; if(0.75 < aspect_ratio && aspect_ratio < 1.3) { center.x = cvRound((r.x + r.width*0.5)*scale); center.y = cvRound((r.y + r.height*0.5)*scale); radius = cvRound((r.width + r.height)*0.25*scale); circle(img, center, radius, color, 3, 8, 0); } else { rectangle(img, cvPoint(cvRound(r.x*scale), cvRound(r.y*scale)), cvPoint(cvRound((r.x + r.width-1)*scale), cvRound((r.y + r.height-1)*scale)), color, 3, 8, 0); } smallImgROI = smallImg(r); nestedCascade.detectMultiScale(smallImgROI, nestedObjects, 1.1, 2, 0 |CASCADE_SCALE_IMAGE, Size(30, 30) ); for (size_t j = 0; j < nestedObjects.size(); ++j) { Rect nr = nestedObjects[j]; center.x = cvRound((r.x + nr.x + nr.width*0.5)*scale); center.y = cvRound((r.y + nr.y + nr.height*0.5)*scale); radius = cvRound((nr.width + nr.height)*0.25*scale); circle(img, center, radius, color, 3, 8, 0); } } imshow("result", img); waitKey(1); } void img_Callback(const sensor_msgs::ImageConstPtr& msg) { Mat image ; CascadeClassifier cascade, nestedCascade; try { image = cv_bridge::toCvShare(msg, "bgr8")->image; //CascadeClassifier cascade, nestedCascade; nestedCascade.load(nestedCascadeName); cascade.load(cascadeName); detectFace(image, cascade, nestedCascade); //Convert an immutable sensor_msgs::Image message to an OpenCV-compatible CvImage, //sharing the image data if possible. //imshow("img", cv_bridge::toCvShare(msg, "bgr8")->image);//IplImage 類型的 mat //waitKey(1); } catch (cv_bridge::Exception &e) { ROS_ERROR("Could not convert from '%s' to 'bgr8'.", msg->encoding.c_str()); } if (!image.empty()) detectFace(image, cascade, nestedCascade); else printf("no image!"); } int main(int argc, char *argv[]) { ros::init(argc, argv, "img_subscribe"); ros::NodeHandle nh; namedWindow("webcamimg"); startWindowThread();/* CascadeClassifier cascade, nestedCascade; if (!nestedCascade.load(nestedCascadeName)) cerr << "WARNING: Could not load classifier cascade for nested objects" << endl; if (!cascade.load(cascadeName)) { cerr << "ERROR: Could not load classifier cascade" << endl; return -1; } */ image_transport::ImageTransport it(nh); image_transport::Subscriber img_sub = it.subscribe("/webcam/img", 1, &(img_Callback)); destroyWindow("webcamimg"); ros::spin(); return 0; }
camera_driver代碼
/* *********************************************** Author :guanjun guanjunace@foxmail.com Created Time :2017/7/8 10:40:13 File Name :camera_publisher.cpp ************************************************ */ #include <bits/stdc++.h> #include <ros/ros.h> #include <image_transport/image_transport.h> #include <cv_bridge/cv_bridge.h> #include <sensor_msgs/image_encodings.h> #include <opencv2/highgui/highgui.hpp> using namespace std; using namespace cv; class WebCam { public: WebCam (ros::NodeHandle& nh, int video_source = 0) : it(nh), cap(video_source) { if (!cap.isOpened()) { ROS_ERROR("Cannot open the camera!\n"); } /*設置主題名和緩沖區*/ imgPub = it.advertise("webcam/img", 1); /*初始化CvImage智能指針,CvImage為Mat與ROS圖像之間轉換的載體*/ frame = boost::make_shared<cv_bridge::CvImage>(); /*設置ROS圖片為BGR且每個像素點用1個字節來表示類似於CV_8U*/ frame->encoding = sensor_msgs::image_encodings::BGR8; } /*圖像發布函數*/ int publishImage(){ /*將攝像頭獲取到的圖像存放在frame中的image*/ cap >> frame->image; /*判斷是否獲取到圖像,若獲取到圖像,將其轉化為ROS圖片*/ if (!(frame->image.empty())){ frame->header.stamp = ros::Time::now(); imgPub.publish(frame->toImageMsg()); } return 0; } private: /*設置圖片節點*/ image_transport::ImageTransport it; /*設置圖片的發布者*/ image_transport::Publisher imgPub; /*設置存放攝像頭圖像的變量*/ VideoCapture cap; /*設置cvImage的智能指針*/ cv_bridge::CvImagePtr frame; }; int main(int argc, char *argv[]) { /*初始化節點,並設定節點名*/ ros::init(argc, argv, "img_publiser"); /*設置節點句柄*/ ros::NodeHandle nh; /*判斷輸入參數是否完成*/ if (argv[1] == NULL){ ROS_WARN("Please choose the camera you want to use !"); return 1; } /*獲取打開攝像機的設備號*/ int video_source = 0; int default_p = 0; istringstream default_param(argv[1]); default_param >> default_p; nh.param<int>("video_source", video_source, default_p); /*定義攝像機對象*/ WebCam webcam(nh, video_source); /*設置主題的發布頻率為10Hz*/ ros::Rate loop_rate(10); /*圖片節點進行主題的發布*/ while (ros::ok()) { webcam.publishImage(); ros::spinOnce(); /*按照設定的頻率來將程序掛起*/ loop_rate.sleep(); } return 0; }
在人臉檢測的過程中,隨着人臉的移動,有個別幀並不能檢測出人臉,考慮以后哦要加個跟蹤