PCL——3.滤波

本文详细介绍了点云数据处理的多种滤波技术,包括直通滤波器、体素滤波器、统计离群点去除、参数化模型投影、点云索引提取、基于半径的离群点去除、任意多边形内点云提取以及双边滤波。这些方法在减少数据冗余、去除噪声、提取关键特征等方面具有重要作用,广泛应用于3D点云的预处理和分析。
摘要由CSDN通过智能技术生成

1 直通滤波器

适用场景:
对于在空间分布有一定空间特征的点云数据,比如使用线结构光扫描的方式采集点云,沿z向分布较广,但x,y向的分布处于有限范围内。此时可使用直通滤波器,确定点云在x或y方向上的范围,可较快剪除离群点,达到第一步粗处理的目的。

#include <iostream>
#include <pcl/point_types.h>
#include <pcl/filters/passthrough.h>

int
 main (int argc, char** argv)
{
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>);

  //生成并填充点云
  cloud->width  = 5;
  cloud->height = 1;
  cloud->points.resize (cloud->width * cloud->height);

  for (size_t i = 0; i < cloud->points.size (); ++i)   //填充数据
  {
    cloud->points[i].x = 1024 * rand () / (RAND_MAX + 1.0f);
    cloud->points[i].y = 1024 * rand () / (RAND_MAX + 1.0f);
    cloud->points[i].z = 1024 * rand () / (RAND_MAX + 1.0f);
  }

  std::cerr << "Cloud before filtering: " << std::endl;   //打印
  for (size_t i = 0; i < cloud->points.size (); ++i)
    std::cerr << "    " << cloud->points[i].x << " " 
                        << cloud->points[i].y << " " 
                        << cloud->points[i].z << std::endl;
  /************************************************************************************
   创建直通滤波器的对象,设立参数,滤波字段名被设置为Z轴方向,可接受的范围为(0.0,1.0)
   即将点云中所有点的Z轴坐标不在该范围内的点过滤掉或保留,这里是过滤掉,由函数setFilterLimitsNegative设定
   ***********************************************************************************/
  // 设置滤波器对象
  pcl::PassThrough<pcl::PointXYZ> pass;
  pass.setInputCloud (cloud);            //设置输入点云
  pass.setFilterFieldName ("z");         //设置过滤时所需要点云类型的Z字段
  pass.setFilterLimits (0.0, 1.0);        //设置在过滤字段的范围
  //pass.setFilterLimitsNegative (true);   //设置保留范围内还是过滤掉范围内
  pass.filter (*cloud_filtered);            //执行滤波,保存过滤结果在cloud_filtered

  std::cerr << "Cloud after filtering: " << std::endl;   //打印
  for (size_t i = 0; i < cloud_filtered->points.size (); ++i)
    std::cerr << "    " << cloud_filtered->points[i].x << " " 
                        << cloud_filtered->points[i].y << " " 
                        << cloud_filtered->points[i].z << std::endl;

  return (0);
}

在这里插入图片描述

2 体素滤波器

使用体素化网格方法实现下采样,即减少点的数量减少点云数据,并同时保持点云的形状特征,在提高配准、曲面重建,形状识别等算法速度中非常实用。PCL实现的VoxelGrid类通过输入的点云数据创建一个三维体素栅格(可把体素栅格想象为微小的空间三维立方体的集合),然后在每个体素(即,三维立方体)内用体素中所有点的重心来近似显示体素中其他点,这样该体素就内所有点就用一个重心点最终表示,对于所有体素处理后得到过滤后的点云。这种方法比用体素中心来逼近的方法更慢,但它对于采样点对应曲面的表示更为准确。

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/voxel_grid.h>

intmain (int argc, char** argv)
{
  pcl::PCLPointCloud2::Ptr cloud (new pcl::PCLPointCloud2 ());  
  pcl::PCLPointCloud2::Ptr cloud_filtered (new pcl::PCLPointCloud2 ());
  
  //点云对象的读取  
  pcl::PCDReader reader;   
  reader.read ("../table_scene_lms400.pcd", *cloud);   //读取点云到cloud中
  std::cerr << "PointCloud before filtering: " << cloud->width * cloud->height        
  	<< " data points (" << pcl::getFieldsList (*cloud) << ").";
  /****************************************************************************** 
   创建一个voxel叶大小为1cm的pcl::VoxelGrid滤波器,
   **********************************************************************************/     	
   pcl::VoxelGrid<pcl::PCLPointCloud2> sor;  //创建滤波对象  
   sor.setInputCloud (cloud);            //设置需要过滤的点云给滤波对象  
   sor.setLeafSize (0.01f, 0.01f, 0.01f);  //设置滤波时创建的体素体积为1cm的立方体  
   sor.filter (*cloud_filtered);           //执行滤波处理,存储输出
   
   std::cerr << "PointCloud after filtering: " << cloud_filtered->width * cloud_filtered->height        
   	<< " data points (" << pcl::getFieldsList (*cloud_filtered) << ").";
   pcl::PCDWriter writer;  
   writer.write ("../table_scene_lms400_downsampled1.pcd", *cloud_filtered,          
   	Eigen::Vector4f::Zero (), Eigen::Quaternionf::Identity (), false);
  return (0);
}

