SHOT特征描述符、对应关系可视化以及ICP配准

一、SHOT特征描述符可视化

C++

#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/search/kdtree.h>
#include <pcl/io/pcd_io.h>
#include <pcl/features/normal_3d_omp.h>//使用OMP需要添加的头文件
#include <boost/thread/thread.hpp>
#include <pcl/features/shot_omp.h>
#include <pcl/visualization/pcl_plotter.h>// 直方图的可视化 
#include <pcl/visualization/histogram_visualizer.h>
#include <pcl/kdtree/kdtree_flann.h>
using namespace std;
int main()
{
	//------------------加载点云数据-----------------
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
	if (pcl::io::loadPCDFile<pcl::PointXYZ>("pcd/pig_view1.pcd", *cloud) == -1)//需使用绝对路径
	{
		PCL_ERROR("Could not read file\n");
	}

	//--------------------计算法线------------------
	pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal> n;//OMP加速
	pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
	//建立kdtree来进行近邻点集搜索
	pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
	n.setNumberOfThreads(8);//设置openMP的线程数
	n.setInputCloud(cloud);
	n.setSearchMethod(tree);
	n.setKSearch(10);
	n.compute(*normals);//开始进行法向计算

	// ------------------SHOT图像计算------------------
	pcl::SHOTEstimationOMP<pcl::PointXYZ, pcl::Normal, pcl::SHOT352> descr_est;
	pcl::PointCloud<pcl::SHOT352>::Ptr shot_images(new pcl::PointCloud<pcl::SHOT352>);
	descr_est.setNumberOfThreads(4);
	descr_est.setRadiusSearch(50);  //设置搜索半径
	descr_est.setInputCloud(cloud);  //输入模型的关键点
	descr_est.setInputNormals(normals);  //输入模型的法线
	descr_est.setSearchMethod(tree);
	descr_est.compute(*shot_images);

	cout << "SHOT图像计算计算完成" << endl;

	// 显示和检索第一点的自旋图像描述符向量。
	pcl::SHOT352 first_descriptor = shot_images->points[0];
	cout << first_descriptor << endl;


	pcl::PointCloud<pcl::Histogram<352>>::Ptr histograms(new pcl::PointCloud<pcl::Histogram<352>>);
	// Accumulate histograms
	for (int i = 0; i < shot_images->size(); ++i) {
		pcl::Histogram<352>  aggregated_histogram;
		for (int j = 0; j < 352; ++j) {
			aggregated_histogram.histogram[j] = (*shot_images)[i].descriptor[j];
		}
		histograms->push_back(aggregated_histogram);
	}



	pcl::visualization::PCLPlotter plotter;
	plotter.addFeatureHistogram(*histograms, 400); //设置的横坐标长度,该值越大,则显示的越细致
	plotter.setWindowName("SHOT Image");
	plotter.plot();

	return 0;
}

