随着激光雷达,RGBD相机等3D传感器在机器人,无人驾驶领域的广泛应用。 针对三维点云数据的研究也逐渐从低层次几何特征提取( PFH, FPFH,VFH等)向高层次语义理解过渡(点云识别,语义分割)。与图像感知领域深度学习几乎一统天下不同,针对无序点云数据的深度学习方法研究则进展缓慢。分析其背后的原因,不外乎三个方面:

1.点云具有无序性。受采集设备以及坐标系影响,同一个物体使用不同的设备或者位置扫描,三维点的排列顺序千差万别,这样的数据很难直接通过End2End的模型处理。

2.点云具有稀疏性。在机器人和自动驾驶的场景中,激光雷达的采样点覆盖相对于场景的尺度来讲,具有很强的稀疏性。在KITTI数据集中,如果把原始的激光雷达点云投影到对应的彩色图像上,大概只有3%的像素才有对应的雷达点。这种极强的稀疏性让基于点云的高层语义感知变得尤其困难。

3.点云信息量有限。点云的数据结构就是一些三维空间的点坐标构成的点集,本质是对三维世界几何形状的低分辨率重采样,因此只能提供片面的几何信息。 https://robotica.unileon.es/index.php?title=PCL/OpenNI_tutorial_4:3D_object_recognition(descriptors)

数据与特征决定了机器学习的上限, 模型/算法/参数只是来逼近这个上限。

0: 分类方式

按照特征的物理属性,可以将特征分为:几何域,强度域。

按照特征的空间尺度,可以分为:单点特征,局部特征,全局特征。

1:传统特征

在点云分类任务中,可直接利用特征向量训练SVM或者多层感知机来进行分类,而在以点为单位的点云分割或者分块任务中,需要结合每一点的局部特征和全局特征进行特征融合和处理,实现逐点的分类。

单点特征

主要有:三维坐标(X, Y, Z), 回波强度 Intensity, 法线 (Nx,Ny,Nz),主曲率(PCx, PCy, PCz, 及两个特征值 PC1, PC2)

局部特征

(一)几何域 局部特征常见的有各种几何特征描述子:PFH,FPFH,SHOT,C-SHOT,RSD,3D形状描述子等。

点特征直方图(Point Feature Histograms) 点特征直方图(PFH)表示法是基于点与其k邻域之间的关系以及它们的估计法线,简言之,它考虑估计法线方向之间所有的相互作用,试图捕获最好的样本表面变化情况,以描述样本的几何特征。

#include //点类型头文件

#include //pfh特征估计类头文件

...//其他相关操作

pcl::PointCloud::Ptrcloud(new pcl::PointCloud);

pcl::PointCloud::Ptrnormals(new pcl::PointCloud());

...//打开点云文件估计法线等

//创建PFH估计对象pfh,并将输入点云数据集cloud和法线normals传递给它

pcl::PFHEstimation pfh;

pfh.setInputCloud(cloud);

pfh.setInputNormals(normals);

//如果点云是类型为PointNormal,则执行pfh.setInputNormals (cloud);

//创建一个空的kd树表示法,并把它传递给PFH估计对象。

//基于已给的输入数据集,建立kdtree

pcl::KdTreeFLANN::Ptrtree(new pcl::KdTreeFLANN());

pfh.setSearchMethod(tree);

//输出数据集

pcl::PointCloud::Ptrpfhs(new pcl::PointCloud());

//使用半径在5厘米范围内的所有邻元素。

//注意:此处使用的半径必须要大于估计表面法线时使用的半径!!!

pfh.setRadiusSearch(0.05);

//计算pfh特征值

pfh.compute(*pfhs);

// pfhs->points.size ()应该与input cloud->points.size ()有相同的大小,即每个点都有一个pfh特征向量

(二)强度域

强度梯度(IGx, IGy, IGz):

全局特征

(一)几何域

常见的几何域全局特征有: PFH: VFH:Viewpoint Feature Histogram CVFH:在VFH基础上解决了点云残缺的问题。