在这里插入图片描述

3 滤波器移除离群点

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/statistical_outlier_removal.h>

int
main (int argc, char** argv)
{
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>);

  // 定义读取对象
  pcl::PCDReader reader;
  // 读取点云文件
  reader.read<pcl::PointXYZ> ("../table_scene_lms400.pcd", *cloud);

  std::cerr << "Cloud before filtering: " << std::endl;
  std::cerr << *cloud << std::endl;

  // 创建滤波器,对每个点分析的临近点的个数设置为50 ,并将标准差的倍数设置为1  这意味着如果一
   //个点的距离超出了平均距离一个标准差以上,则该点被标记为离群点,并将它移除,存储起来
  pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;   //创建滤波器对象
  sor.setInputCloud (cloud);                           //设置待滤波的点云
  sor.setMeanK (50);                               //设置在进行统计时考虑查询点临近点数
  sor.setStddevMulThresh (1.0);                      //设置判断是否为离群点的阀值
  sor.filter (*cloud_filtered);                    //存储

  std::cerr << "Cloud after filtering: " << std::endl;
  std::cerr << *cloud_filtered << std::endl;

  pcl::PCDWriter writer;
  writer.write<pcl::PointXYZ> ("../table_scene_lms400_inliers1.pcd", *cloud_filtered, false);
//true:滤波结果取反,被过滤的点
  sor.setNegative (true);
  sor.filter (*cloud_filtered);
  writer.write<pcl::PointXYZ> ("../table_scene_lms400_outliers1.pcd", *cloud_filtered, false);

  return (0);
}

在这里插入图片描述

4 使用参数化模型投影点云

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/ModelCoefficients.h>             //模型系数头文件
#include <pcl/filters/project_inliers.h>          //投影滤波类头文件

int
 main (int argc, char** argv)
{
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_projected (new pcl::PointCloud<pcl::PointXYZ>);

  //创建点云并打印出来
  cloud->width  = 5;
  cloud->height = 1;
  cloud->points.resize (cloud->width * cloud->height);

  for (size_t i = 0; i < cloud->points.size (); ++i)
  {
    cloud->points[i].x = 1024 * rand () / (RAND_MAX + 1.0f);
    cloud->points[i].y = 1024 * rand () / (RAND_MAX + 1.0f);
    cloud->points[i].z = 1024 * rand () / (RAND_MAX + 1.0f);
  }

  std::cerr << "Cloud before projection: " << std::endl;
  for (size_t i = 0; i < cloud->points.size (); ++i)
    std::cerr << "    " << cloud->points[i].x << " " 
                        << cloud->points[i].y << " " 
                        << cloud->points[i].z << std::endl;

  // 填充ModelCoefficients的值,使用ax+by+cz+d=0平面模型,其中 a=b=d=0,c=1 也就是X——Y平面
  //定义模型系数对象,并填充对应的数据
  pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients ());
  coefficients->values.resize (4);
  coefficients->values[0] = coefficients->values[1] = 0;
  coefficients->values[2] = 1.0;
  coefficients->values[3] = 0;

  // 创建ProjectInliers对象,使用ModelCoefficients作为投影对象的模型参数
  pcl::ProjectInliers<pcl::PointXYZ> proj;     //创建投影滤波对象
  proj.setModelType (pcl::SACMODEL_PLANE);      //设置对象对应的投影模型
  proj.setInputCloud (cloud);                   //设置输入点云
  proj.setModelCoefficients (coefficients);       //设置模型对应的系数
  proj.filter (*cloud_projected);                 //投影结果存储cloud_projected

  std::cerr << "Cloud after projection: " << std::endl;
  for (size_t i = 0; i < cloud_projected->points.size (); ++i)
    std::cerr << "    " << cloud_projected->points[i].x << " " 
                        << cloud_projected->points[i].y << " " 
                        << cloud_projected->points[i].z << std::endl;

  return (0);
}

在这里插入图片描述

5 从一个点云中提取索引

