PCL学习五:Range Images-距离图像

news/2024/10/18 5:50:48/

参考引用

  • Point Cloud Library
  • 黑马机器人 | PCL-3D点云
  • PCL深度图像

1. 引言

1.1 深度图像的获取与研究方向

  • 获取方法

    • 激光雷达深度成像法,计算机立体视觉成像,坐标测量机法,莫尔条纹法,结构光法
  • 研究方向

    • 深度图像的分割技术
    • 深度图像的边缘检测技术
    • 基于不同视点的多幅深度图像的配准技术
    • 基于深度数据的三维重建技术
    • 基于三维深度图像的三维目标识别技术
    • 深度图像的多分辨率建模和几何压缩技术

1.2 深度图像定义

  • 深度图像(Depth Images)也被称为距离影像(Range Image),是指:将从图像采集器到场景中各点的距离值作为像素值的图像,它直接反应了景物可见表面的几何形状(来自传感器一个特定角度拍摄的一个三维场景获取的有规则的有焦距等基本信息的深度图)

  • 从数学模型上看,深度图像可以看做是标量函数 j : I 2 → R j:I^2\to R j:I2R 在几何 K K K 上的离散采样,得到 r i = j ( u i ) r_i=j(u_i) ri=j(ui),其中 u i ∈ I 2 u_i\in I^2 uiI2 为二维网格(矩阵)的索引, r i ∈ R , i = 1 , … , k r_i\in R,i=1,\ldots,k riR,i=1,,k以下为从不同视角获得的深度图像过程示意
    在这里插入图片描述

  • 点云:当一束激光照射到物体表面时,所反射的激光会携带方位、距离和强度等信息。若将激光束按照某种轨迹进行扫描,便会边扫描边记录到反射的激光点信息,由于扫描极为精细,则能够得到大量的激光点,因而就可形成激光点云。点云格式有 .las、.pcd、.txt

    在 PCL 中深度图像与点云最主要的区别在于:近邻的检索方式不同,并且可以互相转换,深度图像经过点云变换可以计算为点云数据,有规则及有必要信息的点云数据可以反算为深度图像数据

2. 从点云创建深度图

2.1 pcl::RangeImage

  • RangeImage 类继承于 PointCloud,主要功能是:实现一个特定视点得到一个三维场景的深度图像,其继承关系如下
    在这里插入图片描述

2.2 核心函数及参数说明

  • 函数
    createFromPointCloud(pointCloud, angularResolution, maxAngleWidth, maxAngleHeight,sensorPose, coordinate_frame, noiseLevel, minRange, borderSize)
    
  • 参数
    • pointCloud:被检测点云
    • angularResolution = 1:邻近的像素点所对应的每个光束之间相差 1°
    • maxAngleWidth = 360:进行模拟的距离传感器对周围的环境拥有一个完整的 360° 视角,无论任何数据集都推荐使用此设置,因为最终获取的深度图像将被裁剪到有空间物体存在的区域范围
    • maxAngleHeight = 180:当传感器后面没有可以观测的点时,设置一个水平视角为 180° 的激光扫描仪即可,因为只需要观察距离传感器前面就可以了
    • sensorPose:定义了模拟深度图像获取传感器的 6 自由度位置,其原始值为横滚角 roll、俯仰角 pitch、偏航角 yaw 都为 0,设置的模拟传感器的位姿是一个仿射变换矩阵,默认为 4*4 的单位矩阵变换
    • coordinate_frame:设置为 CAMERA_FRAME 说明系统的 X 轴是向右的、Y 轴是向下的、Z 轴是向前的,另外参数值是 LASER_FRAME,其 X 轴向前、Y 轴向左、Z 轴向上
    • noiseLevel = 0:是指使用一个归一化的 Z 缓存区来创建深度图像,如果想让邻近点集都落在同一个像素单元,可以设置一个较高的值,例如 noiseLevel = 0.05 可以理解为深度距离值是通过查询点半径为 5cm 的圆内包含的点用来平均计算而得到的
    • minRange = 0:设置最小的获取距离,如果设置 > 0 则所有模拟器所在位置半径 minRange 内的邻近点都将被忽略,即为盲区
    • borderSize = 1:设置获取深度图像边缘的宽度,默认为 0。如果设置 > 0 ,在裁剪图像时,将在图像周围留下当前视点不可见点的边界

