VFH特征的使用(二)

news2024/10/4 10:31:08

一、VFH特征识别

C++

recognize_objects.h

#pragma once
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/common/common.h>
#include <pcl/common/transforms.h>
#include <pcl/console/parse.h>
#include <pcl/console/print.h>
#include <pcl/io/pcd_io.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/features/vfh.h>
#include <pcl/features/normal_3d.h>
#include <boost/filesystem.hpp>
#include <flann/flann.h>
#include <flann/io/hdf5.h>
#include <fstream>
#include <iostream>
#include <boost/algorithm/string/replace.hpp>
// Define vfh_model type for storing data file name and histogram data
typedef std::pair<std::string, std::vector<float> > vfh_model;

// This function estimates VFH signatures of an input point cloud 
// Input: File path
// Output: VFH signature of the object
void estimate_VFH(const boost::filesystem::path& path, pcl::PointCloud <pcl::VFHSignature308>& signature)
{
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>());

    if (pcl::io::loadPCDFile<pcl::PointXYZ>(path.string(), *cloud) == -1) //* load the file
    {
        PCL_ERROR("Couldn't read file %s \n", path.string());
        return;
    }

    // Estimate point cloud normals
    pcl::search::KdTree<pcl::PointXYZ>::Ptr normalTree(new pcl::search::KdTree<pcl::PointXYZ>());
    pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
    ne.setInputCloud(cloud);
    ne.setSearchMethod(normalTree);
    ne.setRadiusSearch(0.03);
    ne.compute(*normals);

    // Estimate VFH signature of the point cloud
    pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());
    pcl::VFHEstimation<pcl::PointXYZ, pcl::Normal, pcl::VFHSignature308> vfh;
    vfh.setInputCloud(cloud);
    vfh.setInputNormals(normals);
    vfh.setSearchMethod(tree);
    vfh.compute(signature);
}

// This function checks if the file already contains a VFH signature
// Input: File path
// Output: true if file contains VFH signature, false if file does not contain VFH signature
bool checkVFH(const boost::filesystem::path& path)
{
    int vfh_idx;

    try
    {
        // Read point cloud header
        pcl::PCLPointCloud2 cloud;
        int version;
        Eigen::Vector4f origin;
        Eigen::Quaternionf orientation;
        pcl::PCDReader r;
        int type; unsigned int idx;
        r.readHeader(path.string(), cloud, origin, orientation, version, type, idx);

        // Check if there is VFH field in the cloud header
        vfh_idx = pcl::getFieldIndex(cloud, "vfh");
        if (vfh_idx == -1)
            return (false);
        if ((int)cloud.width * cloud.height != 1)
            return (false);
    }
    catch (const pcl::InvalidConversionException&)
    {
        return (false);
    }
    return (true);
}

// This function loads VFH signature histogram into a vfh model
// Input: File path
// Output: A boolean that returns true if the histogram is loaded successfully and a vfh_model data that holds the histogram information
bool loadHist(const boost::filesystem::path& path, vfh_model& vfh)
{
    int vfh_idx;
    // Read file header to check if the file contains VFH signature
    try
    {
        pcl::PCLPointCloud2 cloud;
        int version;
        Eigen::Vector4f origin;
        Eigen::Quaternionf orientation;
        pcl::PCDReader r;
        int type; unsigned int idx;
        r.readHeader(path.string(), cloud, origin, orientation, version, type, idx);

        vfh_idx = pcl::getFieldIndex(cloud, "vfh");
        if (vfh_idx == -1)
        {
            return (false);
        }
        if ((int)cloud.width * cloud.height != 1)
            return (false);
    }
    catch (const pcl::InvalidConversionException&)
    {
        return (false);
    }

    // Treat the VFH signature as a single Point Cloud and load data from it
    pcl::PointCloud <pcl::VFHSignature308> point;
    pcl::io::loadPCDFile(path.string(), point);
    vfh.second.resize(308);

    std::vector <pcl::PCLPointField> fields;
    pcl::getFieldIndex(point, "vfh", fields);

    // Fill vfh_model.second with histogram data
    for (size_t i = 0; i < fields[vfh_idx].count; ++i)
    {
        vfh.second[i] = point.points[0].histogram[i];
    }
    // Put file path in vfh_model.first
    vfh.first = path.string();
    return (true);
}

// This gets PCD file names from a directory and passes them to histogram loader one by one
// then pushes the results into a vfh_model vector to keep them in a data storage
// Input: Training data set file path
// Output: A vfh_model vector that contains all VFH signature information
void loadData(const boost::filesystem::path& base_dir, std::vector<vfh_model>& models)
{
    if (!boost::filesystem::exists(base_dir) && !boost::filesystem::is_directory(base_dir))
        return;
    else
    {
        // Iterate through the data set directory to read VFH signatures
        for (boost::filesystem::directory_iterator i(base_dir); i != boost::filesystem::directory_iterator(); i++)
        {
            // If read path is a directory, then print path name on console and call data loader again
            if (boost::filesystem::is_directory(i->status()))
            {
                std::stringstream ss;
                ss << i->path();
                pcl::console::print_highlight("Loading %s (%lu models loaded so far).\n", ss.str().c_str(), (unsigned long)models.size());
                loadData(i->path(), models);
            }
            // If read path is a file with *.pcd extension, then check if it contains VFH signature
            // If not, then estimate VFH signature of it
            // Either way, pass the file to histogram loader since it can differentiate between files with VFH signatures and files without them
            if (boost::filesystem::is_regular_file(i->status()) && boost::filesystem::extension(i->path()) == ".pcd")
            {
                vfh_model m;
                std::string str = i->path().string();
                if (!checkVFH(str))
                {
                    str.replace(str.find(".pcd"), 4, "_vfh.pcd");
                    if (!boost::filesystem::exists(str))
                    {
                        pcl::PointCloud <pcl::VFHSignature308> signature;
                        estimate_VFH(i->path().string(), signature);

                        pcl::io::savePCDFile(str, signature);
                    }
                }
                if (loadHist(i->path().string(), m))
                {
                    models.push_back(m);
                    std::cout << m.first << std::endl;
                }
            }
        }
    }
}

