复现PointNet(分割网络):Windows + PyTorch+代码

news2024/11/17 8:15:47

一、平台

Windows 10

GPU RTX 3090 + CUDA 11.1 + cudnn 8.9.6

Python 3.9

Torch 1.9.1+cu111

所用的原始代码:https://github.com/fxia22/pointnet.pytorch​​​​​​​

二、数据

shapenetcore_partanno_segmentation_benchmark_v0

三、代码

分享给有需要的人,代码质量勿喷。

对源代码进行了简化和注释。

不搞原作者的可视化工具,分割结果保存成txt,或者利用 laspy 生成点云。

别问为啥在C盘,问就是2T的三星980Pro

3.1 文件组织结构

3.2 dataset.py

修改了部分txt文件的路径

from __future__ import print_function
import os
import os.path
import numpy as np
import sys
from tqdm import tqdm 
import json
from plyfile import PlyData, PlyElement
import torch
import torch.utils.data as data


def get_segmentation_classes(root):
    catfile = os.path.join(root, 'synsetoffset2category.txt')
    cat = {}
    meta = {}

    with open(catfile, 'r') as f:
        for line in f:
            ls = line.strip().split()
            cat[ls[0]] = ls[1]

    for item in cat:
        dir_seg = os.path.join(root, cat[item], 'points_label')
        dir_point = os.path.join(root, cat[item], 'points')
        fns = sorted(os.listdir(dir_point))
        meta[item] = []
        for fn in fns:
            token = (os.path.splitext(os.path.basename(fn))[0])
            meta[item].append((os.path.join(dir_point, token + '.pts'), os.path.join(dir_seg, token + '.seg')))
    
    with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'num_seg_classes.txt'), 'w') as f:
        for item in cat:
            datapath = []
            num_seg_classes = 0
            for fn in meta[item]:
                datapath.append((item, fn[0], fn[1]))

            for i in tqdm(range(len(datapath))):
                l = len(np.unique(np.loadtxt(datapath[i][-1]).astype(np.uint8)))
                if l > num_seg_classes:
                    num_seg_classes = l

            print("category {} num segmentation classes {}".format(item, num_seg_classes))
            f.write("{}\t{}\n".format(item, num_seg_classes))

def gen_modelnet_id(root):
    classes = []
    with open(os.path.join(root, 'train.txt'), 'r') as f:
        for line in f:
            classes.append(line.strip().split('/')[0])
    classes = np.unique(classes)
    with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'modelnet_id.txt'), 'w') as f:
        for i in range(len(classes)):
            f.write('{}\t{}\n'.format(classes[i], i))


