当前位置: 首页 > news >正文

点云处理入门--PointNetPointNet++论文与代码详解

基础知识

点云数据:
点云是一种通过三维扫描设备或计算机图形学技术获取的三维空间数据,通常由一系列点组成,每个点包含其在三维空间中的坐标(如 x,y,z),有时还可能包含颜色、强度等附加信息。
介绍几种常见的点云存储格式:
1、XYZ
最简单的点云文件格式,通常是一个纯文本文件,每行表示一个点,包含三个数值(x,y,z坐标)
2、PLY
文件可以是纯文本或二进制格式,支持存储点的坐标、颜色、法线等附加信息,支持点云和网格(mesh)数据。
3、OBJ
主要用于存储三维模型(网格),但也可用于点云数据。文件格式为纯文本,支持顶点坐标、纹理坐标、法线等信息,不支持颜色。
Mesh和点云的转换:
Mesh(网格)是一种由顶点、边和面组成的三维模型,而点云是离散的点集合。虽然它们在形式上有所不同,但可以通过一些算法相互转换:

  • Mesh 转点云:可以通过泊松采样(Poisson Sampling)、均匀采样(Uniform Sampling)等方法从网格表面生成点云。
  • 点云转 Mesh:可以通过泊松重建(Poisson Surface Reconstruction)、Marching Cubes 等算法从点云生成网格。

PointNet论文讲解

论文地址:https://arxiv.org/abs/1612.00593
code:https://github.com/charlesq34/pointnet
pytorch实现:https://github.com/fxia22/pointnet.pytorch

简单来说,点云数据就是一堆坐标点。

如何处理点云数据?

体素化方法:将点云数据转换为规则的三维网格(体素),每个体素包含一定范围内的点云数据,对每个体素内的点云进行特征提取(平均值、最大值等),然后利用3D卷积神经网络(CNN)进行处理。
多视图方法:将点云从多个视角投影到二维平面上,生成多个二维图像,然后使用传统的2D卷积网络进行处理。
这两种方法都没有直接利用点云原始数据,而是经过转换后输入网络。

PointNet直接使用原始点云数据,近年来越来越多的方法这样做。
直接对点云特征进行学习,就需要解决两个问题:点云数据的无序性、旋转的不变性。

1、数据的无序性

点云中的点的排列顺序是随机的,不具有固定的结构。点云中的点可以任意交换位置,而不会影响其对物体或场景的描述。这点和图像数据是不同的,而传统的深度学习方法(如卷积神经网络)通常依赖于输入数据的固定结构(如图像的像素排列)。
这就要求模型能够不受数据排列顺序的影响,提取到准确的特征。
如何能够不受排列顺序影响呢?我们可以使用一些函数来处理,比如max,min,sum,avg等,这些函数的处理结果不受序列顺序影响。但是还有一个问题,如果说一个点云nx3,我们对每一个维度采取max处理,得到1x3的特征(这反映了这个点云在三个维度上的最大值),这样做损失的信息太多了,输出的特征仅仅继承了三个坐标轴上的最大特征。
所以我们需要更多的信息,我们首先将特征映射到高维空间,简单来说将点云数据通过MLP扩充维度,如nx1024,然后 取点云序列在每一个维度的最大值1x1024,组成的向量代表整个点云序列的特征,这样一来,避免了序列顺序的影响,同时也增加了更多的特征信息。
在这里插入图片描述

2、旋转不变性

点云在经过旋转变换后,其特征表示应保持不变。
换句话说,如果我们在空间内旋转一个物体,比如一把伞,那么他的点云数据(坐标)都已经被改变,我们需要模型能够包容这种旋转操作,依然识别出这是一把伞。
PointNet保证旋转不变性的做法是–设计了T-Net来学习点云的旋转。
T-Net是一个小型的神经网络。主要目的是通过学习一个仿射变换矩阵,对输入点云或特征空间进行对齐,从而减少姿态变化对模型性能的影响。
具体做法:输入点云为nx3,先扩充维度(三次卷积操作,卷积核为1x1,通道数分别是64,128,1024)得到nx1024向量,然后对其进行最大池化(确保模型对点云无序性具有不变性),得到特征1x1024,然后先通过两层全连接层,分别映射到 512 维和 256 维,最后把256维的特征映射到kxk的矩阵(在网络架构中有两处,一处是3x3,一处是64x64)。这个映射也很简单,就是再过一个全连接,维度变成 k 2 k^2 k2,然后reshape成kxk的矩阵就行。最后这个矩阵就是学习了点云旋转的特征,将其乘以输出的点云即可。