// This function visualizes given k point cloud arguments on a PCL_Viewer
// It shows distances of candidates from the query object at the left bottom corner of each window
// If the distance is smaller than a given threshold then the distances are shown in green, else they are shown in red
// Inputs: Main function arguments, candidate count (k), threshold value, vfh_model vector that contains VFH signatures 
// from the data set, indices of candidates and distances of candidates
void visualize(int argc, char** argv, int k, double thresh, std::vector<vfh_model> models, flann::Matrix<int> k_indices, flann::Matrix<float> k_distances)
{
    // Load the results
    pcl::visualization::PCLVisualizer p(argc, argv, "VFH Cluster Classifier");
    int y_s = (int)floor(sqrt((double)k));
    int x_s = y_s + (int)ceil((k / (double)y_s) - y_s);
    double x_step = (double)(1 / (double)x_s);
    double y_step = (double)(1 / (double)y_s);
    pcl::console::print_highlight("Preparing to load ");
    pcl::console::print_value("%d", k);
    pcl::console::print_info(" files (");
    pcl::console::print_value("%d", x_s);
    pcl::console::print_info("x");
    pcl::console::print_value("%d", y_s);
    pcl::console::print_info(" / ");
    pcl::console::print_value("%f", x_step);
    pcl::console::print_info("x");
    pcl::console::print_value("%f", y_step);
    pcl::console::print_info(")\n");

    int viewport = 0, l = 0, m = 0;
    for (int i = 0; i < k; ++i)
    {
        std::string cloud_name = models.at(k_indices[0][i]).first;

        boost::replace_last(cloud_name, "_vfh", "");

        p.createViewPort(l * x_step, m * y_step, (l + 1) * x_step, (m + 1) * y_step, viewport);
        l++;
        if (l >= x_s)
        {
            l = 0;
            m++;
        }

        pcl::PCLPointCloud2 cloud;
        pcl::console::print_highlight(stderr, "Loading "); pcl::console::print_value(stderr, "%s ", cloud_name.c_str());
        if (pcl::io::loadPCDFile(cloud_name, cloud) == -1)
            break;

        // Convert from blob to PointCloud
        pcl::PointCloud<pcl::PointXYZ> cloud_xyz;
        pcl::fromPCLPointCloud2(cloud, cloud_xyz);

        if (cloud_xyz.points.size() == 0)
            break;

        pcl::console::print_info("[done, ");
        pcl::console::print_value("%d", (int)cloud_xyz.points.size());
        pcl::console::print_info(" points]\n");
        pcl::console::print_info("Available dimensions: ");
        pcl::console::print_value("%s\n", pcl::getFieldsList(cloud).c_str());

        // Demean the cloud
        Eigen::Vector4f centroid;
        pcl::compute3DCentroid(cloud_xyz, centroid);
        pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_xyz_demean(new pcl::PointCloud<pcl::PointXYZ>);
        pcl::demeanPointCloud<pcl::PointXYZ>(cloud_xyz, centroid, *cloud_xyz_demean);
        // Add to renderer*
        p.addPointCloud(cloud_xyz_demean, cloud_name, viewport);

        // Check if the model found is within our inlier tolerance
        std::stringstream ss;
        ss << k_distances[0][i];
        if (k_distances[0][i] > thresh)
        {
            p.addText(ss.str(), 20, 30, 1, 0, 0, ss.str(), viewport);  // display the text with red

            // Create a red line
            pcl::PointXYZ min_p, max_p;
            pcl::getMinMax3D(*cloud_xyz_demean, min_p, max_p);
            std::stringstream line_name;
            line_name << "line_" << i;
            p.addLine(min_p, max_p, 1, 0, 0, line_name.str(), viewport);
            p.setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, 5, line_name.str(), viewport);
        }
        else
            p.addText(ss.str(), 20, 30, 0, 1, 0, ss.str(), viewport);

        // Increase the font size for the score*
        p.setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_FONT_SIZE, 18, ss.str(), viewport);

        // Add the cluster name
        p.addText(cloud_name, 20, 10, cloud_name, viewport);
    }
    // Add coordinate systems to all viewports
    p.addCoordinateSystem(0.1, "global", 0);

    p.spin();
}

// This function finds the k nearest neighbors of the query object and returns their indices in the K-d tree and distances from the query object
// Inputs: K-d tree, query object model, desired candidate count (k)
// Outputs: K-d tree indices of candidates, Distances of candidates from the query object
inline void nearestKSearch(flann::Index<flann::ChiSquareDistance<float> >& index, const vfh_model& model,
    int k, flann::Matrix<int>& indices, flann::Matrix<float>& distances)
{
    // Query point
    flann::Matrix<float> p = flann::Matrix<float>(new float[model.second.size()], 1, model.second.size());
    memcpy(&p.ptr()[0], &model.second[0], p.cols * p.rows * sizeof(float));

    indices = flann::Matrix<int>(new int[k], 1, k);
    distances = flann::Matrix<float>(new float[k], 1, k);
    index.knnSearch(p, indices, distances, k, flann::SearchParams(512));
    delete[] p.ptr();
}

 recognize_objects.cpp

#include "recognize_objects.h"

int main(int argc, char** argv)
{
	int k = 6;
	double thresh = DBL_MAX;
	std::string model_dir = "vfh_training_data";
	std::string inputname = "pcd/pig_view1.pcd";
	std::vector<vfh_model> models;
	flann::Matrix<int> k_indices;
	flann::Matrix<float> k_distances;

	/*if (argc < 2) {
		PCL_ERROR("Usage: %s [-i <inputname>] [-m <model_dir>] [-k <neighbors>] [-t <threshold>]\n", argv[0]);
		return(-1);
	}*/

	// Parse console inputs
	/*pcl::console::parse_argument(argc, argv, "-i", inputname);
	pcl::console::parse_argument(argc, argv, "-m", model_dir);
	pcl::console::parse_argument(argc, argv, "-k", k);
	pcl::console::parse_argument(argc, argv, "-t", thresh);*/

	vfh_model histogram;
	std::string str = inputname;
	// Check if the query object has VFH signature, if not estimate signature and save it in a file
	if (!checkVFH(inputname))
	{
		pcl::PointCloud <pcl::VFHSignature308> signature;
		estimate_VFH(str, signature);
		str.replace(str.find(".pcd"), 4, "_vfh.pcd");
		pcl::io::savePCDFile(str, signature);
	}
	// Load VFH signature of the query object
	if (!loadHist(str, histogram))
	{
		pcl::console::print_error("Cannot load test file %s\n", inputname);
		return (-1);
	}

	// Load training data
	loadData(model_dir, models);

	// Convert data into FLANN format
	flann::Matrix<float> data(new float[models.size() * models[0].second.size()], models.size(), models[0].second.size());

	for (size_t i = 0; i < data.rows; ++i)
		for (size_t j = 0; j < data.cols; ++j)
			data[i][j] = models[i].second[j];

	// Place data in FLANN K-d tree
	flann::Index<flann::ChiSquareDistance<float> > index(data, flann::LinearIndexParams());
	index.buildIndex();

	// Search for query object in the K-d tree
	nearestKSearch(index, histogram, k, k_indices, k_distances);

	// Print closest candidates on the console
	std::cout << "The closest " << k << " neighbors for " << inputname << " are: " << std::endl;
	for (int i = 0; i < k; ++i)
		pcl::console::print_info("    %d - %s (%d) with a distance of: %f\n",
			i, models.at(k_indices[0][i]).first.c_str(), k_indices[0][i], k_distances[0][i]);

	// Visualize closest candidates on the screen
	visualize(argc, argv, k, thresh, models, k_indices, k_distances);

	return 0;
}