class ShapeNetDataset(data.Dataset):
    def __init__(self,
                 root,
                 npoints=2500,
                 classification=False,
                 class_choice=None,
                 split='train',
                 data_augmentation=True):
        self.npoints = npoints
        self.root = root
        self.catfile = os.path.join(self.root, 'synsetoffset2category.txt')
        self.cat = {}
        self.data_augmentation = data_augmentation # 数据扩充
        self.classification = classification
        self.seg_classes = {}

        # 读synsetoffset2category.txt中的数据,并以字典的形式存储到self.cat中
        with open(self.catfile, 'r') as f:
            for line in f:
                # strip():移除字符串头尾指定的字符(默认为空格或换行符)
                # split():指定分隔符对字符串进行切片,返回分割后的字符串列表(默认为所有的空字符,包括空格、换行\n、制表符\t等)
                ls = line.strip().split()
                # cat为字典,通过[键]索引。键:类别;值:文件夹名称
                self.cat[ls[0]] = ls[1]
        # print(self.cat) #所有类和代号

        if not class_choice is None:
            self.cat = {k: v for k, v in self.cat.items() if k in class_choice}

        self.id2cat = {v: k for k, v in self.cat.items()} # key和value互换

        self.meta = {}
        # json文件类似xml文件,可存储键值对和数组等
        # split=train
        # format():字符串格式化函数,使用{}代替之前的%
        splitfile = os.path.join(self.root, 'train_test_split', 'shuffled_{}_file_list.json'.format(split))
        #from IPython import embed; embed()
        filelist = json.load(open(splitfile, 'r'))

        # for item in self.cat:item为键
        # for item in self.cat.values():item为值
        # for item in self.cat.items():item为键值对(元组的形式)
        # for k, v in self.cat.items():更为规范的键值对读取方式
        # meta为字典,键为类别,键值为空
        for item in self.cat:
            self.meta[item] = []

        for file in filelist:
            _, category, uuid = file.split('/')
            if category in self.cat.values():
                self.meta[self.id2cat[category]].append((os.path.join(self.root, category, 'points', uuid+'.pts'),
                                        os.path.join(self.root, category, 'points_label', uuid+'.seg')))

        self.datapath = []
        # cat存储类别及其所在文件夹,item访问键,即类别
        for item in self.cat:
            # meta为字典,fn访问值,即路径
            for fn in self.meta[item]:
                # item为类别,fn[0]为点云路径,fn[1]为用于分割的标签路径
                self.datapath.append((item, fn[0], fn[1]))
                # sorted():对所有可迭代兑现进行排序,默认为升序;sorted(self.cat)对字典cat中的键(种类)进行排序,排序结果的类型为list
                # zip():  函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组
                # dict(): 创建字典。dict(zip(['one', 'two'], [1, 2])) -> {'two': 2, 'one': 1}

        self.classes = dict(zip(sorted(self.cat), range(len(self.cat))))
        # print(self.classes) #训练所用的类别

        with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'num_seg_classes.txt'), 'r') as f:
            for line in f:
                ls = line.strip().split()
                self.seg_classes[ls[0]] = int(ls[1])
        self.num_seg_classes = self.seg_classes[list(self.cat.keys())[0]]
        # print(self.seg_classes, self.num_seg_classes) #所有类和被分割的数量

    # 该方法的实例对象可通过索引取值,自动调用该方法
    def __getitem__(self, index):
        # 获取类别、点云路径、分割标签路径元组
        fn = self.datapath[index]
        # 获取数字编码的类别标签
        cls = self.classes[self.datapath[index][0]]
        # 读取pts点云
        point_set = np.loadtxt(fn[1]).astype(np.float32)
        # 读取分割标签
        seg = np.loadtxt(fn[2]).astype(np.int64)
        #print(point_set.shape, seg.shape)

        # 重新采样到self.npoints个点
        choice = np.random.choice(len(seg), self.npoints, replace=True)
        #resample
        point_set = point_set[choice, :]

        # 去中心化
        point_set = point_set - np.expand_dims(np.mean(point_set, axis = 0), 0) # center
        # 计算到原点的最远距离
        dist = np.max(np.sqrt(np.sum(point_set ** 2, axis = 1)),0)
        # 归一化
        point_set = point_set / dist #scale

        # 默认False  开启旋转任意角度并加上一个bias,增强数据的抗干扰能力
        if self.data_augmentation:
            theta = np.random.uniform(0,np.pi*2)
            rotation_matrix = np.array([[np.cos(theta), -np.sin(theta)],[np.sin(theta), np.cos(theta)]])
            point_set[:,[0,2]] = point_set[:,[0,2]].dot(rotation_matrix) # random rotation
            point_set += np.random.normal(0, 0.02, size=point_set.shape) # random jitter

        seg = seg[choice]
        point_set = torch.from_numpy(point_set) #转换数据格式
        seg = torch.from_numpy(seg)
        cls = torch.from_numpy(np.array([cls]).astype(np.int64)) #cls为对应的代号,比如Airplane对应0

        if self.classification:
            return point_set, cls
        else:
            return point_set, seg

    def __len__(self):
        return len(self.datapath)


