论文阅读——KPConv: Flexible and Deformable Convolution for Point Clouds

(一)Abstract

  • KPConv: 点卷积,可以在没有任何中间表示的情况下在点云上运行。
  • KPConv的卷积权重: 通过kernel points位于Euclidean space中,并应用于靠近它们的输入点。其使用任意数量的kernel points的能力使KPConv比固定网格卷积更具灵活性。
  • 核点位置:在空间上是连续的,并且可以由网络获知。因此,可以扩展KPConv变形卷积,学习使kernel points适应局部几何形状。
  • 下采样:使KPConv在不同密度也非常有效和强大。

(二)Introduction

分析:

  • 点云由两个元素组成:点 P R N × 3 P\in R^{N\times3} 和特征 F R N × D F\in R^{N\times D}
  • 与grid不同属性:点云是一种稀疏结构,具有无序的属性。在grid中,特征通过其索引在矩阵中进行定位,而在点云中,特征通过其对应的点坐标进行定位。
  • 与grid共同属性:点云在空间上是局部的(对于定义卷积至关重要)。

因而: 将点视为结构元素,将特征视为真实数据。

KPConv受基于图像的卷积的启发,但是代替核像素,使用一组kernel points 来定义每个kernel weight 的应用区域,如图1所示:

图释: The kernel weights 由点(如输入特征)承载,并且它们的影响范围由相关函数定义。 内核点数不受限制,因此设计非常灵活。

KPConv优点:

  • KPConv在每个卷积位置生成不同的移位,这意味着它可以针对输入点云的不同区域调整其内核的形状。 由于数据的不同性质,需要进行正则化以帮助deformed kernels适应点云的几何形状并避免empty space。
  • 倾向于使用radius neighborhoods而不是k-nearest-neighbors (KNN). 在non-uniform sampling中,KNN并不鲁棒。本文通过对radius neighborhoods和输入点云的常规下采样的组合来确保对卷积的鲁棒性。 与归一化策略相比,减轻了卷积的计算成本。
  • KPConv可用于构建非常深的分类和分割网络架构,同时保持快速的训练和推理时间。
  • Rigid KPConv在更简单的任务(例如对象分类或小型分割数据集)上可获得更好的性能。
  • Deformable KPConv可以完成更困难的任务,例如提供许多对象实例和更大多样性的大型分割数据集。 Deformable KPConv在较少的kernel points下更强大,这意味着更大的描述能力。
  • 对KPConv ERF的定性研究表明,deformable KPConv提高了网络适应场景对象的几何形状的能力。

(三)Related Work

3.1 Projection networks

多视图:

  1. 部分方法使用从点云在不同视点渲染的一组2D图像。 对于场景分割,这些方法受遮挡的表面和密度变化的影响。
  2. 部分方法建议将局部邻域投影到局部切平面,并用2D卷积对其进行处理。 但是,此方法严重依赖于切线估计。

体素:

  1. 将点投影到Euclidean space 中的3D网格上。 使用稀疏结构(如八叉树或哈希图)可以使用更大的网格并提高性能,但是由于kernels 被限制使用 3 3 3^3 = 27或 5 3 5^3 = 125体素,这些网络仍然缺乏灵活性。 使用permutohedral lattice代替Euclidean grid 将核减少到15个 lattices,但是这个数目仍然受到限制。

KPConv允许任何数目的kernel points。 此外,避免使用中间结构会使诸如 instance mask detector 或generative models 之类的更为复杂的体系结构的设计在以后的工作中更加直接。

3.2 Graph convolution networks

图卷积结合了局部表面patches的特征,但是对这些patches在Euclidean space中的变形是不变的。

KPConv根据3D几何形状局部组合特征,从而捕获表面的变形。

3.3 Pointwise MLP network

PointNet被认为是点云深度学习的里程碑。

继PointNet之后,一些分层体系结构将本地邻域信息与MLP聚合在一起。

3.4 Point convolution networks

点卷积的内核可以用MLP来实现,因为它具有近似任何连续函数的能力。 但是,使用这种表示方式会使卷积运算符更加复杂,并且网络的收敛也更加困难。