编译过程的问题:

PCL中有1个位置有一个bug以及c++17已经抛弃了的两处STL函数,会提示错误,这时,需要修改后才能通过编译。(以下都是我自己的文件路径)

1.文件路径:C:\Compiler\PCL\PCL 1.14.0\3rdParty\FLANN\include\flann\algorithms\dist.h

2.文件路径:C:\Compiler\PCL\PCL 1.14.0\3rdParty\FLANN\include\flann\util\heap.h 

这里注释掉

3.文件路径:C:\Compiler\PCL\PCL 1.14.0\3rdParty\FLANN\include\flann\util\random.h 

增加头文件:

注释掉:

std::random_shuffle(vals_.begin(), vals_.end(), generator);

增加:

std::random_device rd;
std::mt19937 g(rd());
std::shuffle(vals_.begin(), vals_.end(), g);

4.文件路径:C:\PCL1.12.1\3rdParty\FLANN\include\flann\algorithms\kdtree_index.h

 增加头文件:

for循环上面增加 :

std::random_device rd;
std::mt19937 g(rd());

for循环里面增加 : 

std::shuffle(vals_.begin(), vals_.end(), g);

for循环里面注释 :  

std::random_shuffle(ind.begin(), ind.end());

5.文件路径:C:\PCL1.12.1\3rdParty\FLANN\include\flann\util\lsh_table.h

增加头文件:

注释掉:

std::random_shuffle(indices.begin(), indices.end());

增加:

std::random_device rd;
std::mt19937 g(rd());
std::shuffle(indices.begin(), indices.end(), g);

6.安装HDF5

官网网址:Download HDF5® - The HDF Group

如果网速太慢,我这里有下载好的HDF5的C++安装包资源-CSDN文库

解压之后:

双击运行 ,基本上一直下一步就行了,中间有一步可以修改安装路径,安装完成。

在visual studio22里找到你之前配置好PCL环境的项目,而且已经放入我上述 recognize_objects.cpp和 recognize_objects.h,然后在该项目的包含目录里增加C:\Compiler\Format\HDF5\include,库目录里增加C:\Compiler\Format\HDF5\lib,附加依赖项增加hdf5.lib和hdf5_cpp.lib

7.完成上述步骤之后就可以运行了

关键代码解析:

typedef std::pair<std::string, std::vector<float> > vfh_model;

void estimate_VFH(const boost::filesystem::path& path, pcl::PointCloud <pcl::VFHSignature308>& signature)
{
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>());

    if (pcl::io::loadPCDFile<pcl::PointXYZ>(path.string(), *cloud) == -1) //* load the file
    {
        PCL_ERROR("Couldn't read file %s \n", path.string());
        return;
    }

    pcl::search::KdTree<pcl::PointXYZ>::Ptr normalTree(new pcl::search::KdTree<pcl::PointXYZ>());
    pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
    ne.setInputCloud(cloud);
    ne.setSearchMethod(normalTree);
    ne.setRadiusSearch(0.03);
    ne.compute(*normals);

    pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());
    pcl::VFHEstimation<pcl::PointXYZ, pcl::Normal, pcl::VFHSignature308> vfh;
    vfh.setInputCloud(cloud);
    vfh.setInputNormals(normals);
    vfh.setSearchMethod(tree);
    vfh.compute(signature);
}


bool checkVFH(const boost::filesystem::path& path)
{
    int vfh_idx;

    try
    {
        pcl::PCLPointCloud2 cloud;
        int version;
        Eigen::Vector4f origin;
        Eigen::Quaternionf orientation;
        pcl::PCDReader r;
        int type; unsigned int idx;
        r.readHeader(path.string(), cloud, origin, orientation, version, type, idx);

        vfh_idx = pcl::getFieldIndex(cloud, "vfh");
        if (vfh_idx == -1)
            return (false);
        if ((int)cloud.width * cloud.height != 1)
            return (false);
    }
    catch (const pcl::InvalidConversionException&)
    {
        return (false);
    }
    return (true);
}
  1. typedef std::pair<std::string, std::vector<float> > vfh_model;: 定义了一个 vfh_model 类型,它是一个包含字符串和浮点数向量的 pair,用于表示 VFH 特征模型。

  2. void estimate_VFH(const boost::filesystem::path& path, pcl::PointCloud<pcl::VFHSignature308>& signature): 这是一个函数,用于估计输入点云的 VFH 特征。

    • const boost::filesystem::path& path: 输入参数,表示点云文件的路径。

    • pcl::PointCloud<pcl::VFHSignature308>& signature: 输出参数,存储计算得到的 VFH 特征。

    • pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);: 创建一个指向点云的智能指针 cloud,用于存储加载的点云数据。

    • pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>());: 创建一个指向法线的智能指针 normals,用于存储估计的法线信息。

    • pcl::io::loadPCDFile<pcl::PointXYZ>(path.string(), *cloud) == -1: 通过 PCL 加载点云文件,如果加载失败则输出错误信息。

    • pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;: 创建法线估计对象 ne

    • ne.setInputCloud(cloud);: 设置法线估计的输入点云。

    • ne.setSearchMethod(normalTree);: 设置法线估计的搜索方法,这里使用了 KD 树。

    • ne.setRadiusSearch(0.03);: 设置法线估计的搜索半径。

    • ne.compute(*normals);: 计算法线并存储在 normals 中。

    • pcl::VFHEstimation<pcl::PointXYZ, pcl::Normal, pcl::VFHSignature308> vfh;: 创建 VFH 估计对象 vfh

    • vfh.setInputCloud(cloud);: 设置 VFH 估计的输入点云。

    • vfh.setInputNormals(normals);: 设置 VFH 估计的输入法线。

    • vfh.setSearchMethod(tree);: 设置 VFH 估计的搜索方法,这里同样使用了 KD 树。

    • vfh.compute(signature);: 计算 VFH 特征并存储在 signature 中。

  3. bool checkVFH(const boost::filesystem::path& path): 这是一个函数,用于检查点云文件是否包含 VFH 特征。

    • const boost::filesystem::path& path: 输入参数,表示点云文件的路径。

    • int vfh_idx;: 存储 VFH 特征的字段索引。

    • pcl::PCLPointCloud2 cloud;: 存储点云数据的 PCLPointCloud2 对象。

    • pcl::PCDReader r;: 创建 PCD 读取对象 r

    • r.readHeader(path.string(), cloud, origin, orientation, version, type, idx);: 读取点云文件的头信息。

    • vfh_idx = pcl::getFieldIndex(cloud, "vfh");: 获取 VFH 特征的字段索引。

    • if (vfh_idx == -1) return (false);: 如果 VFH 特征索引为 -1,表示未找到 VFH 特征字段,返回 false。

    • if ((int)cloud.width * cloud.height != 1) return (false);: 如果点云数据不是单帧(width * height 不等于 1),返回 false。

    • return (true);: 如果以上条件都满足,返回 true,表示点云包含 VFH 特征。