发展历程:PFH、 FPHF、VFH

PFH点特征的描述子一般是基于点坐标、法向量、曲率来描述某个点周围的几何特征。用点特征描述子不能提供特征之间的关系,减少了全局特征信息。因此诞生了一直基于直方图的特征描述子:PFH–point feature histogram(点特征直方图)。

PFH通过参数化查询点和紧邻点之间的空间差异,形成了一个多维直方图对点的近邻进行几何描述,直方图提供的信息对于点云具有平移旋转不变性,对采样密度和噪声点具有稳健性。PFH是基于点与其邻近之间的关系以及它们的估计法线,也即是它考虑估计法线之间的相互关系,来描述几何特征。

统计点对直接的关系,所有的四元组将会以某种统计的方式放进直方图中 通过点对数据,转为四组值, 把两点和它们法线相关的12个参数(xyz坐标值和法线信息)减少到4个。 如下方式:

#include

#include //法线特征

------------------------------------------------------

// =====计算法线========创建法线估计类====================================

pcl::NormalEstimation ne;

ne.setInputCloud (cloud_ptr);

// 添加搜索算法 kdtree search 最近的几个点 估计平面 协方差矩阵PCA分解 求解法线

pcl::search::KdTree::Ptr tree (new pcl::search::KdTree ());

ne.setSearchMethod (tree);//设置近邻搜索算法

// 输出点云 带有法线描述

pcl::PointCloud::Ptr cloud_normals_ptr (new pcl::PointCloud);

pcl::PointCloud& cloud_normals = *cloud_normals_ptr;

// Use all neighbors in a sphere of radius 3cm

ne.setRadiusSearch (0.03);//半价内搜索临近点 3cm

// 计算表面法线特征

ne.compute (cloud_normals);

//=======创建VFH估计对象vfh,并把输入数据集cloud和法线normal传递给它================

pcl::VFHEstimation vfh;

//pcl::PFHEstimation pfh;// phf特征估计其器

//pcl::FPFHEstimation fpfh;// fphf特征估计其器

// pcl::FPFHEstimationOMP fpfh;//多核加速

vfh.setInputCloud (cloud_ptr);

vfh.setInputNormals (cloud_normals_ptr);

//如果点云是PointNormal类型,则执行vfh.setInputNormals (cloud);

//创建一个空的kd树对象,并把它传递给FPFH估计对象。

//基于已知的输入数据集,建立kdtree

pcl::search::KdTree::Ptr tree2 (new pcl::search::KdTree ());

//pcl::KdTreeFLANN::Ptr tree2 (new pcl::KdTreeFLANN ()); //-- older call for PCL 1.5-

vfh.setSearchMethod (tree2);//设置近邻搜索算法

//输出数据集

//pcl::PointCloud::Ptr pfh_fe_ptr (new pcl::PointCloud ());//phf特征

//pcl::PointCloud::Ptr fpfh_fe_ptr (new pcl::PointCloud());//fphf特征

pcl::PointCloud::Ptr vfh_fe_ptr (new pcl::PointCloud ());//vhf特征

//使用半径在5厘米范围内的所有邻元素。

//注意:此处使用的半径必须要大于估计表面法线时使用的半径!!!

//fpfh.setRadiusSearch (0.05);

//计算pfh特征值

vfh.compute (*vfh_fe_ptr);

三维不变矩:矩特征主要表征了图像区域的几何特征,又称为几何矩, 由于其具有旋转、平移、尺度等特性的不变特征,所以又称其为不变矩。但要注意,不变矩对点云密度十分敏感!

常用的特征描述算法有: 法线和曲率计算 normal_3d_feature 、 特征值分析、 PFH 点特征直方图描述子 nk2、 FPFH 快速点特征直方图描述子 FPFH是PFH的简化形式 nk、 3D Shape Context、 文理特征 Spin Image VFH视点特征直方图(Viewpoint Feature Histogram) NARF关键点 pcl::NarfKeypoint narf特征 pcl::NarfDescriptor RoPs特征(Rotational Projection Statistics)