class ModelNetDataset(data.Dataset):
    def __init__(self,
                 root,
                 npoints=2500,
                 split='train',
                 data_augmentation=True):
        self.npoints = npoints
        self.root = root
        self.split = split
        self.data_augmentation = data_augmentation
        self.fns = []
        with open(os.path.join(root, '{}.txt'.format(self.split)), 'r') as f:
            for line in f:
                self.fns.append(line.strip())

        self.cat = {}
        with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../misc/modelnet_id.txt'), 'r') as f:
            for line in f:
                ls = line.strip().split()
                self.cat[ls[0]] = int(ls[1])

        print(self.cat)
        self.classes = list(self.cat.keys())

    def __getitem__(self, index):
        fn = self.fns[index]
        cls = self.cat[fn.split('/')[0]]
        with open(os.path.join(self.root, fn), 'rb') as f:
            plydata = PlyData.read(f)
        pts = np.vstack([plydata['vertex']['x'], plydata['vertex']['y'], plydata['vertex']['z']]).T
        choice = np.random.choice(len(pts), self.npoints, replace=True)
        point_set = pts[choice, :]

        point_set = point_set - np.expand_dims(np.mean(point_set, axis=0), 0)  # center
        dist = np.max(np.sqrt(np.sum(point_set ** 2, axis=1)), 0)
        point_set = point_set / dist  # scale

        if self.data_augmentation:
            theta = np.random.uniform(0, np.pi * 2)
            rotation_matrix = np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]])
            point_set[:, [0, 2]] = point_set[:, [0, 2]].dot(rotation_matrix)  # random rotation
            point_set += np.random.normal(0, 0.02, size=point_set.shape)  # random jitter

        point_set = torch.from_numpy(point_set.astype(np.float32))
        cls = torch.from_numpy(np.array([cls]).astype(np.int64))
        return point_set, cls


    def __len__(self):
        return len(self.fns)

if __name__ == '__main__':
    dataset = sys.argv[1]
    datapath = sys.argv[2]

    if dataset == 'shapenet':
        d = ShapeNetDataset(root = datapath, class_choice = ['Chair'])
        print(len(d))
        ps, seg = d[0]
        print(ps.size(), ps.type(), seg.size(),seg.type())

        d = ShapeNetDataset(root = datapath, classification = True)
        print(len(d))
        ps, cls = d[0]
        print(ps.size(), ps.type(), cls.size(),cls.type())
        # get_segmentation_classes(datapath)

    if dataset == 'modelnet':
        gen_modelnet_id(datapath)
        d = ModelNetDataset(root=datapath)
        print(len(d))
        print(d[0])

3.3 model.py

没变化

3.4 train_segmentation.py

修改了部分文件的路径,我看起来更舒服

# 参考
# 牙牙要健康 https://blog.csdn.net/yangyu0515/article/details/129362565
# LingbinBu https://blog.csdn.net/yuanmiyu6522/article/details/121435650

#使用最新版本的 print 函数
from __future__ import print_function
import numpy as np
import os
import random
import torch
import torch.nn.parallel
import torch.nn.functional as F
import torch.optim as optim
import torch.utils.data
from tqdm import tqdm
#不显示warnning
import warnings
warnings.filterwarnings('ignore')

from pointnet.dataset import ShapeNetDataset
from pointnet.model import PointNetDenseCls, feature_transform_regularizer



# region 超参数
batchSize = 8
learn_rate = 0.001
epochs = 100
workers = 0
outFolder = 'C:/xinjiang/py/xjPointNet/trainModelSeg'
try:
    os.makedirs(outFolder)
except OSError:
    pass
pathModel = ''
pathDataset = 'C:/xinjiang/py/xjPointNet/shapenetcore_partanno_segmentation_benchmark_v0/'
class_choice = 'Chair'
featureTransform = False
# endregion


