ROS_take_photos.cpp

要想搞明白拍照的过程,要搞清楚下面几件事:

1

整个程序的功能是: 对摄像头前的人进行拍照保存。。

从ros的img话题里面拿出图像,然后把图像的灰度啦,直方图啦,处理一下。。

用级联分类器(说白了就是一个xml文件,这个xml里面全是数字,代表了一个人鼻子眼睛耳朵等等的特征数据,opencv 通过和这些数据进行比较,判断摄像头给过来的数据像不像一个人,决定拍不拍这一张图片) 进行人脸拍照。

然后把这个人脸画框(用红色框把脸框起来), 通过话题把 这个照片发布出去。
再保存原始的(没框)人脸数据到本地。。

2.

拍的照片第一次 肯定是ros格式的,毕竟你的ros装在树莓派上,使用的usb摄像头肯定信息先到树莓派里面。。 因此先转化成 opencv 的格式,让opencv的级联分类器使用。当opencv 框出人脸之后, 再把这个图片转化成ros格式的, 随后通过话题发布出去

3 代码解释

#include<ros/ros.h>
#include<image_transport/image_transport.h>
//cv_bridge 将opencv的图像转化成ros格式
#include<cv_bridge/cv_bridge.h>
//image encodings(包含了很多有用的常量和函数),
#include<sensor_msgs/image_encodings.h>
//包含OpenCV的图像处理和GUI模块头文件
#include<opencv2/highgui.hpp>
#include<opencv2/objdetect.hpp>

using namespace cv;
using namespace std;

string face_default_model;
string face_save_path;
CascadeClassifier face_cascade;
image_transport::Publisher pub;
Mat src;
int pic_num = 1;
int interval = 0;

void imageCallback(const sensor_msgs::ImageConstPtr &msg)
{
  // 先把ros格式的图像转化成opencv的格式再说
  src = cv_bridge::toCvShare(msg,"bgr8")->image;
  //Mat就是一个数据类型,矩阵类型的
  Mat gray;
  Mat result;
  result = src.clone();
  vector<Rect> faces;

  ///第二步,图像灰度化
  cvtColor(src,gray,CV_BGR2GRAY);         //转化为灰度图
  equalizeHist(gray,gray);                // 直方图均衡化 ,清晰度,对比度变大
  //第三步,调用detectMultiScale()实现多尺度检测
    //第二个参数faces 就是检测到的图像输出到faces
    //第三个参数1.1就是搜索前后的窗口比例,每次检测窗口 扩大 1.1倍
    //第四个参数构成检测目标的相邻矩形的最小个数 ,太小就会误检,太大就会目标检验不出来, 相当于阈值
    //后面两个就是能够检测的最小和最大尺寸
  face_cascade.detectMultiScale(gray,faces,1.1,2,0|CASCADE_SCALE_IMAGE,Size(30,30));
  


  //在 faces上面画出红色的框,另存为result 
  for(size_t i=0;i<faces.size();++i)
  {
    rectangle(result,faces[i],Scalar(0,0,255),1);
  }



  if(faces.size()==1)
  {
    Mat faceROI = gray(faces[0]);
    Mat myFace;
    resize(faceROI,myFace,Size(92,112));
    string filename = face_save_path+format("%d.jpg",pic_num);
    //可以看到interval 直到50次才会自己加一次
    //这意味着 这个 callback 回调函数被回调了50次才存下来一张照片
    // 这意味着image_raw 订阅50次才会保存一次
    if((pic_num <= 30) && (interval == 50))  
    {
     imwrite(filename,myFace);
     pic_num++;
     interval = 0;
    }
    interval += 1;
  }  
  //把这个opencv的图像再转化成ros图像,发布出去
  sensor_msgs::ImagePtr pubmsg = cv_bridge::CvImage(std_msgs::Header(),"bgr8",result).toImageMsg();
  if(pub!=NULL)
  {
     pub.publish(pubmsg);
  }
}


int main(int argc, char *argv[])
{ 
  /*
分类器: 判别某个事物是否属于某种分类的器件,两种结果:是、否
级联分类器: 可以理解为将N个单类的分类器串联起来。如果一个事物能属于这一系列串联起来的的所有分类器,则最终结果就是 是,若有一项不符,则判定为否
比如人脸,它有很多属性,我们将每个属性做一成个分类器,如果一个模型符合了我们定义的人脸的所有属性,
则我们人为这个模型就是一个人脸。那么这些属性是指什么呢? 比如人脸需要有两条眉毛,两只眼睛,一个鼻子,一张嘴,一个大概U形状的下巴或者是轮廓等等
  */
  ros::init(argc,argv,"takephotos");
  ros::NodeHandle n;
  ros::param::get("~model",face_default_model); //加载微软的级联分类器
  ros::param::get("~savepath",face_save_path);

  if(!face_cascade.load(face_default_model))  
  {
    ROS_INFO("cant load xml");
    return 0;
  }

  image_transport::ImageTransport it(n);
  
  pub = it.advertise("/image_detect",1);
  
  image_transport::Subscriber sub = it.subscribe("/camera/rgb/image_raw",1,imageCallback);
  ros::spin();
  return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章