https://github.com/Ewenwan/MVision/tree/master/PCL_APP

例如:利用VFH+SVM训练模型 VFH特征提取

#include

#include //法线特征

pcl::PointCloud GetVFHFeature(pcl::PointCloud::Ptr cloud_ptr)

{

pcl::NormalEstimation ne;

ne.setInputCloud(cloud_ptr);

pcl::search::KdTree::Ptr tree(new pcl::search::KdTree());

ne.setSearchMethod(tree);//设置近邻搜索算法

pcl::PointCloud::Ptr cloud_normals_ptr(new pcl::PointCloud);

pcl::PointCloud& cloud_normals = *cloud_normals_ptr;

ne.setKSearch(40); // 临近值50

// 计算表面法线特征

ne.compute(cloud_normals);

pcl::VFHEstimation vfh;

vfh.setInputCloud(cloud_ptr);

vfh.setInputNormals(cloud_normals_ptr);

pcl::search::KdTree::Ptr tree2(new pcl::search::KdTree());

vfh.setSearchMethod(tree2);//设置近邻搜索算法

pcl::PointCloud::Ptr vfh_fe_ptr(new pcl::PointCloud());//vhf特征

vfh.compute(*vfh_fe_ptr);

return *vfh_fe_ptr;

}

#include // plotter

int main()

{

pcl::PointCloud::Ptr cloud(new pcl::PointCloud);

pcl::io::loadPCDFile("./banana_train/banana_1.pcd", *cloud);

pcl::PointCloud::Ptr fphf_ (new pcl::PointCloud());;

*fphf_ = Get_FPFH_Feature(cloud);

pcl::visualization::PCLPlotter *plotter = new pcl::visualization::PCLPlotter("My Plotter");

//设置特性

plotter->setShowLegend(true);

std::cout << pcl::getFieldsList(*fphf_);

//显示

plotter->addFeatureHistogram(*fphf_, "fpfh", 0);

plotter->spin();

plotter->clearPlots();

system("pause");

return 0;

}

特征可视化如下:

SVM 特征分类

SVM

1.两分类的时候标签定义为-1和1

2.多分类的时候标签定义从1开始,用到测试时是int index = model->detect();

3.两分类的时候可生成txt或者xml文件,多分类只能用xml

选择核函数非常重要:

enum KernelTypes {

/** Returned by SVM::getKernelType in case when custom kernel has been set */

CUSTOM=-1,

//线性核

LINEAR=0,

//多项式核

POLY=1,

//径向基核(高斯核)

RBF=2,

//sigmoid核

SIGMOID=3,

//指数核,与高斯核类似

CHI2=4,

//直方图核

INTER=5

};

VFH数据提取代码 对扩展的FPFH分量来说,默认的VFH的实现使用45个子区间进行统计, 而对于视点分量要使用128个子区间进行统计,这样VFH就由一共308个浮点数组成阵列

void

getPointCloud::getTrainingData(std::vector & _trainingData,

std::vector & _trainingLabels){

float minX,minY,minZ=1000.0;

float maxX,maxY,maxZ=0.0;

pcl::VoxelGrid vg;

cv::Mat label = cv::Mat::zeros(1,1,CV_32FC1);

for(size_t fileIdx=0; fileIdx

std::vector files = std::vector();

pcl::PointXYZ minPt, maxPt;

int skip=0;

getdir(DataFiles[fileIdx]+"/",files);

cv::Mat trainingData;

cv::Mat trainingLabels;

for (unsigned int i = 0;i < files.size();i++) {

// remove this after testing classifier training

// if(skip > 5){break;}

// skip++;

if(files[i] == "." || files[i] == ".."){

ROS_INFO(" . or .. files ignored");

}

else{

std::cout << " [ processing ] " << files[i];

cloud = loadCloud(DataFiles[fileIdx]+"/"+files[i]);

//-- voxelization of the cloud --------//

vg.setInputCloud (cloud);

vg.setLeafSize (0.005f, 0.005f, 0.005f);

vg.filter (*cloud);

//-- Getting the descriptors ----------//

pc.cloudinput(cloud);

VFH_descriptor = pc.getDescriptor();

cv::Mat _descriptor = cv::Mat::zeros(1,306,CV_32FC1);

for(size_t i=0;i<306;i++){

_descriptor.at(0,i)=(float)VFH_descriptor->points[0].histogram[i];

}

//-------------------------------------//

trainingData.push_back(_descriptor);

if(DataFiles[fileIdx] == (PCD_BASE_PATH+CLASSIFIER_NAME)){

label.at(0,0)=1.0;

trainingLabels.push_back(label);

std::cout << " [label] 1.0";

}

else{

label.at(0,0)= -1.0;

trainingLabels.push_back(label);

std::cout << " [label] -1.0";

}

std::cout << std::endl;

}

}

_trainingData.push_back(trainingData);

_trainingLabels.push_back(trainingLabels);

}

}