2.3 代码实现(C++)

#include <pcl/range_image/range_image.h>
#include <pcl/io/pcd_io.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/visualization/range_image_visualizer.h>int main(int argc, char **argv) {pcl::PointCloud<pcl::PointXYZ>::Ptr pointCloudPtr(new pcl::PointCloud<pcl::PointXYZ>);// 智能指针 pointCloudPtr 指向堆内存中的一个点云对象,而传递对象时通常有两种方式:通过值进行传递或通过引用进行传递// 1. 值传递方式,要将整个对象从一个地方复制到另一个地方,会产生大量开销,尤其当对象较大时程序运行缓慢// 2. 引用的方式,只是将指针所指向的对象暴露出来,不会进行拷贝操作,可提高程序效率pcl::PointCloud<pcl::PointXYZ> &pointCloud = *pointCloudPtr; // 使用引用的形式赋值是为了避免拷贝操作,可提高效率// 创建一个矩形形状的点云// 1.遍历一个立方体的所有点,并将每个点添加到点云对象中。这个立方体的长、宽、高为 1,中心点坐标为(1, 0, 0)// 2.最终生成了一个包含 100000 个点的点云对象,其中每个点在以(1, 0, 0)为中心// 的 1x1x1 的立方体内,并且在 y-z 平面内均匀分布for (float y = -0.5f; y <= 0.5f; y += 0.01f) {for (float z = -0.5f; z <= 0.5f; z += 0.01f) {pcl::PointXYZ point;point.x = 2.0f - y;point.y = y;point.z = z;pointCloud.points.push_back(point); // 循环添加点数据到点云对象}}pointCloud.width = (uint32_t) pointCloud.points.size();pointCloud.height = 1;// pcl::io::loadPCDFile("./data/table_scene_lms400.pcd", pointCloud);// 根据前面得到的点云图,通过 1deg 的分辨率生成深度图// 模拟深度传感器的角度分辨率,即深度图像中一个像素对应的角度大小(都转为弧度)float angularResolution = (float)(1.0f * (M_PI / 180.0f)); // 弧度 1°// 模拟深度传感器的水平最大采样角度(都转为弧度)float maxAngleWidth = (float) (360.0f * (M_PI / 180.0f)); // 弧度 360°// 模拟传感器的垂直方向最大采样角度(都转为弧度)float maxAngleHeight = (float) (180.0f * (M_PI / 180.0f)); // 弧度 180°Eigen::Affine3f sensorPose = (Eigen::Affine3f) Eigen::Translation3f(0.0f, 0.0f, 0.0f);  // 传感器采集位置pcl::RangeImage::CoordinateFrame coordinate_frame = pcl::RangeImage::CAMERA_FRAME;      // 相机坐标系float noiseLevel = 0.00;    // 获取深度图像深度时,近邻点对查询点距离值的影响水平float minRange = 0.0f;    // 设置最小的获取距离,小于最小获取距离的位置为传感器的盲区int borderSize = 1;    // 获得深度图像的边缘的宽度// 使用智能指针 boost::shared_ptr 创建一个名为 range_image_ptr 的指向 pcl::RangeImage 对象的指针// 同时动态分配内存,该对象可被多个指针共享,直到没有指针引用它时才会自动释放boost::shared_ptr<pcl::RangeImage> range_image_ptr(new pcl::RangeImage);pcl::RangeImage &rangeImage = *range_image_ptr;rangeImage.createFromPointCloud(pointCloud, angularResolution, maxAngleWidth, maxAngleHeight,sensorPose, coordinate_frame, noiseLevel, minRange, borderSize);std::cout << rangeImage << "\n";// 3D 可视化操作pcl::visualization::PCLVisualizer viewer ("3D Viewer");viewer.setBackgroundColor (1, 1, 1);// 添加深度图点云pcl::visualization::PointCloudColorHandlerCustom<pcl::PointWithRange> 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, 4, "range image");// 添加原始点云pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> org_image_color_handler (pointCloudPtr, 255, 100, 0);viewer.addPointCloud (pointCloudPtr, org_image_color_handler, "orginal image");viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "orginal image");viewer.initCameraParameters();viewer.addCoordinateSystem(1.0);// 在可视化窗口中实时显示点云数据,并能够对其进行交互操作。当用户关闭窗口时,循环结束,程序自动退出while (!viewer.wasStopped ()) {viewer.spinOnce(); // 检查并处理可视化窗口的所有事件(如:鼠标点击、键盘输入等),然后更新可视化窗口的渲染结果pcl_sleep(0.01); // 让程序暂停 0.01 秒,这是为了控制循环的速度,防止过快更新可视化窗口导致程序卡死}return (0);
}
  • 配置文件 CMakeLists.txt

    cmake_minimum_required(VERSION 3.5 FATAL_ERROR)project(range_image_creation)find_package(PCL 1.2 REQUIRED)include_directories(${PCL_INCLUDE_DIRS})
    link_directories(${PCL_LIBRARY_DIRS})
    add_definitions(${PCL_DEFINITIONS})add_executable (range_image_creation range_image_creation.cpp)
    target_link_libraries (range_image_creation ${PCL_LIBRARIES})
    
  • 编译并执行

    $ mkdir build
    $ cd build
    $ cmake ..
    $ make$ ./range_image_creation
    
    // 输出结果
    header: 
    seq: 0 stamp: 0 frame_id: 
    points[]: 1360
    width: 40
    height: 34
    sensor_origin_: 0 0 0
    sensor_orientation_: 0 0 0 1
    is_dense: 0
    angular resolution: 1deg/pixel in x and 1deg/pixel in y.
    

