SIRA-PCR: Sim-to-Real Adaptation for 3D Point Cloud Registration 论文解读

目录

一、导言

二、 相关工作

 1、三维点云配准工作

2、无监督域适应

三、SIRA-PCR

1、FlyingShape数据集

2、Sim-to-real自适应方法

3、配准

4、损失函数


一、导言

        该论文来自于ICCV2023,论文提出了一种新的方法SIRA-PCR,通过利用合成数据FlyingShapes解决现有数据稀缺问题。

        数据稀缺原因:现有的基于数据驱动的深度学习方法一般依赖于两类数据,一是单一物体级别如ModelNet40,ShapeNet,二是对于室内场景水平如3DMatch。虽然单一物体级别数据集有较强的几何形状,但很难推广到真实的室内场景,室内场景的数据集来训练性能很好,但捕获真实的室内场景十分耗时且估计的相机姿态存在错误标签。所以针对场景级的合成数据集仍然数据稀缺,所以本文创建了一个合成场景数据集FlyingShapes。

(1)构建了第一个大规模的室内合成数据集FlyingShapes,使用基于物理和随机的策略将ShapeNet对象插入3D-FRONT场景中。

(2)设计了一个称为SIRA的管道,包括一个自适应重采样模块,目的是缓解合成数据和真实数据之间的域差距(分布差异),从而提高点云配准的性能。

二、 相关工作

 1、三维点云配准工作

        一种是直接配准,一种是基于对应的方法(与以前的博文类似)

2、无监督域适应

        无监督自适应是一种解决源域(合成数据)与目标域(真实数据)之间分布差异的方法,旨在利用源域的标注数据和目标域的无标注数据,学习一个可以将源域数据映射到目标域的模型,从而提高目标域任务的性能,通过这样的方式,利用合成数据集来解决真实数据集标注不足的问题。

        对于以往的点云配准任务来说,一般通过利用GAN来执行域对抗训练,使得两个域的点云特征难以被鉴别器区分。还有方法通过自监督学习里解决域对齐。

三、SIRA-PCR

        SIRA-PCR框架,首先通过SIRA结构基于FlyingShapes数据集来训练合成数据到真实数据的域自适应,之后基于3DMatch或3DLoMatch(实验工作)使用GeoTransformer来训练配准工作。

        SIRA-PCR框架的目的:就是通过先训练一遍我们改进的合成数据集FlyingShape,来学习到一些真实数据集中应该有的特征,这样优化了真实数据集中由于标注错误,数据样本少而训练不足的问题,其实从本质上就是增加更多的样本量,完全可以把做合成数据集到合成2真实的域适应的工作看成一种增加样本量的方式。

1、FlyingShapes数据集

        3D-FRONT数据集是一个由专业设计师设计的室内场景和家具布局的场景数据集,但由于结构过于简单,FlyingShape数据集从3D-FRONT场景数据集中添加一定的家具模型来模拟真实的场景。

        FlyingShapes数据集考虑了三个因素进行优化:

(1)几何增强

        由于现实场景中家具摆放不一定合理,可能存在一定的随机性。所以使用两种方式添加对象数据集(单一对象),分别是基于物理意义的(重力要求),随机放置(无视重力)。

        由于3D-FRONT数据集中存在大量的地板、墙壁、天花板等简单的平面,对结构的泛化效果有限,从而也使得网络过分关注这些简单的平面结构,所以以50%的概率去除这些平坦平面,进而提高对于对象数据集中物体的几何结构平衡。

(2)高质量的视角选择

        视角选择着重考虑,点云数据集中,每一团点云结构应该包含较为足够数量的对象(大于5个对象结构),并且视角保持人类视角,且移动保持人类转动行进的速度(设置高度为1.6m,水平视角360°,垂直方向仰角0到45°,以30°或15°来均匀采样视图)

