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;
}

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