这些函数主要用于加载点云、估计 VFH 特征以及检查点云是否包含 VFH 特征。参数的设置主要包括法线估计的搜索半径、VFH 估计的搜索方法等,这些参数的选择需要根据点云数据的性质和具体应用场景进行调整。例如,搜索半径的大小会影响法线和 VFH 特征的计算精度,需要根据点云的密度和噪声程度来确定。

bool loadHist(const boost::filesystem::path& path, vfh_model& vfh)
{
    int vfh_idx;
    try
    {
        pcl::PCLPointCloud2 cloud;
        int version;
        Eigen::Vector4f origin;
        Eigen::Quaternionf orientation;
        pcl::PCDReader r;
        int type; unsigned int idx;
        r.readHeader(path.string(), cloud, origin, orientation, version, type, idx);

        vfh_idx = pcl::getFieldIndex(cloud, "vfh");
        if (vfh_idx == -1)
        {
            return (false);
        }
        if ((int)cloud.width * cloud.height != 1)
            return (false);
    }
    catch (const pcl::InvalidConversionException&)
    {
        return (false);
    }

    pcl::PointCloud <pcl::VFHSignature308> point;
    pcl::io::loadPCDFile(path.string(), point);
    vfh.second.resize(308);

    std::vector <pcl::PCLPointField> fields;
    pcl::getFieldIndex(point, "vfh", fields);

    for (size_t i = 0; i < fields[vfh_idx].count; ++i)
    {
        vfh.second[i] = point.points[0].histogram[i];
    }
    vfh.first = path.string();
    return (true);
}

这段代码是一个函数,名为 loadHist,其目的是从指定路径加载包含 VFH(Viewpoint Feature Histogram)数据的点云文件,并将加载的数据存储到自定义结构 vfh_model 中。下面是对代码的逐行解析:

  1. 函数签名

    • bool loadHist(const boost::filesystem::path& path, vfh_model& vfh)
  2. 参数

    • path:类型为 boost::filesystem::path 的引用,表示点云文件的路径。
    • vfh:类型为 vfh_model 的引用,用于存储加载后的 VFH 数据。
  3. 函数体

    • int vfh_idx;:声明了一个整型变量 vfh_idx,用于存储 VFH 字段在点云数据中的索引。
    • try { ... } catch (const pcl::InvalidConversionException&) { ... }:使用 try-catch 块捕获可能的异常,包括文件读取过程中的无效转换异常。如果出现异常,函数将返回 false
    • pcl::PCLPointCloud2 cloud;:声明了一个 pcl::PCLPointCloud2 类型的变量 cloud,用于存储点云数据。
    • int version;Eigen::Vector4f origin;Eigen::Quaternionf orientation;int type;unsigned int idx;:声明了一些其他变量,用于存储点云数据的版本、原点、方向、类型等信息。
    • r.readHeader(path.string(), cloud, origin, orientation, version, type, idx);:使用 PCL 中的 readHeader 函数读取点云文件的头部信息,并将信息存储到相应的变量中。
    • vfh_idx = pcl::getFieldIndex(cloud, "vfh");:获取 VFH 字段在点云数据中的索引,并存储到 vfh_idx 中。
    • if (vfh_idx == -1) { return false; }:如果 VFH 字段在点云数据中不存在,则返回 false
    • if ((int)cloud.width * cloud.height != 1) return false;:确保点云数据是一维的,即宽度乘以高度等于 1。如果不是,则返回 false
    • pcl::PointCloud<pcl::VFHSignature308> point;:声明了一个类型为 pcl::PointCloud<pcl::VFHSignature308> 的点云变量 point,用于存储加载的点云数据。
    • pcl::io::loadPCDFile(path.string(), point);:使用 PCL 中的 loadPCDFile 函数加载点云文件到 point 变量中。
    • vfh.second.resize(308);:调整 vfh 结构体中的 second 成员的大小为 308,以存储 VFH 数据。
    • std::vector<pcl::PCLPointField> fields;:声明了一个 std::vector 类型的变量 fields,用于存储点云数据的字段信息。
    • pcl::getFieldIndex(point, "vfh", fields);:获取 VFH 字段的信息,并存储到 fields 变量中。
    • for (size_t i = 0; i < fields[vfh_idx].count; ++i) { vfh.second[i] = point.points[0].histogram[i]; }:遍历 VFH 字段中的直方图数据,将其存储到 vfh 结构体的 second 成员中。
    • vfh.first = path.string();:将点云文件的路径存储到 vfh 结构体的 first 成员中。
    • return true;:返回 true,表示加载成功。
  4. 参数设置及影响

    • path 参数用于指定点云文件的路径。调用该函数时,需要提供有效的点云文件路径。
    • vfh 参数用于存储加载后的 VFH 数据。调用该函数时,需要传递一个 vfh_model 类型的变量的引用,以便在函数内部存储加载后的数据。
  5. 影响

    • 如果加载成功,函数会将点云文件中的 VFH 数据存储到 vfh 结构体中,并返回 true
    • 如果加载失败(例如,无法读取文件、文件中不存在 VFH 数据等),则函数将返回 false