可以利用opencv的SVM进行训练

classifier::classifier(void){

SVMclf = cv::ml::SVM::create();

}

classifier::classifier(std::string filename){

SVMclf = cv::ml::SVM::create();

SVMclf->load(filename.c_str());

}

(二)关键点

我们都知道在二维图像上, Harris、 SIFT、 SURF、 KAZE 常见的三维点云关键点提取算法有一下几种: ISS3D、 Harris3D、 NARF、 SIFT3D

pcl中的sift等关键点 NARF关键点   NARF(Normal Aligned Radial Feature)关键点是为了从深度图像中识别物体而提出来的,关键点探测的重要一步是减少特征提取时的搜索空间,把重点放在重要的结构上,对NARF关键点提取过程有以下要求:提取的过程必须将边缘及物体表面变化信息考虑在内;关键点的位置必须稳定,可以被重复探测,即使换了不同的视角;关键点所在的位置必须有稳定的支持区域,可以计算描述子并进行唯一的法向量估计。为了满足以上要求,提出以下探测步骤来进行关键点提取:   (1)遍历每个深度图像点,通过寻找在近邻区域有深度突变的位置进行边缘检测。   (2)遍历每个深度图像点,根据近邻区域的表面变化决定一种测度表面变化的系数,以及变化的主方向。   (3)根据第二步找到的主方向计算兴趣值,表征该方向与其他方向的不同,以及该处表面的变化情况,即该点有多稳定。   (4)对兴趣值进行平滑过滤。   (5)进行无最大值压缩找到最终的关键点,即为NARF关键点。

#include

#include

#include

#include

#include

#include

#include

#include

#include

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: "<\n\n"

<< "Options:\n"

<< "-------------------------------------------\n"

<< "-r angular resolution in degrees (default "<

<< "-c coordinate frame (default "<< (int)coordinate_frame<<")\n"

<< "-m Treat all unseen points as maximum range readings\n"

<< "-s support size for the interest points (diameter of the used sphere - "

<< "default "<

<< "-h this help\n"

<< "\n\n";

}

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;

std::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);

std::cout << "Using coordinate frame "<< (int)coordinate_frame<<".\n";

}

if (pcl::console::parse (argc, argv, "-s", support_size) >= 0)

std::cout << "Setting support size to "<

if (pcl::console::parse (argc, argv, "-r", angular_resolution) >= 0)

std::cout << "Setting angular resolution to "<

angular_resolution = pcl::deg2rad (angular_resolution);

/* 读取pcd文件;如果没有指定文件,就创建样本点 */

pcl::PointCloud::Ptr point_cloud_ptr (new pcl::PointCloud);

pcl::PointCloud& point_cloud = *point_cloud_ptr;

pcl::PointCloud far_ranges;

Eigen::Affine3f scene_sensor_pose (Eigen::Affine3f::Identity ());

std::vector 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)

{

std::cerr << "Was not able to open file \""<

printUsage (argv[0]);

return 0;

}

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)

std::cout << "Far ranges file \""<

}

else