if __name__ == "__main__":

    # region 随机数
    # 返回1~10000间的一个整数,作为随机种子 opt的类型为:<class 'argparse.Namespace'>
    manualSeed = random.randint(1, 10000)  # fix seed
    print("Random Seed: ", manualSeed)
    # 保证在有种子的情况下生成的随机数都是一样的
    random.seed(manualSeed)
    # 设置一个用于生成随机数的种子,返回的是一个torch.Generator对象
    torch.manual_seed(manualSeed)
    # endregion


    # region 数据集 分割
    train_dataset = ShapeNetDataset(
        root=pathDataset,
        classification=False,
        class_choice=[class_choice])
    train_dataloader = torch.utils.data.DataLoader(
        train_dataset,
        batch_size=batchSize,
        shuffle=True,                       #shuffle=True 打乱数据顺序
        num_workers=int(workers))
    test_dataset = ShapeNetDataset(
        root=pathDataset,
        classification=False,
        class_choice=[class_choice],
        split='test',
        data_augmentation=False)
    testdataloader = torch.utils.data.DataLoader(
        test_dataset,
        batch_size=batchSize,
        shuffle=True,
        num_workers=int(workers))

    #训练集-验证集-测试集 位于数据集文件夹中的 train_test_split
    print('Amount of train =',len(train_dataset), '. Amount of test =',len(test_dataset))
    num_classes = train_dataset.num_seg_classes
    print(class_choice,'is segmented into {', num_classes,'} sections.')
    # endregion


    # region 点云分割模型 实例化
    classifier = PointNetDenseCls(k=num_classes, feature_transform=featureTransform)
    # 如果有预训练模型,将预训练模型加载
    if pathModel != '':
        classifier.load_state_dict(torch.load(pathModel))

    #Adam优化器,用于优化神经网络的权重
    #betas是Adam优化器的两个衰减因子,分别用于一阶矩估计(mean)和二阶矩估计(uncentered variance)
    #0.9 是用于计算梯度的指数移动平均的衰减因子
    #0.999 是用于计算梯度平方的指数移动平均的衰减因子
    optimizer = optim.Adam(classifier.parameters(), lr=learn_rate, betas=(0.9, 0.999))

    #学习率调度器,用于调整优化器的学习率。StepLR 是一种简单的调度器,它在每个指定的步数(step_size)降低学习率。
    #每经过 20 个epoch(训练数据集的完整循环),学习率将被调整
    #将当前学习率乘以gamma来降低学习率。每经过step_size步,学习率将变为当前学习率的一半。
    scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.5)

    #将模型移动到 GPU 上进行计算
    classifier.cuda()
    # endregion


    # region 训练

    #将输入字符串 x 用深蓝色着色,通常用于在命令行界面中打印带有颜色的文本。
    test_blue = lambda x: '\033[94m' + x + '\033[0m'

    for epoch in range(epochs):
        # 表示完成了一个训练周期,更新学习率。
        scheduler.step()

        # 遍历
        for i, data in enumerate(train_dataloader, 0):
            # 点-标签
            points, target = data # points: torch.Size([batchSize,2500,3])。target:torch.Size([batchSize,1,2500])
            points = points.transpose(2, 1) # points:torch.Size([batchSize, 3, 2500]) = batchSize个3行2500列

            # cuda() 方法用于将张量的数据存储在GPU。
            points, target = points.cuda(), target.cuda()

            # 模型参数的梯度归零。避免backward时梯度累加。通常在每个训练迭代的开始处调用
            optimizer.zero_grad()

            # 训练
            classifier = classifier.train()
            # 预测:pred=torch.Size([batchSize, 2500, 4]);trans=torch.Size([2, 3, 3]);trans_feat不保存,为None。
            pred, trans, trans_feat = classifier(points)

            # 将张量 pred 重新调整为一个二维张量,其中每行有num_classes列。-1表示由PyTorch自动计算该维度的大小,以保持原有张量元素的总数不变。
            pred = pred.view(-1, num_classes)       #torch.Size([batchSize*2500, 4])

            # target.view(-1, 1):通过view方法将target调整为一个二维张量,其中每行有一个元素。-1表示由PyTorch自动计算该维度的大小,以保持原有张量元素的总数不变。
            # [:, 0]:使用切片操作,保留每行的第一个元素。
            # -1:将每个元素减去1。这样的操作可能用于调整标签的范围,使其符合模型输出的范围。
            target = target.view(-1, 1)[:, 0] - 1   #torch.Size([batchSize*2500, 4])

            # 负对数似然损失(Negative Log Likelihood Loss)
            loss = F.nll_loss(pred, target)

            # 将计算得到的正则项乘以一个系数(0.001)后添加到原始的损失上。防止过拟合或者提高模型的泛化能力。
            # 对feature_transform中64X64的变换矩阵做正则化,满足AA^T=I
            if featureTransform:
                loss += feature_transform_regularizer(trans_feat) * 0.001

            # loss反向传播。计算损失相对于模型参数的梯度。在前向传播之后,通过调用该方法,PyTorch会自动计算各个模型参数对损失的梯度。这些梯度将被存储在相应参数的.grad 属性中。
            loss.backward()
            # 梯度下降,参数优化。根据优化算法的规则,使用梯度信息来更新模型的参数,使损失函数值减小,从而让模型更好地适应训练数据。
            optimizer.step()

            # pred_choice包含了每个样本的模型预测的类别索引。
            # max(1):对底层数据进行操作,沿着第 1 个维度(通常是类别的维度)找到每行的最大值。返回一个元组,包含最大值和对应的索引。
            # [1]:取元组中的第二个元素,即最大值对应的索引。
            pred_choice = pred.data.max(1)[1] # torch.Size([batchSize*2500])

            # 当前批次中模型的正确预测数量。eq:逐元素比较
            correct = pred_choice.eq(target.data).cpu().sum()

            # 输出信息
            print('[%d: %d/%d] train loss=%f; accuracy=%f' % (
            epoch, i, len(train_dataset)/batchSize, loss.item(), correct.item()/float(batchSize*2500)))


            # ---------- 每隔10个批次,验证一次
            if i % 10 == 0:
                j, data = next(enumerate(testdataloader, 0))
                points, target = data
                points = points.transpose(2, 1)
                points, target = points.cuda(), target.cuda()
                classifier = classifier.eval()
                pred, _, _ = classifier(points)
                pred = pred.view(-1, num_classes)
                target = target.view(-1, 1)[:, 0] - 1
                loss = F.nll_loss(pred, target)
                pred_choice = pred.data.max(1)[1]
                correct = pred_choice.eq(target.data).cpu().sum()
                print('[%d: %d/%d] %s loss: %f  accuracy: %f' % (
                epoch, i, len(train_dataset)/batchSize, test_blue('test==='), loss.item(), correct.item() / float(batchSize * 2500)))

        # 保存模型
        torch.save(classifier.state_dict(), '%s/seg_model_%s_%d.pth' % (outFolder, class_choice, epoch))
    # endregion


    # region benchmark mIOU
    shape_ious = []
    # tqdm 进度条,以显示循环迭代的进度。tqdm 的名称来自阿拉伯语 "taqaddum",意为 "进展" 或 "前进"。
    for i, data in tqdm(enumerate(testdataloader, 0)):
        points, target = data
        points = points.transpose(2, 1)
        points, target = points.cuda(), target.cuda()
        classifier = classifier.eval()
        pred, _, _ = classifier(points)
        pred_choice = pred.data.max(2)[1] #第 2 个维度的最大值

        pred_np = pred_choice.cpu().data.numpy()
        target_np = target.cpu().data.numpy() - 1 #标签的取值范围调整为从0开始

        for shape_idx in range(target_np.shape[0]):
            parts = range(num_classes)
            part_ious = []
            for part in parts:
                I = np.sum(np.logical_and(pred_np[shape_idx] == part, target_np[shape_idx] == part))#计算当前类别 part 的交集数量
                U = np.sum(np.logical_or(pred_np[shape_idx] == part, target_np[shape_idx] == part)) #计算当前类别 part 的并集数量
                if U == 0:
                    # 如果交集和并集为空,将 IoU 设置为 1。这是为了处理分母为零的情况,确保 IoU 在这种情况下被定义为 1。
                    iou = 1  # If the union of groundtruth and prediction points is empty, then count part IoU as 1。
                else:
                    iou = I / float(U)
                part_ious.append(iou) #将每个类别 part 的 IoU 添加到 part_ious 列表中
            shape_ious.append(np.mean(part_ious)) # 计算所有类别的 IoU 的平均值,并将其添加到 shape_ious 列表中,表示当前形状的平均 IoU。

    print("mIOU for class {}: {}".format(class_choice, np.mean(shape_ious)))
    # endregion

