随着激光雷达,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
...//其他相关操作
pcl::PointCloud
pcl::PointCloud
...//打开点云文件估计法线等
//创建PFH估计对象pfh,并将输入点云数据集cloud和法线normals传递给它
pcl::PFHEstimation
pfh.setInputCloud(cloud);
pfh.setInputNormals(normals);
//如果点云是类型为PointNormal,则执行pfh.setInputNormals (cloud);
//创建一个空的kd树表示法,并把它传递给PFH估计对象。
//基于已给的输入数据集,建立kdtree
pcl::KdTreeFLANN
pfh.setSearchMethod(tree);
//输出数据集
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.setInputCloud (cloud_ptr);
// 添加搜索算法 kdtree search 最近的几个点 估计平面 协方差矩阵PCA分解 求解法线
pcl::search::KdTree
ne.setSearchMethod (tree);//设置近邻搜索算法
// 输出点云 带有法线描述
pcl::PointCloud
pcl::PointCloud
// Use all neighbors in a sphere of radius 3cm
ne.setRadiusSearch (0.03);//半价内搜索临近点 3cm
// 计算表面法线特征
ne.compute (cloud_normals);
//=======创建VFH估计对象vfh,并把输入数据集cloud和法线normal传递给它================
pcl::VFHEstimation
//pcl::PFHEstimation
//pcl::FPFHEstimation
// pcl::FPFHEstimationOMP
vfh.setInputCloud (cloud_ptr);
vfh.setInputNormals (cloud_normals_ptr);
//如果点云是PointNormal类型,则执行vfh.setInputNormals (cloud);
//创建一个空的kd树对象,并把它传递给FPFH估计对象。
//基于已知的输入数据集,建立kdtree
pcl::search::KdTree
//pcl::KdTreeFLANN
vfh.setSearchMethod (tree2);//设置近邻搜索算法
//输出数据集
//pcl::PointCloud
//pcl::PointCloud
pcl::PointCloud
//使用半径在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
{
pcl::NormalEstimation
ne.setInputCloud(cloud_ptr);
pcl::search::KdTree
ne.setSearchMethod(tree);//设置近邻搜索算法
pcl::PointCloud
pcl::PointCloud
ne.setKSearch(40); // 临近值50
// 计算表面法线特征
ne.compute(cloud_normals);
pcl::VFHEstimation
vfh.setInputCloud(cloud_ptr);
vfh.setInputNormals(cloud_normals_ptr);
pcl::search::KdTree
vfh.setSearchMethod(tree2);//设置近邻搜索算法
pcl::PointCloud
vfh.compute(*vfh_fe_ptr);
return *vfh_fe_ptr;
}
#include
int main()
{
pcl::PointCloud
pcl::io::loadPCDFile("./banana_train/banana_1.pcd", *cloud);
pcl::PointCloud
*fphf_ = Get_FPFH_Feature(cloud);
pcl::visualization::PCLPlotter *plotter = new pcl::visualization::PCLPlotter("My Plotter");
//设置特性
plotter->setShowLegend(true);
std::cout << pcl::getFieldsList
//显示
plotter->addFeatureHistogram
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
std::vector
float minX,minY,minZ=1000.0;
float maxX,maxY,maxZ=0.0;
pcl::VoxelGrid
cv::Mat label = cv::Mat::zeros(1,1,CV_32FC1);
for(size_t fileIdx=0; fileIdx 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 } //-------------------------------------// trainingData.push_back(_descriptor); if(DataFiles[fileIdx] == (PCD_BASE_PATH+CLASSIFIER_NAME)){ label.at trainingLabels.push_back(label); std::cout << " [label] 1.0"; } else{ label.at 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: "< << "Options:\n" << "-------------------------------------------\n" << "-r << "-c << "-m Treat all unseen points as maximum range readings\n" << "-s << "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 pcl::PointCloud pcl::PointCloud Eigen::Affine3f scene_sensor_pose (Eigen::Affine3f::Identity ()); std::vector 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 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 //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 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 pcl::PointCloud keypoints.resize (keypoint_indices.size ()); for (std::size_t i=0; i keypoints[i].getVector3fMap () = range_image[keypoint_indices[i]].getVector3fMap (); pcl::visualization::PointCloudColorHandlerCustom viewer.addPointCloud 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 #include #include #include using namespace std; int main(int argc,char *argv[]) { 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 pcl::PointCloud 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 harris_detector->compute (*Harris_keypoints); cout<< "Harris_keypoints的大小是" << Harris_keypoints->size() < writer.write 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 //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 //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 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 // 训练分类器 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 } //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)
发表评论