{

setUnseenToMaxRange = true;

std::cout << "\nNo *.pcd file given => Generating 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 = point_cloud.size (); point_cloud.height = 1;

}

/* 从点云数据,创建深度图像 */

float noise_level = 0.0;

float min_range = 0.0f;

int border_size = 1;

pcl::RangeImage::Ptr range_image_ptr (new pcl::RangeImage);

pcl::RangeImage& range_image = *range_image_ptr;

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");

viewer.setBackgroundColor (1, 1, 1);

pcl::visualization::PointCloudColorHandlerCustom range_image_color_handler (range_image_ptr, 0, 0, 0);

viewer.addPointCloud (range_image_ptr, range_image_color_handler, "range image");

viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "range image");

//viewer.addCoordinateSystem (1.0f, "global");

//PointCloudColorHandlerCustom 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);

/* 提取NARF关键点 */

pcl::RangeImageBorderExtractor range_image_border_extractor; // 创建深度图像的边界提取器,用于提取NARF关键点

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 = 0.5;

pcl::PointCloud keypoint_indices; // 用于存储关键点的索引

narf_keypoint_detector.compute (keypoint_indices); // 计算NARF关键点

std::cout << "Found "<

/* 在range_image_widget中显示关键点 */

//for (std::size_t i=0; i

//range_image_widget.markPoint (keypoint_indices[i]%range_image.width,

//keypoint_indices[i]/range_image.width);

/* 在3D viwer窗口中显示关键点 */

pcl::PointCloud::Ptr keypoints_ptr (new pcl::PointCloud);

pcl::PointCloud& keypoints = *keypoints_ptr;

keypoints.resize (keypoint_indices.size ());

for (std::size_t i=0; i

keypoints[i].getVector3fMap () = range_image[keypoint_indices[i]].getVector3fMap ();

pcl::visualization::PointCloudColorHandlerCustom keypoints_color_handler (keypoints_ptr, 0, 255, 0);

viewer.addPointCloud (keypoints_ptr, keypoints_color_handler, "keypoints");

viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 7, "keypoints");

while (!viewer.wasStopped ())

{

range_image_widget.spinOnce (); // 处理GUI事件

viewer.spinOnce ();

pcl_sleep(0.01);

}

}

NARF 深度图关键点 Harris算子是常见的特征检测算子,既可以提取角点也可以提取边缘点。与2D Harris角点检测原理不同,3D Harris角点检测利用的是点云法向量的信息。

#include

#include

#include

#include

#include

#include //harris特征点估计类头文件声明

#include

#include

#include

using namespace std;

int main(int argc,char *argv[])

{

pcl::PointCloud::Ptr input_cloud (new pcl::PointCloud);

pcl::io::loadPCDFile ("../pcd/room.pcd", *input_cloud);

pcl::PCDWriter writer;

float r_normal;

float r_keypoint;

r_normal=stof(argv[2]);

r_keypoint=stof(argv[3]);

typedef pcl::visualization::PointCloudColorHandlerCustom ColorHandlerT3;

pcl::PointCloud::Ptr Harris_keypoints (new pcl::PointCloud ());

pcl::HarrisKeypoint3D* harris_detector = new pcl::HarrisKeypoint3D ;

//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::LOWE);

harris_detector->compute (*Harris_keypoints);

cout<< "Harris_keypoints的大小是" << Harris_keypoints->size() <

writer.write ("Harris_keypoints.pcd", *Harris_keypoints, false);

pcl::visualization::PCLVisualizer visu3("clouds");

visu3.setBackgroundColor(255,255,255);

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 ();

}

自定义特征

例如:

相差较大的物体,利用特征值区别

如果面状性较好的话,可以知道会有两个特征值大小接近,一个特征值很小; 如果点云很分散的话,那么3个特征值的大小应该是比较接近的。

https://blog.csdn.net/CVSvsvsvsvs/article/details/85784438

例如: 多分类mnist用法

#include

#include

#include

#include

#include

#include

#include

#include

using namespace std;

using namespace cv;

using namespace ml;

Mat dealimage;

Mat src;