void loadData(const boost::filesystem::path& base_dir, std::vector<vfh_model>& models)
{
    if (!boost::filesystem::exists(base_dir) && !boost::filesystem::is_directory(base_dir))
        return;
    else
    {
        for (boost::filesystem::directory_iterator i(base_dir); i != boost::filesystem::directory_iterator(); i++)
        {
            if (boost::filesystem::is_directory(i->status()))
            {
                std::stringstream ss;
                ss << i->path();
                pcl::console::print_highlight("Loading %s (%lu models loaded so far).\n", ss.str().c_str(), (unsigned long)models.size());
                loadData(i->path(), models);
            }
            
            if (boost::filesystem::is_regular_file(i->status()) && boost::filesystem::extension(i->path()) == ".pcd")
            {
                vfh_model m;
                std::string str = i->path().string();
                if (!checkVFH(str))
                {
                    str.replace(str.find(".pcd"), 4, "_vfh.pcd");
                    if (!boost::filesystem::exists(str))
                    {
                        pcl::PointCloud <pcl::VFHSignature308> signature;
                        estimate_VFH(i->path().string(), signature);

                        pcl::io::savePCDFile(str, signature);
                    }
                }
                if (loadHist(i->path().string(), m))
                {
                    models.push_back(m);
                    std::cout << m.first << std::endl;
                }
            }
        }
    }
}
  1. base_dir:这是一个 boost::filesystem::path 类型的引用,表示要加载数据的基本目录。
  2. models:这是一个 std::vector<vfh_model> 类型的引用,用于存储加载的数据模型。

现在让我们逐行解析代码:

  1. if (!boost::filesystem::exists(base_dir) && !boost::filesystem::is_directory(base_dir)):这一行检查指定的 base_dir 是否存在且是否是一个目录。如果 base_dir 既不存在也不是目录,则函数直接返回,不执行后续操作。

  2. for (boost::filesystem::directory_iterator i(base_dir); i != boost::filesystem::directory_iterator(); i++):这一行通过遍历指定目录 base_dir 中的文件和子目录来实现。

  3. if (boost::filesystem::is_directory(i->status())):在循环中,对于每个目录项,首先检查是否是一个目录。如果是目录,则递归调用 loadData 函数,加载子目录中的数据。

  4. if (boost::filesystem::is_regular_file(i->status()) && boost::filesystem::extension(i->path()) == ".pcd"):对于每个文件项,检查是否是一个常规文件且是否是以 ".pcd" 为扩展名的文件。

  5. vfh_model m;:在满足条件的情况下,创建一个名为 mvfh_model 对象,用于存储加载的模型数据。

  6. std::string str = i->path().string();:获取当前文件的路径并将其转换为 std::string 类型。

  7. if (!checkVFH(str)):调用 checkVFH 函数检查是否存在与当前文件相关联的 VFH(Viewpoint Feature Histogram)文件。如果不存在,将生成该文件。

  8. str.replace(str.find(".pcd"), 4, "_vfh.pcd");:将当前文件的扩展名从 ".pcd" 替换为 "_vfh.pcd",以获取相关的 VFH 文件路径。

  9. if (!boost::filesystem::exists(str)):检查生成的 VFH 文件是否已经存在,如果不存在,则执行以下操作。

  10. pcl::PointCloud<pcl::VFHSignature308> signature;:声明一个 pcl::PointCloud 类型的对象 signature,用于存储 VFH 特征。

  11. estimate_VFH(i->path().string(), signature);:调用 estimate_VFH 函数计算当前文件的 VFH 特征。

  12. pcl::io::savePCDFile(str, signature);:将计算得到的 VFH 特征保存到生成的 VFH 文件中。

  13. if (loadHist(i->path().string(), m)):尝试加载当前文件的直方图数据到 m 中。

  14. models.push_back(m);:将加载的模型数据 m 添加到 models 向量中。

  15. std::cout << m.first << std::endl;:打印加载的模型的第一个元素到控制台。

关于参数设置和影响:

  • base_dir 参数用于指定要加载数据的基本目录。
  • models 参数用于存储加载的数据模型。
  • 修改 base_dir 可以改变加载数据的根目录。
  • 修改 models 可以改变加载的数据存储方式,例如,可以将加载的模型数据存储到其他容器或者进行其他处理。
void visualize(int argc, char** argv, int k, double thresh, std::vector<vfh_model> models, flann::Matrix<int> k_indices, flann::Matrix<float> k_distances)
{
    // Load the results
    pcl::visualization::PCLVisualizer p(argc, argv, "VFH Cluster Classifier");
    int y_s = (int)floor(sqrt((double)k));
    int x_s = y_s + (int)ceil((k / (double)y_s) - y_s);
    double x_step = (double)(1 / (double)x_s);
    double y_step = (double)(1 / (double)y_s);
    pcl::console::print_highlight("Preparing to load ");
    pcl::console::print_value("%d", k);
    pcl::console::print_info(" files (");
    pcl::console::print_value("%d", x_s);
    pcl::console::print_info("x");
    pcl::console::print_value("%d", y_s);
    pcl::console::print_info(" / ");
    pcl::console::print_value("%f", x_step);
    pcl::console::print_info("x");
    pcl::console::print_value("%f", y_step);
    pcl::console::print_info(")\n");

    int viewport = 0, l = 0, m = 0;
    for (int i = 0; i < k; ++i)
    {
        std::string cloud_name = models.at(k_indices[0][i]).first;

        boost::replace_last(cloud_name, "_vfh", "");

        p.createViewPort(l * x_step, m * y_step, (l + 1) * x_step, (m + 1) * y_step, viewport);
        l++;
        if (l >= x_s)
        {
            l = 0;
            m++;
        }

        pcl::PCLPointCloud2 cloud;
        pcl::console::print_highlight(stderr, "Loading "); pcl::console::print_value(stderr, "%s ", cloud_name.c_str());
        if (pcl::io::loadPCDFile(cloud_name, cloud) == -1)
            break;

        pcl::PointCloud<pcl::PointXYZ> cloud_xyz;
        pcl::fromPCLPointCloud2(cloud, cloud_xyz);

        if (cloud_xyz.points.size() == 0)
            break;

        pcl::console::print_info("[done, ");
        pcl::console::print_value("%d", (int)cloud_xyz.points.size());
        pcl::console::print_info(" points]\n");
        pcl::console::print_info("Available dimensions: ");
        pcl::console::print_value("%s\n", pcl::getFieldsList(cloud).c_str());

        Eigen::Vector4f centroid;
        pcl::compute3DCentroid(cloud_xyz, centroid);
        pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_xyz_demean(new pcl::PointCloud<pcl::PointXYZ>);
        pcl::demeanPointCloud<pcl::PointXYZ>(cloud_xyz, centroid, *cloud_xyz_demean);
        p.addPointCloud(cloud_xyz_demean, cloud_name, viewport);

        std::stringstream ss;
        ss << k_distances[0][i];
        if (k_distances[0][i] > thresh)
        {
            p.addText(ss.str(), 20, 30, 1, 0, 0, ss.str(), viewport);

            pcl::PointXYZ min_p, max_p;
            pcl::getMinMax3D(*cloud_xyz_demean, min_p, max_p);
            std::stringstream line_name;
            line_name << "line_" << i;
            p.addLine(min_p, max_p, 1, 0, 0, line_name.str(), viewport);
            p.setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, 5, line_name.str(), viewport);
        }
        else
            p.addText(ss.str(), 20, 30, 0, 1, 0, ss.str(), viewport);

        p.setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_FONT_SIZE, 18, ss.str(), viewport);

        p.addText(cloud_name, 20, 10, cloud_name, viewport);
    }

    p.addCoordinateSystem(0.1, "global", 0);

    p.spin();
}