PointNet代码

PointNet分为两个版本,点云分类和点云分割
首先看分类,分类的网络结构图应该如下图所示,红色箭头代表跳过中间的feature_transform
在这里插入图片描述
输入为一个 N × 3 N \times 3 N×3的点云,代码里实际的输入格式为Bx3xN
先过一个inout_transform,也就是论文中说的T-Net(这部分怎么处理的,可以看下面的代码注释)
然后得到特征为Bx3xN,然后过一个卷积,变为Bx64xN,也就是图中的mlp(64,64),两个64分别代表之前的通道数和之后的通道数,
然后按照我画的红色箭头,再过一个mlp(64,128,1024),也就是三个卷积,最后输出Bx1024xN,然后做一个max pool变为Bx1024
然后用全卷积神经网络nn.Linear,将1024–>512–>256–>k,也就是图中的mlp(512,256,k)
k就是分类的类别数量,最后再过一个softmax得到分类结果。
比如分两类,最后输出的Bx2也就是每个点云2个概率值,分别代表该点云是这两类的概率。
这就是分类架构。
分割与分类不同,分类预测整个点云的概率,如果一共有五类,那么就输出五个数,代表该点云是这五类的概率。
而分割需要预测每一个点的类别,要输出Nx5个数字,代表n个点是这个五类的概率。
分割的网络结构如下图所示。
在这里插入图片描述

与分类相比有几个地方不一样。
首先就是第一次mlp(64,64)之后,需要再过一个feature_transformer,这次仿射变换矩阵是64x64的,过了之后保持大小形状不变,然后把这个Bx64xN保存下来,后面继续正常往下走,直到走到最后max pool之后输出一个全局特征Bx1024,这个时候我们使用复制操作,将其复制为Bx1024xN,也就是复制n个,然后把这个特征与前面保存下来的那个Bx64xN,做一个拼接,得到Bx1088xN(64+1024),拿这个特征去做一维卷积,将通道数由1088一直降到128,也就是得到Bx128xN,最后再来一个一维卷积,直接维度降到k,BxkxN,k代表分类的类别。然后做一个softmax,得到每个点的k个类别概率(softmax之前要转换一下,可以参考代码操作)。

代码以及注释(pytorch版本)