3.5 show_seg.py

生成txt,或者利用 laspy 生成点云。不费那老鼻子劲搞原作者的可视化工具。

from __future__ import print_function
import numpy as np
import matplotlib.pyplot as plt
import torch
import torch.nn.parallel
import torch.utils.data
from torch.autograd import Variable
import warnings
warnings.filterwarnings('ignore')

from pointnet.dataset import ShapeNetDataset
from pointnet.model import PointNetDenseCls


pathModel = 'C:/xinjiang/py/xjPointNet/trainModelSeg/seg_model_Chair_9.pth'
pathDataset = 'C:/xinjiang/py/xjPointNet/shapenetcore_partanno_segmentation_benchmark_v0/'
choicedClass = 'Earphone'


if __name__ == "__main__":
    test_ds = ShapeNetDataset(
        root=pathDataset,
        class_choice=[choicedClass],
        split='test',
        data_augmentation=False)

    idx = 5
    print("model %d/%d" % (idx, len(test_ds)))

    # torch.Size([2500, 3]),torch.Size([2500])
    point, seg = test_ds[idx]

    # xyz <class 'numpy.ndarray'>    (2500, 3)
    point_np = point.numpy()

    cmap = plt.cm.get_cmap("hsv", 10)
    cmap = np.array([cmap(i) for i in range(10)])[:, :3]
    gt = cmap[seg.numpy() - 1, :]

    state_dict = torch.load(pathModel)
    classifier = PointNetDenseCls(k= state_dict['conv4.weight'].size()[0])
    classifier.load_state_dict(state_dict)
    classifier.eval()

    point = point.transpose(1, 0).contiguous()

    point = Variable(point.view(1, point.size()[0], point.size()[1]))
    pred, _, _ = classifier(point)
    # label <class 'torch.Tensor'> torch.Size([1, 2500])
    pred_choice = pred.data.max(2)[1]
    # label <class 'numpy.ndarray'> (2500, 1)
    pred_choice_np = np.reshape(pred_choice.numpy(), (pred_choice.numpy().size, 1))

    # rgb <class 'numpy.ndarray'>    (2500, 3)
    pred_color = cmap[pred_choice.numpy()[0], :]

    # xyzrgbl <class 'numpy.ndarray'>    (2500, 7)
    pcrgbl = np.hstack((point_np, pred_color*255, pred_choice_np))

    # # ---------- 保存成 txt ----------
    # pathResTxt = 'result_' + choicedClass+'_'+str(idx) + '.txt'
    # np.savetxt(pathResTxt, pcrgbl, fmt='%f', delimiter='\t')

    # ---------- 保存成 las ----------
    import laspy
    # data
    newx = point_np[:, 0]
    newy = point_np[:, 1]
    newz = point_np[:, 2]
    newred = 255*pred_color[:, 0]
    newgreen = 255*pred_color[:, 1]
    newblue = 255*pred_color[:, 2]
    newclassification = pred_choice_np[:, 0]
    minx = min(newx)
    miny = min(newy)
    minz = min(newz)
    # create a new header
    newheader = laspy.LasHeader(point_format=3, version="1.2")
    newheader.scales = np.array([0.0001, 0.0001, 0.0001])
    newheader.offsets = np.array([minx, miny, minz])
    newheader.add_extra_dim(laspy.ExtraBytesParams(name="Classification", type=np.uint8))
    # create a Las
    newlas = laspy.LasData(newheader)
    newlas.x = newx
    newlas.y = newy
    newlas.z = newz
    newlas.red = newred
    newlas.green = newgreen
    newlas.blue = newblue
    newlas.Classification = newclassification
    # write
    newLasPath = 'result_' + choicedClass + '_' + str(idx) + '.las'
    newlas.write(newLasPath)