这段代码是一个PCL(Point Cloud Library)程序,用于可视化点云匹配结果。通过VFH(Viewpoint Feature Histogram)特征模型与已知模型数据库的匹配,选出k个最相似的模型,并在一个PCLVisualizer窗口中可视化显示。

代码参数如下:

  • argcargv:命令行参数的数量和值,用于初始化PCLVisualizer。

  • k:要显示的最相似模型的数量。

  • thresh:距离阈值,用于决定是否在模型上绘制文本和线条。

  • models:一个结构体vector,包含数据库中所有模型的名称和对应的VFH特征。

  • k_indices:FLANN库输出的最近邻索引,这里表示最相似模型的索引。

  • k_distances:FLANN库输出的最近邻距离,表示每个最相似模型与查询模型的距离。

代码逻辑如下:

  1. 初始化PCLVisualizer对象p

  2. 根据k的值计算可视化窗口网格的大小(x_sy_s)和步长(x_stepy_step)。

  3. 循环k次,每次处理一个最相似的模型:

    • 获取模型名称并加载对应的点云文件。

    • 创建一个新的视图端口,用于显示当前模型的点云。

    • 将加载的点云转换为PointXYZ类型,并居中处理。

    • 将处理过的点云添加到对应的视图端口中。

    • 如果当前模型与查询模型的距离大于thresh阈值,则在视图端口中添加一条红色线段和红色文本。否则,添加绿色文本。

    • 设置文本的字体大小。

    • 添加模型的名称作为文本。

  4. 在可视化窗口中添加一个全局坐标系。

  5. 启动可视化并等待用户交互。

调整参数会产生以下影响:

  • k的值会改变显示的模型数量。如果k太大,窗口中的模型可能会太密集,难以查看;如果k太小,可能无法显示足够的匹配结果。

  • thresh阈值会影响哪些模型会在视图端口中被突出显示。阈值太大,可能会有过多的模型被突出显示;阈值太小,则可能没有模型被突出显示。

  • modelsk_indicesk_distances是根据点云匹配结果提供的,通常不会手动调整,但如果匹配算法或数据发生变化,这些值也会相应变化,影响可视化结果。

请注意,代码中可能会有错误处理不足(如在加载点云失败后直接使用break退出循环),在实际使用中可能需要更健壮的错误处理机制。此外,这段代码假定了k_indicesk_distances有足够的结果来支持k个最相似模型的显示,这在实际应用中可能需要确保。

inline void nearestKSearch(flann::Index<flann::ChiSquareDistance<float> >& index, const vfh_model& model,
    int k, flann::Matrix<int>& indices, flann::Matrix<float>& distances)
{
    flann::Matrix<float> p = flann::Matrix<float>(new float[model.second.size()], 1, model.second.size());
    memcpy(&p.ptr()[0], &model.second[0], p.cols * p.rows * sizeof(float));

    indices = flann::Matrix<int>(new int[k], 1, k);
    distances = flann::Matrix<float>(new float[k], 1, k);
    index.knnSearch(p, indices, distances, k, flann::SearchParams(512));
    delete[] p.ptr();
}
  1. inline void nearestKSearch(flann::Index<flann::ChiSquareDistance<float> >& index, const vfh_model& model, int k, flann::Matrix<int>& indices, flann::Matrix<float>& distances): 这是函数的声明,它接受一个FLANN索引(index)、一个包含模型数据的结构体(model)、一个整数k(指定要搜索的最近邻的数量)、两个用于存储搜索结果的矩阵indicesdistances

  2. flann::Matrix<float> p = flann::Matrix<float>(new float[model.second.size()], 1, model.second.size());: 在这一行中,创建了一个flann::Matrix<float>对象p,用于存储模型数据。这里使用new分配了一块内存来存储模型数据,其大小为model.second.size()float元素。这个model参数似乎是一个结构体,其中的.second成员存储了模型数据。

  3. memcpy(&p.ptr()[0], &model.second[0], p.cols * p.rows * sizeof(float));: 这一行通过memcpy函数将模型数据从model结构体中的.second成员复制到了矩阵p中。这样做是为了将数据转换为FLANN库中使用的矩阵格式。

  4. indices = flann::Matrix<int>(new int[k], 1, k);: 这里创建了一个flann::Matrix<int>对象indices,用于存储搜索结果中的最近邻的索引。同样使用new分配了一块大小为k的内存来存储索引。

  5. distances = flann::Matrix<float>(new float[k], 1, k);: 这里创建了一个flann::Matrix<float>对象distances,用于存储搜索结果中最近邻的距离。同样使用new分配了一块大小为k的内存来存储距离。

  6. index.knnSearch(p, indices, distances, k, flann::SearchParams(512));: 这一行调用了FLANN库中的knnSearch函数来执行最近邻搜索。它传递了模型数据矩阵p、最近邻的索引矩阵indices、最近邻的距离矩阵distances、要搜索的最近邻数量k,以及一些搜索参数(例如,flann::SearchParams(512),这可能是一个搜索参数对象,但具体的含义取决于FLANN库的文档或者实现)。

  7. delete[] p.ptr();: 最后,释放了先前为模型数据分配的内存。

参数的设置和影响:

  • index: 这是一个FLANN索引对象,用于加速最近邻搜索。
  • model: 包含模型数据的结构体或类对象。
  • k: 指定要搜索的最近邻的数量。
  • indices: 用于存储最近邻的索引的矩阵。
  • distances: 用于存储最近邻的距离的矩阵。
  • flann::SearchParams(512): 这可能是一个FLANN库中的搜索参数对象,其具体含义需要查阅FLANN库的文档来确定。