(3)数据准备

        为模拟RGB-D摄像机的深度信息,我们通过虚拟摄像机绘制深度图并转换为点云,保留重叠范围在30%以上的点云,提高模型的泛化能力。

2、Sim-to-real自适应方法

        这一部分就是做无监督域适应工作,其实本质来说也是一个GAN网络。

生成器(ResampleGAN):

        生成器采用Encoder-Decoder结构,Encoder部分是ResampleKPConvEncoder结构(KPConv+ARM自适应重采样模块+FPN,用于提取特征),Decoder部分是MLPDecoder结构(本质是三层1维卷积通过LeakyReLU激活函数相连,用于将特征转换为三维坐标)

        Encoder部分又可以看做一个KPConv与FPN的结合,并且每一层都会添加一个ARM重采样结构。KPConv负责从顶到底提取特征,FPN部分负责多层次的特征再次提取。

        ARM重采样:Adaptive Re-sample Module,实现了基于注意力机制的点的局部重采样,利用点的特征和邻点的信息来计算一个点的加权平均坐标,保证了每一步的卷积操作输出后都有针对邻点特征的优化。

        ARM算法:输入所有点的坐标(坐标矩阵),构建每个点周围的一个局部patch,并得到该点的特征和该点邻点特征,并通过加权邻点特征和该点特征,得到新的坐标点,作为调整点位置。

        对于论文图3的解释如下:

        我们举点云中任何一个点P_i为例子,实际是直接用点云的坐标矩阵来进行下面计算。

        对于一个点P_i的特征f_i(d维列向量)与P_i周围K个点的邻点特征F_i^P(K*d维矩阵)的转置相乘,并除以\sqrt{d}进行归一化,之后通过Softmax函数得到权重系数w_i(K维列向量),w_i乘以K个邻近点坐标矩阵P_i (K*3维矩阵)得到重采样点y_i

        公式解释:(其中,W^A就是将点运算转换为矩阵运算,可忽略看)

                                                   w_i=softmax(\frac{(F_i^PW^A)(f_iW^A)^T}{\sqrt{d}})

                                            y_i=\sum_{k=1}^K w_{i,k} P_{i,k}^\mathrm{P}, \qquad \sum_{k=1}^Kw_{i,k}=1

         

生成器代码如下:

#重采样代码
class PatchResampleBlock(nn.Module):def __init__(self, feat_channels) -> None:r"""Initialize a patch resample block.Args:feat_channels: dimension of input features"""super(PatchResampleBlock, self).__init__()self.feat_channels = feat_channelsself.feat_proj = nn.Linear(self.feat_channels, self.feat_channels)def forward(self, points, feats, neighbor_indices):point_num, neighbor_limit = neighbor_indices.shape# adjust neighbor_indices (stand still when the patch has few neighbors)point_indices = torch.arange(point_num,device=neighbor_indices.device).reshape((point_num, 1)).repeat((1, neighbor_limit))  # (N ,K)neighbor_indices = torch.where(neighbor_indices < point_num,neighbor_indices, point_indices)# feature projectionfeats = self.feat_proj(feats)neighbor_feats = feats[neighbor_indices]  # (N, K, d)neighbor_points = points[neighbor_indices]  # (N, K, 3)neighbor_weights = torch.einsum("nd,nkd->nk", feats,neighbor_feats)  # (N, d) x (N, K, d) -> (N, K)neighbor_weights = nn.functional.softmax(neighbor_weights /self.feat_channels**0.5,dim=-1)  # (N, K) -> (N, K)output_points = torch.einsum("nk,nkp->np", neighbor_weights,neighbor_points)  # (N, K) x (N, K, 3) -> (N, 3)return output_points#KPconv+FPN+ARM作为Encoder
class ResampleKPConvEncoder(nn.Module):def __init__(self, input_dim, output_dim, init_dim, kernel_size,init_radius, init_sigma, group_norm):super(ResampleKPConvEncoder, self).__init__()self.encoder1_1 = ConvBlock(input_dim, init_dim, kernel_size,init_radius, init_sigma, group_norm)self.encoder1_2 = ResidualBlock(init_dim, init_dim * 2, kernel_size,init_radius, init_sigma, group_norm)self.encoder2_1 = ResidualBlock(init_dim * 2,init_dim * 2,kernel_size,init_radius,init_sigma,group_norm,strided=True)self.encoder2_2 = ResidualBlock(init_dim * 2, init_dim * 4,kernel_size, init_radius * 2,init_sigma * 2, group_norm)self.encoder2_3 = ResidualBlock(init_dim * 4, init_dim * 4,kernel_size, init_radius * 2,init_sigma * 2, group_norm)self.encoder3_1 = ResidualBlock(init_dim * 4,init_dim * 4,kernel_size,init_radius * 2,init_sigma * 2,group_norm,strided=True)self.encoder3_2 = ResidualBlock(init_dim * 4, init_dim * 8,kernel_size, init_radius * 4,init_sigma * 4, group_norm)self.encoder3_3 = ResidualBlock(init_dim * 8, init_dim * 8,kernel_size, init_radius * 4,init_sigma * 4, group_norm)self.encoder4_1 = ResidualBlock(init_dim * 8,init_dim * 8,kernel_size,init_radius * 4,init_sigma * 4,group_norm,strided=True)self.encoder4_2 = ResidualBlock(init_dim * 8, init_dim * 16,kernel_size, init_radius * 8,init_sigma * 8, group_norm)self.encoder4_3 = ResidualBlock(init_dim * 16, init_dim * 16,kernel_size, init_radius * 8,init_sigma * 8, group_norm)self.resample4 = PatchResampleBlock(feat_channels=init_dim * 16)self.decoder4 = UnaryBlock(init_dim * 16 + 3, init_dim * 16,group_norm)self.resample3 = PatchResampleBlock(feat_channels=init_dim * 8)self.decoder3 = UnaryBlock(init_dim * 24 + 3, init_dim * 8, group_norm)self.resample2 = PatchResampleBlock(feat_channels=init_dim * 4)self.decoder2 = UnaryBlock(init_dim * 12 + 3, init_dim * 4, group_norm)self.resample1 = PatchResampleBlock(feat_channels=init_dim * 2)self.decoder1 = UnaryBlock(init_dim * 6 + 3, output_dim, group_norm)self.outputlayer = LastUnaryBlock(output_dim, output_dim)def forward(self, data_dict):points_list = data_dict['points']neighbors_list = data_dict['neighbors']subsampling_list = data_dict['subsamples']upsampling_list = data_dict['upsamples']feats_s1 = torch.ones((points_list[0].shape[0], 1),dtype=torch.float32).to(points_list[0])feats_s1 = self.encoder1_1(feats_s1, points_list[0], points_list[0],neighbors_list[0])feats_s1 = self.encoder1_2(feats_s1, points_list[0], points_list[0],neighbors_list[0])feats_s2 = feats_s1feats_s2 = self.encoder2_1(feats_s2, points_list[1], points_list[0],subsampling_list[0])feats_s2 = self.encoder2_2(feats_s2, points_list[1], points_list[1],neighbors_list[1])feats_s2 = self.encoder2_3(feats_s2, points_list[1], points_list[1],neighbors_list[1])feats_s3 = feats_s2feats_s3 = self.encoder3_1(feats_s3, points_list[2], points_list[1],subsampling_list[1])feats_s3 = self.encoder3_2(feats_s3, points_list[2], points_list[2],neighbors_list[2])feats_s3 = self.encoder3_3(feats_s3, points_list[2], points_list[2],neighbors_list[2])feats_s4 = feats_s3feats_s4 = self.encoder4_1(feats_s4, points_list[3], points_list[2],subsampling_list[2])feats_s4 = self.encoder4_2(feats_s4, points_list[3], points_list[3],neighbors_list[3])feats_s4 = self.encoder4_3(feats_s4, points_list[3], points_list[3],neighbors_list[3])resampled_points4 = self.resample4(points_list[3], feats_s4,neighbors_list[3])latent_s4 = torch.cat([feats_s4, resampled_points4],dim=1)  # (N4, 64*16+3)latent_s4 = self.decoder4(latent_s4)resampled_points3 = self.resample3(points_list[2], feats_s3,neighbors_list[2])latent_s3 = nearest_upsample(latent_s4, upsampling_list[2])latent_s3 = torch.cat([latent_s3, feats_s3, resampled_points3],dim=1)  # (N3, 64*16+64*8+3)latent_s3 = self.decoder3(latent_s3)resampled_points2 = self.resample2(points_list[1], feats_s2,neighbors_list[1])latent_s2 = nearest_upsample(latent_s3, upsampling_list[1])latent_s2 = torch.cat([latent_s2, feats_s2, resampled_points2],dim=1)  # (N2, 64*8+64*4+3)latent_s2 = self.decoder2(latent_s2)  # (N1, 256)resampled_points1 = self.resample1(points_list[0], feats_s1,neighbors_list[0])latent_s1 = nearest_upsample(latent_s2, upsampling_list[0])latent_s1 = torch.cat([latent_s1, feats_s1, resampled_points1],dim=1)  # (N1, 64*4+64*2+3)latent_s1 = self.decoder1(latent_s1)  # (N1, 256)output = self.outputlayer(latent_s1)  # (N1, 256)return output#三层卷积作为Decoder
class MLPDecoder(nn.Module):def __init__(self, dimoffeat=256):super(MLPDecoder, self).__init__()self.sharedmlp = nn.Sequential(nn.Conv1d(dimoffeat, int(dimoffeat / 2), 1),nn.LeakyReLU(negative_slope=0.2),nn.Conv1d(int(dimoffeat / 2), int(dimoffeat / 8), 1),nn.LeakyReLU(negative_slope=0.2),nn.Conv1d(int(dimoffeat / 8), 3, 1))def forward(self, feat):feat = feat.transpose(-1, -2)  # (N, d) -> (d, N)feat = feat.unsqueeze(0)  # (d, N) -> (batch, d, N)output = self.sharedmlp(feat)  # (batch, d, N) -> (batch, 3, N)output = output.squeeze(0)  # (batch, 3, N) -> (3, N)output = output.transpose(-1, -2)  # (3, N) -> (N, 3)return output#生成器
class ResampleGAN(nn.Module):def __init__(self, input_dim, dimofbottelneck, init_dim, kernel_size,init_radius, init_sigma, group_norm):super(ResampleGAN, self).__init__()self.encoder = ResampleKPConvEncoder(input_dim, dimofbottelneck,init_dim, kernel_size,init_radius, init_sigma,group_norm)self.decoder = MLPDecoder(dimoffeat=dimofbottelneck)def forward(self, data_dict):feat = self.encoder(data_dict)points_recovered = self.decoder(feat)return points_recovered

 判别器(ResampleGAN):

        判别器输入点云中的坐标矩阵和邻点坐标信息,考虑到生成器采用不同的ARM重采样自适应,会导致改变局部的密度,所以在多尺度下(小、中、大尺度,长度分别是5,10,20)进行特征提取(特征提取使用PointNet网络),并进行特征融合,判别器的目的是判断不同层次的点是真实还是合成的。