关键代码解析:
 

    pcl::SHOTEstimationOMP<pcl::PointXYZ, pcl::Normal, pcl::SHOT352> descr_est;
	pcl::PointCloud<pcl::SHOT352>::Ptr shot_images(new pcl::PointCloud<pcl::SHOT352>);
	descr_est.setNumberOfThreads(4);
	descr_est.setRadiusSearch(50);  //设置搜索半径
	descr_est.setInputCloud(cloud);  //输入模型的关键点
	descr_est.setInputNormals(normals);  //输入模型的法线
	descr_est.setSearchMethod(tree);
	descr_est.compute(*shot_images);

	cout << "SHOT图像计算计算完成" << endl;

	// 显示和检索第一点的自旋图像描述符向量。
	pcl::SHOT352 first_descriptor = shot_images->points[0];
	cout << first_descriptor << endl;


	pcl::PointCloud<pcl::Histogram<352>>::Ptr histograms(new pcl::PointCloud<pcl::Histogram<352>>);
	// Accumulate histograms
	for (int i = 0; i < shot_images->size(); ++i) {
		pcl::Histogram<352>  aggregated_histogram;
		for (int j = 0; j < 352; ++j) {
			aggregated_histogram.histogram[j] = (*shot_images)[i].descriptor[j];
		}
		histograms->push_back(aggregated_histogram);
	}
  1. pcl::SHOTEstimationOMP<pcl::PointXYZ, pcl::Normal, pcl::SHOT352> descr_est;

    • 创建了一个用于计算SHOT特征的对象descr_est。这里使用了OMP(OpenMP)多线程加速。
  2. pcl::PointCloud<pcl::SHOT352>::Ptr shot_images(new pcl::PointCloud<pcl::SHOT352>);

    • 创建了一个指向pcl::PointCloud<pcl::SHOT352>类型的智能指针shot_images,用于存储计算得到的SHOT特征。
  3. descr_est.setNumberOfThreads(4);

    • 设置了并行计算的线程数为4。这个参数控制了在计算SHOT特征时使用的并行线程数量。
  4. descr_est.setRadiusSearch(50);

    • 设置了用于计算SHOT特征的搜索半径为50个单位。这个参数决定了在计算每个点的SHOT特征时,应该考虑多远范围内的点。
  5. descr_est.setInputCloud(cloud);

    • 设置了输入点云cloud,其中包含了关键点。
  6. descr_est.setInputNormals(normals);

    • 设置了输入法线normals,用于计算SHOT描述子。
  7. descr_est.setSearchMethod(tree);

    • 设置了搜索方法tree,可能是一种用于加速搜索的数据结构,比如kd-tree。
  8. descr_est.compute(*shot_images);

    • 调用了compute函数,开始计算SHOT特征。计算结果将存储在shot_images中。
  9. pcl::SHOT352 first_descriptor = shot_images->points[0];

    • 获取第一个点的SHOT描述子。
  10. 下面的循环将SHOT特征存储在直方图中:

  • 首先创建了一个指向pcl::PointCloud<pcl::Histogram<352>>类型的智能指针histograms,用于存储直方图。
  • 然后遍历计算得到的SHOT特征,将每个特征的描述子存储在直方图中。

参数设置的影响:

  • 线程数会影响计算的速度和系统资源的利用率。增加线程数可能会加快计算速度,但也会增加系统负担。
  • 搜索半径决定了计算SHOT特征时考虑的邻域范围。如果搜索半径设置过小,可能会导致遗漏关键信息;如果设置过大,可能会增加计算的复杂度和耗时。
  • 输入点云和法线的质量和数量直接影响了计算得到的SHOT特征的准确性和鲁棒性。
  • 使用合适的搜索方法能够加快特征计算的速度,提高效率。

需要根据具体的应用场景和数据特点来选择合适的参数值,以达到较好的计算效果和性能。

结果:

二、SHOT对应关系可视化

C++

#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/io/pcd_io.h>
#include <pcl/features/normal_3d_omp.h>
#include <pcl/features/shot_omp.h>
#include <pcl/registration/correspondence_estimation.h>
#include <boost/thread/thread.hpp>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/registration/transformation_estimation_svd.h> 


typedef pcl::PointCloud<pcl::PointXYZ> pointcloud;
typedef pcl::PointCloud<pcl::Normal> pointnormal;
typedef pcl::PointCloud<pcl::SHOT352> shotFeature;

shotFeature::Ptr compute_shot_feature(pointcloud::Ptr input_cloud, pcl::search::KdTree<pcl::PointXYZ>::Ptr tree)
{
    pointnormal::Ptr normals(new pointnormal);
    pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal> n;
    n.setInputCloud(input_cloud);
    n.setNumberOfThreads(12);
    n.setSearchMethod(tree);
    n.setKSearch(30);
    n.compute(*normals);

    shotFeature::Ptr shot(new shotFeature);
    pcl::SHOTEstimationOMP<pcl::PointXYZ, pcl::Normal, pcl::SHOT352> descr_est;
    descr_est.setRadiusSearch(50);  //设置搜索半径
    descr_est.setInputCloud(input_cloud);  //输入模型的关键点
    descr_est.setInputNormals(normals);  //输入模型的法线
    descr_est.compute(*shot);     //计算描述子
    return shot;
}