在这里插入图片描述

3. 从深度图提取边界

3.1 引言

  • 对三种不同类型的点感兴趣

    • obstacle border:对象边界(属于对象的最外面的可见点)
    • shadow border:阴影边界(在背景中与遮挡物相邻的点)
    • veil points:面纱点集(对象边界与阴影边界之间的内插点)
  • 以下是一个典型的激光雷达获得的 3D 数据对应的点云图
    在这里插入图片描述

3.2 代码实现(C++)

注意事项

  • 要提取边界信息,重点区分未观察到的图像点和应该观察到但超出传感器范围的点。后者通常用来标记边界,而未观察到的点通常不标记边界。因此,最好可以提供这些测量信息。如果无法提供超出这些应该观察到的传感器范围的点,则可以使用 setUnseenToMaxRange 函数,将那些点设置为最大深度(本例添加 -m 参数)
  • range_image_border_extraction.cpp
#include <iostream>#include <boost/thread/thread.hpp>
#include <pcl/range_image/range_image.h>
#include <pcl/io/pcd_io.h>
#include <pcl/visualization/range_image_visualizer.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/features/range_image_border_extractor.h>
#include <pcl/console/parse.h>
#include <pcl/common/file_io.h>  // for getFilenameWithoutExtensiontypedef pcl::PointXYZ PointType;float angular_resolution = 0.5f;
pcl::RangeImage::CoordinateFrame coordinate_frame = pcl::RangeImage::LASER_FRAME;
// 若为 true,则:将 应该观察到 但超出传感器范围 的点设置为最大深度
bool setUnseenToMaxRange = false; // 命令帮助提示
void printUsage (const char* progName) {std::cout << "\n\nUsage: "<< progName <<" [options] <scene.pcd>\n\n"<< "Options:\n"<< "-------------------------------------------\n"<< "-r <float>   angular resolution in degrees (default "<< angular_resolution << ")\n"    // 指定角度分辨率的浮点数值,默认值为 angular_resolution<< "-c <int>     coordinate frame (default "<< (int)coordinate_frame << ")\n"    // 指定坐标系的整数值,默认值为 coordinate_frame<< "-m           Treat all unseen points to max range\n"    // 将所有未被观察到的点视为最大距离处理<< "-h           this help\n"    // 输出帮助信息<< "\n\n";
}int main (int argc, char** argv) {// 解析命令行参数(执行时在 ./range_image_border_extraction.cpp 后添加)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;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);cout << "Using coordinate frame "<< (int)coordinate_frame <<".\n";}if (pcl::console::parse (argc, argv, "-r", angular_resolution) >= 0)cout << "Setting angular resolution to " << angular_resolution << "deg.\n";// 将一个以度为单位表示的 角度分辨率值 转换为弧度制的值angular_resolution = pcl::deg2rad (angular_resolution);// 读取点云 PCD 文件;如果没有输入 PCD 文件就生成一个点云pcl::PointCloud<PointType>::Ptr point_cloud_ptr(new pcl::PointCloud<PointType>);pcl::PointCloud<PointType> &point_cloud = *point_cloud_ptr;pcl::PointCloud<pcl::PointWithViewpoint> far_ranges; // 定义点云中每个点的位置和视角信息Eigen::Affine3f scene_sensor_pose(Eigen::Affine3f::Identity ()); // 申明传感器的位置是一个 4*4 的仿射变换// 解析命令行参数,并返回所有 .pcd 扩展名的文件的索引列表std::vector<int> 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]]; // 保存从命令行参数获取到的第一个 PCD 文件名if (pcl::io::loadPCDFile(filename, point_cloud) == -1) {cout << "Was not able to open file \"" << filename << "\".\n";printUsage(argv[0]);return 0;}// 给传感器的位姿赋值,就是获取点云的传感器的 平移与旋转的向量// scene_sensor_pose:相机在场景中的位姿(位置和朝向)// Eigen::Affine3f:可以表示平移、旋转等多种变换,并且支持矩阵乘法来组合多个变换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_);// 去除文件扩展名,并添加 "_far_ranges.pcd" 来创建输出文件名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 \"" << far_ranges_filename << "\" does not exists.\n";} else {    // 如果没有读取点云,则要自己生成点云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 = (int) point_cloud.points.size();point_cloud.height = 1;}// 从创建的原始点云中获取深度图float noise_level = 0.0;float min_range = 0.0f;int border_size = 1;// 使用智能指针 boost::shared_ptr 管理内存,确保在不需要该对象时自动释放其占用的空间boost::shared_ptr<pcl::RangeImage> 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();// 设置点云可视化pcl::visualization::PCLVisualizer viewer("3D Viewer");viewer.setBackgroundColor(1, 1, 1);viewer.addCoordinateSystem(1.0f, "global");// 创建一个自定义的点云颜色处理器,用于将点云数据中的每个点赋予相同的黑色颜色pcl::visualization::PointCloudColorHandlerCustom<PointType> point_cloud_color_handler(point_cloud_ptr, 0, 0, 0);// 将点云数据加入到场景中,并使用上述颜色处理器使其呈现黑色viewer.addPointCloud(point_cloud_ptr, point_cloud_color_handler, "original point cloud");//PointCloudColorHandlerCustom<pcl::PointWithRange> range_image_color_handler(range_image_ptr, 150, 150, 150);//viewer.addPointCloud(range_image_ptr, range_image_color_handler, "range image");//viewer.setPointCloudRenderingProperties(PCL_VISUALIZER_POINT_SIZE, 2, "range image");// 提取深度图边界pcl::RangeImageBorderExtractor border_extractor(&range_image); // 从深度图像或距离图像中提取边界信息pcl::PointCloud<pcl::BorderDescription> border_descriptions; // 用于存储每个点是否是 边界点及其边界类型 的信息border_extractor.compute(border_descriptions);// 在 3D viewer 中显示点云// pcl::PointWithRange 存储了每个点的三维坐标 xyz 和距离信息 rangepcl::PointCloud<pcl::PointWithRange>::Ptr border_points_ptr(new pcl::PointCloud<pcl::PointWithRange>),veil_points_ptr(new pcl::PointCloud<pcl::PointWithRange>),shadow_points_ptr(new pcl::PointCloud<pcl::PointWithRange>);pcl::PointCloud<pcl::PointWithRange> &border_points = *border_points_ptr,&veil_points = * veil_points_ptr,&shadow_points = *shadow_points_ptr;// 从给定的 border_descriptions 中提取特定属性的点,并将这些点分别存储到三个不同的 PointCloud 中// 使用双重循环遍历整个 range_image,在每个像素位置 (x, y) 处检查该位置的点是否具有特定的边界属性for (int y = 0; y < (int)range_image.height; ++y) {for (int x = 0; x < (int)range_image.width; ++x) {/*首先,通过 border_descriptions.points[y * range_image.width + x] 访问像素点(x, y)对应的边界特征然后,使用 pcl::BORDER_TRAIT__OBSTACLE_BORDER 常量进行比较,判断该像素点是否具有障碍物边界特征如果是,则通过 range_image.points[y * range_image.width + x] 获取该像素点对应的三维点,并添加到 border_points 容器中1. border_descriptions:表示边界描述的容器,其中包含了每个像素点的边界特征2. range_image:表示一个基于深度图的三维点云数据结构,其中每个像素点对应于一个三维空间中的点3. x 和 y:表示像素点的坐标4. pcl::BORDER_TRAIT__OBSTACLE_BORDER:表示障碍物边界特征的枚举常量*/if (border_descriptions.points[y * range_image.width + x].traits[pcl::BORDER_TRAIT__OBSTACLE_BORDER])border_points.points.push_back(range_image.points[y * range_image.width + x]);if (border_descriptions.points[y * range_image.width + x].traits[pcl::BORDER_TRAIT__VEIL_POINT])veil_points.points.push_back(range_image.points[y * range_image.width + x]);if (border_descriptions.points[y * range_image.width + x].traits[pcl::BORDER_TRAIT__SHADOW_BORDER])shadow_points.points.push_back(range_image.points[y * range_image.width + x]);}}// 分别设置三种类型点云可视化pcl::visualization::PointCloudColorHandlerCustom<pcl::PointWithRange> border_points_color_handler (border_points_ptr, 0, 255, 0);viewer.addPointCloud<pcl::PointWithRange> (border_points_ptr, border_points_color_handler, "border points");viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 7, "border points");pcl::visualization::PointCloudColorHandlerCustom<pcl::PointWithRange> veil_points_color_handler (veil_points_ptr, 255, 0, 0);viewer.addPointCloud<pcl::PointWithRange> (veil_points_ptr, veil_points_color_handler, "veil points");viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 7, "veil points");pcl::visualization::PointCloudColorHandlerCustom<pcl::PointWithRange> shadow_points_color_handler (shadow_points_ptr, 0, 255, 255);viewer.addPointCloud<pcl::PointWithRange> (shadow_points_ptr, shadow_points_color_handler, "shadow points");viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 7, "shadow points");// 在深度图中显示点云pcl::visualization::RangeImageVisualizer *range_image_borders_widget = NULL;// 要显示的距离图像,显示范围的最小值,显示范围的最大值(返回 float 类型的正无穷大)// 是否应该在边框上显示描述信息,可选的边框描述信息,显示窗口的名称range_image_borders_widget = pcl::visualization::RangeImageVisualizer::getRangeImageBordersWidget(range_image, -std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), false,border_descriptions, "Range image with borders");while (!viewer.wasStopped ()) {range_image_borders_widget->spinOnce();viewer.spinOnce ();pcl_sleep(0.01);}
}
  • 配置文件 CMakeLists.txt

    cmake_minimum_required(VERSION 3.5 FATAL_ERROR)project(range_image_border_extraction)find_package(PCL 1.3 REQUIRED)include_directories(${PCL_INCLUDE_DIRS})
    link_directories(${PCL_LIBRARY_DIRS})
    add_definitions(${PCL_DEFINITIONS})add_executable (range_image_border_extraction range_image_border_extraction.cpp)
    target_link_libraries (range_image_border_extraction ${PCL_LIBRARIES})
    
  • 编译并执行

    $ mkdir build
    $ cd build
    $ cmake ..
    $ make$ ./range_image_border_extraction -m table_scene_lms400.pcd # 使用 -m 设置传感器看不见的位置
    