四、结果

训练了10Chairepoch,用最后一个模型分割某个Table

效果还不错呢

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

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

相关文章

数据分析工具PlotJuggler使用小技巧

一款优秀的开源的工具能事倍功倍。今天给大家推荐的工具主要是Davide Faconti开发。该工具是基于QT开发&#xff0c;支持静态文件和实时数据流画图分析。以下是该工具的官网链接https://github.com/facontidavide/PlotJuggler。本人旨在介绍使用心得。 1.支持静态文件和实时数…

IDEA中自动导包及快捷键

导包设置及快捷键 设置&#xff1a;Setting->Editor->General->Auto import快捷键 设置&#xff1a;Setting->Editor->General->Auto import java区域有两个关键选项 Add unambiguous imports on the fly 快速添加明确的导包 IDEA将在我们书写代码的时候…

SwinTransformer

patch embedding (b,3,224,224)->(b,N,96) N:patch数量 为每个stage中的每个Swin Transformer block设置drop_rate&#xff0c;根据设置[2,2,6,2]&#xff0c;每个Swin Transformer block的drop_path为0~0.1等间距采样的12个小数&#xff0c;参数0.1也可以更改。还有个drop参…

前端页面的生命周期

性能问题呈现给用户的感受往往就是简单而直接的&#xff1a;加载资源缓慢、运行过程卡顿或响应交互延迟等。而在前端工程师的眼中&#xff0c;从域名解析、TCP建立连接到HTTP的请求与响应&#xff0c;以及从资源请求、文件解析到关键渲染路径等&#xff0c;每一个环节都有可能因…