int main(int argc, char** argv)
{
    pointcloud::Ptr source_cloud(new pointcloud);
    pointcloud::Ptr target_cloud(new pointcloud);
    pcl::io::loadPCDFile<pcl::PointXYZ>("pcd/pig_view1.pcd", *source_cloud);
    pcl::io::loadPCDFile<pcl::PointXYZ>("pcd/pig_view2.pcd", *target_cloud);
    pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());
    shotFeature::Ptr source_shot = compute_shot_feature(source_cloud, tree);
    shotFeature::Ptr target_shot = compute_shot_feature(target_cloud, tree);
    pcl::registration::CorrespondenceEstimation<pcl::SHOT352, pcl::SHOT352> crude_cor_est;
    boost::shared_ptr<pcl::Correspondences> cru_correspondences(new pcl::Correspondences);
    crude_cor_est.setInputSource(source_shot);
    crude_cor_est.setInputTarget(target_shot);
    crude_cor_est.determineCorrespondences(*cru_correspondences, 0.1);
    Eigen::Matrix4f Transform = Eigen::Matrix4f::Identity();
    pcl::registration::TransformationEstimationSVD<pcl::PointXYZ, pcl::PointXYZ, float>::Ptr trans(new pcl::registration::TransformationEstimationSVD<pcl::PointXYZ, pcl::PointXYZ, float>);

    trans->estimateRigidTransformation(*source_cloud, *target_cloud, *cru_correspondences, Transform);

    cout << "变换矩阵为:\n" << Transform << endl;


    boost::shared_ptr<pcl::visualization::PCLVisualizer>viewer(new pcl::visualization::PCLVisualizer("v"));
    viewer->setBackgroundColor(0, 0, 0);
    // 对目标点云着色可视化 (red).
    pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>target_color(target_cloud, 255, 0, 0);
    viewer->addPointCloud<pcl::PointXYZ>(target_cloud, target_color, "target cloud");
    viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "target cloud");
    // 对源点云着色可视化 (green).
    pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>input_color(source_cloud, 0, 255, 0);
    viewer->addPointCloud<pcl::PointXYZ>(source_cloud, input_color, "input cloud");
    viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "input cloud");
    //对应关系可视化
    viewer->addCorrespondences<pcl::PointXYZ>(source_cloud, target_cloud, *cru_correspondences, "correspondence");
    //viewer->initCameraParameters();
    while (!viewer->wasStopped())
    {
        viewer->spinOnce(100);
        boost::this_thread::sleep(boost::posix_time::microseconds(100000));
    }


    return 0;
}

关键代码解析:

我之前在iss关键点检测以及SAC-IA粗配准-CSDN博客

和Spin Image自旋图像描述符可视化以及ICP配准-CSDN博客以及本章第一部分已经解释了大部分函数,这里就不赘述了

结果:

运行速度很慢,可以适当修改参数

三、SHOT结合ICP配准 

C++

#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/features/normal_3d_omp.h>
#include <pcl/registration/icp.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
#include <pcl/keypoints/iss_3d.h>
#include <pcl/registration/ia_ransac.h>
#include <pcl/features/shot_omp.h>
typedef pcl::PointXYZ PointT;
typedef pcl::PointCloud<PointT> PointCloud;
typedef pcl::SHOT352 SHOTT;
typedef pcl::PointCloud<SHOTT> PointCloudshot;
typedef pcl::search::KdTree<PointT> Tree;

// 关键点提取
void extract_keypoint(PointCloud::Ptr& cloud, PointCloud::Ptr& keypoint, Tree::Ptr& tree)
{
    pcl::ISSKeypoint3D<PointT, PointT> iss;
    iss.setInputCloud(cloud);
    iss.setSearchMethod(tree);
    iss.setNumberOfThreads(8);     //初始化调度器并设置要使用的线程数
    iss.setSalientRadius(5);  // 设置用于计算协方差矩阵的球邻域半径
    iss.setNonMaxRadius(5);   // 设置非极大值抑制应用算法的半径
    iss.setThreshold21(0.95);     // 设定第二个和第一个特征值之比的上限
    iss.setThreshold32(0.95);     // 设定第三个和第二个特征值之比的上限
    iss.setMinNeighbors(6);       // 在应用非极大值抑制算法时,设置必须找到的最小邻居数
    iss.compute(*keypoint);
}
// 法线计算和 计算特征点的Spinimage描述子
void computeKeyPointsShot(PointCloud::Ptr& cloud_in, PointCloud::Ptr& key_cloud, PointCloudshot::Ptr& dsc, Tree::Ptr& tree)
{
    pcl::NormalEstimationOMP<PointT, pcl::Normal> n;//OMP加速
    pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
    n.setNumberOfThreads(6);//设置openMP的线程数
    n.setInputCloud(key_cloud);
    n.setSearchSurface(cloud_in);
    n.setKSearch(30);
    //n.setRadiusSearch(0.3);
    n.compute(*normals);

    //[pcl::SHOTEstimation::computeFeature] The local reference frame is not valid! Aborting description of point with index 1689
    pcl::SHOTEstimationOMP<pcl::PointXYZ, pcl::Normal, pcl::SHOT352> descr_est;
    descr_est.setNumberOfThreads(4);
    descr_est.setRadiusSearch(110);  //设置搜索半径
    descr_est.setInputCloud(key_cloud);  //输入模型的关键点
    descr_est.setInputNormals(normals);  //输入模型的法线
    descr_est.setSearchMethod(tree);
    descr_est.compute(*dsc);
}