from __future__ import print_function
import torch
import torch.nn as nn
import torch.nn.parallel
import torch.utils.data
from torch.autograd import Variable
import numpy as np
import torch.nn.functional as Fclass STN3d(nn.Module): #空间变换网络(Spatial Transformer Network)def __init__(self):super(STN3d, self).__init__()self.conv1 = torch.nn.Conv1d(3, 64, 1)self.conv2 = torch.nn.Conv1d(64, 128, 1)self.conv3 = torch.nn.Conv1d(128, 1024, 1)self.fc1 = nn.Linear(1024, 512)self.fc2 = nn.Linear(512, 256)self.fc3 = nn.Linear(256, 9)self.relu = nn.ReLU()self.bn1 = nn.BatchNorm1d(64)self.bn2 = nn.BatchNorm1d(128)self.bn3 = nn.BatchNorm1d(1024)self.bn4 = nn.BatchNorm1d(512)self.bn5 = nn.BatchNorm1d(256)def forward(self, x): # x:BxDxN  eg:(32, 3, 2500) 32个点云,每个点云2500个点,每个点三个坐标xyzbatchsize = x.size()[0] # 输入点云数据 BxDxN  D=3x = F.relu(self.bn1(self.conv1(x))) # 一维卷积 通道数3-->64x = F.relu(self.bn2(self.conv2(x))) # 一维卷积 通道数64-->128x = F.relu(self.bn3(self.conv3(x))) # 一维卷积 通道数128-->1024x = torch.max(x, 2, keepdim=True)[0] # Bx1024xN 在维度2上取最大值 Bx1024x1 即在每个维度上对N个点取最大值x = x.view(-1, 1024) # 去除最后一列 Bx1024x = F.relu(self.bn4(self.fc1(x))) # 1024-->512x = F.relu(self.bn5(self.fc2(x))) # 512-->256x = self.fc3(x) # 256-->9# 为每一个batch生成一个单位矩阵iden = Variable(torch.from_numpy(np.array([1,0,0,0,1,0,0,0,1]).astype(np.float32))).view(1,9).repeat(batchsize,1)if x.is_cuda:iden = iden.cuda()x = x + iden # 变换矩阵与单位矩阵相加 这个操作可以理解为:防止网络乱学 引导其接近单位矩阵x = x.view(-1, 3, 3) # 调整为Bx3x3return xclass STNkd(nn.Module):#这个与上面的STN是一样的 这个输出64x64  上面输出3x3def __init__(self, k=64):super(STNkd, self).__init__()self.conv1 = torch.nn.Conv1d(k, 64, 1)self.conv2 = torch.nn.Conv1d(64, 128, 1)self.conv3 = torch.nn.Conv1d(128, 1024, 1)self.fc1 = nn.Linear(1024, 512)self.fc2 = nn.Linear(512, 256)self.fc3 = nn.Linear(256, k*k)self.relu = nn.ReLU()self.bn1 = nn.BatchNorm1d(64)self.bn2 = nn.BatchNorm1d(128)self.bn3 = nn.BatchNorm1d(1024)self.bn4 = nn.BatchNorm1d(512)self.bn5 = nn.BatchNorm1d(256)self.k = kdef forward(self, x):batchsize = x.size()[0]x = F.relu(self.bn1(self.conv1(x)))x = F.relu(self.bn2(self.conv2(x)))x = F.relu(self.bn3(self.conv3(x)))x = torch.max(x, 2, keepdim=True)[0]x = x.view(-1, 1024)x = F.relu(self.bn4(self.fc1(x)))x = F.relu(self.bn5(self.fc2(x)))x = self.fc3(x)iden = Variable(torch.from_numpy(np.eye(self.k).flatten().astype(np.float32))).view(1,self.k*self.k).repeat(batchsize,1)if x.is_cuda:iden = iden.cuda()x = x + idenx = x.view(-1, self.k, self.k)return xclass PointNetfeat(nn.Module):def __init__(self, global_feat = True, feature_transform = False):super(PointNetfeat, self).__init__()self.stn = STN3d()self.conv1 = torch.nn.Conv1d(3, 64, 1)self.conv2 = torch.nn.Conv1d(64, 128, 1)self.conv3 = torch.nn.Conv1d(128, 1024, 1)self.bn1 = nn.BatchNorm1d(64)self.bn2 = nn.BatchNorm1d(128)self.bn3 = nn.BatchNorm1d(1024)self.global_feat = global_featself.feature_transform = feature_transformif self.feature_transform:self.fstn = STNkd(k=64)def forward(self, x): # x:BxDxN D就是通道数 输入的时候D=3 代表xyz坐标n_pts = x.size()[2]trans = self.stn(x) # 做一个仿射变换  trans:Bx3x3x = x.transpose(2, 1) # BxDxN --> BxNx3x = torch.bmm(x, trans) # 与仿射变换的结果相乘 结果大小为BxNx3x = x.transpose(2, 1) # x:Bx3xNx = F.relu(self.bn1(self.conv1(x))) # 3-->64  Bx64xNif self.feature_transform:trans_feat = self.fstn(x) # 做一个仿射变换  trans_feat: 64x64x = x.transpose(2,1)x = torch.bmm(x, trans_feat) # 与仿射变换的结果相乘 x = x.transpose(2,1) # 结果大小为BxNx64else:trans_feat = Nonepointfeat = x # feature_transform=false : pointfeat Bx64xNx = F.relu(self.bn2(self.conv2(x))) # 64-->128  Bx128xNx = self.bn3(self.conv3(x)) # 128-->1024  Bx1024xNx = torch.max(x, 2, keepdim=True)[0] # 最大池化 Bx1024x1x = x.view(-1, 1024) # Bx1024if self.global_feat:return x, trans, trans_feat # x是编码后的特征 teans是仿射变换矩阵 trans_feat是else:x = x.view(-1, 1024, 1).repeat(1, 1, n_pts) # 使用复制 将x:Bx1024x1-->Bx1024xN# x(Bx1024xN)pointfeat(Bx64xN)在维度1上拼接 结果大小为Bx1088xNreturn torch.cat([x, pointfeat], 1), trans, trans_featclass PointNetCls(nn.Module): # 分类def __init__(self, k=2, feature_transform=False):# 分类时 feature_transform=Falsesuper(PointNetCls, self).__init__()self.feature_transform = feature_transformself.feat = PointNetfeat(global_feat=True, feature_transform=feature_transform) self.fc1 = nn.Linear(1024, 512)self.fc2 = nn.Linear(512, 256)self.fc3 = nn.Linear(256, k)self.dropout = nn.Dropout(p=0.3) # 每个神经元有 30% 的概率被置零 最后一层使用self.bn1 = nn.BatchNorm1d(512)self.bn2 = nn.BatchNorm1d(256)self.relu = nn.ReLU()def forward(self, x): # BxDxNx, trans, trans_feat = self.feat(x) # 提取特征 x:Bx1024 trans:Bx3x3 trans_feat=Nonex = F.relu(self.bn1(self.fc1(x))) # Bx1024-->Bx512x = F.relu(self.bn2(self.dropout(self.fc2(x))))  # Bx512-->Bx256 x = self.fc3(x) # Bx256 --> k  要分几类 默认是2return F.log_softmax(x, dim=1), trans, trans_feat # 过一个log_softmax输出结果class PointNetDenseCls(nn.Module): # 分割def __init__(self, k = 2, feature_transform=False):super(PointNetDenseCls, self).__init__()self.k = kself.feature_transform=feature_transformself.feat = PointNetfeat(global_feat=False, feature_transform=feature_transform)self.conv1 = torch.nn.Conv1d(1088, 512, 1)self.conv2 = torch.nn.Conv1d(512, 256, 1)self.conv3 = torch.nn.Conv1d(256, 128, 1)self.conv4 = torch.nn.Conv1d(128, self.k, 1)self.bn1 = nn.BatchNorm1d(512)self.bn2 = nn.BatchNorm1d(256)self.bn3 = nn.BatchNorm1d(128)def forward(self, x):batchsize = x.size()[0]n_pts = x.size()[2]x, trans, trans_feat = self.feat(x) # 提取特征 x:Bx1088xN trans:Bx3x3 trans_feat=Bx64x64x = F.relu(self.bn1(self.conv1(x))) # Bx1088xN --> Bx512xNx = F.relu(self.bn2(self.conv2(x))) # Bx512xN --> Bx256xNx = F.relu(self.bn3(self.conv3(x))) # Bx256xN --> Bx128xNx = self.conv4(x) # Bx128xN --> BxkxNx = x.transpose(2,1).contiguous() # BxNxKx = F.log_softmax(x.view(-1,self.k), dim=-1) # BxNxK-->B*NxK 然后过一个log_softmaxx = x.view(batchsize, n_pts, self.k) # BxNxKreturn x, trans, trans_feat # x:BxNxK, trans:Bx3x3, trans_feat:Bx64x64def feature_transform_regularizer(trans):d = trans.size()[1]batchsize = trans.size()[0]I = torch.eye(d)[None, :, :]if trans.is_cuda:I = I.cuda()loss = torch.mean(torch.norm(torch.bmm(trans, trans.transpose(2,1)) - I, dim=(1,2)))return lossif __name__ == '__main__':sim_data = Variable(torch.rand(32,3,2500))trans = STN3d()out = trans(sim_data)print('stn', out.size())print('loss', feature_transform_regularizer(out))sim_data_64d = Variable(torch.rand(32, 64, 2500))trans = STNkd(k=64)out = trans(sim_data_64d)print('stn64d', out.size())print('loss', feature_transform_regularizer(out))pointfeat = PointNetfeat(global_feat=True)out, _, _ = pointfeat(sim_data)print('global feat', out.size())pointfeat = PointNetfeat(global_feat=False)out, _, _ = pointfeat(sim_data)print('point feat', out.size())cls = PointNetCls(k = 5)out, _, _ = cls(sim_data)print('class', out.size())seg = PointNetDenseCls(k = 3)out, _, _ = seg(sim_data)print('seg', out.size())