C语言编译器(C语言编程软件)完全攻略(第十一部分:VS2022使用教程(使用VS2022编写C语言程序))

介绍常用C语言编译器的安装、配置和使用。 十一、VS2022使用教程&#xff08;使用VS2022编写C语言程序&#xff09; 继《十、VS2022下载和安装教程&#xff08;图解版&#xff09;》之后&#xff0c;本节教大家如何用 VS2022 运行 C 语言程序。 例如&#xff0c;在 VS2022 中…

精致旅游公司Treker网页设计 html模板

一、需求分析 旅游网站通常具有多种功能&#xff0c;以下是一些常见的旅游网站功能&#xff1a; 酒店预订&#xff1a;旅游网站可以提供酒店预订服务&#xff0c;让用户搜索并预订符合其需求和预算的酒店房间。 机票预订&#xff1a;用户可以通过旅游网站搜索、比较和预订机票…

用js封装实现余额函数

要求: 1. 运行程序后, 浏览器显示输入确认框(prompt) 2. 第一个输入确认框提示输入银行卡余额 3. 第二个输入确认框提示输入当月食宿消费金额 4. 第三个输入确认框提示输入当月生活消费金额 5. 输入完毕后,在页面中显示银行卡剩余金额 6. 提示: 所有功能代码封装在函数内部&…

【物联网】手把手完整实现STM32+ESP8266+MQTT+阿里云+APP应用——第3节-云产品流转配置

&#x1f31f;博主领域&#xff1a;嵌入式领域&人工智能&软件开发 本节目标&#xff1a;本节目标是进行云产品流转配置为后面实际的手机APP的接入做铺垫。云产品流转配置的目的是为了后面能够让后面实际做出来的手机APP可以控制STM32/MCU&#xff0c;STM32/MCU可以将数…

计算机基础知识——数据的表示概述

目录 1 进制转换 1.1 二进制、十进制和十六进制等常用数制及其相互转换 1.2 十进制和二进制之间转换 1.3 二进制数与八进制数、十六进制数之间的转换 2 码值&#xff1a;原码、反码、补码 2.1 原码 2.2 反码 2.3 补码 3 浮点数表示 3.1 浮点数的运算 1 进制转换 1…

基于闪电搜索算法优化的Elman神经网络数据预测 - 附代码

