基于ROS的OPCV的人脸识别
人脸检测采用基于 Haar 特征的 AdaBoost 算法,由于 Haar特征数量庞大,用常规方式计算其特征值的计算量大、占用资源多、耗时较长,因此,可以借助积分图的方式来计算 Haar 特征值,可以大大提高计算效率。借助积分图方式进行人脸 Haar 特征识别的总体思路是将图像中的 Haar 特征( 矩形特征) 提取出来并计算出积分图像,得到的积分图像与人脸的积分图像对比,符合人脸特征的区域则被标记起来。
2 OpenCV 人脸检测原理
OpenCV 目标检 测 的 方 法 是 利 用 样 本 的 Haar特征进行分类器训练,由弱分类器到强分类器,得到级 联 boosted 分 类 器 ( Cascade Classification) 。Haar-like 算法原理介绍如下。
2. 1 Haar-like 特征
由三类 Haar 特征( 边缘特征、线性特征、中心特征) 组合成特征模板。特征模板内有白色和黑色两种矩形,并定义该模板的特征值为白色矩形像素之和减去黑色矩形像素之和。Haar 特征值反映了图像的灰度变化情况。人的一些脸部特征能简单的由矩形描述,例如: 眼睛的颜色要比脸颊颜色要深,鼻梁两侧的颜色比鼻梁的颜色要深,嘴巴的颜色要比周围颜色要深等。但是单个 Haar-like 特征的分类能力很弱,矩形特征只能描述一些简单的特征图形,还需要利用特定的级联算法将简单Haar-like 特征应用于目标的检测。Haar-like 特征下图 。
2.2 图像积分图
利用图像积分图可以快速得到图像中任意矩形的像素和,大大提高了计算矩形相应特征值的速度,是 Haar 分类器能够实时检测人脸的保证。积分图像是将图像左上侧的全部像素进行累加计算,并将图像中的每一个像素替换成所求得的和。在Haar - like 分类器的训练和检测过程中都需要计算当前子图像特征值,积分图算法求出图像中所有区域像素之和只需要遍历一次,解决了算法的耗时问题。构造的积分图如下图 。
图 2 中由角标 L1、L2、L3、L4四个像素所表示的矩形区域 D 像素和为:
SumD= L4+ L1-L2-L3;
2.3 AdaBoost 算法
AdaBoost 算法就是一种基于级联分类模型在Haar 特征上构建多个简单的分类器,允许设计者不断加入新的“弱分类器”。首先对每个样本的权值分布进行初始化,如果有 N 个样本,则将每个训练样本赋予相同的权重 1 /N,随后训练弱分类器。在具体训练过程中,如果某个样本已经被准确分类,那么在下一个训练集的构造过程中,其权重将会被降低; 相反,如果某个样本点没有被准确分类,其权重将会升高。与此同时,得到弱分类器相对应的话语权。在结束对各个弱分类器的训练过程后,分类误差率小的弱分类器拥有较大话语权,在最终的分类函数中起着较大决定作用,而分类误差率大的弱分类器拥有较小的话语权,在最终的分类函数中起着较小决定作用。其次,更新权值后的样本集将被用于训练下一个分类器,整个训练过程不断进行迭代。最后,若干个训练后的弱分类器组合成一个强分类器,而一个级联分类器将多个强分类器连接在一起并进行操作。
由于每一个强分类器对负样本的判别准确度非常高,所以检测到负例样本,便不再继续调用后面的强分类器,减少了大量的检测时间。因为一张图像中的待检测区域大部分都是负例样本,级联分类器在分类器的初期就不会进行很多负例样本的复杂检测,所以级联分类器的速度是非常快的; 只有正例样本才会被送到下一个强分类器进行再次检验,这样就保证了最后输出的正例样本的伪正( false positive) 的可能性非常低。级联分类模型见下图。
从上面所述内容我们可以总结Haar分类器训练的五大步骤:
1、准备人脸、非人脸样本集;
2、计算特征值和积分图;
3、筛选出T个优秀的特征值(即最优弱分类器);
4、把这个T个最优弱分类器传给AdaBoost进行训练。
5、级联,也就是强分类器的强强联手。
以20*20窗口为例,就有78,460的特征数量,筛选出T个优秀的特征值(即最优弱分类器),然后把这个T个最优弱分类器传给AdaBoost进行训练得到一个强分类器。
参考:https://blog.csdn.net/qq_27806947/article/details/84108793
摄像头文件安装参考:https://mp.csdn.net/console/editor/html/103929781
cd ~/catkin_ws/src
git clone https://github.com/bosch-ros-pkg/usb_cam.git
roslaunch usb_cam usb_cam-test.launch
打开摄像头
新建文件功能文件包
catkin_create_pkg cv_bridge_tutorial_cpp cv_bridge
image_transport
roscpp
sensor_msgs
std_msgs
cd ./cv_bridge_tutorial_cpp/src
新建文件sample_cv_bridge_node.cpp
#include <ros/ros.h>
#include <image_transport/image_transport.h>
#include <cv_bridge/cv_bridge.h>
#include <sensor_msgs/image_encodings.h>
#include <std_msgs/Int16.h>
#include <std_msgs/Float64MultiArray.h>
#include <std_msgs/UInt16MultiArray.h>
#include <opencv2/objdetect.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core.hpp>
using namespace std;
using namespace cv;
CascadeClassifier face_cascade;
static const std::string OPENCV_WINDOW = "Raw Image window";
class Face_Detector
{
ros::NodeHandle nh_;
image_transport::ImageTransport it_;
image_transport::Subscriber image_sub_;
ros::Publisher chatter_pub;
public:
Face_Detector()
: it_(nh_)
{
// Subscribe to input video feed and publish output video feed
image_sub_ = it_.subscribe("/usb_cam/image_raw", 1,
&Face_Detector::imageCb, this);
chatter_pub = nh_.advertise<std_msgs::UInt16MultiArray>("chatter", 100);
cv::namedWindow(OPENCV_WINDOW);
}
~Face_Detector()
{
cv::destroyWindow(OPENCV_WINDOW);
}
void imageCb(const sensor_msgs::ImageConstPtr& msg)
{
cv_bridge::CvImagePtr cv_ptr;
namespace enc = sensor_msgs::image_encodings;
try
{
cv_ptr = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::BGR8);
}
catch (cv_bridge::Exception& e)
{
ROS_ERROR("cv_bridge exception: %s", e.what());
return;
}
// Draw an example circle on the video stream
if (cv_ptr->image.rows > 400 && cv_ptr->image.cols > 600){
detect_faces(cv_ptr->image);
//chatter_pub.publish(position_x);
}
}
void detect_faces(cv::Mat img)
{
RNG rng( 0xFFFFFFFF );
std_msgs::Int16 position_x;
std_msgs::Int16 position_y;
std_msgs::UInt16MultiArray val;
int data_x;
int data_y;
int lineType = 8;
Mat frame_gray;
cvtColor( img, frame_gray, COLOR_BGR2GRAY );
equalizeHist( frame_gray, frame_gray );
//-- Detect faces
std::vector<Rect> faces;
face_cascade.detectMultiScale( frame_gray, faces );
if (faces.size()>=1){
size_t i = 0;
Point center( faces[i].x + faces[i].width/2, faces[i].y + faces[i].height/2 );
ellipse( img, center, Size( faces[i].width/2, faces[i].height/2 ), 0, 0, 360, Scalar( 255, 0, 255 ), 4 );
data_x = cvRound(faces[i].x + faces[i].width/2);
data_y = cvRound(faces[i].y + faces[i].height/2);
position_x.data=data_x;
position_x.data=data_y;
val.data.push_back(data_x);
val.data.push_back(data_y);
chatter_pub.publish(val);
}
imshow(OPENCV_WINDOW,img);
waitKey(3);
}
};
int main(int argc, char** argv)
{
//从命令行读取必要的信息,注意路径
CommandLineParser parser(argc, argv,
"{help h||}"
"{face_cascade|/home/jankin/dashgo_ws/src/cv_bridge_tutorial_pkg/src/haarcascade_frontalface_alt.xml|Path to face cascade.}");
parser.about( "\nThis program demonstrates using the cv::CascadeClassifier class to detect objects (Face + eyes) in a video stream.\n"
"You can use Haar or LBP features.\n\n" );
parser.printMessage();
String face_cascade_name = parser.get<String>("face_cascade");
//-- 1. Load the cascades
if( !face_cascade.load( face_cascade_name ) )
{
cout << "--(!)Error loading face cascade\n";
return -1;
};
ros::init(argc, argv, "Face_Detector");
Face_Detector ic;
ros::spin();
return 0;
}
CMakeLists.txt文件
include_directories(
# include
${catkin_INCLUDE_DIRS}
${OpenCV_INCLUDE_DIRS}
)
add_executable(sample_cv_bridge_node src/sample_cv_bridge_node.cpp)
target_link_libraries(sample_cv_bridge_node
${catkin_LIBRARIES}
${OpenCV_LIBRARIES}
)
include_directories(${catkin_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS})
上一篇: ROS学习小结——ROS的安装
推荐阅读
-
微信小程序实现人脸识别登陆的示例代码
-
python人脸识别项目之基础学习(三):矩阵的基本运算 + 张量的阶和形态
-
基于jupyter notebook的python编程(Win10通过OpenCv-3.4.1进行人脸口罩数据集的模型训练并进行戴口罩识别检测)
-
基于语音识别的IVR系统的设计与实现
-
基于FDC2214的手势识别
-
基于LD332O语音识别专用芯片实现的语音控制
-
人脸识别背后“肮脏的小秘密”:肆无忌惮搜集照片
-
Python基于whois模块简单识别网站域名及所有者的方法
-
手写数字识别 ----在已经训练好的数据上根据28*28的图片获取识别概率(基于Tensorflow,Python)
-
师出同门:人脸识别与下围棋相比谁的难度更大?