// 点云可视化
void visualize_pcd(PointCloud::Ptr icp_result, PointCloud::Ptr cloud_target)
{
    //创建初始化目标
    pcl::visualization::PCLVisualizer viewer("registration Viewer");
    pcl::visualization::PointCloudColorHandlerCustom<PointT> final_h(icp_result, 0, 255, 0);
    pcl::visualization::PointCloudColorHandlerCustom<PointT> tgt_h(cloud_target, 255, 0, 0);
    viewer.setBackgroundColor(0, 0, 0);
    viewer.addPointCloud(cloud_target, tgt_h, "tgt cloud");
    viewer.addPointCloud(icp_result, final_h, "final cloud");

    while (!viewer.wasStopped())
    {
        viewer.spinOnce(100);
        boost::this_thread::sleep(boost::posix_time::microseconds(100000));
    }
}

int main()
{
    // 加载源点云和目标点云
    PointCloud::Ptr cloud(new PointCloud);
    PointCloud::Ptr cloud_target(new PointCloud);
    if (pcl::io::loadPCDFile<pcl::PointXYZ>("pcd/pig_view1.pcd", *cloud) == -1)
    {
        PCL_ERROR("加载点云失败\n");
    }
    if (pcl::io::loadPCDFile<pcl::PointXYZ>("pcd/pig_view2.pcd", *cloud_target) == -1)
    {
        PCL_ERROR("加载点云失败\n");
    }
    visualize_pcd(cloud, cloud_target);
    //关键点
    pcl::PointCloud<PointT>::Ptr keypoints1(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PointCloud<PointT>::Ptr keypoints2(new pcl::PointCloud<pcl::PointXYZ>);
    Tree::Ptr tree(new Tree);

    extract_keypoint(cloud, keypoints1, tree);
    extract_keypoint(cloud_target, keypoints2, tree);
    cout << "iss完成!" << endl;
    cout << "cloud的关键点的个数:" << keypoints1->size() << endl;
    cout << "cloud_target的关键点的个数:" << keypoints2->size() << endl;



    // 使用SpinImage描述符计算特征
    PointCloudshot::Ptr source_features(new PointCloudshot);
    PointCloudshot::Ptr target_features(new PointCloudshot);
    computeKeyPointsShot(cloud, keypoints1, source_features, tree);
    computeKeyPointsShot(cloud_target, keypoints2, target_features, tree);
    cout << "FPFH完成!" << endl;




    //SAC配准
    pcl::SampleConsensusInitialAlignment<PointT, PointT, SHOTT> scia;
    scia.setInputSource(keypoints1);
    scia.setInputTarget(keypoints2);
    scia.setSourceFeatures(source_features);
    scia.setTargetFeatures(target_features);
    scia.setMinSampleDistance(7);     // 设置样本之间的最小距离
    scia.setNumberOfSamples(100);       // 设置每次迭代计算中使用的样本数量(可省),可节省时间
    scia.setCorrespondenceRandomness(6);// 在选择随机特征对应时,设置要使用的邻居的数量;
    PointCloud::Ptr sac_result(new PointCloud);
    scia.align(*sac_result);
    Eigen::Matrix4f sac_trans;
    sac_trans = scia.getFinalTransformation();
    cout << "SAC配准完成!" << endl;

    //icp配准
    PointCloud::Ptr icp_result(new PointCloud);
    pcl::IterativeClosestPoint<PointT, PointT> icp;
    icp.setInputSource(keypoints1);
    icp.setInputTarget(keypoints2);
    icp.setMaxCorrespondenceDistance(20);
    icp.setMaximumIterations(35);        // 最大迭代次数
    icp.setTransformationEpsilon(1e-10); // 两次变化矩阵之间的差值
    icp.setEuclideanFitnessEpsilon(0.01);// 均方误差
    icp.align(*icp_result, sac_trans);
    cout << "ICP配准完成!" << endl;

    // 输出配准结果
    std::cout << "ICP converged: " << icp.hasConverged() << std::endl;
    std::cout << "Transformation matrix:\n" << icp.getFinalTransformation() << std::endl;
    Eigen::Matrix4f icp_trans;
    icp_trans = icp.getFinalTransformation();
    cout << icp_trans << endl;
    使用创建的变换对未过滤的输入点云进行变换
    pcl::transformPointCloud(*cloud, *icp_result, icp_trans);

    visualize_pcd(icp_result, cloud_target);

    return 0;
}

关键代码解析:

我之前在iss关键点检测以及SAC-IA粗配准-CSDN博客

和Spin Image自旋图像描述符可视化以及ICP配准-CSDN博客以及本章第一部分已经解释了大部分函数,这里就不赘述了

结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/394065.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

考完PMP如何让学习价值最大化?考PRINCE2!

01什么是PRINCE2 PRINCE2的全称是Project IN Controlled Environment。也就是受控环境下的项目管理&#xff0c;国际项目管理师认证&#xff0c;在国际上被称为王者认证。PRINCE2描述了如何以一种逻辑性的、有组织的方法&#xff0c;按照明确的步骤对项目进行管理。 95%以上全…

软件实例分享,酒店酒水寄存管理系统软件教程

软件实例分享&#xff0c;酒店酒水寄存管理系统软件教程 一、前言 以下软件教程以 佳易王酒水寄存管理系统软件V16.0为例说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 1、寄存的商品名称可以预先设置 2、寄存人可以使用手.机号识别 3、会员充值…

C#,计算几何,贝塞耳插值(Bessel‘s interpolation)的算法与源代码

Friedrich Wilhelm Bessel 1 贝塞耳插值&#xff08;Bessels interpolation&#xff09; 首先要区别于另外一个读音接近的插值算法&#xff1a;贝塞尔插值&#xff08;Bzier&#xff09;。 &#xff08;1&#xff09;读音接近&#xff0c;但不是一个人&#xff1b; &#x…

嵌入式调试工具之GDB

在单片机开发中&#xff0c;我们可以通过集成式的IDE 来进行调试&#xff0c;比如 MDK、IAR 等。 GDB 工具是 GNU 项目调试器&#xff0c;基于命令行使用。和其他的调试器一样&#xff0c;可使用 GDB工具单步运行程序、单步执行、跳入/跳出函数、设置断点、查看变量等等&#…

基于SSM的宁夏旅游网站平台(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的宁夏旅游网站平台&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring …

《苍穹外卖》知识梳理P11-Apache POI导出报表

一.Apache POI 可以通过Apache POI处理excel文件&#xff0c;核心操作是读和写 应用场景 银行网银交易明细各种业务系统导出Excel报表批量导入业务数据 使用步骤 1.导入maven坐标 <dependency><groupId>org.apache.poi</groupId><artifactId>poi&…

使用C++,实现高精度加减乘除法运算!

我的个人主页 {\large \mathsf{{\color{Red} 我的个人主页} } } 我的个人主页 我的专栏&#xff1a; \mathcal{{\color{Green} 我的专栏&#xff1a;} } 我的专栏&#xff1a; 《精选文章》《算法》《每日一道编程题》《高精度算法》 文章目录 前言高精度计算初始模版string 转…

游泳听音乐最好的耳机推荐,游泳防水耳机排行榜推荐

在当今社会&#xff0c;随着人民生活水平的不断提高&#xff0c;人们对健康生活的追求也越来越高。运动成为了人们日常生活中不可或缺的一部分&#xff0c;而游泳作为一种全身性的锻炼方式&#xff0c;更是受到了广大人群的喜爱。然而&#xff0c;对于音乐爱好者来说&#xff0…

【机构vip教程】​python(1):python正则表达式匹配指定的字符开头和指定的字符结束

一&#xff0c;使用python的re.findall函数&#xff0c;匹配指定的字符开头和指定的字符结束 代码示例&#xff1a; 1 import re 2 # re.findall函数;匹配指定的字符串开头和指定的字符串结尾(前后不包含指定的字符串) 3 str01 hello word 4 str02 re.findall((?<e).*?…

Java学习笔记------static

static 创建Javabean类 public class student {private int age;private String name;private String gender;public student() {}public student(int age, String name, String gender) {this.age age;this.name name;this.gender gender;}/*** 获取* return age*/public…

数据结构——lesson3单链表介绍及实现

目录 1.什么是链表&#xff1f; 2.链表的分类 &#xff08;1&#xff09;无头单向非循环链表&#xff1a; &#xff08;2&#xff09;带头双向循环链表&#xff1a; 3.单链表的实现 &#xff08;1&#xff09;单链表的定义 &#xff08;2&#xff09;动态创建节点 &#…

【Gitea】配置 Push To Create

引 在 Git 代码管理工具使用过程中&#xff0c;经常需要将一个文件夹作为仓库上传到一个未创建的代码仓库。如果 Git 服务端使用的是 Gitea&#xff0c;通常会推送失败。 PS D:\tmp\git-test> git remote add origin http://192.1.1.1:3000/root/git-test.git PS D:\tmp\g…

VMware虚拟机安装CentOS7

对于系统开发来说&#xff0c;开发者时常会需要涉及到不同的操作系统&#xff0c;比如Windows系统、Mac系统、Linux系统、Chrome OS系统、UNIX操作系统等。由于在同一台计算机上安装多个系统会占据我们大量的存储空间&#xff0c;所以虚拟机概念应运而生。本篇将介绍如何下载安…

SaaS系统介绍

本文系个人学习笔记&#xff0c;内容来源于资料整合及个人理解。 1. 概念介绍 SaaS系统英文全称为Software as a Service&#xff08;软件即服务&#xff09;&#xff0c;通俗来讲就是提供固定功能的在线软件。从宏观上看&#xff0c;SaaS有三大特点&#xff1a; 1. 用户无需…

如何在Linux系统中配置并优化硬盘的RAID

在Linux系统中配置和优化硬盘的RAID技术可以帮助提高数据存储性能和安全性。RAID&#xff08;Redundant Array of Independent Disks&#xff09;技术通过将多个硬盘组合起来&#xff0c;以增加性能、容量或冗余度&#xff0c;提高数据的可靠性和可用性。本文将介绍如何在Linux…

代码随想录算法训练营第15天—二叉树04 | ● *110.平衡二叉树 ● *257. 二叉树的所有路径 ● 404.左叶子之和

*110.平衡二叉树 题目链接/文章讲解/视频讲解&#xff1a;https://programmercarl.com/0110.%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91.html 考点 后序遍历二叉树高度计算 我的思路 错误地将平衡二叉树的定义等价为判断整体二叉树的最大深度和最小深度之差是否大于1 视…

黑马程序员-瑞吉外卖-day8

目录 菜品新增 菜品代码准备&#xff1a; 1.entity 2.mapper 3.service 4.sevice目录下的impl目录 5.controller 菜品口味代码准备&#xff1a; 1.entity 2.mapper 3.service 4.sevice目录下的impl目录 菜品新增 分析&#xff1a; 后台系统中可以管理菜品信息&…

胆小勿入!AI创作恐怖电影宣传片《生化危机:重生》

胆小勿入&#xff01;AI创作恐怖电影宣传片《生化危机&#xff1a;重生》 "The city is falling, and the dead walk among us." "In the shadow of the apocalypse, the fight for survival begins." "The streets are silent, but the nightmare …

若依项目改造

ctrlalt l 格式化项目 alt f6 修改包和import包名 替换com.ruoyi 为 com.cj 替换若依版本为自己的版本 将ruoyi改成自己项目的英文名 修改中文名字 修改文件包名 修改有ruoyi的类名 &#xff1a; 验证码生成器包名修改&#xff1a;

蝶阀、球阀、阀门百科

一、D71X是蝶阀的型号其中D 就代表了蝶阀,7 代表是对夹式链接,1代表这个蝶阀是中线结构,x就是密封面材质为橡胶。结合起来D71X表示的就是手柄对夹中线蝶阀。 二、J41H-100C型号字母含义介绍 J41H-100C型号是德特森阀门常用的高压截止阀型号字母代表的意思是: J——代表阀门类…