基于闪电搜索算法优化的Elman神经网络数据预测 - 附代码 文章目录 基于闪电搜索算法优化的Elman神经网络数据预测 - 附代码1.Elman 神经网络结构2.Elman 神经用络学习过程3.电力负荷预测概述3.1 模型建立 4.基于闪电搜索优化的Elman网络5.测试结果6.参考文献7.Matlab代码 摘要&…

Android 相机库CameraView源码解析 (四) : 带滤镜预览

1. 前言 这段时间&#xff0c;在使用 natario1/CameraView 来实现带滤镜的预览、拍照、录像功能。 由于CameraView封装的比较到位&#xff0c;在项目前期&#xff0c;的确为我们节省了不少时间。 但随着项目持续深入&#xff0c;对于CameraView的使用进入深水区&#xff0c;逐…

Javaweb之Mybatis的基础操作之新增和更新操作的详细解析

1.4 新增 功能&#xff1a;新增员工信息 1.4.1 基本新增 员工表结构&#xff1a; SQL语句&#xff1a; insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) values (songyuanqiao,宋远桥,1,1.jpg,2,2012-10-09,2,2022-10-…

十年磨一剑

随着不停的优化和改进&#xff0c;JRT开发已经接近尾声&#xff0c;计划过年时候低调发布JRT1.0&#xff0c;框架目标&#xff1a;只做信创下的医疗龙头而不是信创下的苟活着。 十年前&#xff0c;我从南京踏上去沈阳的火车&#xff0c;去东北参加三方协议的启航计划&#xff…

电极箔,预计到2025年市场规模将达到35亿美元

电极箔是一种关键性材料&#xff0c;广泛应用于太阳能电池、电动汽车电池、储能电池、5G基站电池等领域。随着新能源产业的迅猛发展&#xff0c;电极箔市场也在逐步壮大。下面将从全球市场和中国市场进行分析其发展趋势。全球市场分析&#xff1a; 在全球范围内&#xff0c;随着…

HarmonyOS4.0系统性深入开发14AbilityStage组件容器

AbilityStage组件容器 AbilityStage是一个Module级别的组件容器&#xff0c;应用的HAP在首次加载时会创建一个AbilityStage实例&#xff0c;可以对该Module进行初始化等操作。 AbilityStage与Module一一对应&#xff0c;即一个Module拥有一个AbilityStage。 DevEco Studio默…

深入理解Python中的二分查找与bisect模块

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

PCIe 6.0生态业内进展分析总结-2

3.PCIe 6.0协议分析仪 (1)Keysight Keysight是德科技在2023年6月份对外宣布&#xff0c;第一款支持PCIe 6.0协议验证调试工具。 Keysight PCIe 6.0架构解决方案具备以下特点&#xff1a; 分析PCIe 6.0技术设计的数据链路/事务层 支持所有PCIe技术速率——从2.5 GT/s至64 GT/…

一篇文章认识微服务中Eureka的原理和服务注册与发现

目录 1、认识Eureka 2、Eureka原理 2.1 和Dubbo架构对比&#xff1a; 2.2 三大角色 3、微服务常见的注册中心 3.1 Zookeeper 3.2 Eureka 3.3 Consul 3.4 Nacos 3.5 区别 Netflix 在设计Eureka 时&#xff0c;遵循的就是AP原则。 CAP原则又称CAP定理&#xff0c;指的…

STM32使用中断方式进行USART数据收发以及printf函数的重写

时间记录&#xff1a;2024/1/5 一、USART/UART介绍 协议介绍 &#xff08;1&#xff09;起始位&#xff0c;一位逻辑电平0表示 &#xff08;2&#xff09;数据位&#xff0c;8-9位&#xff0c;逻辑高低电平&#xff0c;一般使用8位 &#xff08;3&#xff09;校验位&#xff…

5分钟搞懂AI的可解释性

大家好啊&#xff0c;我是董董灿。 想象一下&#xff0c;如果有一天&#xff0c;有人跑过来突然告诉你&#xff0c;他搞懂了人类大脑记忆的运行机制&#xff0c;你会是什么反应&#xff1f; 你可能会和我一样&#xff0c;把他当做疯子。 因为我觉得这个课题太深奥了&#xf…