本文定义了一个显式卷积核,类似于图像卷积,其权重可以直接学习,而无需MLP的中间表示。 还提供了一种简单易用的deformable version,因为可以将偏移量直接应用于 kernel points。

  • Pointwise CNN 使用体素容器定位内核权重,因此缺乏网格网络之类的灵活性。此外,其归一化策略使网络负担不必要的计算,而KPConv二次采样策略则减轻了密度和计算成本的变化
  • SpiderCNN 将其kernel 定义为多项式函数族,并对每个neighbor应用不同的权重。 应用于neighbor的权重取决于neighbor的距离顺序,从而使过滤器在空间上不一致。 相比之下,PConv权重位于空间中,其结果对于点顺序不变
  • Flex-convolution 使用线性函数对其内核进行建模,这可能会限制其代表性功能。 它还使用KNN,如上所述,KNN对于变化的密度并不稳健。
  • PCNN 设计最接近KPConv。 其定义也使用点来承载kernel weights以及相关函数。 但是,该设计不可扩展,因为它不使用任何形式的邻域,从而使卷积计算在点数上呈二次方。 另外,它使用高斯相关,而KPConv使用更简单的线性相关,这有助于在学习 deformations 时进行梯度反向传播

(四)Kernel Point Convolution

4.1 A Kernel Function Defined by Points

卷积核g在点 x R 3 x∈R^3 上对 F F 进行的一般点卷积定义为:

对于函数 g g 而言,具有一致的球形域有助于网络学习有意义的表示。 而等式1中的关键部分也是内核函数 g g 的定义,它是KPConv突出性所在。 g g 将以 x x 为中心的领域位置作为输入。在下面称它们为 y i = x i x y_i = x_i − x

就像图像卷积内核一样(有关图像卷积和KPConv的详细比较,参见图2),希望 g g 对该球域内的不同区域应用不同的权重。
在这里插入图片描述
将任意点 y i B r 3 y_{i}\in B_{r}^{3} 的核函数 g g 定义:

h h x ~ k \tilde{x}_{k} y i y_i 之间的相关性,应当在 x ~ k \tilde{x}_{k} 接近 y i y_i 时更高,为线性相关

σ是内核点的影响距离,并根据输入密度被选择(请参见第4.3节)。 与高斯相关相比,线性相关是一个更简单的表示。 这种更简单的相关性,可以在学习 kernel deformations时缓解梯度反向传播。 可以使用整流线性单元绘制平行线,这是深层神经网络最常用的激活函数,归功于其梯度反向传播的效率。

4.2 Rigid or Deformable Kernel

Kernel point位置对于卷积运算符至关重要。 特别是rigid kernels需要被合理安排以提高效率。

通过解决优化问题来放置内核点,其中每个点对彼此施加排斥力。 这些点被约束以吸引力停留在球体中,并且其中之一被限制在中心位置。

论文在附录中详细介绍了此过程,如下:

希望 K K 个点 x ~ k \tilde{x}_{k} 在给定的球体内尽可能地彼此远离。 因此,为每个点分配了排斥力:

并向球心添加吸引力,以防止它们无限地发散:

然后,问题在于将global energy最小化:

该解决方案是通过梯度下降找到的,其中的点随机初始化并带有一些可选约束。将点固定在球体的中心。 对于某些K值(在表4中列出),这些点收敛到独特的稳定配置。

这些稳定的配置实际上是规则的多面体。 每个多面体可以通过对共享垂直于多面体对称轴的平面的点进行分组来描述。 为了更好地理解,图10显示了其中的一些配置。

最终,将周围的点重新缩放为1.5σ的平均半径,以确保影响的每个核心点区域之间的重叠很小,并具有良好的空间覆盖范围。

注意:

  • 使用正确初始化的kernels,对于rigid KPConv非常有效,特别是当给定足够大的 K K 以覆盖 g g 的球面域时。
  • 学习kernel point位置可能会增加其容量。核函数 g g 相对 x ~ k \tilde{x}_{k} 是可微的,这意味着它们是可学习的参数。
  • 考虑为每个卷积层学习一个全局 { x ~ k } \left \{\tilde{x}_{k}\right \} 集,但是它不会带来比固定的常规配置更多的描述能力。相反,网络会为每个卷积位置 x R 3 x∈R^3 生成一组 K K 位移 ( x ) \triangle(x)

可变形的KPConv定义为:

图解:将偏移量 ( x ) \triangle(x) 定义为将Din输入特征映射到 3 K 3K values的刚性KPConv的输出。在训练期间,网络同时学习生成位移的rigid kernel和生成输出特征的deformable kernel。 但学习第一个的速率设置为全局网络学习速率的0.1倍。

很遗憾,这种对图像可变形卷积的直接适应并不适合点云。 实际上,内核点最终被拉离输入点。这些内核点会被网络丢失,因为当没有邻元素在其影响范围内时,它们的位移 ( x ) \triangle(x) 的梯度为零。

为了解决此问题,提出:

  • “fitting” regularization loss ,该损失会损失输入相邻点之间的kernel points与其最近的相邻点之间的距离。
  • “repulsive”regularization loss ,当所有成对的kernel points之间的影响区域重叠时,添加该损失使它们不会一起崩溃。

总体而言,所有卷积位置 x R 3 x∈R^3 的正则化损失为:

添加损失后,网络就会产生适合输入点云局部几何形状的偏移。

有关这些“丢失的”内核点的更多详细信息,论文在附录中提到:

当设计可变形KPConv时,首先使用了图像可变形卷积的直接适应方法,但是网络的性能非常差。 研究了网络收敛后的kerneldeformations,并注意到kernel points 经常被拉离输入点这种现象来自点云的稀疏性质,存在空白空间围绕点。 这些变化是由网络预测的,因此,它们取决于输入形状。

对于训练期间的特殊输入,如果核心点偏离输入点,则其偏移∆k(x)的梯度为零。 因此,它会被网络“丢失”,并且对于相似的输入形状不会出现。 由于网络优化器的随机性,这种情况在收敛期间发生在许多输入形状上。

图解 : 说明了房间楼层示例中的“丢失”内核点。 红色的rigid kernel,其比例给出了核点影响范围的概念。 中间的紫色点表示网络预测的deformed kernel,没有任何正则化损失。 大多数紫色点都远离地板,因此“丢失”。可以注意到,正则化策略不仅可以防止“丢失”kernel points。 它还有助于最大化KPConv中active kernel points 的数量(输入点在范围内)。 几乎每个黄点都靠近地板。

4.3 Kernel Point Network Layers

主要任务:本节说明了如何有效地将KPConv理论付诸实践。

Subsampling to deal with varying densities.
为了确保点采样位置的空间一致性,使用 grid subsampling。 因此,选择每层支撑特征位置的支撑点作为所有非空网格单元中包含的原始输入点的重心。

Pooling layer. 要创建具有multiple layer scales的体系结构,需要逐步减少点数。 由于已经有了grid subsampling,因此将每个池化层的像元大小加倍,并加上其他相关参数,从而逐渐增加KPConv的接收场。 在每个新位置合并的特征可以通过max-pooling 或KPConv获得。 论文使用后者,并称其为“ stride KPConv”,类似于image strided convolution.