class MultiScalePointNet(nn.Module):def __init__(self, dimoffeat=256, multiscale=[5, 10, 20]) -> None:super(MultiScalePointNet, self).__init__()self.multiscale = multiscaleself.patchpointnets = nn.ModuleList()for scale in multiscale:self.patchpointnets.append(nn.Sequential(nn.Conv1d(3, dimoffeat // 4, kernel_size=1, stride=1),nn.LeakyReLU(negative_slope=0.2),nn.Conv1d(dimoffeat // 4,dimoffeat // 2,kernel_size=1,stride=1), nn.LeakyReLU(negative_slope=0.2),nn.Conv1d(dimoffeat // 2,dimoffeat,kernel_size=1,stride=1)))self.maxpool = nn.AdaptiveMaxPool1d(output_size=1)self.sharedmlp = nn.Sequential(nn.Conv1d(len(multiscale) * dimoffeat,dimoffeat,kernel_size=1,stride=1), nn.LeakyReLU(negative_slope=0.2),nn.Conv1d(dimoffeat, dimoffeat // 2, kernel_size=1, stride=1),nn.LeakyReLU(negative_slope=0.2),nn.Conv1d(dimoffeat // 2, dimoffeat // 4, kernel_size=1, stride=1),nn.LeakyReLU(negative_slope=0.2),nn.Conv1d(dimoffeat // 4, dimoffeat // 8, kernel_size=1, stride=1),nn.LeakyReLU(negative_slope=0.2),nn.Conv1d(dimoffeat // 8, 1, kernel_size=1, stride=1))def forward(self, points, neighbor_indices):patchfeats_list = []for idx in range(len(self.multiscale)):neighbors = points[neighbor_indices[:, :self.multiscale[idx]]]  # shape (N, K, 3)neighbors = neighbors - points[:, None, :]neighbors = neighbors.transpose(1, 2)  # (N, K, 3) -> (N, 3, K)feats = self.patchpointnets[idx](neighbors)  # (N, 3, K) -> (N, d, K)patchfeats = self.maxpool(feats)  # (N, d, K) -> (N, d, 1)patchfeats = patchfeats.squeeze(-1)  # (N, d, 1) -> (N, d)patchfeats = patchfeats.transpose(0, 1)  # (N, d) -> (d, N)patchfeats_list.append(patchfeats)multiscalefeats = torch.cat(patchfeats_list,dim=0)  # m x (d, N) -> (md, N)multiscalefeats = multiscalefeats.unsqueeze(0)  # (md, N) -> (batch, md, N)out = self.sharedmlp(multiscalefeats)  # (batch, md, N) -> (batch, 1, N)out = out.squeeze(0)  # (batch, 1, N) -> (1, N)out = out.squeeze(0)return out

3、配准

        配准工作backbone使用的是GeoTransformer结构作为配准模块,并且使用GeoTransformer中提到的局部到全局的配准策略LGR策略来替换RANSAC。

        LGR可以利用所有的对应关系来估计变换矩阵,而不像RANSAC使用一部分内点而存在内点分布不均匀的影响,LGR可以更好地处理重复几何结构和低重叠比例情况。

4、损失函数

        损失函数包含两个模块,对于SIRA和点云配准两个部分分别进行训练。

        对于SIRA模块,使用倒角损失,生成器损失和判别器损失三部分来训练域自适应。

        对于配准模块,使用重叠圆损失(点对应损失)和全局点对损失两部分训练(使用的就是Geotrans的配准模块,损失也一模一样)。

四、实验

1、对比不同框架

        提前获得了一部分数据集(合成数据集)的情况下,其实效果相比GeoTransformer提升并没有很大,说明这个方法idea很好,但其实用在配准工作其实一般。

2、点云配准训练中,不同数量的数据集对性能指标的影响

        在不同的backbone下,显然Samples越少,对其他backbone影响越来越明显,对SIRA-PCR影响则很微小。性能指标包括:特征匹配召回,离群比、配准召回。不得不说这不就是因为提前吃了一遍数据集,学到了特征的影响吗。

3、其他实验

        对于FlyingShapes数据集的建立,引入structure3D效果提升,这个不太了解情况,证明了删除平面确实有效。

        消融实验中对于SIRA的不同组件以及FlyingShapes的object data和scene data进行消融。

论文参考:ICCV 2023 Open Access Repository        

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

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

相关文章

网易云音乐故障 2 小时,这次到底谁背锅?(今天记得领补偿)

大家好&#xff0c;我是程序员鱼皮&#xff0c;8 月 19 日下午&#xff0c;网易云音乐突发严重故障&#xff0c;并登顶微博热搜&#xff0c;跟黑神话悟空抢了热度。 根据用户的反馈&#xff0c;故障的具体表现为&#xff1a;用户无法登录、歌单加载失败、播放信息获取失败、无法…

了解数据库中常用存储引擎数据结构(2)

目录 深入了解B树及其变种 BTree BTree B*Tree BTree并发机制 B-Link Tree 深入了解B树及其变种 先把我们要解释的B树变种都列出来&#xff0c;B树的变种主要有B树、B*树、B-Link树、COW B树、惰性B树、Bw树等。 下面具体来分析这些变种的优势和发展趋势。 BTree 下图…

Taro+Vue 创建微信小程序

TaroVue 创建微信小程序 一、全局安装 tarojs/cli二、项目初始化三、现在去启动项目吧 一、全局安装 tarojs/cli npm install -g tarojs/cli //安装 npm info tarojs/cli //查看安装信息 如果正常显示版本说明成功了&#xff0c;就直接跳到第二步吧官网说&#xff1a;…

AI产品经理如何入门?零基础入门到精通,收藏这一篇就够了

现在做产品经理&#xff0c;真的挺累的。 现在产品越来越难做&#xff0c;晋升困难&#xff0c;工资迟迟不涨……公司裁员&#xff0c;产品经理首当其冲&#xff01;&#xff01; 做产品几年了&#xff0c;还没升职&#xff0c;就先到了“职业天花板”。 想凭工作几年积累的…

BFS解决单源最短路问题

目录 迷宫中离入口最近的出口 最小基因变化 单词接龙 为高尔夫比赛砍树 迷宫中离入口最近的出口 题目 思路 使用宽度优先遍历解决这道题&#xff0c;需要一个二维数组标记是否被遍历过&#xff0c;也需要一个队列辅助完成宽度优先遍历&#xff0c;类似于水波纹一样&#x…

JAVA基础:File类

目录 前言 file对象的创建 file的常用方法 前言 file类表示的是系统中的一个文件或者文件夹 file类和系统中的文件或者文件夹不需要是一一对应的&#xff0c;我们可以在file类中写你系统中不存在的文件或文件夹 file类中存储的实际上是文件或文件夹的抽象路径&#xff0c…

面试经典算法150题系列-最长公共前缀

最长公共前缀 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 ""。 示例 1&#xff1a; 输入&#xff1a;strs ["flower","flow","flight"] 输出&#xff1a;"fl"示例 2&…

软件测试 - 自动化测试(概念)(Java)(自动化测试分类、web自动化测试、驱动、selenium自动化测试工具的安装)

一、自动化的概念 ⾃动洒⽔机&#xff0c;主要通上⽔就可以⾃动化洒⽔并且可以⾃动的旋转。 ⾃动洗⼿液&#xff0c;免去了⼿动挤压可以⾃动感应出洗⼿液 超市⾃动闸⻔&#xff0c;不需要⼿动的开⻔关⻔ ⽣活中的⾃动化案例有效的减少了⼈⼒的消耗&#xff0c;同时也提⾼了⽣…

C语言程序设计(初识C语言后部分)

代码是一门艺术&#xff0c;键盘是我的画笔。 3.递归和迭代&#xff08;循环就是一种迭代&#xff0c;迭代不仅仅是循环&#xff09; 求n&#xff01; 递归方式&#xff1a; n!---> 1 (n1); n*(n-1)! (n>1) #include <stdio.h> //n!-->递归方式 int fac(in…

Kafka运行机制(一):Kafka集群启动,controller选举,生产消费流程

前置知识 Kafka基本概念https://blog.csdn.net/dxh9231028/article/details/141270920?spm1001.2014.3001.5501 1. Kafka集群启动 Kafka在启动集群中的各个broker时&#xff0c;broker会向controller注册自己&#xff0c;并且从controller节点同步集群元数据。 broker是Kaf…

C++入门——16C++11新特性

1.列表初始化 初始化列表时&#xff0c;可添加等号()&#xff0c;也可不添加。 struct Point {int _x;int _y; };int main() {int x1 1;int x2{ 2 };int array1[]{ 1, 2, 3, 4, 5 };int array2[5]{ 0 };Point p{ 1, 2 };// C11中列表初始化也可以适用于new表达式中int* pa …

jstack结果提取特定线程池线程的堆栈

这里假设你已经知道如何定位java进程PID&#xff0c;以及如何执行jstack命令进行导出&#xff0c;下面仅提供相关命令&#xff0c;及示例 ps aux|grep java jstack 8229 > jstack_output.log v1版本 grep second jstack_output.log 如果此时我们想重点关注一下下面这个线程…

高性价比全屋智能家居解决方案,提升生活幸福感!

在快节奏的城市生活中&#xff0c;公司、住宅两点一线的上班族不在少数。近年来&#xff0c;随着物联网、大数据技术的不断发展与5G的广泛普及&#xff0c;生活品质的提升成为上班族新的关注焦点。忙碌的工作时间结束后&#xff0c;智慧家居系统打造便捷、安全、智能、舒适的居…

(javaweb)SpringBootWeb案例(毕业设计)案例--部门管理

目录 1.准备工作 2.部门管理--查询功能 3.前后端联调 3.部门管理--新增功能 1.准备工作 mapper数据访问层相当于dao层 根据页面原型和需求分析出接口文档--前后端必须遵循这种规范 大部分情况下 接口文档由后端人员来编写 前后端进行交互基于restful风格接口 http的请求方式…

文书智能助手

背景 司法、医疗等行业存在着大量的文书&#xff0c;一份文书或者卷宗少则几十页&#xff0c;多则几万页。在查看和检查这些文书时&#xff0c;会遇到大量的信息。当需要查询进一步的详细内容时&#xff0c;往往需要选择一下文字&#xff0c;然后再在各种系统中 查询详细的信息…

日拱一卒(4)——leetcode学习记录:路径总和

一、任务&#xff1a; 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 叶…

二叉树(四)

一、二叉树的性质 二、练习 1.某二叉树共有399个节点&#xff0c;其中有199个度为2的节点&#xff0c;则二叉树中的叶子节点数为&#xff08; &#xff09;。 A.不存在这样的树 B.200 C.198 D.199 答案&#xff1a;B 参考二叉树的性质第三条 2.在具有2…

【Hadoop】核心组件深度剖析:HDFS、YARN与MapReduce的奥秘

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《大数据前沿&#xff1a;技术与应用并进》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、Hadoop简介 2、Hadoop生态系统概览 二、Hadoo…

如何做萤石开放平台的物联网卡定向?

除了用萤石自带的4G卡外&#xff0c;我们也可以自己去电信、移动和联通办物联网卡连接萤石云平台。 1、说在前面 注意&#xff1a;以下流程必须全部走完&#xff0c;卡放在设备上才能连接到萤石云平台。 2、大致流程 登录官网→下载协议→盖章&#xff08;包括骑缝章&#…

多平台编译libexif

下载地址&#xff1a;https://github.com/libexif/libexif/releases 1. ubuntu x64 &#xff08;银河麒麟系统aarch64步骤相同&#xff09; # 解压 > tar -jxvf libexif-0.6.24.tar.bz2 > cd libexif-0.6.24 # 配置 > ./configure # 编译 > make # 安装 > mak…