Mat yangben_gray;

Mat yangben_thresh;

int ReverseInt(int i)

{

unsigned char ch1, ch2, ch3, ch4;

ch1 = i & 255;

ch2 = (i >> 8) & 255;

ch3 = (i >> 16) & 255;

ch4 = (i >> 24) & 255;

return((int)ch1 << 24) + ((int)ch2 << 16) + ((int)ch3 << 8) + ch4;

}

void read_Mnist_Label(string filename, Mat* &trainLabel)

{

ifstream file(filename, ios::binary);

if (file.is_open())

{

int magic_number = 0;

int number_of_images = 0;

file.read((char*)&magic_number, sizeof(magic_number));

file.read((char*)&number_of_images, sizeof(number_of_images));

magic_number = ReverseInt(magic_number);

number_of_images = ReverseInt(number_of_images);

cout << "magic number = " << magic_number << endl;

cout << "number of images = " << number_of_images << endl;

trainLabel = new Mat(number_of_images, 1, CV_32SC1);

for (int i = 0; i < number_of_images; i++)

{

unsigned char label = 0;

file.read((char*)&label, sizeof(label));

if (label > 0) label = 255;

trainLabel->at(i, 0) = label;

//cout << "Label: " << labels[i] << endl;

}

}

}

void read_Mnist_Images(string filename, Mat* &trainImages)

{

ifstream file(filename, ios::binary);

if (file.is_open())

{

int magic_number = 0;

int number_of_images = 0;

int n_rows = 0;

int n_cols = 0;

file.read((char*)&magic_number, sizeof(magic_number));

file.read((char*)&number_of_images, sizeof(number_of_images));

file.read((char*)&n_rows, sizeof(n_rows));

file.read((char*)&n_cols, sizeof(n_cols));

magic_number = ReverseInt(magic_number);

number_of_images = ReverseInt(number_of_images);

n_rows = ReverseInt(n_rows);

n_cols = ReverseInt(n_cols);

cout << "magic number = " << magic_number << endl;

cout << "number of images = " << number_of_images << endl;

cout << "rows = " << n_rows << endl;

cout << "cols = " << n_cols << endl;

trainImages = new Mat(number_of_images, n_rows * n_cols, CV_32F);

for (int i = 0; i < number_of_images; i++)

{

for (int r = 0; r < n_rows; r++)

{

for (int c = 0; c < n_cols; c++)

{

unsigned char image = 0;

file.read((char*)&image, sizeof(image));

if (image > 0) image = 255;

trainImages->at(i, r * n_cols + c) = image;

//if (i == 9999) cout << "IMAGE: " << i << " " << r * n_cols + c << " " << images[i][r * n_cols + c ] << endl;

//cout << images[i][r * n_cols + c] << endl;

}

}

}

}

}

int main()

{

cout << "训练数据请输入 1, 直接使用训练模型预测输入2" << endl;

string flag = "";

while (1) {

cin >> flag;

if (flag == "1" || flag == "2")

break;

else {

cout << "输入1,2" << endl;

}

}

// 创建分类器并设置参数

Ptr SVM_params = SVM::create();

if (flag == "1") {

// 训练 加载模型

// 读取训练样本的数据

Mat* trainingDataMat = nullptr;

read_Mnist_Images("mnist_dataset/train-images.idx3-ubyte", trainingDataMat);

//训练样本的响应值

Mat* responsesMat = nullptr;

read_Mnist_Label("mnist_dataset/train-labels.idx1-ubyte", responsesMat);

===============================创建SVM模型===============================

cout << SVM_params->getVarCount() << " " << endl;

SVM_params->setType(SVM::C_SVC); //C_SVC用于分类,C_SVR用于回归

SVM_params->setKernel(SVM::RBF); //LINEAR线性核函数。SIGMOID为高斯核函数

// 注释掉部分对本项目不影响,影响因子只有两个

//SVM_params->setDegree(0); //核函数中的参数degree,针对多项式核函数;

SVM_params->setGamma(0.50625); //核函数中的参数gamma,针对多项式/RBF/SIGMOID核函数;

//SVM_params->setCoef0(0); //核函数中的参数,针对多项式/SIGMOID核函数;

SVM_params->setC(12.5); //SVM最优问题参数,设置C-SVC,EPS_SVR和NU_SVR的参数;

//SVM_params->setNu(0); //SVM最优问题参数,设置NU_SVC, ONE_CLASS 和NU_SVR的参数;

//SVM_params->setP(0); //SVM最优问题参数,设置EPS_SVR 中损失函数p的值.

//结束条件,即训练1000次或者误差小于0.01结束

SVM_params->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 1000, 0.01));

