要想搞明白拍照的過程,要搞清楚下面幾件事:
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;
}