精华内容
下载资源
问答
  • 关键点
    千次阅读
    2019-08-12 17:49:54

    一、NARF关键点提取 

    1、背景

    关键点也称为兴趣点,是通过定义检测标准来获取的具有稳定性、区别性的点集。从技术上来说,关键点的数量要比原始点云的数目少很多,与局部特征描述子结合在一起,组成关键点描述子常用来形成原始数据的紧凑表示,而且不失代表性与描述性,从而加快识别、追踪有等对数据的处理速度。故而,关键点提取就成了2D和3D信息处理中不可或缺的关键技术。

    本例程将点云数据生成深度图数据,然后对深度图数据进行关键点提取,最后将关键点在点云视图中进行可视化

    2、关键点提取代码

    //提取NARF关键点
    pcl::RangeImageBorderExtractor range_image_border_extractor;//创建深度图像边界提取对象
    pcl::NarfKeypoint narf_keypoint_detector(&range_image_border_extractor);//创建Narf关键点提取器,输入为深度图像边缘提取器
    narf_keypoint_detector.setRangeImage(&range_image);//关键点提取设置输入深度图像
    narf_keypoint_detector.getParameters().support_size=support_size;//关键点提取的参数:搜索空间球体的半径
    narf_keypoint_detector.getParameters ().add_points_on_straight_edges = true;//对竖直边是否感兴趣
    //narf_keypoint_detector.getParameters ().distance_for_additional_points = 10;//与support_size一起作用
    
    pcl::PointCloud<int> keypoint_indices;//关键点索引
    narf_keypoint_detector.compute(keypoint_indices);//关键点计算,结果放置到keypoint_indices
    std::cerr<<"Found "<<keypoint_indices.points.size()<<" key points.\n";//输出得到的NARF关键点数目

    3、全部代码

    /* \author Bastian Steder */
    
    #include <iostream>
    #include <boost/thread/thread.hpp>
    #include <pcl/range_image/range_image.h>
    #include <pcl/io/pcd_io.h>
    #include <pcl/visualization/range_image_visualizer.h>
    #include <pcl/visualization/pcl_visualizer.h>
    #include <pcl/features/range_image_border_extractor.h>
    #include <pcl/keypoints/narf_keypoint.h>//特征点提取头文件
    #include <pcl/console/parse.h>//命令行解析
    
    #include <pcl/visualization/common/float_image_utils.h>//保存深度图像相关头文件
    #include <pcl/io/png_io.h>//保存深度图像相关头文件
    
    typedef pcl::PointXYZ PointType;
    //参数
    float angular_resolution=0.5f;
    float support_size=0.2f;//默认关键点提取的参数(关键点提取器所支持的范围:搜索空间球体的半径,指定了计算感兴趣值的测度时所使用的邻域范围)
    pcl::RangeImage::CoordinateFrame coordinate_frame=pcl::RangeImage::CAMERA_FRAME;
    bool setUnseenToMaxRange=false;
    //打印帮助
    void
    printUsage(const char* progName)
    {
    std::cout<<"\n\nUsage: "<<progName<<" [options] <scene.pcd>\n\n"
    <<"Options:\n"
    <<"-------------------------------------------\n"
    <<"-r <float>   angular resolution in degrees (default "<<angular_resolution<<")\n"
    <<"-c <int>     coordinate frame (default "<<(int)coordinate_frame<<")\n"
    <<"-m           Treat all unseen points as maximum range readings\n"
    <<"-s <float>   support size for the interest points (diameter of the used sphere - "
    <<"default "<<support_size<<")\n"
    <<"-h           this help\n"
    <<"\n\n";
    }
    
    void
    setViewerPose(pcl::visualization::PCLVisualizer& viewer, const Eigen::Affine3f& viewer_pose)
    {
    Eigen::Vector3f pos_vector = viewer_pose*Eigen::Vector3f(0,0,0);
    Eigen::Vector3f look_at_vector=viewer_pose.rotation()*Eigen::Vector3f(0,0,1)+pos_vector;
    Eigen::Vector3f up_vector=viewer_pose.rotation()*Eigen::Vector3f(0,-1,0);
    viewer.setCameraPosition(pos_vector[0],pos_vector[1],pos_vector[2],
                             look_at_vector[0],look_at_vector[1],look_at_vector[2],
                             up_vector[0],up_vector[1],up_vector[2]);
    /*
    viewer.camera_.pos[0]=pos_vector[0];
    viewer.camera_.pos[1]=pos_vector[1];
    viewer.camera_.pos[2]=pos_vector[2];
    viewer.camera_.focal[0]=look_at_vector[0];
    viewer.camera_.focal[1]=look_at_vector[1];
    viewer.camera_.focal[2]=look_at_vector[2];
    viewer.camera_.view[0]=up_vector[0];
    viewer.camera_.view[1]=up_vector[1];
    viewer.camera_.view[2]=up_vector[2];
    viewer.updateCamera();
     */
    }
    
    // -----Main-----
    int
    main(int argc,char** argv)
    {
    //解析命令行参数
    if(pcl::console::find_argument(argc,argv,"-h")>=0)
    {
    printUsage(argv[0]);
    return 0;
    }
    if(pcl::console::find_argument(argc,argv,"-m")>=0)
    {
    setUnseenToMaxRange=true;
    cout<<"Setting unseen values in range image to maximum range readings.\n";
    }
    int tmp_coordinate_frame;
    if(pcl::console::parse(argc,argv,"-c",tmp_coordinate_frame)>=0)
    {
    coordinate_frame=pcl::RangeImage::CoordinateFrame(tmp_coordinate_frame);
    cout<<"Using coordinate frame "<<(int)coordinate_frame<<".\n";
    }
    if(pcl::console::parse(argc,argv,"-s",support_size)>=0)
    cout<<"Setting support size to "<<support_size<<".\n";
    if(pcl::console::parse(argc,argv,"-r",angular_resolution)>=0)
    cout<<"Setting angular resolution to "<<angular_resolution<<"deg.\n";
    angular_resolution=pcl::deg2rad(angular_resolution);
    //读取给定的pcd文件或者自行创建随机点云
    pcl::PointCloud<PointType>::Ptr point_cloud_ptr(new pcl::PointCloud<PointType>);
    pcl::PointCloud<PointType>&point_cloud=*point_cloud_ptr;
    pcl::PointCloud<pcl::PointWithViewpoint> far_ranges;
    Eigen::Affine3f scene_sensor_pose(Eigen::Affine3f::Identity());
    std::vector<int> pcd_filename_indices = pcl::console::parse_file_extension_argument(argc,argv,"pcd");
    if(!pcd_filename_indices.empty())
    {
    std::string filename=argv[pcd_filename_indices[0]];
    if(pcl::io::loadPCDFile(filename,point_cloud)==-1)//打开失败
    {
    cerr<<"Was not able to open file \""<<filename<<"\".\n";
    printUsage(argv[0]);
    return 0;
    }
    //打开pcd文件成功
    scene_sensor_pose=Eigen::Affine3f(Eigen::Translation3f(point_cloud.sensor_origin_[0],
    point_cloud.sensor_origin_[1],
    point_cloud.sensor_origin_[2]))*
    Eigen::Affine3f(point_cloud.sensor_orientation_);
    std::string far_ranges_filename = pcl::getFilenameWithoutExtension(filename)+"_far_ranges.pcd";
    if(pcl::io::loadPCDFile(far_ranges_filename.c_str(),far_ranges)==-1)//如果打开far_ranges文件失败
    std::cout<<"Far ranges file \""<<far_ranges_filename<<"\" does not exists.\n";
    }
    
    //没有给的输入点云,则生成一个点云
    else
    {
    setUnseenToMaxRange=true;
    cout<<"\nNo *.pcd file given =>Genarating example point cloud.\n\n";
    for(float x = -0.5f;x<=0.5f;x+=0.01f)
    {
    for(float y = -0.5f;y<=0.5f;y+=0.01f)
    {
    PointType point;point.x=x;point.y=y;point.z=2.0f-y;
    point_cloud.points.push_back(point);
    }
    }
    point_cloud.width=(int)point_cloud.points.size();point_cloud.height=1;//height = 1为无序点云
    }
    //从点云创建距离图像
    float noise_level=0.0;
    float min_range=0.0f;
    int border_size=1;//深度图边界大小
    boost::shared_ptr<pcl::RangeImage> range_image_ptr(new pcl::RangeImage);
    pcl::RangeImage&range_image=*range_image_ptr;
    //生成深度图像range_image
    range_image.createFromPointCloud(point_cloud,angular_resolution,pcl::deg2rad(360.0f),pcl::deg2rad(180.0f),scene_sensor_pose,coordinate_frame,noise_level,min_range,border_size);
    range_image.integrateFarRanges(far_ranges);//不是太懂
    if(setUnseenToMaxRange)
    range_image.setUnseenToMaxRange();
    // 创建3D点云可视化窗口,并显示点云
    pcl::visualization::PCLVisualizer viewer("3D Viewer");//3D窗口
    //第一个窗口,点云和关键点都显示
    int v1(0);
    viewer.createViewPort(0,0,0.5,1,v1);
    viewer.setBackgroundColor(255,1,1,v1);//背景颜色
    pcl::visualization::PointCloudColorHandlerCustom<PointType> color1(point_cloud_ptr,200,10,10);//输入点云的颜色
    //pcl::visualization::PointCloudColorHandlerCustom<pcl::PointWithRange>range_image_color_handler(range_image_ptr,0,0,0);//输入点云的颜色,赋值兼容规则
    viewer.addPointCloud(point_cloud_ptr,color1,"point_cloud",v1);//添加点云
    //viewer.addPointCloud(range_image_ptr,range_image_color_handler,"range image",v1);//添加点云
    viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,1,"range image",v1);//设置点的大小
    
    //第二个窗口,只显示关键点
    int v2(0);
    viewer.createViewPort(0.5,0,1,1,v2);
    viewer.setBackgroundColor(128,0,190,v2);
    
    //viewer.addCoordinateSystem (1.0f);
    //PointCloudColorHandlerCustom<PointType>point_cloud_color_handler (point_cloud_ptr, 150, 150, 150);
    //viewer.addPointCloud (point_cloud_ptr, point_cloud_color_handler, "original point cloud");
    viewer.initCameraParameters();
    setViewerPose(viewer,range_image.getTransformationToWorldSystem());
    // 显示距离图像
    pcl::visualization::RangeImageVisualizer range_image_widget("Range image");
    range_image_widget.showRangeImage(range_image);
    //保存深度图像
    float* ranges = range_image.getRangesArray();
    unsigned char* rgb_image = pcl::visualization::FloatImageUtils::getVisualImage(ranges,range_image.width,range_image.height);
    pcl::io::saveRgbPNGFile("range_image.png",rgb_image,range_image.width,range_image.height);//保存深度图像至range_image.png
    std::cerr << "range_image has been saved!" << std::endl;
    
    
    //提取NARF关键点
    pcl::RangeImageBorderExtractor range_image_border_extractor;//创建深度图像边界提取对象
    pcl::NarfKeypoint narf_keypoint_detector(&range_image_border_extractor);//创建Narf关键点提取器,输入为深度图像边缘提取器
    narf_keypoint_detector.setRangeImage(&range_image);//关键点提取设置输入深度图像
    narf_keypoint_detector.getParameters().support_size=support_size;//关键点提取的参数:搜索空间球体的半径
    narf_keypoint_detector.getParameters ().add_points_on_straight_edges = true;//对竖直边是否感兴趣
    //narf_keypoint_detector.getParameters ().distance_for_additional_points = 10;//与support_size一起作用
    
    pcl::PointCloud<int> keypoint_indices;//关键点索引
    narf_keypoint_detector.compute(keypoint_indices);//关键点计算,结果放置到keypoint_indices
    std::cerr<<"Found "<<keypoint_indices.points.size()<<" key points.\n";//输出得到的NARF关键点数目
    //保存关键点
    //转换关键点类型
    pcl::PointCloud<PointType>::Ptr narf_points_ptr(new pcl::PointCloud<PointType>);
    narf_points_ptr->width = keypoint_indices.points.size();
    narf_points_ptr->height = 1;
    narf_points_ptr->resize(keypoint_indices.points.size());
    narf_points_ptr->is_dense = false;
    for (size_t i = 0;i<keypoint_indices.points.size();i++)
    {
        narf_points_ptr->points[i].getVector3fMap() = range_image.points[keypoint_indices.points[i]].getVector3fMap();
    }
    //输出narf转换后的关键点
    std::cerr<<"narf_points: "<<narf_points_ptr->points.size()<<std::endl;
    //for (size_t i = 0;i<narf_points_ptr->points.size();i++)
    //   std::cerr<<"   ["<<i<<"]   "<<narf_points_ptr->points[i].x<<", "<<narf_points_ptr->points[i].y<<", "<<narf_points_ptr->points[i].z<<std::endl;
    //保存narf关键点
    pcl::io::savePCDFileASCII("narf_points.pcd",*narf_points_ptr);
    std::cerr<<"narf_points save succeed!"<<std::endl;
    //在距离图像显示组件内显示关键点
    //for (size_ti=0; i<keypoint_indices.points.size (); ++i)
    //range_image_widget.markPoint (keypoint_indices.points[i]%range_image.width,
    //keypoint_indices.points[i]/range_image.width);
    //在3D窗口中显示关键点
    pcl::PointCloud<pcl::PointXYZ>::Ptr keypoints_ptr(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PointCloud<pcl::PointXYZ>&keypoints=*keypoints_ptr;
    keypoints.points.resize(keypoint_indices.points.size());
    for(size_t i=0;i<keypoint_indices.points.size();++i)
    keypoints.points[i].getVector3fMap()=range_image.points[keypoint_indices.points[i]].getVector3fMap();//将得到的关键点转换给keypoints(pcl::PointCloud<pcl::PointXYZ>类型)
    //设置关键点在3D Viewer中的颜色大小属性
    pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>keypoints_color_handler(keypoints_ptr,0,255,0);//颜色
    viewer.addPointCloud<pcl::PointXYZ>(keypoints_ptr,keypoints_color_handler,"keypoints");//显示
    viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,7,"keypoints");//关键点的大小
    // 主循环
    while(!viewer.wasStopped())
    {
    range_image_widget.spinOnce();// process GUI events
    viewer.spinOnce();
    pcl_sleep(0.01);
    }
    }
    

    4、可视化 

    1)、

    将” narf_keypoint_detector.getParameters ().add_points_on_straight_edges = true;//对竖直边是否感兴趣”设置为不感兴趣(false<默认>),此时关键点少

    2)、

    将” narf_keypoint_detector.getParameters ().add_points_on_straight_edges = true;//对竖直边是否感兴趣”设置为感兴趣(true),此时关键点多 

    二、SIFT关键点提取

    1、背景

    本例程将演示如何检测点云的SIFT关键点。SIFT,即尺度不变特征变换(Scale-invariant feature transform, SIFT),最初是图像处理领域的一种描述。这种描述具有尺度不变性,可在图像中检测出关键点,是一种局部特征描述子,后来被引入3D点云领域用于关键点检测。

    2、SIFT检测代码

      const float min_scale = atof(argv[2]);//尺度空间中最小尺度的标准偏差
      const int n_octaves = atoi(argv[3]);//高斯金字塔中组的数目
      const int n_scales_per_octave = atoi(argv[4]);//每组计算的尺度数目
      const float min_contrast = atof(argv[5]);//设置关键点检测的阈值
    
      //SIFT关键点检测
      pcl::SIFTKeypoint<pcl::PointXYZ, pcl::PointWithScale> sift;//创建sift关键点检测对象
      pcl::PointCloud<pcl::PointWithScale> result;//SIFT关键点提取结果
      sift.setInputCloud(cloud_xyz);//设置输入点云
      pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ> ());
      sift.setSearchMethod(tree);//创建一个空的kd树对象tree,并把它传递给sift检测对象
      sift.setScales(min_scale, n_octaves, n_scales_per_octave);//指定搜索关键点的尺度范围
      sift.setMinimumContrast(min_contrast);//设置限制关键点检测的阈值
      sift.compute(result);//执行sift关键点检测,保存结果在result

    3、全部代码

    #include <iostream>
    #include <pcl/io/pcd_io.h>
    #include <pcl/point_types.h>
    #include <pcl/common/io.h>
    #include <pcl/keypoints/sift_keypoint.h>
    #include <pcl/features/normal_3d.h>
    #include <pcl/visualization/pcl_visualizer.h>
    #include <pcl/console/time.h>
    using namespace std;
    
    namespace pcl
    {
      template<>
        struct SIFTKeypointFieldSelector<PointXYZ>
        {
          inline float
          operator () (const PointXYZ &p) const
          {
        return p.z;
          }
        };
    }
    
    int
    main(int argc, char *argv[])
    {
    
      pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_xyz (new pcl::PointCloud<pcl::PointXYZ>);
      pcl::io::loadPCDFile (argv[1], *cloud_xyz);//将第二个命令行参数所代表的文件作为输入文件读入
    
      const float min_scale = atof(argv[2]);//尺度空间中最小尺度的标准偏差
      const int n_octaves = atoi(argv[3]);//高斯金字塔中组的数目
      const int n_scales_per_octave = atoi(argv[4]);//每组计算的尺度数目
      const float min_contrast = atof(argv[5]);//设置关键点检测的阈值
    
      //SIFT关键点检测
      pcl::SIFTKeypoint<pcl::PointXYZ, pcl::PointWithScale> sift;//创建sift关键点检测对象
      pcl::PointCloud<pcl::PointWithScale> result;//SIFT关键点提取结果
      sift.setInputCloud(cloud_xyz);//设置输入点云
      pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ> ());
      sift.setSearchMethod(tree);//创建一个空的kd树对象tree,并把它传递给sift检测对象
      sift.setScales(min_scale, n_octaves, n_scales_per_octave);//指定搜索关键点的尺度范围
      sift.setMinimumContrast(min_contrast);//设置限制关键点检测的阈值
      sift.compute(result);//执行sift关键点检测,保存结果在result
    
      //类型转换
      pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_temp (new pcl::PointCloud<pcl::PointXYZ>);
      copyPointCloud(result, *cloud_temp);//将点类型pcl::PointWithScale的数据转换为点类型pcl::PointXYZ的数据
     
      //可视化输入点云和关键点
      pcl::visualization::PCLVisualizer viewer("Sift keypoint");
      viewer.setBackgroundColor( 255, 255, 255 );
      viewer.addPointCloud(cloud_xyz, "cloud");//在视窗中添加原始点云数据
      viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,0,0,0,"cloud");
      viewer.addPointCloud(cloud_temp, "keypoints");//将SIFT关键点添加至视窗
      viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 9, "keypoints");
      viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,0,0,255,"keypoints");
    
      while(!viewer.wasStopped ())
      {
        viewer.spinOnce ();
      }
      return 0;
      
    }

    4、可视化

    三、Harris关键点提取

    1、背景

     本小结将演示如何检测点云的3D Harris角点。Harris算子是常见的特征检测算子,既可以提取角点又可以提取边缘点。与2D Harris角点检测原理不同,3D Harris角点检测利用的就是点云的法向量信息。

    2、Harris关键点提取代码

    	pcl::PointCloud<pcl::PointXYZI>::Ptr Harris_keypoints (new pcl::PointCloud<pcl::PointXYZI> ());//存放最后的特征点提取结果
    	//实例化一个Harris特征检测对象harris_detector
    	pcl::HarrisKeypoint3D<pcl::PointXYZ,pcl::PointXYZI,pcl::Normal>* harris_detector = new pcl::HarrisKeypoint3D<pcl::PointXYZ,pcl::PointXYZI,pcl::Normal> ;
    
    	//harris_detector->setNonMaxSupression(true);
    	harris_detector->setRadius(r_normal);//设置法向量估计的半径
    	harris_detector->setRadiusSearch(r_keypoint);//设置关键点估计的近邻搜索半径
    	harris_detector->setInputCloud (input_cloud);//设置输入点云
    	//harris_detector->setNormals(normal_source);
    	//harris_detector->setMethod(pcl::HarrisKeypoint3D<pcl::PointXYZRGB,pcl::PointXYZI>::LOWE);
    	harris_detector->compute (*Harris_keypoints);//结果存放在Harris_keypoints
    	cout<<"Harris_keypoints number: "<<Harris_keypoints->size()<<endl;
    	//writer.write<pcl::PointXYZI> ("Harris_keypoints.pcd",*Harris_keypoints,false);//结果保存

    3、全部代码

    #include <iostream>
    #include <pcl/io/pcd_io.h>
    #include <pcl/point_cloud.h>
    #include <pcl/visualization/pcl_visualizer.h>
    #include <pcl/io/io.h>
    #include <pcl/keypoints/harris_3d.h>//harris特征点估计类头文件声明
    #include <cstdlib>
    #include <vector>
    #include <pcl/console/parse.h>
    using namespace std;
    
    
    
    int main(int argc,char *argv[]) 
    {
    	pcl::PointCloud<pcl::PointXYZ>::Ptr input_cloud (new pcl::PointCloud<pcl::PointXYZ>);//输入点云的指针
    	pcl::io::loadPCDFile (argv[1], *input_cloud);//将命令行参数文件读至input_cloud指针
    	pcl::PCDWriter writer;
    	float r_normal;//法向量估计的半径
    	float r_keypoint;//关键点估计的近邻搜索半径
    
    	r_normal=atof(argv[2]);//将命令行的第三个参数解析为法向量估计的半径
    	r_keypoint=atof(argv[3]);//将命令行的第四个参数解析为关键点估计的近邻搜索半径
    
    	typedef pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZI> ColorHandlerT3;
    
    	pcl::PointCloud<pcl::PointXYZI>::Ptr Harris_keypoints (new pcl::PointCloud<pcl::PointXYZI> ());//存放最后的特征点提取结果
    	//实例化一个Harris特征检测对象harris_detector
    	pcl::HarrisKeypoint3D<pcl::PointXYZ,pcl::PointXYZI,pcl::Normal>* harris_detector = new pcl::HarrisKeypoint3D<pcl::PointXYZ,pcl::PointXYZI,pcl::Normal> ;
    
    	//harris_detector->setNonMaxSupression(true);
    	harris_detector->setRadius(r_normal);//设置法向量估计的半径
    	harris_detector->setRadiusSearch(r_keypoint);//设置关键点估计的近邻搜索半径
    	harris_detector->setInputCloud (input_cloud);//设置输入点云
    	//harris_detector->setNormals(normal_source);
    	//harris_detector->setMethod(pcl::HarrisKeypoint3D<pcl::PointXYZRGB,pcl::PointXYZI>::LOWE);
    	harris_detector->compute (*Harris_keypoints);//结果存放在Harris_keypoints
    	cout<<"Harris_keypoints number: "<<Harris_keypoints->size()<<endl;
    	//writer.write<pcl::PointXYZI> ("Harris_keypoints.pcd",*Harris_keypoints,false);//结果保存
    	pcl::io::savePCDFileASCII("Harris keypoints.pcd" , *Harris_keypoints);
    	cout<<"Points: "<<Harris_keypoints->points.size()<<endl;
    
    	//可视化点云
    	pcl::visualization::PCLVisualizer visu3("clouds");
    	visu3.setBackgroundColor(255,255,255);
    	//Harris_keypoints关键点可视化
    	visu3.addPointCloud (Harris_keypoints, ColorHandlerT3 (Harris_keypoints, 0.0, 0.0, 255.0), "Harris_keypoints");
    	visu3.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,8,"Harris_keypoints");
    	//原始点云可视化
    	visu3.addPointCloud(input_cloud,"input_cloud");
    	visu3.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,0,0,0,"input_cloud");
    	visu3.spin ();
    }

    4、可视化

    深度图: 

    更多相关内容
  • 人体关键点检测

    千次阅读 2022-01-09 20:10:52
    最近准备学习行为识别相关的内容,而人体关键点检测算法一类行为识别算法的前置任务,因此决定先学习人体关键点检测相关算法。 本文先对人体关键点检测进行简单的介绍,直观说明关键点检测是什么;然后对人体关键点...


    前言

    最近准备学习行为识别相关的内容,而人体关键点检测算法一类行为识别算法的前置任务,因此决定先学习人体关键点检测相关算法。

    本文先对人体关键点检测进行简单的介绍,直观说明关键点检测是什么;然后对人体关键点检测算法进行分类,并列举一些典型的、重要的算法,方便读者进一步学习;最后对各个算法进行理论和创新点的总结,抛砖引玉

    在图像中检测人类是一项具有挑战性的任务,因为他们的外表多变,姿势多样。首先需要的是一个健壮的特征集,即使在困难的光照下,即使在杂乱无章的背景中,也可以清晰地分辨出人体的形状。对检测器性能的各种实现选择,采取“行人检测”(检测最多可见)。人在或多或少直立的姿势)作为一个测试用例。

    话不多说,让我们进入人体姿态检测的学习吧!

     


     

    一、人体关键点检测是什么?

    人体关键点检测(Human Keypoints Detection)又称为人体姿态估计,是计算机视觉中一个相对基础的任务,是人体动作识别、行为分析、人机交互等的前置任务。一般情况下可以将人体关键点检测细分为单人/多人关键点检测、2D/3D关键点检测,同时有算法在完成关键点检测之后还会进行关键点的跟踪,也被称为人体姿态跟踪。

    目前COCO keypoint track是人体关键点检测的权威公开比赛之一,COCO数据集中把人体关键点表示为17个关节,分别是鼻子,左右眼,左右耳,左右肩,左右肘,左右腕,左右臀,左右膝,左右脚踝。而人体关键点检测的任务就是从输入的图片中检测到人体及对应的关键点位置。

    我从CPN和Objects as Points论文中随后截取了几张2D人体关键点检测的效果图,如下图所示。

     

    二、人体关键点检测算法步骤

    1.引入库

    所需要的库如下:

     

    2.算法概述

    多人人体骨骼关键点检测主要有两个方向,一种是自上而下,一种是自下而上,其中自上而上的人体骨骼关键点定位算法主要包含两个部分,人体检测和单人人体关键点检测,即首先通过目标检测算法将每一个人检测出来,然后在检测框的基础上针对单个人做人体骨骼关键点检测,其中代表性算法有G-RMI, CFN, RMPE, Mask R-CNN, and CPN,目前在MSCOCO数据集上最好的效果是72.6%;自下而上的方法也包含两个部分,关键点检测和关键点聚类,即首先需要将图片中所有的关键点都检测出来,然后通过相关策略将所有的关键点聚类成不同的个体,其中对关键点之间关系进行建模的代表性算法有PAF, Associative Embedding, Part Segmentation, Mid-Range offsets,目前在MSCOCO数据集上最好的效果是68.7%。

    Coordinate、Heatmap和Heatmap + Offsets

    在介绍多人人体骨骼关键点检测算法之间,首先介绍一下关键点回归的Ground Truth的构建问题,主要有两种思路,Coordinate和Heatmap,Coordinate即直接将关键点坐标作为最后网络需要回归的目标,这种情况下可以直接得到每个坐标点的直接位置信息;Heatmap即将每一类坐标用一个概率图来表示,对图片中的每个像素位置都给一个概率,表示该点属于对应类别关键点的概率,比较自然的是,距离关键点位置越近的像素点的概率越接近1,距离关键点越远的像素点的概率越接近0,具体可以通过相应函数进行模拟,如Gaussian等,如果同一个像素位置距离不同关键点的距离大小不同,即相对于不同关键点该位置的概率不一样,这时可以取Max或Average,如下图

     

    3.算法分析

    对于两种Ground Truth的差别,Coordinate网络在本质上来说,需要回归的是每个关键点的一个相对于图片的offset,而长距离offset在实际学习过程中是很难回归的,误差较大,同时在训练中的过程,提供的监督信息较少,整个网络的收敛速度较慢;Heatmap网络直接回归出每一类关键点的概率,在一定程度上每一个点都提供了监督信息,网络能够较快的收敛,同时对每一个像素位置进行预测能够提高关键点的定位精度,在可视化方面,Heatmap也要优于Coordinate,除此之外,实践证明,Heatmap确实要远优于Coordinate,具体结构如下图所示。

    自上而下(Top-Down)的人体骨骼关键点检测算法主要包含两个部分,目标检测和单人人体骨骼关键点检测,对于目标检测算法,这里不再进行描述,而对于关键点检测算法,首先需要注意的是关键点局部信息的区分性很弱,即背景中很容易会出现同样的局部区域造成混淆,所以需要考虑较大的感受野区域;其次人体不同关键点的检测的难易程度是不一样的,对于腰部、腿部这类关键点的检测要明显难于头部附近关键点的检测,所以不同的关键点可能需要区别对待;最后自上而下的人体关键点定位依赖于检测算法的提出的Proposals,会出现检测不准和重复检测等现象,大部分相关论文都是基于这三个特征去进行相关改进,接下来我将简要介绍其中几种经典的算法思路。

    Convolutional Pose Machines:本论文将深度学习应用于人体姿态分析,同时用卷积图层表达纹理信息和空间信息。主要网络结构分为多个stage,其中第一个stage会产生初步的关键点的检测效果,接下来的几个stage均以前一个stage的预测输出和从原图提取的特征作为输入,进一步提高关键点的检测效果。具体的流程图如下图

     用各部件响应图来表达各部件之间的空间约束,响应图和特征图一起作为数据在网络中传递。人体关键点在空间上的先验分布会指导网络的学习,假如stage 1的预测结果中右肩关键点的预测结果是正确的,而右肘关键点的预测是错误的,那么在接下来的stage中肩和肘在空间上的先验分布会指导网络的学习。如下图

     

    自下而上的人体关键点检测算法

    自下而上(Bottom-Up)的人体骨骼关键点检测算法主要包含两个部分,关键点检测和关键点聚类,其中关键点检测和单人的关键点检测方法上是差不多的,区别在于这里的关键点检测需要将图片中所有类别的所有关键点全部检测出来,然后对这些关键点进行聚类处理,将不同人的不同关键点连接在一块,从而聚类产生不同的个体。而这方面的论文主要侧重于对关键点聚类方法的探索,即如何去构建不同关键点之间的关系,接下来我将介绍几种经典的方法。


    Part Segmentation:即对人体进行不同部位分割,而关键点都落在分割区域的特定位置,通过部位分割对关键点之间的关系进行建模,既可以显式的提供人体关键点的空间先验知识,指导网络的学习,同时在最后对不同人体关键点进行聚类时也能起到相应的连接关键点的作用。如下图

     

    Part Affinity Fields:该方法通过对人体的不同肢体结构进行建模,使用向量场来模拟不同肢体结构,解决了单纯使用中间点是否在肢干上造成的错连问题。

     

    4.代码展示

    class ImageReader(object):
        def __init__(self, file_names):
            self.file_names = file_names
            self.max_idx = len(file_names)
    
        def __iter__(self):
            self.idx = 0
            return self
    
        def __next__(self):
            if self.idx == self.max_idx:
                raise StopIteration
            img = cv2.imread(self.file_names[self.idx], cv2.IMREAD_COLOR)
            if img.size == 0:
                raise IOError('Image {} cannot be read'.format(self.file_names[self.idx]))
            self.idx = self.idx + 1
            return img
    
    
    class VideoReader(object):
        def __init__(self, file_name):
            self.file_name = file_name
            try:  # OpenCV needs int to read from webcam
                self.file_name = int(file_name)
            except ValueError:
                pass
    
        def __iter__(self):
            self.cap = cv2.VideoCapture(self.file_name)
            if not self.cap.isOpened():
                raise IOError('Video {} cannot be opened'.format(self.file_name))
            return self
    
        def __next__(self):
            was_read, img = self.cap.read()
            if not was_read:
                raise StopIteration
            return img
    
    
    def infer_fast(net, img, net_input_height_size, stride, upsample_ratio, cpu,
                   pad_value=(0, 0, 0), img_mean=np.array([128, 128, 128], np.float32), img_scale=np.float32(1/256)):
        height, width, _ = img.shape
        scale = net_input_height_size / height
    
        scaled_img = cv2.resize(img, (0, 0), fx=scale, fy=scale, interpolation=cv2.INTER_LINEAR)
        scaled_img = normalize(scaled_img, img_mean, img_scale)
        min_dims = [net_input_height_size, max(scaled_img.shape[1], net_input_height_size)]
        padded_img, pad = pad_width(scaled_img, stride, pad_value, min_dims)
    
        tensor_img = torch.from_numpy(padded_img).permute(2, 0, 1).unsqueeze(0).float()
        if not cpu:
            tensor_img = tensor_img.cuda()
    
        stages_output = net(tensor_img)
    
        stage2_heatmaps = stages_output[-2]
        heatmaps = np.transpose(stage2_heatmaps.squeeze().cpu().data.numpy(), (1, 2, 0))
        heatmaps = cv2.resize(heatmaps, (0, 0), fx=upsample_ratio, fy=upsample_ratio, interpolation=cv2.INTER_CUBIC)
    
        stage2_pafs = stages_output[-1]
        pafs = np.transpose(stage2_pafs.squeeze().cpu().data.numpy(), (1, 2, 0))
        pafs = cv2.resize(pafs, (0, 0), fx=upsample_ratio, fy=upsample_ratio, interpolation=cv2.INTER_CUBIC)
    
        return heatmaps, pafs, scale, pad
    
    
    def run_demo(net, image_provider, height_size, cpu, track, smooth):
        net = net.eval()
        if not cpu:
            net = net.cuda()
    
        stride = 8
        upsample_ratio = 4
        num_keypoints = Pose.num_kpts
        previous_poses = []
        delay = 1
        for img in image_provider:
            orig_img = img.copy()
            heatmaps, pafs, scale, pad = infer_fast(net, img, height_size, stride, upsample_ratio, cpu)
    
            total_keypoints_num = 0
            all_keypoints_by_type = []
            for kpt_idx in range(num_keypoints):  # 19th for bg
                total_keypoints_num += extract_keypoints(heatmaps[:, :, kpt_idx], all_keypoints_by_type, total_keypoints_num)
    
            pose_entries, all_keypoints = group_keypoints(all_keypoints_by_type, pafs)
            for kpt_id in range(all_keypoints.shape[0]):
                all_keypoints[kpt_id, 0] = (all_keypoints[kpt_id, 0] * stride / upsample_ratio - pad[1]) / scale
                all_keypoints[kpt_id, 1] = (all_keypoints[kpt_id, 1] * stride / upsample_ratio - pad[0]) / scale
            current_poses = []
            for n in range(len(pose_entries)):
                if len(pose_entries[n]) == 0:
                    continue
                pose_keypoints = np.ones((num_keypoints, 2), dtype=np.int32) * -1
                for kpt_id in range(num_keypoints):
                    if pose_entries[n][kpt_id] != -1.0:  # keypoint was found
                        pose_keypoints[kpt_id, 0] = int(all_keypoints[int(pose_entries[n][kpt_id]), 0])
                        pose_keypoints[kpt_id, 1] = int(all_keypoints[int(pose_entries[n][kpt_id]), 1])
                pose = Pose(pose_keypoints, pose_entries[n][18])
                current_poses.append(pose)
    
            if track:
                track_poses(previous_poses, current_poses, smooth=smooth)
                previous_poses = current_poses
            for pose in current_poses:
                pose.draw(img)
            img = cv2.addWeighted(orig_img, 0.6, img, 0.4, 0)
            for pose in current_poses:
                cv2.rectangle(img, (pose.bbox[0], pose.bbox[1]),
                              (pose.bbox[0] + pose.bbox[2], pose.bbox[1] + pose.bbox[3]), (0, 255, 0))
                if track:
                    cv2.putText(img, 'id: {}'.format(pose.id), (pose.bbox[0], pose.bbox[1] - 16),
                                cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 255))
            cv2.imshow('Lightweight Human Pose Estimation Python Demo', img)
            key = cv2.waitKey(delay)
            if key == 27:  # esc
                return
            elif key == 112:  # 'p'
                if delay == 1:
                    delay = 0
                else:
                    delay = 1
    
    
    if __name__ == '__main__':
        parser = argparse.ArgumentParser(
            description='''Lightweight human pose estimation python demo.
                           This is just for quick results preview.
                           Please, consider c++ demo for the best performance.''')
        parser.add_argument('--checkpoint-path', type=str, required=True, help='path to the checkpoint')
        parser.add_argument('--height-size', type=int, default=256, help='network input layer height size')
        parser.add_argument('--video', type=str, default='', help='path to video file or camera id')
        parser.add_argument('--images', nargs='+', default='', help='path to input image(s)')
        parser.add_argument('--cpu', action='store_true', help='run network inference on cpu')
        parser.add_argument('--track', type=int, default=1, help='track pose id in video')
        parser.add_argument('--smooth', type=int, default=1, help='smooth pose keypoints')
        args = parser.parse_args()
    
        if args.video == '' and args.images == '':
            raise ValueError('Either --video or --image has to be provided')
    
        net = PoseEstimationWithMobileNet()
        checkpoint = torch.load(args.checkpoint_path, map_location='cpu')
        load_state(net, checkpoint)
    
        frame_provider = ImageReader(args.images)
        if args.video != '':
            frame_provider = VideoReader(args.video)
        else:
            args.track = 0
    
        run_demo(net, frame_provider, args.height_size, args.cpu, args.track, args.smooth)
    

    总结

    人体关键点检测至今仍然是计算机视觉领域较为活跃的一个研究方向,人体关键点检测算法还没有达到比较完美的效果,在较为复杂的场景下仍然会出现很多错误的检测结果。自上而下的关键点检测算法在效果上要明显好于自下而上的关键点检测算法,因为自上而下的检测方法加入了整个人体的一个空间先验。个人认为,自下而上的关键点定位算法没有显示的去建模整个人体的空间关系,而只是建模了局部的空间关系,以至于在效果上目前还远低于自上而上的关键点检测方法。

    展开全文
  • 人脸识别之人脸关键点(仅供本人参考)

    千次阅读 多人点赞 2022-05-12 16:18:58
    人脸关键点

     深度学习人脸关键点检测方法----综述

    转自人脸关键点检测综述

    不知道为什么在ubuntu下知呼中的图片无法显示

    人脸关键点检测是人脸识别和分析领域中的关键一步,它是诸如自动人脸识别、表情分析、三维人脸重建及三维动画等其它人脸相关问题的前提和突破口。因此,本文针对深度学习方法进行了人脸关键点检测的研究。

    一、人脸关键点数据集

    传统人脸关键点检测数据库为室内环境下采集的数据库,比如 Multi-pie、Feret、Frgc、AR、BioID 等人脸数据库。而现阶段人脸关键点检测数据库通常为复杂环境下采集的数据库.LFPW 人脸数据库有 1132 幅训练人脸图像和 300 幅测试人脸图像,大部分为正面人脸图像,每个人脸标定 29 个关键点AFLW 人脸数据库包含 25993 幅从 Flickr 采集的人脸图像,每个人脸标定 21 个关键点。COFW 人脸数据库包含 LFPW 人脸数据库训练集中的 845 幅人脸图像以及其他 500 幅遮挡人脸图像,而测试集为 507 幅严重遮挡(同时包含姿态和表情的变化)的人脸图像,每个人脸标定 29 个关键点。MVFW 人脸数据库为多视角人脸数据集,包括 2050 幅训练人脸图像和 450 幅测试人脸图像,每个人脸标定 68 个关键点。OCFW 人脸数据库包含 2951 幅训练人脸图像(均为未遮挡人脸)和 1246 幅测试人脸图像(均为遮挡人脸),每个人脸标定 68 个关键点。

    1.1 传统方法

    1.1.1 ASM(Active Shape Model)

     An Introduction to Active Shape Models

    人脸Pose检测:ASM、AAM、CLM总结 - wishchin - 博客园 (详细说明)

    ASM(Active Shape Model)算法介绍

     ASM(Active Shape Model)主动形状模型通俗易懂讲解一:理论

    搜索ASM有很多。

    ASM(Active Shape Model) 是由 Cootes 于 1995 年提出的经典的人脸关键点检测算法,主动形状模型即通过形状模型对目标物体进行抽象,ASM 是一种基于点分布模型(Point Distribution Model, PDM)的算法。在 PDM 中,外形相似的物体,例如人脸、人手、心脏、肺部等的几何形状可以通过若干关键点(landmarks)的坐标依次串联形成一个形状向量来表示。ASM 算法需要通过人工标定的方法先标定训练集,经过训练获得形状模型,再通过关键点的匹配实现特定物体的匹配。

    ASM 主要分为两步:

    第一步:训练

    首先,构建形状模型:

    1. 搜集 n 个训练样本(n=400);
    2. 手动标记脸部关键点(68个);
    3. 将训练集中关键点的坐标串成特征向量;
    4. 对形状进行归一化和对齐(对齐采用 Procrustes 方法);
    5. 对对齐后的形状特征做 PCA 处理。

    接着,为每个关键点构建局部特征。目的是在每次迭代搜索过程中每个关键点可以寻找新的位置。局部特征一般用梯度特征,以防光照变化。有的方法沿着边缘的法线方向提取,有的方法在关键点附近的矩形区域提取。

    第二步:搜索。

    首先:计算眼睛(或者眼睛和嘴巴)的位置,做简单的尺度和旋转变化,对齐人脸;

    接着,在对齐后的各个点附近搜索,匹配每个局部关键点(常采用马氏距离),得到初步形状;

    再用平均人脸(形状模型)修正匹配结果;迭代直到收敛。

    ASM 算法的优点在于模型简单直接,架构清晰明确,易于理解和应用,而且对轮廓形状有着较强的约束,但是其近似于穷举搜索的关键点定位方式在一定程度上限制了其运算效率

    1.1.2 Active Appearance Models

    主动外观模型(AAM) - OpenCV China

    AAM(Active Appearance Model)算法介绍

    AAM算法简介

    1998 年,Cootes 对 ASM 进行改进,不仅采用形状约束,而且又加入整个脸部区域的纹理特征,提出了 AAM 算法。AAM 于 ASM 一样,主要分为两个阶段,模型建立阶段和模型匹配阶段。其中模型建立阶段包括对训练样本分别建立形状模型 (Shape Model) 和纹理模型 (Texture Model),然后将两个模型进行结合,形成 AAM 模型。

    1.1.3 Cascaded pose regression

    Cascaded pose regression 介绍说明

    2010 年,Dollar 提出 CPR(Cascaded Pose Regression, 级联姿势回归),CPR 通过一系列回归器将一个指定的初始预测值逐步细化,每一个回归器都依靠前一个回归器的输出来执行简单的图像操作,整个系统可自动的从训练样本中学习。

    所谓的线性回归,其实就是一个不断迭代的过程,对于每一个stage中,用上一个stage的状态作为输入来更新,产生下一个stage的输入,以此类推,直到达到最底层stage。

    训练过程中,先初始化一个shape ,用一种特征映射方法Φt生成局部二值特征。根据特征和目标shape的增量生成最终shape。权值通过线性回归进行更新。在测试阶段,直接预测形状增量,并应用于更新当前估计的形状。形状增量:

    如上公式所示,I为输入图像,St-1为第t-1 stage的形状,Φt为t stage的特征匹配函数,W为线性回归矩阵。

    人脸关键点检测的目的是估计向量,其中 K 表示关键点的个数,由于每个关键点有横纵两个坐标,所以 S 的长度为 2K。CPR 检测流程如图所示,一共有 T 个阶段,在每个阶段中首先进行特征提取,得到ft, 这里使用的是 shape-indexed features,也可以使用诸如 HOG、SIFT 等人工设计的特征,或者其他可学习特征(learning based features),然后通过训练得到的回归器 R 来估计增量ΔS( update vector),把ΔS 加到前一个阶段的 S 上得到新的 S,这样通过不断的迭代即可以得到最终的 S(shape)。

     1.1.4 人脸关键点定位3000fps的LBF方法

    人脸识别经典算法二:LBP方法

    人脸检测(七)--LBP特征原理及实现

    人脸对齐(八)--LBF算法_Eason.wxd的博客-CSDN博客_lbf算法

    计算机视觉基础-图像处理: LBP特征描述算子 - 知乎

    LBP特征及其一些变种

    基于LBP纹理特征计算GLCM的纹理特征统计量+SVM/RF识别纹理图片

    基于LBF方法的人脸对齐,出自Face Alignment at3000 FPS via Regressing Local Binary Features,由于该方法提取的是局部二值特征(LBF),论文作者的LBF fast达到了3000fps的速度,网上热心网友分享的程序也达到了近300fps的速度,绝对是人脸对齐方面速度最快的一种算法。因此,好多网友也将该方法称为,3000fps。

    该算法的核心工作主要有两部分,总体上采用了随机森林和全局线性回归相结合的方法,相对于使用卷积神经的深度学习方法,LBF采用的算法是传统的机器学习方法。

    1、LBF特征的提取

    LBF算法,将随机森林的输出组成一种特征(这里也就是LBF),并用LBF来做预测,除了采用随机森林的结构做预测,LBF还针对每个关键点给出一个随机森林来做预测,并将所有关键点对应的随机森林输出的局部特征相互联系起来,称作为局部二值特征,然后利用这个局部二值特征做全局回归,来预测形状变化量。

    在特征点附近,随机选择点做残差来学习LBF特征,每一个特征点都会学到由好多随机树组成的随机森林,因此,一个特征点就得用一个随机森林生成的0,1特征向量来表示,将所有的特征点的随机森林都连接到一起,生成一个全局特征,后续过程就可以使用该全局特征做全局线性回归了

    其中

    表示在第t次级联回归中,第j个特征点所对应的随机森林,所有的关键点的随机森林一起组成了,它的输出为LBF特征。然后利用LBF特征来训练全局线性回归或者预测形状变化量

    上图描述生成局部二值特征LBF的过程,图的下半部分描述了单个关键点上随机森林输出了一个局部二值特征,然后把所有随机森林的输出前后连接起来组成一个非常大但又十分稀疏的LBF特征。第一颗树遍历后来到了最左边的叶子节点所以记为[1,0,0,0],对于第一课树访问到的叶子节点记为1,其他的记为0,把所有的结果串联起来就是=[1,0,0,0,0,1,0,0,0,0,1,0.....],其中这个特征只有0,1组成,且大部分是0,特征非常的稀疏。真正的局部 二值特征就是将所有的landmark的特征串联起来,即是

    2、形状索引特征(shape-indexed)

    每一个关键点对应一个随机森林,而随机森林是由多个独立的随机树组成。论文中的随机树采用的特征是形状索引特征,ESR(CVPR‘12)和RCPR(ICCV'13)也是采用了类似的特征,这个特征主要是描述人脸区域中两个点的像素差值。关于两个像素点的选取,三个方法都使用了不同的策略。

    ESR方法采用的是在两个关键点附近随机出两个点,做这两个点之间的差值作为形状索引特征。

    RCPR方法采用的选取两个关键点的中点,外加一个随机偏置来生成特征点,用两个这样的特征点的差值作为形状索引特征。

    在LBF算法中,由于随机森林是针对单个关键点的,所有随机树使用到的特征点不会关联到其他关键点,3000fps的做法是在当前关键点的附近区域随机产生两个特征点,用这两个点的像素差值来作为形状索引特征。下图是每个特征点的局部区域面积变化,索引面积从大到小逐渐变小,目的是为了获得更加准确的局部索引特征。

    3、随机森林训练

    在LBF算法中,由于随机森林是针对单个关键点的,所有随机树种使用到的特征点不会关联到其他关键点,只有在当前关键点的附近区域随机产生两个特征点,做像素差值来作为形状索引特征。

    计算每一张图片的这两个点处的像素值的像素差,从事先随机生成的形状索引特征集合F中选取一个,作为分裂特征,之后随机产生一个分裂阈值,如果一幅图像的像素差小于这个阈值,就往左分裂,否则往右分裂,将所有图片都这样判断一次,就将所有图片分成了两部分,一部分在左,一部分在右。我们重复上面这个过程若干次,看看哪一次分裂的好(判断是否分裂的好的依据是方差,如果分在左边的样本的方差小,这说明它们是一类,分的就好),就把这个分裂的好的选择保存下来,即保存下这两个点的坐标值和分裂阈值。这样一个节点的分裂就完成了。然后每一个节点的分裂都按照这个步骤进行,直到分裂到叶子节点。

    分配的目的是希望左右子树的样本点的Y具有相同的模式。论文实现中,特征选取可以采用如下的方式进行。

    信息增益

    上式中f表示选取到的特征函数(即利用随机采样到的特征点计算形状索引特征(这里用的像素差值), Δ表示随机生成的阈值,S用来刻画样本点之间的相似度或者样本集合的熵(论文中,采用的是方差)。针对每一个几点,训练数据(X,Y)被分成两部分,(X1,Y1)和(X2,Y2),我们期望左右字数的样本数据具有相同的模式,论文中采用了方差刻画,所以选择特征函数f时,我们希望方差减小最大。

    权值学习:使用的双坐标下降方法,使下公式最小:

    λ是控制Wt长度,即让Wt尽量稀疏。测试的时候,先对一幅图像提取形状索引特征, 然后使用随机森林进行编码,最后使用w估计shape增量。

    from:人脸对齐 - ERP 和 LBF

    from:LBF特征_zhou894509的专栏-CSDN博客_lbf特征

    github上热心网友的程序,

    matab版本:jwyang/face-alignment

    c++版本:yulequan/face-alignment-in-3000fps

    1.2 深度学习方法

    1.2.1 Deep Convolutional Network Cascade for Facial Point Detection

    2013 年,Sun 等人首次将 CNN 应用到人脸关键点检测,提出一种级联的 CNN(拥有三个层级)——DCNN(Deep Convolutional Network),此种方法属于级联回归方法。作者通过精心设计拥有三个层级的级联卷积神经网络,不仅改善初始不当导致陷入局部最优的问题,而且借助于 CNN 强大的特征提取能力,获得更为精准的关键点检测。

    如图所示,DCNN 由三个 Level 构成。Level-1 由 3 个 CNN 组成;Level-2 由 10 个 CNN 组成(每个关键点采用两个 CNN);Level-3 同样由 10 个 CNN 组成。

    evel-1 分 3 个 CNN,分别是 F1(Face 1)、EN1(Eye,Nose)、NM1(Nose,Mouth);F1 输入尺寸为 39*39,输出 5 个关键点的坐标;EN1 输入尺寸为 39*31,输出是 3 个关键点的坐标;NM11 输入尺寸为 39*31,输出是 3 个关键点。Level-1 的输出是由三个 CNN 输出取平均得到。

    Level-2,由 10 个 CNN 构成,输入尺寸均为 15*15,每两个组成一对,一对 CNN 对一个关键点进行预测,预测结果同样是采取平均。Level-3 与 Level-2 一样,由 10 个 CNN 构成,输入尺寸均为 15*15,每两个组成一对。Level-2 和 Level-3 是对 Level-1 得到的粗定位进行微调,得到精细的关键点定位。

    Level-1 之所以比 Level-2 和 Level-3 的输入要大,是因为作者认为,由于人脸检测器的原因,边界框的相对位置可能会在大范围内变化,再加上面部姿态的变化,最终导致输入图像的多样性,因此在 Level-1 应该需要有足够大的输入尺寸。Level-1 与 Level-2 和 Level-3 还有一点不同之处在于,Level-1 采用的是局部权值共享(Lcally Sharing Weights),作者认为传统的全局权值共享是考虑到,某一特征可能在图像中任何位置出现,所以采用全局权值共享。然而,对于类似人脸这样具有固定空间结构的图像而言,全局权值共享就不奏效了。因为眼睛就是在上面,鼻子就是在中间,嘴巴就是在下面的。所以作者借鉴文献 [28] 中的思想,采用局部权值共享,作者通过实验证明了局部权值共享给网络带来性能提升。

    DCNN 采用级联回归的思想,从粗到精的逐步得到精确的关键点位置,不仅设计了三级级联的卷积神经网络,还引入局部权值共享机制,从而提升网络的定位性能。最终在数据集 BioID 和 LFPW 上均获得当时最优结果。速度方面,采用 3.3GHz 的 CPU,每 0.12 秒检测一张图片的 5 个关键点。

    1.2.2 Extensive Facial Landmark Localization with Coarse-to-fine Convolutional Network Cascade

    2013 年,Face++在 DCNN 模型上进行改进,提出从粗到精的人脸关键点检测算法,实现了 68 个人脸关键点的高精度定位。该算法将人脸关键点分为内部关键点和轮廓关键点,内部关键点包含眉毛、眼睛、鼻子、嘴巴共计 51 个关键点,轮廓关键点包含 17 个关键点。

    针对内部关键点和外部关键点,该算法并行的采用两个级联的 CNN 进行关键点检测,网络结构如图所示。

    针对内部 51 个关键点,采用四个层级的级联网络进行检测。其中,Level-1 主要作用是获得面部器官的边界框;Level-2 的输出是 51 个关键点预测位置,这里起到一个粗定位作用,目的是为了给 Level-3 进行初始化;Level-3 会依据不同器官进行从粗到精的定位;Level-4 的输入是将 Level-3 的输出进行一定的旋转,最终将 51 个关键点的位置进行输出。针对外部 17 个关键点,仅采用两个层级的级联网络进行检测。Level-1 与内部关键点检测的作用一样,主要是获得轮廓的 bounding box;Level-2 直接预测 17 个关键点,没有从粗到精定位的过程,因为轮廓关键点的区域较大,若加上 Level-3 和 Level-4,会比较耗时间。最终面部 68 个关键点由两个级联 CNN 的输出进行叠加得到。

    算法主要创新点由以下三点:(1)把人脸的关键点定位问题,划分为内部关键点和轮廓关键点分开预测,有效的避免了 loss 不均衡问题;(2)在内部关键点检测部分,并未像 DCNN 那样每个关键点采用两个 CNN 进行预测,而是每个器官采用一个 CNN 进行预测,从而减少计算量;(3)相比于 DCNN,没有直接采用人脸检测器返回的结果作为输入,而是增加一个边界框检测层(Level-1),可以大大提高关键点粗定位网络的精度。

    Face++版 DCNN 首次利用卷积神经网络进行 68 个人脸关键点检测,针对以往人脸关键点检测受人脸检测器影响的问题,作者设计 Level-1 卷积神经网络进一步提取人脸边界框,为人脸关键点检测获得更为准确的人脸位置信息,最终在当年 300-W 挑战赛上获得领先成绩

    1.2.3 TCDCN-Facial Landmark Detection by Deep Multi-task Learning

    优点是快和多任务,不仅使用简单的端到端的人脸关键点检测方法,而且能够做到去分辨人脸的喜悦、悲伤、愤怒等分类标签属性,这样跟文章的标题或者说是文章的主题贴合——多任务。

    我们可以从下图看到,缺点容易漂移,也就是对于人脸关键点的检测上面,并不能做到很好的精度或者很高的精度,因此有待进一步修改网络的雏形。另外一点是对于人脸关键点的检测上,检测关键点小,如果增加其人脸关键点的检测,或降低精度,这是神经网络模型的通病。

    1.2.4 Joint Face Detection and Alignment using Multi-task Cascaded Convolutional Networks

    2016 年,Zhang 等人提出一种多任务级联卷积神经网络(MTCNN, Multi-task Cascaded Convolutional Networks)[9] 用以同时处理人脸检测和人脸关键点定位问题。作者认为人脸检测和人脸关键点检测两个任务之间往往存在着潜在的联系,然而大多数方法都未将两个任务有效的结合起来,本文为了充分利用两任务之间潜在的联系,提出一种多任务级联的人脸检测框架,将人脸检测和人脸关键点检测同时进行。

    MTCNN 包含三个级联的多任务卷积神经网络,分别是 Proposal Network (P-Net)、Refine Network (R-Net)、Output Network (O-Net),每个多任务卷积神经网络均有三个学习任务,分别是人脸分类、边框回归和关键点定位。网络结构如图所示:

    MTCNN 实现人脸检测和关键点定位分为三个阶段。首先由 P-Net 获得了人脸区域的候选窗口和边界框的回归向量,并用该边界框做回归,对候选窗口进行校准,然后通过非极大值抑制(NMS)来合并高度重叠的候选框。然后将 P-Net 得出的候选框作为输入,输入到 R-Net,R-Net 同样通过边界框回归和 NMS 来去掉那些 false-positive 区域,得到更为准确的候选框;最后,利用 O-Net 输出 5 个关键点的位置。

    为了提升网络性能,需要挑选出困难样本(Hard Sample),传统方法是通过研究训练好的模型进行挑选,而本文提出一种能在训练过程中进行挑选困难的在线挑选方法。方法为,在 mini-batch 中,对每个样本的损失进行排序,挑选前 70% 较大的损失对应的样本作为困难样本,同时在反向传播时,忽略那 30% 的样本,因为那 30% 样本对更新作用不大。

    实验结果表明,MTCNN 在人脸检测数据集 FDDB 和 WIDER FACE 以及人脸关键点定位数据集 LFPW 均获得当时最佳成绩。在运行时间方面,采用 2.60GHz 的 CPU 可以达到 16fps,采用 Nvidia Titan Black 可达 99fps。

    1.2.5 TCNN(Tweaked Convolutional Neural Networks)

    2016 年,Wu 等人研究了 CNN 在人脸关键点定位任务中到底学习到的是什么样的特征,在采用 GMM(Gaussian Mixture Model, 混合高斯模型)对不同层的特征进行聚类分析,发现网络进行的是层次的,由粗到精的特征定位,越深层提取到的特征越能反应出人脸关键点的位置。针对这一发现,提出了 TCNN(Tweaked Convolutional Neural Networks)[8],其网络结构如图所示

    上图为 Vanilla CNN,针对 FC5 得到的特征进行 K 个类别聚类,将训练图像按照所分类别进行划分,用以训练所对应的 FC6K。测试时,图片首先经过 Vanilla CNN 提取特征,即 FC5 的输出。将 FC5 输出的特征与 K 个聚类中心进行比较,将 FC5 输出的特征划分至相应的类别中,然后选择与之相应的 FC6 进行连接,最终得到输出。

    作者通过对 Vanilla CNN 中间层特征聚类分析得出的结论是什么呢?又是如何通过中间层聚类分析得出灵感从而设计 TCNN 呢?

    作者对 Vanilla CNN 中间各层特征进行聚类分析,并统计出关键点在各层之间的变化程度。越深层提取到的特征越紧密,因此越深层提取到的特征越能反应出人脸关键点的位置。作者在采用 K=64 时,对所划分簇的样本进行平均后绘图如下

    从图上可发现,每一个簇的样本反应了头部的某种姿态,甚至出现了表情和性别的差异。因此可推知,人脸关键点的位置常常和人脸的属性相关联。因此为了得到更准确的关键点定位,作者使用具有相似特征的图片训练对应的回归器,最终在人脸关键点检测数据集 AFLW,AFW 和 300W 上均获得当时最佳效果。

    1.2.6 DAN(Deep Alignment Networks)

    2017 年,Kowalski 等人提出一种新的级联深度神经网络——DAN(Deep Alignment Network)[10],以往级联神经网络输入的是图像的某一部分,与以往不同,DAN 各阶段网络的输入均为整张图片。当网络均采用整张图片作为输入时,DAN 可以有效的克服头部姿态以及初始化带来的问题,从而得到更好的检测效果。之所以 DAN 能将整张图片作为输入,是因为其加入了关键点热图(Landmark Heatmaps),关键点热图的使用是本文的主要创新点。DAN 基本框架如图所示:

    DAN 包含多个阶段,每一个阶段含三个输入和一个输出,输入分别是被矫正过的图片关键点热图由全连接层生成的特征图,输出是面部形状(Face Shape)。其中,CONNECTION LAYER 的作用是将本阶段得输出进行一系列变换,生成下一阶段所需要的三个输入,具体操作如下图所示:

    从第一阶段开始讲起,第一阶段的输入仅有原始图片和 S0。面部关键点的初始化即为 S0,S0 是由所有关键点取平均得到,第一阶段输出 S1。对于第二阶段,首先,S1 经第一阶段的 CONNECTION LAYERS 进行转换,分别得到转换后图片 T2(I)S1 所对应的热图 H2第一阶段 fc1 层输出,这三个正是第二阶段的输入。如此周而复始,直到最后一个阶段输出 SN。文中给出在数据集 IBUG 上,经过第一阶段后的 T2(I)、T2(S1)和特征图,如图所示:

     

    从图中发现,DAN 要做的「变换」,就是把图片给矫正了,第一行数据尤为明显,那么 DAN 对姿态变换具有很好的适应能力,或许就得益于这个「变换」。至于 DAN 采用何种「变换」,需要到代码中具体探究。

    接下来看一看,St 是如何由 St-1 以及该阶段 CNN 得到,先看 St 计算公式:

    其中是由 CNN 输出的,各阶段 CNN 网络结构如图所示:

    该 CNN 的输入均是经过了「变换」——的操作,因此得到的偏移量是在新特征空间下的偏移量,在经过偏移之后应经过一个反变换还原到原始空间。而这里提到的新特征空间,或许是将图像进行了「矫正」,使得网络更好的处理图像。

    关键点热度图的计算就是一个中心衰减,关键点处值最大,越远则值越小,公式如下:

    总而言之,DAN 是一个级联思想的关键点检测方法,通过引入关键点热图作为补充,DAN 可以从整张图片进行提取特征,从而获得更为精确的定位。

    1.2.7 Robust Facial Landmark Detection via a Fully-Convolutional Local-Global Context Network

    网络的设计思路特别,非常特别。下面在CVPR中的一篇论文Look at Boundary: A Boundary-Aware Face Alignment Algorithm具有相同的思路,都是利用heatmap作为引导进行神经网络的监督信号进行引导。不过这里需要preprocess进行热图的产生,即根据标记的数据和高斯模型产生heatmap,然后利用heatmap作为引导进行卷积神经网络的监督学习去逼近人脸关键点

    问题是根据产生的人脸关键点是无差的,只是根据函数的映射关系找到相对应的标记点,缺点是人脸关键点之间并不具有可区别性,也就是不知道哪里是眼睛,哪里是嘴巴,哪里是轮廓,与分层(或者说是级联)的神经网络具有本质性的差异。虽然说设计思路或者概念具有本质性的差异,但是并不妨碍这种方法具有很高的精度。

    1.2.8 Supervision-by-Registration: An Unsupervised Approach to Improve the Precision of Facial Landmark Detectors

    这篇文章一开始看标题觉得非常有意思,通过非监督学习去提升人脸检测关键点的精确率,非监督学习?just kidding?后来细看了一下,确实有意思的是使用CNN模型去检测人脸关键点位置,然后使用光流(flow tracking)跟踪视频流的下一帧图片人脸关键点位置作为融合信息给CNN检测器去作为人脸关键点的辅助信息。

    最后对于连续视频帧的检测效果如下所示:

    1.2.9 2018 最新研究

    ICCV在下一次举行时间为19年,上一次举行时间为17年,因此不太具有参考性。CVPR在2018年刚举行完毕,因此参考CVPR大量的文献,其中关于人脸的有56篇,32篇与2D人脸图像相关,其中与人脸关键点检测相关的只有6篇。因此在这里做简单的文献阅读综述。

    CVPR: Beyond Trade-off: Accelerate FCN-based Face Detector with Higher Accuracy

    参考FCN和SSD网络模型,利用多层FCN特征层对大小不一的人脸进行检测,并且添加有SSCU算法层(基本原理是基于NMS非极大值抑制算法)作为FCN人脸热图判断,然后利用不同层检测的结果进行合并,对于背景中小人脸有较好的检测效果。文中并没有给出时间效率,并且SAP层的实现具有一定难度。

    CVPR:Direct Shape Regression Networks for End-to-End Face Alignment

    使用Doubly convolutional neural networks提出的卷积层进行卷积,然后将得到的特征图经过该论文提出的傅里叶池化层,损失函数使用normalized mean error。对比TCDCN精度有所提高,但是并没有给出时间效率。特点是简单易懂,实现成本客观。

    Real-Time Rotation-Invariant Face Detection with Progressive Calibration Networks

    旋转人脸检测,利用自定义的PCN网络模型,每一次检测旋转角度、boundingbox、分类得分进行判断,通过级联的方式对boundingbox和分类进行过滤,旋转角度作为下一层的输入信息进行累加,从粗到精的一个过程,该文章之前已经见过一次在neurcomputting2017上发表的。

    CVPR:Look at Boundary: A Boundary-Aware Face Alignment Algorithm

    直接对关键点进行预测会存在过拟合问题,特别是对一些具有具有扭曲表情,夸张表情,遮挡表情的人脸关键点检测使用现有的算法是非常不准确的。为了更好地解决这个问题,这篇文章提出了使用boundary边界的检测方法融合人脸关键点检测方法,使得人脸关键点检测更加精准(特殊情况下),在论文实验部分给出的精确度进步并不是很大。

    对于网络模型是把人脸关键点转换成为连接的人脸边界,然后网络同时训练关键点和人脸边界(注意是有顺序的),然后把人脸边界获得Heatmaps信息往下传递给人脸关键点检测作为FMF层融合参考信息,最终使得人脸关键点被控制在人脸边界heatmap上,从原理上可以看出对于关键点漂移的情况会减少,更好地拟合人间关键点。

    CVPR:A Prior-Less Method for Multi-Face Tracking in Unconstrained Videos

    为了解决在视频流中人脸变化复杂,黑暗情况变化差异大的问题,文章并没有直接使用一个大统一的模型去解决这个问题,而是结合三个模块分别使用不同的数学模型去检测人脸。对于第一阶段使用一个Co-occurentce model对人体姿态和人脸进行估计,因为人体基本姿态和人脸是相互作用的关系,特别是对于连续帧来说,因此使用Co-occurentce模型作为第一阶段;第二阶段使用VGG特征作为强关联信息进行聚类提取人脸特征出来;最后阶段使用高斯处理模型对深度特征进行补充和提取精细化的结果。因为设计到的数学理论和模型较多,因此如果想要细节理解需要阅读原论文。

    Disentangling 3D Pose in A Dendritic CNNfor Unconstrained 2D Face Alignment

    提出一个Pose Conditioned Dendritic Convolution Neu-ral Network (PCD-CNN)网络。这个网络从原理来看效果不会太好,会存在漂移问题,因为根据heatmap会存在较粗的定位,但是最后(b)阶段引入了Proposed dendritic structure进行弥补,可是这种弥补方式是根据数学公式进行结构特征点计算的方式能否真正地减少误差读者抱着一定的疑惑,如果是基于检索的方式或者搜图的方式可能会效果更好。

    Style Aggregated Network for Facial Landmark Detection

    这篇文章主要聚焦在对于不同style的图像中,人脸关键点的检测效果是不一样的,为了更好地统一检测效果或者说为了更好地更精确的提高检测效果,因此可以对样本进行一个风格聚合网络Style Aggregated Network,通过该网络模型可以产生各种不同风格的图片,然后利用这些图片融合信息一起送入CNN网络中进行检测。如下图所示,根据上面的描述,这里使用到了两个网络模型,一个是GAN网络模型,一个是CNN网络模型。

    上面的style-aggregated face generation module就是使用GAN模型,具体下面的图会给出更加详细的解析。下面的facial landmark predicction module就是使用CNN网络模型。最后输出heatmap热图的位置点,通过高斯分布获得最佳预测位置。

    文章中使用不同的风格style主要是3哥,一个是草图sketch,一个是强光下light,另外一个是gray灰度图,合成一个[12, width, hegih]的矩阵作为网络模型的输入,增加了算法的抗敏性。

    总结

    深度学习技术的出现,有效促进了不同尺度和不同任务信息之间的融合,使得信息的结合方式由平面开始向立体方法发展,对于人脸关键点提取模型的发展,具有突出的实际意义。正因为如此,本文对目前人脸关键点检测任务中常用的深度学习方法进行综述。

    尽管深度学习方法在人脸关键点检测任务上已经获得了长足的发展,算法性能不断提升,与实际应用的要求也越来越接近。然而,该任务的研究还远未结束,目前还有一些关键性难题亟待解决。在此总结三点:

    (1)输入样本多样性问题:多变的人脸姿态和遮挡。姿态和遮挡是人脸关键点检测所面临的经典难题,近年来也出现了很多处理这两方面问题的思路和方法,但目前在实际应用中,尤其在实时低图像质量条件下,对于这两类难题的处理还难以令人满意. 尤其当姿态发生快速和剧烈改变,以及遮挡比例较大时,现有方法的精度距离实际应用的要求还有较大差距,需要进一步研究和提升。

    (2)大场景小样本问题:在图像中非常多的小人脸。小人脸检测是人脸关键点检测所面临的经典难题,近年来出现了很多论文去解决小人脸检测,但是对于小人脸的关键点检测的思路和方法并不常见。主要受制于一个很严重的问题就是图像的质量不够高,小图像中的单张人脸图像质量低,一个像素包含的内容非常少,图片模糊。因此对于这类型的人脸关键点检测依然是个非常困难的话题;另外一个主要原因是受制于前一个步骤——小场景的人脸检测准确率低的问题,既然人脸检测的准确率低,那么在基于人脸检测下的人脸关键点检测问题会更加凸显和严重。

    (3)方法众多但是百变不离其中:深度学习的方法百花齐放,但是在众多的方法当中没有一个非常outstanding的论文能够统一所有的方法,或则凸显出来。因为在深度学习当中,其主要思路是利用神经网络的非线性映射关系在标注的数据和输入的数据之间找到一个最优映射路径,通过这个映射关系去逼近我们的目标,但是这种方法的缺点是肯定会存在过拟合的问题。究其原因是人脸相似度很高(即使说是千人千脸,但是对比起其他动物的脸,其他物体和人脸对比其人脸与人脸之间的相识度依然非常高),在高相似度中间找到不同人脸之间的细微差别是非常困难的,因此出现了很多方法去解决这个问题,但是各论文分别去解决各自所在细分领域或者细分问题上,并没有一个很好的框架或者算法去解决这问题。

    本文针对近年人脸关键点检测方法中的深度学习方法进行了综述研究。本文对人脸关键点检测任务进行了详细描述,并将具有代表性的深度学习网络模型,从模型设计思路到模型框架均进行较为深入的探究。在所面临的挑战性问题和开展相关研究所需的基础知识方面,本文亦抛砖引玉,希望本文能对相关科研人员了解人脸关键点检测问题并开展相关研究起到微薄的作用。

    展开全文
  • 人脸关键点数据集整理

    千次阅读 2021-11-14 23:13:23
    1. 什么是关键点检测? 人脸关键点检测是指给定人脸图像,定位出人脸面部的关键点,包括眉毛、眼睛、鼻子、嘴巴、脸部轮廓区域的点,由于受到姿态和遮挡等因素的影响,人脸关键点检测是一个富有挑战性的任务。 人脸...

    1. 什么是关键点检测?

    人脸关键点检测是指给定人脸图像,定位出人脸面部的关键点,包括眉毛、眼睛、鼻子、嘴巴、脸部轮廓区域的点,由于受到姿态和遮挡等因素的影响,人脸关键点检测是一个富有挑战性的任务。

    人脸关键点是人脸各个部位的重要特征点,通常是轮廓点与角点,下图是96个面部关键点检测结果。
    在这里插入图片描述
    其中点代表位置,数字代表序号。人脸关键点可以有以下主要应用:

    • 人脸姿态对齐,人脸识别等算法都需要对人脸的姿态进行对齐从而提高模型的精度。
    • 人脸美颜与编辑,基于关键点可以精确分析脸型、眼睛形状、鼻子形状等,从而对人脸的特定位置进行修饰加工,实现人脸的特效美颜,贴片等娱乐功能,也能辅助一些人脸编辑算法更好地发挥作用。
    • 人脸表情分析,基于关键点可以对人的面部表情进行分析,从而用于互动娱乐,行为预测等场景。

    2. 人脸关键点数据集

    关键点能够反映各个部位的脸部特征,随着技术的发展和对精度要求的增加,人脸关键点的数量经历了从最初的5个点到如今超过200个点的发展历程,在人脸等算法上拥有领先技术优势的商汤科技先后定出过106个关键点等行业标准。

    2.1 5点标注

    人脸面部最关键的有5个点,分别为左右两个嘴角,两个眼的中心,鼻子,这5个关键点属于人脸内部关键点,根据它们就可以计算出人脸的姿态。当然早期也有标注4个点以及6个点的方案。 2005年发布的 FRGC-V2(Face Recognition Grand Challenge Version2.0)中标注了双眼、鼻子、嘴巴、下巴共5个关键点。 2007年发布的Caltech 10000 Web Faces数据集中标注了双眼、鼻子和嘴巴共4个关键点。 2013年的AFW数据集中标注了双眼、鼻子、嘴唇共6个关键点,其中嘴唇有3个点。 2014年发布的MTFL/MAFL数据集中标注了双眼、鼻子和2个嘴角共5个关键点。
    在这里插入图片描述

    2.2 68点标注

    68点标注是现今最通用的一种标注方案,早期在1999年的Xm2vtsdb数据集中就被提出,300W数据集和XM2VTS等数据集也都采用了68个关键点的方案,被OpenCV中的Dlib算法中所采用。 68个关键点的标注也有一些不同的版本,这里我们介绍最通用的Dlib中的版本,它将人脸关键点分为内部关键点和轮廓关键点,内部关键点包含眉毛、眼睛、鼻子、嘴巴共计51个关键点,轮廓关键点包含17个关键点。
    在这里插入图片描述
    在这里插入图片描述

    Dlib所采用的68个人脸关键点标注可以看上图,单边眉毛有5个关键点,从左边界到右边界均匀采样,共5×2=10个。 眼睛分为6个关键点,分别是左右边界,上下眼睑均匀采样,共6×2=12个。 嘴唇分为20个关键点,除了嘴角的2个,分为上下嘴唇。上下嘴唇的外边界,各自均匀采样5个点,上下嘴唇的内边界,各自均匀采样3个点,共20个。 鼻子的标注增加了鼻梁部分4个关键点,而鼻尖部分则均匀采集5个,共9个关键点。 脸部轮廓均匀采样了17个关键点。 如果把额头部分也加上去,就可以得到更多,比如81个关键点。

    2.3 96/98点标注

    公开的数据集比较少超过68个关键点,其中比较有名的是Wider Facial Landmark in the Wild(WFLW),它提供了98个关键点。 WFLW 包含了 10000 张脸,其中 7500 用于训练,2500 张用于测试。除了关键点之外,还有遮挡、姿态、妆容、光照、模糊和表情等信息的标注。
    在这里插入图片描述
    在这里插入图片描述

    2.4 106与186点标注

    106个关键点标注是商汤科技提出的在业内被广泛采用的方案,包括Face++等企业开放的API多采用这个标注方式,具体信息如下:

    在这里插入图片描述
    在这里插入图片描述
    外轮廓,33个均匀采样点,比Dlib更加密集。 嘴巴20个关键点,与Dlib标注的相同。 鼻子15个关键点,与Dlib相比增加了两侧鼻梁部位。 眼睛20个关键点,每只眼睛轮廓点共8个,眼球中心点2个。 眉毛18个关键点,区分了上眉毛边界。 后续又在106个关键点的基础上提出了更加稠密的186个关键点,如今各个开发团队使用的点数可能会有差异,比如百度使用过72和150个点的方案。 除了以上这些常用的方案,还有很多的数据集也有自己的标注标准,比如BioID Face Dataset包含20个关键点,BUHMAP-DB包含52个关键点,MUCT包含76个关键点,PUT大部分图像包含30个关键点,其中正面人脸包括了194个点。

    3. 数据集下载

    数据集介绍链接
    300W共600张图片(300室内,300室外),68关键点https://ibug.doc.ic.ac.uk/resources/300-W/
    XM2VTS2360张正面图,68关键点http://www.ee.surrey.ac.uk/CVSSP/xm2vtsdb/
    LFPW1432张图片,29关键点https://neerajkumar.org/projects/face-parts/
    HELEN2000张训练集,330张测试集http://www.ifp.illinois.edu/~vuongle2/helen/
    IBUG135张图片https://ibug.doc.ic.ac.uk/resources/facial-point-annotations/
    XM2VTS2360张正面图,68关键点http://www.ee.surrey.ac.uk/CVSSP/xm2vtsdb/
    AFLW20000张训练,4386张测试
    FRGC V250000张训练,4003张测试
    ICME 人脸106关键点检测比赛官方数据集106个点https://facial-landmarks-localization-challenge.github.io/#index https://sites.google.com/view/hailin-shi
    CelebA(2015)5个点,10177个人,共202599幅人脸图像
    WFLW(ECCV2018)98个点,7500训练集,2500测试集
    MTFL/MAFL(2014)68个点,MTFL数据集包含了12995 张脸,5个关键点标注;(MAFL) 数据集则包含了20000张脸,5个关键点标注与40个面部属性
    SCUT-FBP(2017)86个点,2000亚洲女性,2000亚洲男性,750高加索男性,750高加索女性

    4. 人脸关键点难点

    人脸关键点是人脸相关问题的基础,作为基础模块,人脸关键点检测对速度很敏感,否则将会影响系统整体的效率,所以对人脸关键点检测的要求是,又准又快。
    人脸关键点目前存在的问题(不能又准又快)的原因如下:

    • 局部变化:表情、局部特殊光照、部分遮挡,导致一部分关键点偏离了正常的位置,或者不可见了;
    • 全局变化:人脸姿态、成像质量;
    • 数据不均衡:在人脸数据里面,数据不均衡体现在,大部分是正脸数据,侧脸很少,所以对侧脸、大角度的人脸不太准;
    • 模型效率:在 CNN 的解决方案中,模型效率主要由 backbone 网络决定。

    在实际使用中,人脸关键点的问题主要有两个:

    • 对“点是否遮挡”判断是否准确
    • 对大角度人脸(±60 度以内)点位置预测的准确性和稳定性
    展开全文
  • PCL点云处理与关键点提取

    千次阅读 2022-04-04 09:43:09
    关键点简介 关键点也称为兴趣点,它是 2D 图像或 3D 点云或曲面模型上,可以通过检测标准来获取的具有稳定性、区别性的点集。从技术上来说,关键点的数量比原始点云或图像的数据量少很多,其与局部特征描述子结合组成...
  • 2D Pose人体关键点实时检测(Python/Android /C++ Demo)

    万次阅读 多人点赞 2021-04-16 16:25:15
    一般情况下可以将人体关键点检测细分为单人/多人关键点检测、2D/3D关键点检测,同时有算法在完成关键点检测之后还会进行关键点的跟踪,也被称为人体姿态跟踪。 本博客提供2D Pose的Python代码,以及C++版本的推理...
  • PCL中点云关键点提取

    千次阅读 2021-10-04 00:51:56
    PCL中点云关键点提取1 关键点概念及相关算法1.1 NARF关键点1.2 Harris关键点1.3 PCL中keypoints模块及类介绍2 关键点入门级实例解析2.1 如何从深度图像中提取NARF关键点2.2 SIFT关键点提取2.3 Harris关键点提取 ...
  • 68点人脸关键点定位

    千次阅读 2021-06-29 19:11:27
    人脸对齐/人脸关键点基本概念 根据输入的人脸图像,自动定位出面部关键特征点,如眼睛,鼻尖,嘴角点,眉毛以及人脸各部件轮廓点等 想要表示形状信息时,使用的是点的集合(向量) 2D人脸:5 / 21 / 29 / 68 / 96 ...
  • 人脸关键点检测

    千次阅读 2022-04-05 15:26:40
    1、dlib5 左右眼角及人中5。 import dlib import numpy as np import cv2 import time predictor_path = '../model/shape_predictor_5_face_landmarks.dat' detector = dlib.get_frontal_face_dete.
  • 关键点定位︱四款人体姿势关键点估计论文笔记

    万次阅读 多人点赞 2018-03-26 22:23:15
    该作者在比赛数据上当时迭代了60W次,最终的得分为:0.36,而原来的coco数据集,多人关键点定位需要180W次。 笔者自己fork的几个项目,只玩了两个: mattzheng/tf-pose-estimation-applied mattzheng/Alpha...
  • 人脸关键点 人脸关键点检测是人脸识别和分析领域中的关键一步,它是诸如自动人脸识别、表情分析、三维人脸重建及三维动画等其它人脸相关问题的前提和突破口。近些年来,深度学习方法由于其自动学习及持续学习能力,...
  • dilb人脸识别+68关键点检测

    千次阅读 2022-04-01 10:22:05
    _name__ == '__main__': main() 人脸识别+关键点标记68 Python+OpenCV+dlib实现人脸68个关键点检测并标注 ''' 人脸关键点检测——dlib 1 dlib.get_frontal_face_detector() 获取人脸检测器 2 dlib.shape_predictor()...
  • Mediapipe三维实时人体关键点检测与追踪1.Mediapipe简介2.Mediapipe姿态检测器3.Mediapipe图像姿态检测4.Mediapipe摄像头实时姿态检测 1.Mediapipe简介 Google出了一个开源的、跨平台的、可定制化的机器学习解决方案...
  • 理解COCO数据集中的关键点标注

    千次阅读 多人点赞 2020-12-04 11:03:45
    一、COCO中目标关键点的标注格式 打开person_keypoints_val2017.json文件。 从头至尾按照顺序分为以下段落,看起来和目标检测差不多。 1、info 类型,没什么好讲的,就是一些信息记录,如下 2、licenses、包含多个...
  • 人脸关键点检测之PFLD

    千次阅读 2020-11-22 23:07:39
    人脸关键点检测也称为人脸对齐,目的是自动定位一组预定义的人脸基准点(比如眼角点、嘴角点)。作为一系列人脸应用的基础,如人脸识别和验证,以及脸部变形和人脸编辑。这个问题一直以来都受到视觉界的高度关注,在...
  • 关键点检测方法综述

    千次阅读 2019-05-21 23:44:01
    ,形状包含了关键点的位置信息,而这个位置信息一般可以用两种形式表示,第一种是关键点的位置相对于整张图像,第二种是关键点的位置相对于人脸框(标识出人脸在整个图像中的位置)。我们把第一种形状称作绝对形状,它...
  • 关键点模型算法---服装(Keypoints Detection)

    千次阅读 热门讨论 2021-03-03 15:32:59
    关键点模型算法: 简介:服装关键点检测算法的基本思路是:输入服装图片,经过网络,输出关键点集合。 首先:思考 思考,对衣服(物体)做了精确的标记,我们是不是就可以通过画线准确分割出衣服(物体)了呢? ...
  • 热力图就是在关键点周围生成一个高斯圆,整个圆都可以看作是当前的关键点,但是整个圆是有一个概率值在里面,最中心的概率值最大,对应的热力图也越红,越往外概率越小。这种方法相对直接回归来说正样本更多,学起来...
  • 人体骨骼关键点检测综述

    万次阅读 多人点赞 2018-06-11 12:53:45
    其它机器学习、深度学习算法的全面系统讲解可以阅读《机器学习-原理、算法与应用》,清华大学出版社,雷明著,由SIGAI公众号作者倾力打造。...近年来,随着深度学习技术的发展,人体骨骼关键点检测效果不断...
  • 使用Albumentations 对关键点 做增强

    万次阅读 2021-05-20 16:05:34
    您可以对具有关键点的图像使用任何像素级增强,因为像素级增强不会影响关键点。 注意:默认情况下,与关键点配合使用的增强功能不会在转换后更改关键点的标签。 如果要点的标签是特定于侧面的,则可能会引
  • 98点人脸关键点检测算法

    千次阅读 2021-01-04 18:57:02
    使用98关键点的WFLW数据集. 这是由商汤提供的。里面除了关键点外,还对姿态、表情、照度、化妆、遮挡、模糊等信息进行标注。 数据集包含7,500训练(list_98pt_rect_attr_train.txt),2,500测试(list_98pt_rect_...
  • 关键点检测的总结

    万次阅读 2019-05-23 10:38:35
    但是,该比赛的关键点只有:14个(参考:赛题与数据),该作者在生成时候(ai2coco_art_neckhead_json.py),拼凑成17个点,与coco一致,然后就可以完全使用coco框架训练(多人模式),同时共享pairwise stat。...
  • kpt、mesh代表关键点检测或网格检测。 sview、mview代表单人姿态估计(Single Person Pose Estimation, SPPE)和多人姿态估计(Multi-person Pose Estimation, MPPE)。单人姿态估计是基础,目的是要从只包含一...
  • 人脸关键点配准的二三事

    千次阅读 2020-04-16 13:54:34
    人脸关键点检测调研 一、现状 人脸关键点检测是人脸识别和分析领域中的关键一步,它是诸如自动人脸识别、表情分析、三维人脸重建及三维动画等其它人脸相关问题的前提和突破口。但实际应用中人脸的不同尺寸,姿态,...
  • 人体关键点姿态识别

    万次阅读 多人点赞 2020-10-27 18:06:54
    通过检测人体行为表达过程中,每一帧人体姿态关键部位的位置,将人体姿态简化为人体关键点,并通过这些关键点对人体姿态表达的语义进行分类识别。 基于关键点的人体姿态识别可分为两个方面,即静态的人体姿态识别与...
  • 参考: ... ... 1. 导言 人体骨骼关键点对于描述人体姿态,预测人体行为至关重要。因此人体骨骼关键点检测是诸多计算机视觉任务的基础,例如动作分类,异常行为...
  • 关键点检测任务可以看成是一个标准的回归任务。具体来说目前主流的关键点回归就两种做法: 方法一:用全连接层直接回归坐标点,比如k个点同时检测,那就是2k个输出神经元。Human keypoints任务最开始应用CNN就是...
  • 开源人脸106关键点

    千次阅读 2020-01-10 18:56:11
    开源人脸106关键点 https://github.com/zeusees/HyperLandmarkgithub地址,高精度https://github.com/lsy17096535/face-landmarkgithub地址,低精度 HyperLandmark-开源人脸106点关键点检测SDK ...
  • 人体骨骼关键点检测的算法

    千次阅读 2021-10-09 20:52:41
    优点:它被遮挡部分的关键点不会任意获取(即可以只显示看得到的部分) 它的准确率、AP值要比openpose高。 缺点: 随着图片上的人数增加,他的计算量增大,速度变慢。 可实时全身多人姿势估计与跟踪系统 ...
  • Maskrcnn-benchmark利用自己的数据进行目标检测和关键点检测介绍0 环境配置RequirementsInstallation1 目标检测任务1.1 数据准备1.1.1 coco数据集的介绍1.1.2 转coco数据1.1.3 数据检验1.2 数据集在源代码文件中的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,595,756
精华内容 638,302
关键字:

关键点

友情链接: vbdrawfull.rar