//Mat* responsesTransfer = new Mat(responsesMat->size().height, 1, CV_32FC1);

//responsesMat->convertTo(*responsesMat, CV_32SC1); 类型为CV_32SC1,此处省略是因为读取的时候已指明该格式了。

//trainingDataMat->convertTo(*trainingDataMat, CV_32F); 此处需要注意训练数据类型为 CV_32F

//训练数据和标签的结合

cout << "开始训练" << endl;

cout << "训练数据长度" << trainingDataMat->size().width << " 高度 " << trainingDataMat->size().height << endl;

cout << "标签数据长度" << responsesMat->size().width << " 高度 " << responsesMat->size().height << endl;

Ptr tData = TrainData::create(*trainingDataMat, ROW_SAMPLE, *responsesMat);

// 训练分类器

SVM_params->train(tData);//训练

SVM_params->save("svm.xml");

cout << SVM_params->getVarCount() << " " << endl;

//保存模型

SVM_params->save("svm.xml");

cout << "训练好了!!!" << endl;

delete trainingDataMat;

delete responsesMat;

trainingDataMat = NULL;

responsesMat = NULL;

}

else if (flag == "2") {

cout << "训练模型参数加载" << endl;

SVM_params = SVM::load("svm.xml");

//cout << SVM_params.empty() << endl;

}

cout << "-------SVM 开始预测-------------------------------" << endl;

int count = 0; // 统计正确率

Mat* testImage = nullptr;

Mat* testLabel = nullptr;

read_Mnist_Images("mnist_dataSet/t10k-images.idx3-ubyte", testImage);

read_Mnist_Label("mnist_dataSet/t10k-labels.idx1-ubyte", testLabel);

int height = testImage->size().height; // 测试图片的数量

int width = testImage->size().width; // 图片的维度

for (int i = 0; i < height; i++) { // 遍历所有测试图片

Mat image(1, width, CV_32F); // 单张图片

for (int j = 0; j < width; j++) { //

image.at(0, j) = testImage->at(i, j);

}

//cout << image.size().height << " " << image.size().width << " " << endl;

//cout << image.cols << " " << image.rows << " " << endl;

//cout << SVM_params->getVarCount() << " " << endl;

if (SVM_params->predict(image) == testLabel[i]) {

count++;

}

}

cout << "训练预测的准确率为:" << (double)count / height << endl;

system("pause");

//waitKey(0);

return 0;

}

2:深度神经网络

ModelNet40数据集

ScanObjectNN 数据集

CNN方法:

PointNet, DGCNN, PPFNet, PointConv

|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| 其中 PointNet系列方法 常规的 3* N为输入 N即n个点云数据(x,y,z)

3D CNN系列方法

例如 利用kears进行模型训练

""" 3D-CNN is initailized to have grid size 40x40x40 """

cnn = CNNModel(ip_shape=(1, 40, 40, 40))

cnn.load_model(base_path=os.path.join(PACKAGE_PATH, "bin/3DCNN_model"))

from keras.preprocessing.image import ImageDataGenerator

from keras.models import Sequential

from keras.layers.core import Dense, Dropout, Activation, Flatten

from keras.layers.convolutional import Conv3D,Convolution3D, MaxPooling3D

from keras.optimizers import SGD, RMSprop

from keras.utils import np_utils, generic_utils

from sklearn.preprocessing import LabelBinarizer

from termprinter import *