# include <iostream>
# include <pcl/ModelCoefficients.h>
# include <pcl/io/pcd_io.h>
# include <pcl/point_types.h>
# include <pcl/sample_consensus/method_types.h>
# include <pcl/sample_consensus/model_types.h>
# include <pcl/segmentation/sac_segmentation.h>
# include <pcl/filters/voxel_grid.h>
# include <pcl/filters/extract_indices.h>  // 从一个点云中提取索引

int main(int argc, char ** argv)
{
/ ** ** ** ** ** ** ** ** ** ** **  ** ** ** ** *** ** ** ** ** ** ** ** ** ** ** ** **
从输入的.PCD 文件载入数据后,创建一个VoxelGrid滤波器对数据进行下采样,
在这里进行下采样是为了加速处理过程,越少的点意味着分割循环中处理起来越快
** ** ** ** ** ** ** ** ** * ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** /

  pcl::PCLPointCloud2::Ptr cloud_blob(new pcl::PCLPointCloud2), cloud_filtered_blob(new pcl::PCLPointCloud2); // 申明滤波前后的点云
  pcl::PointCloud < pcl::PointXYZ >::Ptr cloud_filtered(new pcl::PointCloud < pcl::PointXYZ >), cloud_p(new pcl::PointCloud < pcl::PointXYZ >), cloud_f(new pcl::PointCloud < pcl::PointXYZ >);

  // 读取PCD文件
  pcl::PCDReader reader;
  reader.read("../table_scene_lms400.pcd", *cloud_blob);
  // 统计滤波前的点云个数
  std::cerr << "PointCloud before filtering: " << cloud_blob->width * cloud_blob->height << " data points." << std::endl;
  
  // 创建体素栅格下采样: 下采样的大小为1cm
  pcl::VoxelGrid < pcl::PCLPointCloud2 > sor; // 体素栅格下采样对象
  sor.setInputCloud(cloud_blob); // 原始点云
  sor.setLeafSize(0.01f, 0.01f, 0.01f); // 设置采样体素大小
  sor.filter(*cloud_filtered_blob); // 保存
  
  // 转换为模板点云
  pcl::fromPCLPointCloud2(*cloud_filtered_blob, *cloud_filtered);
  
  std::cerr << "PointCloud after filtering: " << cloud_filtered->width * cloud_filtered->height << " data points." << std::endl;
  
  // 保存下采样后的点云
  pcl::PCDWriter writer;
  writer.write < pcl::PointXYZ > ("../table_scene_lms400_downsampled.pcd", *cloud_filtered, false);
  
  pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients());
  pcl::PointIndices::Ptr inliers(new pcl::PointIndices());
  
  pcl::SACSegmentation < pcl::PointXYZ > seg; // 创建分割对象
  seg.setOptimizeCoefficients(true); // 设置对估计模型参数进行优化处理
  seg.setModelType(pcl::SACMODEL_PLANE); // 设置分割模型类别
  seg.setMethodType(pcl::SAC_RANSAC); // 设置用哪个随机参数估计方法
  seg.setMaxIterations(1000); // 设置最大迭代次数
  seg.setDistanceThreshold(0.01); // 判断是否为模型内点的距离阀值
  
  // 设置ExtractIndices的实际参数
  pcl::ExtractIndices < pcl::PointXYZ > extract; // 创建点云提取对象
  
  int i = 0, nr_points = (int)
  cloud_filtered->points.size(); // 点云总数
  // While 30 % of the original cloud is still there
  while (cloud_filtered->points.size () > 0.3 * nr_points)
  {
    // 为了处理点云包含的多个模型,在一个循环中执行该过程并在每次模型被提取后,保存剩余的点进行迭代
    seg.setInputCloud(cloud_filtered);
    seg.segment(*inliers, *coefficients);
    if (inliers->indices.size () == 0)
    {
        std:: cerr << "Could not estimate a planar model for the given dataset." << std::endl;
    break;
    }
    
    // Extract the inliers
    extract.setInputCloud(cloud_filtered);
    extract.setIndices(inliers); //
    extract.setNegative(false);
    extract.filter(*cloud_p);
    std::cerr << "PointCloud representing the planar component: " << cloud_p->width * cloud_p->height << " data points." << std::endl;
    
    std::stringstream ss;
    ss << "../table_scene_lms400_plane_" << i << ".pcd";
    writer.write < pcl::PointXYZ > (ss.str(), *cloud_p, false);
    
    // Create the filtering object
    extract.setNegative(true);
    extract.filter(*cloud_f);
    cloud_filtered.swap(cloud_f);
    i + +;
  }
  return (0);
}

在这里插入图片描述