根据FLANN库的实现和所处理的数据类型,不同的参数设置可能会影响搜索的速度、准确性和内存占用等方面。

#include "recognize_objects.h"

int main(int argc, char** argv)
{
	int k = 6;
	double thresh = DBL_MAX;
	std::string model_dir = "vfh_training_data";
	std::string inputname = "pcd/pig_view1.pcd";
	std::vector<vfh_model> models;
	flann::Matrix<int> k_indices;
	flann::Matrix<float> k_distances;

	vfh_model histogram;
	std::string str = inputname;
	if (!checkVFH(inputname))
	{
		pcl::PointCloud <pcl::VFHSignature308> signature;
		estimate_VFH(str, signature);
		str.replace(str.find(".pcd"), 4, "_vfh.pcd");
		pcl::io::savePCDFile(str, signature);
	}
	if (!loadHist(str, histogram))
	{
		pcl::console::print_error("Cannot load test file %s\n", inputname);
		return (-1);
	}
	loadData(model_dir, models);
	flann::Matrix<float> data(new float[models.size() * models[0].second.size()], models.size(), models[0].second.size());

	for (size_t i = 0; i < data.rows; ++i)
		for (size_t j = 0; j < data.cols; ++j)
			data[i][j] = models[i].second[j];
	flann::Index<flann::ChiSquareDistance<float> > index(data, flann::LinearIndexParams());
	index.buildIndex();
	nearestKSearch(index, histogram, k, k_indices, k_distances);
	std::cout << "The closest " << k << " neighbors for " << inputname << " are: " << std::endl;
	for (int i = 0; i < k; ++i)
		pcl::console::print_info("    %d - %s (%d) with a distance of: %f\n",
			i, models.at(k_indices[0][i]).first.c_str(), k_indices[0][i], k_distances[0][i]);
	visualize(argc, argv, k, thresh, models, k_indices, k_distances);

	return 0;
}
  1. int k = 6;: 定义了用于最近邻搜索的邻居数量,这里设置为6。

  2. double thresh = DBL_MAX;: 定义了阈值,默认为DBL_MAX,表示没有特定的距离阈值。

  3. std::string model_dir = "vfh_training_data";: 定义了存储训练模型数据的目录,默认为"vfh_training_data"。

  4. std::string inputname = "pcd/pig_view1.pcd";: 定义了输入点云文件的路径,默认为"pcd/pig_view1.pcd"。

  5. std::vector<vfh_model> models;: 定义了存储训练模型的容器。

  6. flann::Matrix<int> k_indices;: 定义了一个矩阵,用于存储最近邻的索引。

  7. flann::Matrix<float> k_distances;: 定义了一个矩阵,用于存储最近邻的距离。

  8. vfh_model histogram;: 定义了一个VFH特征模型,用于存储输入点云的VFH特征。

  9. std::string str = inputname;: 将输入文件路径赋给str,用于后续的文件处理。

  10. if (!checkVFH(inputname)) { /* ... */ }: 如果输入点云文件的VFH特征不存在,则通过estimate_VFH函数计算VFH特征并保存到文件中。

  11. if (!loadHist(str, histogram)) { /* ... */ }: 如果加载VFH特征失败,则输出错误信息并返回。

  12. loadData(model_dir, models);: 加载训练数据,将训练模型的VFH特征存储到models容器中。

  13. flann::Matrix<float> data(new float[models.size() * models[0].second.size()], models.size(), models[0].second.size());: 创建一个矩阵data,用于存储训练模型的VFH特征数据。

  14. for (size_t i = 0; i < data.rows; ++i) for (size_t j = 0; j < data.cols; ++j) data[i][j] = models[i].second[j];: 将训练模型的VFH特征数据填充到矩阵data中。

  15. flann::Index<flann::ChiSquareDistance<float> > index(data, flann::LinearIndexParams());: 使用FLANN库创建索引结构,采用卡方距离(ChiSquareDistance)作为距离度量。

  16. index.buildIndex();: 建立FLANN索引,以加速最近邻搜索。

  17. nearestKSearch(index, histogram, k, k_indices, k_distances);: 调用先前解析的nearestKSearch函数,搜索输入VFH特征的最近邻。

  18. 输出最近邻的信息到控制台。

  19. visualize(argc, argv, k, thresh, models, k_indices, k_distances);: 调用一个名为visualize的函数,可能用于可视化结果,传递了一些参数,包括阈值、训练模型、最近邻的索引和距离等。

这段代码的参数设置主要包括最近邻的数量k、阈值thresh、模型目录model_dir、输入点云文件路径inputname等。影响主要体现在最近邻搜索的准确性和速度上,以及可视化函数中的可视化效果。在调用FLANN库的函数时,距离度量的选择、搜索参数的设置等也会影响搜索结果的质量和性能。

结果:

PCL使用K-4PCS\SAC\ICP\NDT\GICP进行点云配准-CSDN博客 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Spring Boot的智能物流管理系统,计算机毕业设计(带源码+论文)

源码获取地址&#xff1a; 码呢-一个专注于技术分享的博客平台一个专注于技术分享的博客平台,大家以共同学习,乐于分享,拥抱开源的价值观进行学习交流http://www.xmbiao.cn/resource-details/1759581137025445890

做temu跨境电商,必读这五大秘诀!

随着互联网的飞速发展&#xff0c;电商行业呈现出前所未有的繁荣景象。新兴电商平台Temu成为了众多创业者的关注焦点。本文将为您解析如何在Temu电商蓝海项目中&#xff0c;从自身能力建设、了解市场环境到做好定位等方面&#xff0c;找到属于您的成功之路。 一、自身能力建设 …

【白嫖8k买的机构vip教程】Selenium(3):python+selenium环境安装

准备工具如下&#xff1a; Python安装包&#xff1a;https://www.python.org/getit/PyCharm Pycharm安装包&#xff1a;http://www.jetbrains.com/pycharm/download/Selenium Selenium安装包&#xff1a;https://pypi.python.org/pypi/selenium、或者在pycharm中直接下载seleni…

Vector - CANoe - CAPL重启VN设备退出BusOff

在总线测试中进行BusOff测试的时候&#xff0c;偶尔会遇到将Vector工具链下的VN系列设备进入到BusOff状态&#xff0c;这个时候我们就只能重启CANoe才能将VN系列设备进行重启&#xff0c;才可以再次在Trace窗口上看到发送和接收的报文。不过在某些特定的情况的下&#xff0c;我…

【大模型 知识图谱】ChatKBQA:KBQA知识图谱问答 + 大模型