KPConv layer. 卷积层将点 P R N × 3 P\in R^{N\times3} ,其对应特征 F R N × D i n F\in R^{N\times D_{in}} 和邻域索引 n [ 1 , N ] N × n m a x n\in \left [ 1,N\right ]^{N^{'}\times n_{max}} 矩阵作为输入。 N N' 是被计算邻域的位置数,可以与N(在“strided ” KPConv的情况下)不同。 邻域矩阵被迫具有最大邻域 n m a x n_{max} 的大小。 由于大多数邻域包含少于 n m a x n_{max} 个邻域,因此矩阵 n n 包含未使用的元素。 我们称它们为shadow neighbors,在卷积计算中将其忽略。

Network parameters. 每个层 j j 都有一个像元大小 d l j dl_j ,我们可以从中推断其他参数。kernel points 影响距离设置为等于 σ j = Σ × d l j σ_j=Σ×dl_j ,

  • 对于rigid KPConv,平均 kernel point radius 为1.5 σ j σ_j ,则卷积半径会自动设置为2.5 σ j σ_j
  • 对于deformable KPConv,可以选择卷积半径为 r j = ρ × d l j r_j =ρ×dl_j
  • Σ Σ ρ ρ 是为整个网络设置的比例系数。 论文默认对所有实验使用通过交叉验证选择的以下参数集: K = 15 Σ = 1.0 K = 15,Σ= 1.0 ρ = 5.0 ρ= 5.0
  • 第一个子采样像元大小 d l 0 dl_0 取决于数据集,如上所述, d l j + 1 + 1 = 2 d l j dl_{j+1} + 1 = 2 * dl_j

4.4 Kernel Point Network Architectures

主要任务:设计了两种用于分类和分割任务的网络体系结构。
在这里插入图片描述
KP-CNN: 是一个5层分类卷积网络。

  • 每层包含两个卷积块,除第一层外,第一个都被strided。
  • 卷积块的设计用KPConv代替了the image convolution, batch normalization and leaky ReLu activation。
  • 在最后一层之后,特征通过global average pooling进行聚合,并由全连接层和softmax层进行处理。
  • 对于deformable KPConv的结果,仅在最后5个KPConv卷积块(第3层的第2个块,以及第4层和第5层的两个块)中使用deformable kernels。

KP-FCNN: 是用于分割的完全卷积网络。

  • 编码器部分与KP-CNN中的相同,而解码器部分使用nearest upsampling 来获得最终的逐点特征。
  • Skip links 用于在编码器和解码器的中间层之间传递特征,这些特征被串联到上采样的特征中,并由一元卷积处理,这相当于图像中的1×1卷积或pointnet中共享的MLP。
  • 可以采用与the strided KPConv相同的方式,用KPConv代替最近的上采样操作,但是不会导致性能的显着提高。

网络结构详细信息在附录中。

在图9中,每一层进行了下采样。 它说明了卷积半径(红色球体)如何与下采样网格大小成比例地增长。图9上方各层的绿色数字是使用的卷积块的特征维度(图8中的D)。

网络架构层处理大小可变的点云,因此无法将它们沿新的“batch”维度堆叠。 因此,沿其第一维(点数)堆叠点和特征张量。由于邻居索引和池索引没有从一个输入云指向另一个输入云,因此每个批处理元素都可以独立处理,而无需任何实现技巧。 只需要跟踪批处理元素索引即可定义KPCNN的全局池。 由于点数变化很大,因此使用可变的批次大小,方法是选择尽可能多的元素,直到达到一定数量的批次点为止。选择此限制以使平均批次大小与目标批次相对应尺寸。

KP-CNN训练:

  • 使用动量梯度下降优化器来最小化交叉熵损失,批处理大小为16,动量为0.98,初始学习率为 1 0 3 10^{-3}
  • 学习率被安排成指数下降,选择指数衰减以确保每100个epochs除以10。
  • 在最终的完全连接层中使用0.5的概率下降。 网络收敛于200个epochs。
  • 在deformable kernels的情况下,正则化损失以乘系数0.1加入到输出损失中。

KP-FCNN训练:

  • 使用动量梯度下降优化器来最大程度地减少逐点交叉熵损失,批大小为10,动量为0.98,初始学习率为 1 0 2 10^{-2}
  • 使用相同的学习率时间表,不使用 dropout 。
  • 在所有实验中,网络最多需要400个epochs 才能收敛。
  • 对于真实场景分割,可以生成任意数量的输入球体,因此我们将一个epoch 定义为500个优化器步骤,相当于网络看到的5000个球体。
  • 使用相同的变形正则化损失。

Model sizes and speeds:

  • KPFCNN和KP-CNN具有相似数量的参数,因为KP-FCNN的解码器部分仅涉及1x1卷积。
  • 每个数据集的运行速度与另一个数据集不同。 实际上,在网络正向传播期间执行的操作数取决于当前批次的点数以及这些点的最大neighbors数。

(五)Experiments

5.1 3D Shape Classification and Segmentation

5.11 Data.

首先,在两个常见的模型数据集上评估网络。

  • ModelNet40 用于分类。包含来自40个类别的12,311个网格CAD模型。
  • ShapenetPart 用于部件分割。是16个类别的16681个点云的集合,每个点云都有2-6个零件标签。

在这两种情况下,都遵循标准的训练/测试拆分并重新缩放对象以使其适合单位球体(在本实验的其余部分中,将单位视为米)。 忽略法线,因为它们仅可用于人工数据。

5.12 Classification task.

  • 将第一个下采样网格大小设置为 d l 0 = 2 c m dl_0 = 2cm 。 不添加任何特征作为输入。 每个输入点都分配有一个等于1的常量特征,而空白空间可以视为0。
  • 该常量特征对输入点的几何形状进行编码。扩充过程包括缩放,翻转和扰动点。 在这种设置下,Nvidia Titan Xp每秒可处理2.9批16云。
  • 由于下抽样策略,输入点云并非都具有相同数量的点,这并不是问题,因为我们的网络可以接受可变的输入点云大小。
  • 平均而言,ModelNet40点云在我们的框架中包含6,800个点。

表格分析: 网络仅使用点就优于其他最新方法(论文没有考虑使用法线作为额外输入的方法),而且还注意到rigid KPConv性能略好一些。猜想可以通过任务简单性来解释它。如果deformable kernels 增加了更多的描述能力,它们还将增加整个网络的复杂性,这可能会扰乱收敛或导致过拟合于此类形状分类等较简单的任务。

5.13 Segmentation task.

  • 使用KP-FCNN体系结构,其参数与分类任务中的参数相同,将位置(x,y,z)作为附加特征添加到常数1中,并使用相同的扩充过程。
  • 数据集点云比较小(平均2300点),每秒可以处理4.1个批处理的16个形状。
  • 表1显示了实例平均值和类平均值mIoU。 KP-FCNN的性能优于所有其他算法,包括使用其他输入(例如图像或法线)的算法。
  • 形状分割是比形状分类更困难的任务,并且看到KPConv在可变形内核方面具有更好的性能。

论文在附录中详细列出了每个类别的内容。

在这里插入图片描述

5.2 3D Scene Segmentation

5.21 Data.

第二个实验展示了分割架构如何推广到真实的室内和室外数据。选择在4个不同性质的数据集上测试网络。

  • Scannet: 用于室内杂乱的场景;包含1,513个小型培训场景和100个用于在线基准测试的测试场景,并带有20个语义类。
  • S3DIS: 用于室内大空间; 覆盖了来自三座不同建筑物的六个大型室内区域,总共提供了2.73亿个点,分为13个类别。提倡使用Area-5作为测试场景,以更好地衡量方法的泛化能力
  • Semantic3D: 用于室外固定扫描; 包含多个不同户外场景的固定激光雷达扫描。在此数据集中,用8个类别标注了超过40亿个点,但它们主要覆盖地面,建筑物或植被,并且与其他数据集中的对象实例相比,对象实例更少。我们赞成reduced-8 的挑战,因为它减少了靠近扫描仪的物体的偏斜。
  • Paris-Lille-3D: 用于室外移动扫描; 包含4个不同城市中超过2公里的街道,也是在线基准。此数据集的1.6亿点带有10个语义类的注释。

5.22 Pipeline for real scene segmentation.

这些数据集中的3D场景太大,无法整体分割。 KP-FCNN架构用于分割在领域中的小型子云。

  • 在训练中,在场景中随机拾取球体。
  • 在测试时,会定期在点云中选择球体,但要确保每个点都通过不同的球体位置进行多次测试。
  • 与在模型数据集上的投票方案一样,对每个点的预测概率进行平均。
  • 数据集着色后,将使用三个颜色通道作为特征。 仍然保留常数1,以确保不会忽略黑色的点。
  • 对于卷积,所有特征都等于零的点等于空白空间。
  • 输入球体半径选择为 50 × d l 0 50×dl_0 (根据Modelnet40实验)。

5.23 Results.

由于室外物体大于室内物体,因此在Semantic3D和Paris-Lille3D上使用 d l 0 dl_0 = 6cm,在Scannet和S3DIS上使用 d l 0 dl_0 = 4cm。

表释:

  • 如表2所示,论文网络结构在Scannet上排名第二,并优于其他数据集上的所有其他分割架构。
  • 与其他点卷积体系结构相比[2、20、41],KPConv性能在Scannet上比以前的得分高19 mIoU,而在S3DIS上则高9 mIoU。
  • SubSparseCNN论文中没有报告Scannet上的SubSparseCNN分数[9],因此在不知道他们的实验设置的情况下很难进行比较。 但是可以注意到,在ShapeNetPart分割的相同实验设置中,KPConv比SubSparseCNN的性能高出将近2 mIoU。
  • 在这四个数据集中,KPConv deformable kernels 改善了Paris-Lille-3D和S3DIS的结果,而rigid KPConv在Scannet和Semantic3D上更好。

分析:

  • 如果遵循论文的假设,可以解释Semantic3D得分较低的原因是该数据集缺乏多样性。的确,尽管包含15个场景和40亿个点,但它包含了大部分地面,建筑物和植被点以及一些诸如汽车或行人的真实物体。尽管Scannet并非如此,它包含了1,500多个具有各种对象和形状的场景,但论文的验证研究并未反映在该基准测试的得分上。同时发现,在几个不同的验证集上,可变形的KPConv优于其刚性的对应项(请参见第5.3节)。

结论:

  • 这些结果表明deformable KPConv的描述能力适用于大型多样数据集上的网络。我们相信KPConv可以在较大的数据集上蓬勃发展,因为KPConv的 kernel 具有强大的描述能力和良好的可学习性(MLP卷积的权重学习起来更复杂)。图4中显示了Semantic3D和S3DIS上分段场景的图示。

附录:
在这里插入图片描述

5.3 Ablation Study

主要任务:进行消融研究以支持观点,即deformable KPConv具有比rigid KPConv更强的描述能力。

这个想法是为了揭示可变形内核的真正潜力。 使用Scannet数据集(与以前相同的参数)并使用正式的验证集,因为测试集无法用于此类评估。

如图5所示,当限制在4个内核点时,可变形KPConv仅损失1.5%mIoU。 在相同的配置中,刚性KPConv会损失3.5%mIoU。具有15个内核点的deformable KPConv的性能优于rigid KPConv。

论文提到,尽管测试集并非如此,但尝试了不同的验证集,这些验证集证实了deformable KPConv的出色性能。在S3DIS上获得了相同的结果。 要理解为什么,需要超越数字,看看两个版本的KPConv有效地学习了什么。

5.4 Learned Features and Effective Receptive Field

为了更深入地了解KPConv,提供了两种有关学习机制的见解。

5.41 Learned features.

主要任务:了解KPConv可以大致学习的知识。

在本实验中,使用rigid KPConv在ModelNet40上训练了KP-CNN。

  • 添加了围绕垂直轴的随机旋转增强,以增加输入形状的多样性。
  • 可视化每个通过根据点的激活程度为这些点着色来学习该功能。
  • 在图6中,选择输入点云,以最大程度地激活第一层和第三层的不同功能。

图释:

  • 高激活为红色,低激活为蓝色。
  • 在网络的第一层中,该网络能够学习低级功能,例如垂直/水平平面(a / b),线性结构(c)或角(d)。
  • 在后面的层中,网络检测到更复杂的形状,例如小支撑(e),球(f),圆锥(g)或楼梯(h)。
  • 但是,很难看到rigid KPConv和deformable KPConv之间的区别。

5.42 Effective Receptive Field.

主要任务:在不同位置计算其有效接收场(ERF),了解rigid KPConv和deformable KPConv学习到的表示形式之间的差异。

ERF是每个输入点对特定位置的KPConv层结果的影响的度量。 它被计算为相对于输入点特征在此特定位置的KPConv响应的梯度。

图释:

  • ERF取决于其居中对象。
  • rigid KPConv ERF在每种类型的对象上都有相对一致的范围,而deformable KPConv ERF似乎适合于对象的大小。
  • 它覆盖了整个床,并且更多地集中在椅子上,而不是周围的地面上。当以平面为中心时,似乎也将忽略其中的大部分,并到达场景中的更多细节。
  • 这种自适应行为表明,deformable KPConv提高了网络适应场景对象的几何形状的能力,并说明了在室内数据集上更好的性能。

(六)Conclusion

  • 提出了KPConv,一种在点云上运行的卷积。
  • KPConv将半径邻域作为输入,并使用一小组kernel points在空间上定位的权重来处理它们。
  • 定义一个 deformable 的该卷积算子的版本,它可以学习局部位移,从而有效地使卷积核变形以使其适合点云几何形状。
  • 根据数据集的多样性或所选的网络配置,rigid KPConv和deformable KPConv都很有价值,此论文网络为几乎每个测试的数据集带来了最新的性能。
  • 除了提出的分类和分割网络外,KPConv还可用于CNN解决的任何其他应用程序。 相信,deformable卷积可以在较大的数据集中蓬勃发展,也可以在诸如目标检测,激光雷达流量计算或point cloud completion之类的挑战性任务中蓬勃发展。
发布了42 篇原创文章 · 获赞 56 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Dujing2019/article/details/104178936