6 用ConditionalRemoval或RadiusOutlinerRemoval移除离群点

#include <iostream>
#include <pcl/point_types.h>
#include <pcl/filters/radius_outlier_removal.h>
#include <pcl/filters/conditional_removal.h>

int main(int argc, char **argv)
{
  if (argc != 2) //确保输入的参数
  {
    std::cerr << "please specify command line arg '-r' or '-c'" << std::endl;
    exit(0);
  }
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);

  //填充点云
  cloud->width = 5;
  cloud->height = 1;
  cloud->points.resize(cloud->width * cloud->height);

  for (size_t i = 0; i < cloud->points.size(); ++i)
  {
    cloud->points[i].x = 1024 * rand() / (RAND_MAX + 1.0f);
    cloud->points[i].y = 1024 * rand() / (RAND_MAX + 1.0f);
    cloud->points[i].z = 1024 * rand() / (RAND_MAX + 1.0f);
  }

  if (strcmp(argv[1], "-r") == 0)
  {                                                  // RadiusOutlierRemoval
    pcl::RadiusOutlierRemoval<pcl::PointXYZ> outrem; //创建滤波器

    outrem.setInputCloud(cloud);       //设置输入点云
    outrem.setRadiusSearch(0.8);       //设置半径为0.8的范围内找临近点
    outrem.setMinNeighborsInRadius(2); //设置查询点的邻域点集数小于2的删除
    // apply filter
    outrem.filter(*cloud_filtered); //执行条件滤波   在半径为0.8 在此半径内必须要有两个邻居点,此点才会保存
  }
  else if (strcmp(argv[1], "-c") == 0)
  { //RadiusOutlierRemoval
    //创建条件限定的下的滤波器
    pcl::ConditionAnd<pcl::PointXYZ>::Ptr range_cond(new pcl::ConditionAnd<pcl::PointXYZ>()); //创建条件定义对象
    //添加在Z字段上大于0的比较算子
    //GT greater than
    //EQ equal
    //LT less than
    //GE greater than or equal
    //LE less than

    //为条件定义对象添加比较算子
    range_cond->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(new pcl::FieldComparison<pcl::PointXYZ>("z", pcl::ComparisonOps::GT, 0.0))); //添加在Z字段上大于0的比较算子

    range_cond->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(new pcl::FieldComparison<pcl::PointXYZ>("z", pcl::ComparisonOps::LT, 0.8))); //添加在Z字段上小于0.8的比较算子
    // 创建滤波器并用条件定义对象初始化
    pcl::ConditionalRemoval<pcl::PointXYZ> condrem;
    condrem.setCondition(range_cond);
    condrem.setInputCloud(cloud);   //输入点云
    condrem.setKeepOrganized(true); //设置保持点云的结构
                                    // 设置是否保留滤波后删除的点,以保持点云的有序性,通过setuserFilterValue设置的值填充点云;或从点云中删除滤波后的点,从而改变其组织结构
                                    // 如果设置为true且不设置setUserFilterValue的值,则用nan填充点云
                                    //  https://blog.csdn.net/qq_37124765/article/details/82262863

    // 执行滤波
    condrem.filter(*cloud_filtered); //大于0.0小于0.8这两个条件用于建立滤波器
  }
  else
  {
    std::cerr << "please specify command line arg '-r' or '-c'" << std::endl;
    exit(0);
  }
  std::cerr << "Cloud before filtering: " << std::endl;
  for (size_t i = 0; i < cloud->points.size(); ++i)
    std::cerr << "    " << cloud->points[i].x << " "
              << cloud->points[i].y << " "
              << cloud->points[i].z << std::endl;
  // display pointcloud after filtering
  std::cerr << "Cloud after filtering: " << std::endl;
  for (size_t i = 0; i < cloud_filtered->points.size(); ++i)
    std::cerr << "    " << cloud_filtered->points[i].x << " "
              << cloud_filtered->points[i].y << " "
              << cloud_filtered->points[i].z << std::endl;
  return (0);
}

7 CropHull任意多边形内部点云提取

#include <pcl/visualization/cloud_viewer.h>
#include <iostream>
#include <pcl/io/io.h>
#include <pcl/io/pcd_io.h>
#include <vector>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/filters/crop_hull.h>
#include <pcl/surface/concave_hull.h>