在这里插入图片描述


http://www.ppmy.cn/news/59339.html

相关文章

Linux系统编程——多线程[上]:线程概念和线程控制

0.关注博主有更多知识 操作系统入门知识合集 目录 1.再谈页表 2.Linux线程概念 2.1pthread原生库的基本使用 2.2PID和LWP 2.3Linux线程的资源以及优缺点 2.4Linux线程健壮性问题 2.5可重入函数和线程独立栈 3.Linux线程控制 3.1Linux线程终止 3.2Linux线程等待 3.…

ThreadLocal的使用介绍和底层原理解析和开源框架的使用实例

文章目录 ThreadLocal的使用介绍和底层原理解析和开源框架的使用实例ThreadLocal简介ThreadLocal使用示例ThreadLocal原理解析Spring中ThreadLocal的应用小结ThreadLocal的使用步骤常见面试题案例解析(框架源码经典案例)案例实战 ThreadLocal的使用介绍和底层原理解析和开源框架…

STC32G12K128单片机的 moubus-rtu 从机测试工程

简介 STC32G12K128 是STC 推出的一款32位的 C251 的单片机。最近拿到一块官方申请的 屠龙刀-STC32G开发板&#xff0c;就用它的提供的库函数&#xff0c;查考安富莱提供的 modbus 例程移植了一个 modbus-rtu 从站的工程。 modbus-rtu slave 移植注意点 modbus-rtu 功能配置 …