from keras.models import model_from_json

import numpy as np

import os

class CNNModel:

def __init__(self,nb_classes=5,ip_shape=(1, 40, 40, 40)):

self.encoder = LabelBinarizer()

self.CNN_model(nb_classes=nb_classes,ip_shape=ip_shape)

def CNN_model(self,nb_classes = 5,ip_shape=(1, 40, 40, 40)):

self.model = Sequential()

self.model.add(

Conv3D(32, (5,5,5),

strides=(1, 1, 1), padding='valid', data_format="channels_first",input_shape=ip_shape,

activation='relu', dilation_rate=(1, 1, 1) ))

self.model.add(

Conv3D(32, (5,5,5),

strides=(1, 1, 1), padding='valid', data_format="channels_first",

# activation='relu'))

activation='sigmoid'))

self.model.add(MaxPooling3D(pool_size=(2,2,2)))

self.model.add(Dropout(0.1))

self.model.add(

Conv3D(32, (3,3,3),

strides=(1, 1, 1), padding='valid', data_format="channels_first",

# activation='relu'))

activation='sigmoid'))

self.model.add(

Conv3D(32, (3,3,3),

strides=(1, 1, 1), padding='valid', data_format="channels_first",

activation='relu'))

self.model.add(MaxPooling3D(pool_size=(2,2,2)))

self.model.add(Dropout(0.1))

self.model.add(

Conv3D(32, (3,3,3),

strides=(1, 1, 1), padding='valid', data_format="channels_first",

activation='relu'))

self.model.add(

Conv3D(32, (3,3,3),

strides=(1, 1, 1), padding='valid', data_format="channels_first",

activation='relu'))

self.model.add(MaxPooling3D(pool_size=(2,2,2)))

self.model.add(Dropout(0.1))

self.model.add(Flatten())

# self.model.add(Dense(128, init='normal', activation='relu'))

self.model.add(Dense(128, kernel_initializer='normal', activation='relu'))

# self.model.add(Dropout(0.5))

# self.model.add(Dense(nb_classes,init='normal'))

self.model.add(Dense(nb_classes,kernel_initializer='normal'))

self.model.add(Activation('softmax'))

# self.model.compile(loss='categorical_crossentropy', optimizer='RMSprop', metrics=['mse', 'accuracy'])

self.model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['mse', 'accuracy'])

# return self.model

def train(self,data,labels):

labels = self.encoder.fit_transform(labels)

hist = self.model.fit(data,labels,batch_size=10, epochs = 10,shuffle=True)

def test(self,data,labels):

labels = self.encoder.fit_transform(labels)

score = self.model.evaluate(data,labels,batch_size=10)

print('**********************************************')

print(OKGREEN+"[{d[0]}]{d[3]} [{d[1]}]{d[4]} [{d[2]}]{d[5]}".format(d=self.model.metrics_names+score) + ENDC)

def predict(self,data,labelsSet):

_idx = np.argmax(self.model.predict(data))

return labelsSet[_idx],self.model.predict_proba(data)[0][_idx]

def save_model(self,base_path="."):

# serialize model to JSON

json_path = os.path.join(base_path,'model.json')

print(OKGREEN+json_path+ENDC)

model_json = self.model.to_json()

with open(json_path, "w") as json_file:

json_file.write(model_json)

# serialize weights to HDF5

weight_file = os.path.join(base_path,"model.h5")

print(OKGREEN+weight_file+ENDC)

self.model.save_weights(weight_file)

print("Saved model to disk")

def load_model(self,base_path="."):

# load json and create model

json_path = os.path.join(base_path,'model.json')

print(OKGREEN+json_path+ENDC)

json_file = open(json_path, 'r')

loaded_model_json = json_file.read()

json_file.close()

self.model = model_from_json(loaded_model_json)

# load weights into new model

weight_file = os.path.join(base_path,"model.h5")

print(OKGREEN+weight_file+ENDC)

self.model.load_weights(weight_file)

print(OKGREEN+"Loaded model from disk"+ENDC)

查看原文