int main(int argc, char** argv)
{
   pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
   pcl::PCDReader reader;
   reader.read(argv[1],*cloud);

   pcl::PointCloud<pcl::PointXYZ>::Ptr boundingbox_ptr (new pcl::PointCloud<pcl::PointXYZ>);
   boundingbox_ptr->push_back(pcl::PointXYZ(0.1, 0.1, 0));
   boundingbox_ptr->push_back(pcl::PointXYZ(0.1, -0.1,0 ));
   boundingbox_ptr->push_back(pcl::PointXYZ(-0.1, 0.1,0 ));
   boundingbox_ptr->push_back(pcl::PointXYZ(-0.1, -0.1,0 ));


   pcl::ConvexHull<pcl::PointXYZ> hull;
   hull.setInputCloud(boundingbox_ptr);
   hull.setDimension(2);
   std::vector<pcl::Vertices> polygons;
   pcl::PointCloud<pcl::PointXYZ>::Ptr surface_hull (new pcl::PointCloud<pcl::PointXYZ>);
   hull.reconstruct(*surface_hull, polygons);

   pcl::PointCloud<pcl::PointXYZ>::Ptr objects (new pcl::PointCloud<pcl::PointXYZ>);
   pcl::CropHull<pcl::PointXYZ> bb_filter;
   bb_filter.setDim(2);
   bb_filter.setInputCloud(cloud);
   bb_filter.setHullIndices(polygons);
   bb_filter.setHullCloud(surface_hull);
   bb_filter.filter(*objects);
   std::cout << objects->size() << std::endl;

   //visualize
   boost::shared_ptr<pcl::visualization::PCLVisualizer> for_visualizer_v (new pcl::visualization::PCLVisualizer ("crophull display"));
   for_visualizer_v->setBackgroundColor(255,255,255);

   int v1(0);
   for_visualizer_v->createViewPort (0.0, 0.0, 0.33, 1, v1);
   for_visualizer_v->setBackgroundColor (255, 255, 255, v1);
   for_visualizer_v->addPointCloud (cloud,"cloud",v1);
   for_visualizer_v->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,255,0,0,"cloud");
   for_visualizer_v->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,3,"cloud");
   for_visualizer_v->addPolygon<pcl::PointXYZ>(surface_hull,0,.069*255,0.2*255,"backview_hull_polyline1",v1);

   int v2(0);
   for_visualizer_v->createViewPort (0.33, 0.0, 0.66, 1, v2); 
   for_visualizer_v->setBackgroundColor (255, 255, 255, v2);
   for_visualizer_v->addPointCloud (surface_hull,"surface_hull",v2);
   for_visualizer_v->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,255,0,0,"surface_hull");
   for_visualizer_v->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,8,"surface_hull");
   for_visualizer_v->addPolygon<pcl::PointXYZ>(surface_hull,0,.069*255,0.2*255,"backview_hull_polyline",v2);

   int v3(0);
   for_visualizer_v->createViewPort (0.66, 0.0, 1, 1, v3);
   for_visualizer_v->setBackgroundColor (255, 255, 255, v3);
   for_visualizer_v->addPointCloud (objects,"objects",v3);
   for_visualizer_v->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,255,0,0,"objects");
   for_visualizer_v->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,3,"objects");

   while (!for_visualizer_v->wasStopped())
   {

      for_visualizer_v->spinOnce(1000);
   }
   system("pause");
}

./crophull …/pig.pcd

在这里插入图片描述

8 双边滤波

# include <iostream>
# include <pcl/io/pcd_io.h>
# include <pcl/point_types.h>
# include <pcl/filters/impl/bilateral.hpp>
# include <pcl/visualization/pcl_visualizer.h>
void bilateralFilter(pcl::PointCloud < pcl::PointXYZI >::Ptr & input, pcl::PointCloud < pcl::PointXYZI >::Ptr & output)
{
    pcl:: search::KdTree < pcl::PointXYZI >::Ptr tree1(new pcl::search::KdTree < pcl::PointXYZI >);
    // Apply the filter
    pcl::BilateralFilter < pcl::PointXYZI > fbf;
    fbf.setInputCloud(input);
    fbf.setSearchMethod(tree1);
    fbf.setStdDev(0.1);
    fbf.setHalfSize(0.1);
    fbf.filter(*output);
}
int main(int argc, char ** argv)
{
    pcl:: PointCloud < pcl::PointXYZI >::Ptr cloud(new pcl::PointCloud < pcl::PointXYZI >); // 需要PointXYZI
    pcl::PointCloud < pcl::PointXYZI >::Ptr cloud_filtered(new pcl::PointCloud < pcl::PointXYZI >);
    // Fill in the cloud data
    pcl::PCDReader reader;
    // Replace the path below with the path where you saved your file
    reader.read < pcl::PointXYZI > (argv[1], *cloud);
    
    bilateralFilter(cloud, cloud_filtered);
    return (0);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小屋*

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值