【C++】 重载操作符

目录 概念 类内重载操作符 类外重载操作符 使用注意 对象类型转换 封装Iterator 概念 C提供的运算符&#xff0c;通常只支持对于基本数据类型和标准库中提供的类进行操作&#xff0c;对于自定义类型如果想通过操作符实现对应的操作&#xff0c;需要自定义重载的操作符并实…

Android中Paint字体的灵活使用

在Android开发中&#xff0c;Paint是一个非常重要的绘图工具&#xff0c;可以用于在控制台应用程序或Java GUI应用程序中绘制各种形状和图案。其中&#xff0c;Paint.setText()方法是用于设置Paint绘制的文本内容的。在Android开发中&#xff0c;如果你想要设置文本内容&#x…

红黑树理论详解与Java实现

文章目录 基本定义五大性质红黑树和2-3-4树的关系红黑树和2-3-4树各结点对应关系添加结点到红黑树注意事项添加的所有情况 添加导致不平衡叔父节点不是红色节点&#xff08;祖父节点为红色&#xff09;添加不平衡LL/RR添加不平衡LR/RL 叔父节点是红色节点&#xff08;祖父节点为…

【HTML+CSS+JS】登录注册页面大合集

前言 学JS也学了一段时间&#xff0c;正巧碰上了人工智能要调用人脸识别接口进行真人人脸识别&#xff0c;于是便萌生了用人脸来进行注册和登录的想法&#xff0c;这样的话就需要开发一个登录注册页面&#xff0c;然后用JS绑定注册事件调用人脸识别接口进行登录注册 饭要一口一…

vue首屏白屏原因及解决办法

vue首屏白屏原因大概有以下几点&#xff1a; 一.路由模式错误(路由重复或者没有配置路由) &#xff08;1&#xff09;由于把路由模式mode设置成history了&#xff0c;默认是hash 解决方法&#xff1a;将模式改为hash模式&#xff0c;或者直接把模式配置删除&#xff0c;而且hi…