相关文章:

点云处理入门--PointNetPointNet++论文与代码详解

基础知识 点云数据: 点云是一种通过三维扫描设备或计算机图形学技术获取的三维空间数据,通常由一系列点组成,每个点包含其在三维空间中的坐标(如 x,y,z),有时还可能包含颜色、强度等附加信息。 介绍几种常…...

通过Nginx负载均衡+Keepalived实现业务高可用

通过Nginx负载均衡和Keepalived可以实现业务的高可用,以下是详细的实现步骤: 环境准备 假设我们有3台服务器,IP地址分别为: 服务器1(Nginx Keepalived 主节点):192.168.1.100服务器2&#x…...

Spark技术系列(三):Spark算子全解析——从基础使用到高阶优化

Spark技术系列(三):Spark算子全解析——从基础使用到高阶优化 1. 算子核心概念与分类体系 1.1 算子本质解析 延迟执行机制:转换算子构建DAG,行动算子触发Job执行任务并行度:由RDD分区数决定(可通过spark.default.parallelism全局配置)执行位置优化:基于数据本地性的…...

ES6模块化详解:导入与导出方式

在现代 JavaScript 开发中,模块化是代码管理和组织的重要工具。ES6(ECMAScript 2015)引入了模块化的概念,通过 import 和 export 来组织代码,使得模块的管理变得更加清晰和简洁。本文将详细介绍 ES6 中的各种模块导入导…...