ChatKBQA&#xff1a;KBQA知识图谱问答 大模型 提出背景传统方法处理流程ChatKBQA处理流程对比优势 总结ChatKBQA框架概览特征1&#xff1a;逻辑形式生成特征2&#xff1a;无监督实体和关系检索特征3&#xff1a;参数高效的微调特征4&#xff1a;GQoT 可解释的查询执行特征5&a…

【白嫖8k买的机构vip教程】安卓设备连接电脑进行adb命令操作

Android 真机进行adb 命令的操作&#xff1a; 连接方式 &#xff1a; 数据线连接&#xff1a; 1&#xff09;手机需通过数据线连接电脑&#xff1b; 2&#xff09;手机调出开发者选项&#xff1b;手机操作步骤&#xff1a;设置——系统——关于手机&#xff08;平板电脑&am…

VMware还原Windows11 ghost镜像

文章目录 环境步骤准备制作启动iso文件创建虚拟机启动虚拟机还原Windows 参考 环境 Windows 11 家庭中文版VMware Workstation 17 Pro石大师装机大师Windows 11 ghost系统镜像 步骤 准备 下载好Windows 11 ghost系统镜像&#xff0c;我下载的文件是 FQ_WIN11_X64_VDL_V2080…

【教3妹学编程-算法题】将数组分成最小总代价的子数组 II

2哥 : 叮铃铃&#xff0c;3妹&#xff0c;过年干嘛呢&#xff0c;是不是逛吃逛吃&#xff0c;有没有长胖呢。 3妹&#xff1a;切&#xff0c;我妈张罗着要给我相亲呢。 2哥 : 相亲&#xff1f;哈哈哈哈 3妹&#xff1a;别笑了&#xff0c;我妈说跟我年龄相等的人都已经孩子上小…

为什么2023年是AI视频的突破年,以及对2024年的预期#a16z

2023年所暴露的AI生成视频的各种问题&#xff0c;大部分被OpenAI发布的Sora解决了吗&#xff1f;以下为a16z发布的总结&#xff0c;在关键之处&#xff0c;我做了OpenAI Sora的对照备注。 推荐阅读&#xff0c;了解视频生成技术进展。 Why 2023 Was AI Video’s Breakout Year,…

开发消息多发工具需要用到的源代码

在数字化时代&#xff0c;消息传递是许多应用程序的核心功能之一&#xff0c;从社交媒体到企业通信&#xff0c;从个人聊天到群发消息&#xff0c;消息传递无处不在&#xff0c;为了满足这种需求&#xff0c;开发者经常需要创建或定制消息多发工具。 这些工具通常需要处理多个…

MySQL数据库基础(十):DQL数据查询语言

文章目录 DQL数据查询语言 一、数据集准备 二、select查询 三、简单查询 四、条件查询 1、比较查询 2、范围查询 3、逻辑查询 4、模糊查询 5、非空查询 五、排序查询 六、聚合查询 七、分组查询与having子句 1、分组查询介绍 2、group by的使用 3、group by 聚…

【深入理解设计模式】单例设计模式

单例设计模式 概念&#xff1a; 单例模式&#xff08;Singleton Pattern&#xff09;是 Java 中最简单的设计模式之一。 单例设计模式是一种创建型设计模式&#xff0c;其主要目的是确保类在应用程序中的一个实例只有一个。这意味着无论在应用程序的哪个位置请求该类的实例&a…

网站常见的反爬手段及反反爬思路

摘要:介绍常见的反爬手段和反反爬思路&#xff0c;内容详细具体&#xff0c;明晰解释每一步&#xff0c;非常适合小白和初学者学习&#xff01;&#xff01;&#xff01; 目录 一、明确几个概念 二、常见的反爬手段及反反爬思路 1、检测user-agent 2、ip 访问频率的限制 …

Web服务器基础

Web服务器基础 【一】前端概述 【1】HTML HTML&#xff08;超文本标记语言&#xff09;是用于创建网页结构的标记语言。它定义了网页的骨架&#xff0c;包括标题、段落、列表、链接等元素&#xff0c;但没有样式。可以将HTML视为网页的结构和内容的描述。 【2】CSS css&…

NAS系统折腾记 | TinyMediaManager刮削电影海报

搭建好了NAS系统和Emby Media Server&#xff0c;接下来就是怎样对下载好的电影/电视剧集等内容进行刮削来展示电影海报墙获得更好的效果了。实际上&#xff0c;Emby Server本身就内置了强大的元数据抓取功能&#xff0c;能够自动从互联网上抓取电影、电视剧的元数据和海报等信…

mysql在服务器中的主从复制Linux下

mysql在服务器中的主从复制Linux下 为什么要进行主从复制主从复制的原理主从复制执行流程操作步骤主库创建从库创建 测试 为什么要进行主从复制 在业务中通常会有情况&#xff0c;在sql执行时&#xff0c;将表锁住&#xff0c;导致不能进行查询&#xff0c;这样就会影响业务的…

【教程】MySQL数据库学习笔记(一)——认识与环境搭建(持续更新)

写在前面&#xff1a; 如果文章对你有帮助&#xff0c;记得点赞关注加收藏一波&#xff0c;利于以后需要的时候复习&#xff0c;多谢支持&#xff01; 【MySQL数据库学习】系列文章 第一章 《认识与环境搭建》 第二章 《数据类型》 文章目录 【MySQL数据库学习】系列文章一、认…

C++ 位运算常用操作 二进制中1的个数

给定一个长度为 n 的数列&#xff0c;请你求出数列中每个数的二进制表示中 1 的个数。 输入格式 第一行包含整数 n 。 第二行包含 n 个整数&#xff0c;表示整个数列。 输出格式 共一行&#xff0c;包含 n 个整数&#xff0c;其中的第 i 个数表示数列中的第 i 个数的二进制表…

深度解析 Transformer 模型:原理、应用与实践指南【收藏版】

深度解析 Transformer 模型&#xff1a;原理、应用与实践指南 1. Transformer 模型的背景与引言2. Transformer 模型的原理解析2.1 自注意力机制&#xff08;Self-Attention&#xff09;自注意力机制原理 2.2 多头注意力机制&#xff08;Multi-Head Attention&#xff09;多头注…

vue的生命周期图解

vue的生命周期图解 添加链接描述 vue的生命周期函数及过程的简述&#xff1a; vue的生命周期函数&#xff0c;其实就是vm的生命周期&#xff1b; 创建&#xff1a;beforeCreate、created 挂载&#xff1a;beforeMount、mounted 更新&#xff1a;beforeUpdate、updated [ˌʌpˈ…