每日学习Java之一万个为什么?[MySQL面试篇]

分析SQL语句执行流程中遇到的问题 前言1 MySQL是怎么在一台服务器上启动的2 MySQL主库和从库是同时启动保持Alive的吗?3 如果不是主从怎么在启动的时候保证数据一致性4 ACID原则在MySQL上的体现5 数据在MySQL是通过什么DTO实现的6 客户端怎么与MySQL Server建立连接…...

常用空间数据结构对比

空间数据结构是用来组织和查询多维空间数据的算法结构。它们在地理信息系统 (GIS)、计算机图形学、机器人导航、机器学习等领域非常重要。以下是几种常见空间数据结构的对比: 1. 四叉树(Quadtree) 适用场景:二维空间数据&#x…...

AnythingLLM+LM Studio本地知识库构建

前置操作: 已经安装以下软件,并配置后: DeepSeek-R1-Distill-Llama-8B-Q4_K_M.ggufLM-Studio-0.3.10-6-x64 软件准备: 下载AnythingLLM:AnythingLLM | The all-in-one AI application for everyone 点击"Dow…...

使用 Java 更新 Word 文档中的图表数据-超详细

使用 Java 更新 Word 文档中的图表数据 在日常的工作中,尤其是在数据分析和报告自动化的场景中,可能会遇到需要定期更新 Word 文档中的图表数据的需求。比如,生成数据报告时,我们需要在图表中更新一些动态的数据值。今天&#xf…...

Qt常用控件之下拉框QComboBox

下拉框QComboBox QComboBox 是一个下拉框控件。 1. QComboBox属性 属性说明currentText当前选中的文本。currentIndex当前选中的条目下标(从 0 开始,如果没有条目被选中则该值为 -1)。editable是否允许被修改。为 true 时,QCom…...

Qt 中集成mqtt协议

一,引入qmqtt 库 我是将整个头文件/源文件都添加到了工程中进行编译,这样 跨平台时 方便,直接编译就行了。 原始仓库路径:https://github.com/emqx/qmqtt/tree/master 二,使用 声明一个单例类,将订阅到…...

2024年第十五届蓝桥杯大赛软件赛省赛Python大学A组真题解析

文章目录 试题A: 拼正方形(本题总分:5 分)解析答案试题B: 召唤数学精灵(本题总分:5 分)解析答案试题C: 数字诗意解析答案试题A: 拼正方形(本题总分:5 分) 【问题描述】 小蓝正在玩拼图游戏,他有7385137888721 个2 2 的方块和10470245 个1 1 的方块,他需要从中挑出一些…...

AI大模型-提示工程学习笔记19-自我反思

目录 1. 自我反思的核心思想 (1) LLM 的局限性 (2) Reflexion 的解决方案 2. Reflexion 的工作流程 (1) 任务输入 (2) 初始生成 (3) 反思 (Reflection) (4) 调整与改进 (5) 迭代 (6) 结果输出 3. Reflexion 的关键组件 (1) 大语言模型 (LLM) (2) 反思者 (Reflector…...

GaussDB 学习实战指南:从部署到高并发优化的全流程解析

引言 GaussDB 作为华为推出的高性能分布式数据库,凭借其 分布式架构、高可用性、云原生支持 等特性,成为企业级应用的核心选择。本文将以 实战操作为核心,覆盖 集群部署、数据分片、性能调优、容灾备份、云上迁移 五大场景,通过真实案例与代码示例,助你快速掌握 GaussDB …...

vue3 Props的使用

Props是什么? 官方地址:Props | Vue.js 在 Vue 中,props 是父组件向子组件传递数据的一种机制。 props 是子组件中定义的自定义属性,父组件通过这些属性向子组件传递数据。 它们是单向数据流的一部分,意味着数据只能…...

Ecode前后端传值

说明 在泛微 E9 系统开发过程中,使用 Ecode 调用后端接口并进行传值是极为常见且关键的操作。在上一篇文章中,我们探讨了 Ecode 调用后端代码的相关内容,本文将深入剖析在 Ecode 中如何向后端传值,以及后端又该如何处理接收这些值…...

【Linux】进程状态(二)

目录 前言: 一、进程状态: 1.运行状态(时间片) 2.阻塞状态 3.阻塞挂起状态 二、Linux进程状态: 1.运行状态(R)和阻塞状态(S) 2.深度睡眠状态(D) 3.停止状态(T) 3.1使进程在后台运行 4.追踪暂停状态(t) 5.死亡状态(X)和僵尸状态…...

domain 网络安全 网络安全域

🍅 点击文末小卡片 ,免费获取网络安全全套资料,资料在手,涨薪更快 文章目录 1、域的概述 1.1、工作组与域1.2、域的特点1.3、域的组成1.4、域的部署概述1.5、活动目录1.6、组策略GPO 2、域的部署实验 2.1、建立局域网&#xf…...

链表和STL —— list 【复习笔记】

1. 链表 1.1 链表的定义和类型 和顺序表一样,链表也是一种线性表,线性表存储结构为链式存储就是链表 链式存储不仅要保存数据元素,还要保存数据元素间的关系,这两个部分信息形成了结点。结点有两个域:数据域&#x…...

Java Map实现类面试题

Java Map实现类面试题 HashMap Q1: HashMap的实现原理是什么? HashMap基于哈希表实现,使用数组链表红黑树(Java 8)的数据结构。 public class HashMapPrincipleExample {// 模拟HashMap的基本结构public class SimpleHashMap&…...

技术架构和工程架构区别

技术架构 技术架构‌是对某一技术问题解决方案的结构化描述,包括组件结构及其交互关系。它涵盖部署方案、存储方案、缓存方案、日志方案等多个方面,旨在通过组织人员和技术,以最低的成本满足需求和应对变化,保障软件的稳定高效运…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...