计算机视觉:语义分割理论及实战

news2024/11/24 17:17:59

语义分割

语义分割(Semantic Segmentation)是指将一张图像分割成若干个区域,并对每个区域赋予语义标签的任务。它是计算机视觉中的一种重要技术,被广泛应用于自动驾驶、医学图像分析、地理信息系统等领域。

与传统的图像分割任务不同,语义分割不仅需要将图像分割成若干个区域,还需要对每个区域赋予语义标签。例如,在自动驾驶中,语义分割可以将道路、车辆、行人等区域分割出来,并对每个区域赋予相应的语义标签,以帮助车辆进行自动驾驶。在医学图像分析中,语义分割可以将不同的组织结构(如器官、肌肉、骨骼等)分割出来,并对其进行分析和诊断。

语义分割的实现方法主要可以分为基于区域的方法和基于像素的方法。基于区域的方法将图像分割成一系列的区域,然后对每个区域进行分类并赋予相应的语义标签。基于像素的方法则直接对每个像素进行分类,并将其赋予相应的语义标签。目前,基于深度学习的方法已经成为了实现语义分割的主流方法,其中以基于卷积神经网络(CNN)的方法最为常见。常用的语义分割模型包括 FCN、SegNet、U-Net、DeepLab 等。

计算机视觉领域还有2个与语义分割相似的重要问题,即图像分割(image segmentation)和实例分割(instance segmentation)。 我们在这里将它们同语义分割简单区分一下。
图像分割将图像划分为若干组成区域,这类问题的方法通常利用图像中像素之间的相关性。它在训练时不需要有关图像像素的标签信息,在预测时也无法保证分割出的区域具有我们希望得到的语义。
实例分割也叫同时检测并分割(simultaneous detection and segmentation),它研究如何识别图像中各个目标实例的像素级区域。与语义分割不同,实例分割不仅需要区分语义,还要区分不同的目标实例。例如,如果图像中有两条狗,则实例分割需要区分像素属于的两条狗中的哪一条。


文章目录

  • 语义分割
  • 数据集
    • 下载数据集
    • 读取数据集
    • 预处理数据
    • 自定义语义分割数据集类
    • 整合所有组件
  • 全卷积网络
    • 构造模型
    • 初始化转置卷积层
    • 训练
    • 预测


数据集

最重要的语义分割数据集之一是Pascal VOC2012。

下载数据集

%matplotlib inline
import os
import torch
import torchvision
from d2l import torch as d2l
#@save
d2l.DATA_HUB['voc2012'] = (d2l.DATA_URL + 'VOCtrainval_11-May-2012.tar',
                           '4e443f8a2eca6b1dac8a6c57641b67dd40621a49')

voc_dir = d2l.download_extract('voc2012', 'VOCdevkit/VOC2012')

读取数据集

#@save
def read_voc_images(voc_dir, is_train=True):
    """读取所有VOC图像并标注"""
    txt_fname = os.path.join(voc_dir, 'ImageSets', 'Segmentation',
                             'train.txt' if is_train else 'val.txt')
    mode = torchvision.io.image.ImageReadMode.RGB
    with open(txt_fname, 'r') as f:
        images = f.read().split()
    features, labels = [], []
    for i, fname in enumerate(images):
        features.append(torchvision.io.read_image(os.path.join(
            voc_dir, 'JPEGImages', f'{fname}.jpg')))
        labels.append(torchvision.io.read_image(os.path.join(
            voc_dir, 'SegmentationClass' ,f'{fname}.png'), mode))
    return features, labels

train_features, train_labels = read_voc_images(voc_dir, True)

下面我们绘制前5个输入图像及其标签。 在标签图像中,白色和黑色分别表示边框和背景,而其他颜色则对应不同的类别。

n = 5
imgs = train_features[0:n] + train_labels[0:n]
imgs = [img.permute(1,2,0) for img in imgs]
d2l.show_images(imgs, 2, n);

在这里插入图片描述
接下来,我们列举RGB颜色值和类名。

VOC_COLORMAP = [[0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0],
                [0, 0, 128], [128, 0, 128], [0, 128, 128], [128, 128, 128],
                [64, 0, 0], [192, 0, 0], [64, 128, 0], [192, 128, 0],
                [64, 0, 128], [192, 0, 128], [64, 128, 128], [192, 128, 128],
                [0, 64, 0], [128, 64, 0], [0, 192, 0], [128, 192, 0],
                [0, 64, 128]]

#@save
VOC_CLASSES = ['background', 'aeroplane', 'bicycle', 'bird', 'boat',
               'bottle', 'bus', 'car', 'cat', 'chair', 'cow',
               'diningtable', 'dog', 'horse', 'motorbike', 'person',
               'potted plant', 'sheep', 'sofa', 'train', 'tv/monitor']

通过上面定义的两个常量,我们可以方便地查找标签中每个像素的类索引。 我们定义了voc_colormap2label函数来构建从上述RGB颜色值到类别索引的映射,而voc_label_indices函数将RGB值映射到在Pascal VOC2012数据集中的类别索引。

#@save
def voc_colormap2label():
    """构建从RGB到VOC类别索引的映射"""
    colormap2label = torch.zeros(256 ** 3, dtype=torch.long)
    for i, colormap in enumerate(VOC_COLORMAP):
        colormap2label[
            (colormap[0] * 256 + colormap[1]) * 256 + colormap[2]] = i
    return colormap2label

具体来说,该代码使用了一种将 RGB 颜色值转换为一个唯一整数的方法,即将 R、G、B 三个通道的值分别乘以 25 6 2 256^2 2562 25 6 1 256^1 2561 25 6 0 256^0 2560,然后将它们相加。这样可以将一个三元组的 RGB 颜色值转化为一个唯一的整数。

#@save
def voc_label_indices(colormap, colormap2label):
    """将VOC标签中的RGB值映射到它们的类别索引"""
    colormap = colormap.permute(1, 2, 0).numpy().astype('int32')
    idx = ((colormap[:, :, 0] * 256 + colormap[:, :, 1]) * 256
           + colormap[:, :, 2])
    return colormap2label[idx]

具体来说,函数首先将 “colormap” 张量中的 RGB 值转换为一个大小为 (H, W) 的整数张量 “idx”。这是通过将 R、G、B 三个通道的值分别乘以 25 6 2 256^2 2562 25 6 1 256^1 2561 25 6 0 256^0 2560,然后将它们相加得到的。然后,该整数作为索引,从 “colormap2label” 张量中提取对应的类别索引。这些类别索引构成了一个与 “colormap” 张量形状相同的新张量 “idx”,其中每个元素都对应于 “colormap” 张量中的一个像素。

在这里插入图片描述

预处理数据

在语义分割中,这样做需要将预测的像素类别重新映射回原始尺寸的输入图像。 这样的映射可能不够精确,尤其在不同语义的分割区域。 为了避免这个问题,我们将图像裁剪为固定尺寸,而不是再缩放。 具体来说,我们使用图像增广中的随机裁剪,裁剪输入图像和标签的相同区域。

#@save
def voc_rand_crop(feature, label, height, width):
    """随机裁剪特征和标签图像"""
    rect = torchvision.transforms.RandomCrop.get_params(
        feature, (height, width))
    feature = torchvision.transforms.functional.crop(feature, *rect)
    label = torchvision.transforms.functional.crop(label, *rect)
    return feature, label

imgs = []
for _ in range(n):
    imgs += voc_rand_crop(train_features[0], train_labels[0], 200, 300)

imgs = [img.permute(1, 2, 0) for img in imgs]
d2l.show_images(imgs[::2] + imgs[1::2], 2, n);

该函数使用 torchvision.transforms.RandomCrop.get_params() 获取一个随机裁剪矩形,然后使用 torchvision.transforms.functional.crop() 函数对输入的特征图像和标签图像进行裁剪。最后,该函数返回裁剪后的特征图像和标签图像。具体来说,该函数通过以下步骤实现随机裁剪:

  1. 使用 torchvision.transforms.RandomCrop.get_params() 获取一个随机裁剪矩形,该矩形的高度和宽度分别为 height 和 width。
  2. 使用 torchvision.transforms.functional.crop() 对输入的特征图像和标签图像进行裁剪,裁剪矩形为上一步获取的随机矩形。
  3. 返回裁剪后的特征图像和标签图像。

自定义语义分割数据集类

#@save
class VOCSegDataset(torch.utils.data.Dataset):
    """一个用于加载VOC数据集的自定义数据集"""

    def __init__(self, is_train, crop_size, voc_dir):
        self.transform = torchvision.transforms.Normalize(
            mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
        self.crop_size = crop_size
        features, labels = read_voc_images(voc_dir, is_train=is_train)
        self.features = [self.normalize_image(feature)
                         for feature in self.filter(features)]
        self.labels = self.filter(labels)
        self.colormap2label = voc_colormap2label()
        print('read ' + str(len(self.features)) + ' examples')

    def normalize_image(self, img):
        return self.transform(img.float() / 255)

    def filter(self, imgs):
        return [img for img in imgs if (
            img.shape[1] >= self.crop_size[0] and
            img.shape[2] >= self.crop_size[1])]

    def __getitem__(self, idx):
        feature, label = voc_rand_crop(self.features[idx], self.labels[idx],
                                       *self.crop_size)
        return (feature, voc_label_indices(label, self.colormap2label))

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

这是一个用于加载 Pascal VOC 数据集的自定义数据集类 VOCSegDataset。该数据集类提供了以下方法:

init(self, is_train, crop_size, voc_dir): 构造函数,用于初始化数据集。该函数接受三个参数:

  1. is_train: 一个布尔值,指示该数据集是用于训练还是测试。
  2. crop_size: 一个元组,表示对图像进行随机裁剪后的大小。
  3. voc_dir: 数据集存储路径。

normalize_image(self, img): 用于对图像进行标准化处理。

filter(self, imgs): 用于过滤掉尺寸小于 crop_size 的图像。

getitem(self, idx): 用于获取数据集中的一个样本。该函数接受一个索引参数 idx,返回一个元组 (feature, label),其中 feature 表示特征图像,label 表示对应的标签图像。

len(self): 用于获取数据集中样本的数量。

在构造函数中,该数据集类首先使用 read_voc_images() 函数读取数据集中的图像和标签,并使用 filter() 方法过滤掉尺寸小于 crop_size 的图像。然后,该数据集类对每个特征图像使用 normalize_image() 方法进行标准化处理,并使用 voc_rand_crop() 和 voc_label_indices() 方法实现对特征图像和标签图像的随机裁剪和标签映射。最后,该数据集类在 getitem() 方法中返回裁剪后的特征图像和标签图像。

该数据集类的主要作用是将 Pascal VOC 数据集转换为 PyTorch 中的 Dataset 类型,以便于在训练模型时使用 PyTorch 提供的 DataLoader 类对数据进行批量处理。

crop_size = (320, 480)
voc_train = VOCSegDataset(True, crop_size, voc_dir)
voc_test = VOCSegDataset(False, crop_size, voc_dir)
batch_size = 64
train_iter = torch.utils.data.DataLoader(voc_train, batch_size, shuffle=True,
                                    drop_last=True,
                                    num_workers=d2l.get_dataloader_workers())
for X, Y in train_iter:
    print(X.shape)
    print(Y.shape)
    break

整合所有组件

#@save
def load_data_voc(batch_size, crop_size):
    """加载VOC语义分割数据集"""
    voc_dir = d2l.download_extract('voc2012', os.path.join(
        'VOCdevkit', 'VOC2012'))
    num_workers = d2l.get_dataloader_workers()
    train_iter = torch.utils.data.DataLoader(
        VOCSegDataset(True, crop_size, voc_dir), batch_size,
        shuffle=True, drop_last=True, num_workers=num_workers)
    test_iter = torch.utils.data.DataLoader(
        VOCSegDataset(False, crop_size, voc_dir), batch_size,
        drop_last=True, num_workers=num_workers)
    return train_iter, test_iter

全卷积网络

全卷积模型是一种特殊的卷积神经网络,它可以对输入图像进行像素级别的分类或分割,输出一个与输入图像尺寸相同的分割结果。全卷积模型通常由卷积层、反卷积层和池化层组成,其中卷积层用于提取图像特征,反卷积层用于将特征图像还原为原始图像大小,池化层用于减小特征图像的尺寸。

全卷积模型最常用于图像分割任务,如语义分割、实例分割和边缘检测等。其中,语义分割任务是将图像中的每个像素分配到其对应的类别中,而实例分割任务则是将图像中的每个物体实例分配到不同的类别中。

全卷积模型的一种经典结构是 U-Net,它由编码器和解码器两部分组成。编码器通常采用卷积层和池化层的组合,用于提取图像特征并减小特征图像的尺寸。解码器则采用反卷积层和跳跃连接的方式,用于将特征图像还原为原始图像大小,并将编码器中的特征图像与解码器中的特征图像进行融合。

在训练全卷积模型时,通常使用交叉熵损失函数来度量模型输出与真实标签之间的差异,并使用反向传播算法更新模型参数。由于全卷积模型的输出是一个分割图像,因此在计算损失函数时通常需要将其展平为一个向量。

构造模型

下面我们了解一下全卷积网络模型最基本的设计。 如下图所示,全卷积网络先使用卷积神经网络抽取图像特征,然后通过 1 × 1 1\times 1 1×1卷积层将通道数变换为类别个数,最后通过转置卷积层将特征图的高和宽变换为输入图像的尺寸。 因此,模型输出与输入图像的高和宽相同,且最终输出通道包含了该空间位置像素的类别预测。
在这里插入图片描述
下面,我们使用在ImageNet数据集上预训练的ResNet-18模型来提取图像特征,并将该网络记为pretrained_net。 ResNet-18模型的最后几层包括全局平均汇聚层和全连接层,然而全卷积网络中不需要它们。
在这里插入图片描述
接下来,我们创建一个全卷积网络net。 它复制了ResNet-18中大部分的预训练层,除了最后的全局平均汇聚层和最接近输出的全连接层。

net = nn.Sequential(*list(pretrained_net.children())[:-2])

给定高度为320和宽度为480的输入,net的前向传播将输入的高和宽减小至原来的 1 32 \frac{1}{32} 321,即10和15。

X = torch.rand(size=(1, 3, 320, 480))
net(X).shape

在这里插入图片描述
接下来使用 1 × 1 1\times 1 1×1卷积层将输出通道数转换为Pascal VOC2012数据集的类数(21类)。 最后需要将特征图的高度和宽度增加32倍,从而将其变回输入图像的高和宽。 由于 ( 320 − 64 + 16 ∗ 2 + 32 ) / 32 = 10 (320-64+16*2+32)/32=10 (32064+162+32)/32=10 ( 480 − 64 + 16 ∗ 2 + 32 ) / 32 = 15 (480-64+16*2+32)/32=15 (48064+162+32)/32=15,我们构造一个步幅为 32 32 32的转置卷积层,并将卷积核的高和宽设为 64 64 64,填充为 16 16 16我们可以看到如果步幅为 s s s,填充为 s / 2 s/2 s/2(假设 s s s是整数)且卷积核的高和宽为 2 s 2s 2s,转置卷积核会将输入的高和宽分别放大 s s s倍。

初始化转置卷积层

在图像处理中,我们有时需要将图像放大,即上采样(upsampling)。 双线性插值(bilinear interpolation) 是常用的上采样方法之一,它也经常用于初始化转置卷积层。

上采样是一种将低分辨率图像或信号增加到高分辨率的方法。双线性插值是一种常用的上采样方法之一,它可以通过对已有的低分辨率图像进行插值,生成更高分辨率的图像。

在双线性插值中,假设要将一个低分辨率图像上采样两倍,即将每个像素变成一个 2 × 2 2 \times 2 2×2 的像素块。对于每个新像素块中的像素,双线性插值会根据周围的四个已知像素值值进行计算。具体来说,对于目标图像中的一个新像素 ( x , y ) (x, y) (x,y),它的灰度值可以通过以下步骤计算:

找到目标像素 ( x , y ) (x, y) (x,y) 在原始低分辨率图像中对应的四个像素 ( x 1 , y 1 ) (x_1, y_1) (x1,y1) ( x 2 , y 1 ) (x_2, y_1) (x2,y1) ( x 1 , y 2 ) (x_1, y_2) (x1,y2) ( x 2 , y 2 ) (x_2, y_2) (x2,y2),其中 ( x 1 , y 1 ) (x_1, y_1) (x1,y1) ( x 2 , y 2 ) (x_2, y_2) (x2,y2) 是最靠近 ( x , y ) (x, y) (x,y) 的两个像素, ( x 1 , y 2 ) (x_1, y_2) (x1,y2) ( x 2 , y 1 ) (x_2, y_1) (x2,y1) 是另外两个像素。

计算目标像素 ( x , y ) (x, y) (x,y) 与四个已知像素之间的距离,即 d 1 = ( x − x 1 ) 2 + ( y − y 1 ) 2 d_{1} = \sqrt{(x-x_1)^2 + (y-y_1)^2} d1=(xx1)2+(yy1)2 d 2 = ( x − x 2 ) 2 + ( y − y 1 ) 2 d_{2} = \sqrt{(x-x_2)^2 + (y-y_1)^2} d2=(xx2)2+(yy1)2 d 3 = ( x − x 1 ) 2 + ( y − y 2 ) 2 d_{3} = \sqrt{(x-x_1)^2 + (y-y_2)^2} d3=(xx1)2+(yy2)2 d 4 = ( x − x 2 ) 2 + ( y − y 2 ) 2 d_{4} = \sqrt{(x-x_2)^2 + (y-y_2)^2} d4=(xx2)2+(yy2)2

计算目标像素 ( x , y ) (x, y) (x,y) 的灰度值,用周围四个像素的灰度值进行加权平均,权重与目标像素与四个已知像素之间的距离成反比。即:

f ( x , y ) = 1 d 1 d 3 f ( x 1 , y 1 ) + 1 d 2 d 3 f ( x 2 , y 1 ) + 1 d 1 d 4 f ( x 1 , y 2 ) + 1 d 2 d 4 f ( x 2 , y 2 ) f(x, y) = \frac{1}{d_{1}d_{3}}f(x_1, y_1) + \frac{1}{d_{2}d_{3}}f(x_2, y_1) + \frac{1}{d_{1}d_{4}}f(x_1, y_2) + \frac{1}{d_{2}d_{4}}f(x_2, y_2) f(x,y)=d1d31f(x1,y1)+d2d31f(x2,y1)+d1d41f(x1,y2)+d2d41f(x2,y2)

其中 f ( x 1 , y 1 ) f(x_1, y_1) f(x1,y1) f ( x 2 , y 1 ) f(x_2, y_1) f(x2,y1) f ( x 1 , y 2 ) f(x_1, y_2) f(x1,y2) f ( x 2 , y 2 ) f(x_2, y_2) f(x2,y2) 分别表示四个已知像素的灰度值。

通过双线性插值,我们可以将低分辨率图像上采样到更高分辨率,从而得到更清晰的图像。

def bilinear_kernel(in_channels, out_channels, kernel_size):
    factor = (kernel_size + 1) // 2
    if kernel_size % 2 == 1:
        center = factor - 1
    else:
        center = factor - 0.5
    og = (torch.arange(kernel_size).reshape(-1, 1),
          torch.arange(kernel_size).reshape(1, -1))
    filt = (1 - torch.abs(og[0] - center) / factor) * \
           (1 - torch.abs(og[1] - center) / factor)
    weight = torch.zeros((in_channels, out_channels,
                          kernel_size, kernel_size))
    weight[range(in_channels), range(out_channels), :, :] = filt
    return weight

这是一个用于生成双线性插值卷积核的函数。它的输入包括输入通道数、输出通道数和卷积核大小,它的输出是一个形状为 (in_channels, out_channels, kernel_size, kernel_size) 的张量,表示由该函数生成的双线性插值卷积核。

具体来说,该函数首先计算出卷积核中心的位置,然后生成一个形状为 (kernel_size, kernel_size) 的张量 filt,其中 filt 的每个元素表示双线性插值卷积核中对应位置的权重。最后,该函数根据输入通道数和输出通道数,生成形状为 (in_channels, out_channels, kernel_size, kernel_size) 的张量 weight,其中 weight 的每个元素表示双线性插值卷积核中对应位置的权重。具体来说,weight[i, j, :, :] 表示从第 i 个输入通道到第 j 个输出通道的双线性插值卷积核。

这个函数可以用于定义卷积神经网络中的双线性插值卷积层,该层将输入张量上采样到更高分辨率。

全卷积网络用双线性插值的上采样初始化转置卷积层。对于 1 × 1 1\times 1 1×1卷积层,我们使用Xavier初始化参数。

W = bilinear_kernel(num_classes, num_classes, 64)
net.transpose_conv.weight.data.copy_(W);

训练

def loss(inputs, targets):
    return F.cross_entropy(inputs, targets, reduction='none').mean(1).mean(1)

num_epochs, lr, wd, devices = 5, 0.001, 1e-3, d2l.try_all_gpus()
trainer = torch.optim.SGD(net.parameters(), lr=lr, weight_decay=wd)
d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs, devices)

预测

在预测时,我们需要将输入图像在各个通道做标准化,并转成卷积神经网络所需要的四维输入格式。

def predict(img):
    X = test_iter.dataset.normalize_image(img).unsqueeze(0)
    pred = net(X.to(devices[0])).argmax(dim=1)
    return pred.reshape(pred.shape[1], pred.shape[2])

为了可视化预测的类别给每个像素,我们将预测类别映射回它们在数据集中的标注颜色。

def label2image(pred):
    colormap = torch.tensor(VOC_COLORMAP, device=devices[0])
    X = pred.long()
    return colormap[X, :]

测试数据集中的图像大小和形状各异。 由于模型使用了步幅为32的转置卷积层,因此当输入图像的高或宽无法被32整除时,转置卷积层输出的高或宽会与输入图像的尺寸有偏差。 为了解决这个问题,我们可以在图像中截取多块高和宽为32的整数倍的矩形区域,并分别对这些区域中的像素做前向传播。 请注意,这些区域的并集需要完整覆盖输入图像。 当一个像素被多个区域所覆盖时,它在不同区域前向传播中转置卷积层输出的平均值可以作为softmax运算的输入,从而预测类别。

为简单起见,我们只读取几张较大的测试图像,并从图像的左上角开始截取形状为 320 × 480 320\times 480 320×480的区域用于预测。 对于这些测试图像,我们逐一打印它们截取的区域,再打印预测结果,最后打印标注的类别。

在这里插入图片描述

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

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

相关文章

奇安信应急响应-Linux

Linux需要经常关注的目录 /etc/passwd用户信息文件:我们需要看一下里面到底是什么,保存的用户信息 /etc/rc.d/rc.loacl:开机启动项:类似于Windows的开机启动项,有可能攻击者会在里面写一个后门文件,需要重…

EM算法实现对iris数据集和meat数据集的分类【MATLAB版本】

摘要:本章实验主要是对于学习 EM 算法的原理,掌握并实现混合高斯模型非监督学习 的 EM 算法,要求在两个数据集上面实现混合高斯模型的非监督学习的EM算法。混合模型是相对于单高斯模型而言的,对于某个样本数…

【高级篇】分布式事务

分布式事务 1.分布式事务问题 1.1.本地事务 本地事务,也就是传统的单机事务。在传统数据库事务中,必须要满足四个原则: 1.2.分布式事务 分布式事务,就是指不是在单个服务或单个数据库架构下,产生的事务&#xff0c…

Nginx:Tomcat部署及优化(二)

Nginx:Tomcat部署及优化(二) 一、Tomcat 优化1.1 内核参数优化1.2 Tomcat 配置文件参数优化1.3 Java 虚拟机(JVM)调优 二、NginxTomcat 负载均衡、动静分离 一、Tomcat 优化 Tomcat 默认安装下的缺省配置并不适合生产…

9款超级实用的网页设计工具,快来看看有没有你用过的

随着网络时代的快速发展,游戏、购物、音乐、影视和社交网站的兴起都表明了网页设计的重要性! 网页设计工具作为网页设计师的生产工具,自然要选择好的。 让我们分享9个高质量的网页设计工具,让您的设计效率悄然提高! …

【Python TDD和BDD】零基础也能轻松掌握的学习路线与参考资料

Python TDD和BDD的学习路线 TDD(测试驱动开发)和BDD(行为驱动开发)在软件开发中的作用越来越受到重视。TDD通过先写测试代码,再编写生产代码的方式,使得开发者可以在开发过程中确保代码质量和正确性&#…

黑客学习-xss漏洞总结

1、什么是xss 先来看案例 在一个输入框中,输入js代码,存放alter()其弹窗,结果可以看到,代码成功执行。这个就是xss漏洞 XSS攻击全称跨站脚本攻击,是一种在Web应用中常见的安全漏洞,它允许用户将恶意代码植入到Web页面…

分布式事务 2PC

tip:作为程序员一定学习编程之道,一定要对代码的编写有追求,不能实现就完事了。我们应该让自己写的代码更加优雅,即使这会费时费力。 文章目录 一、简介二、2PC 的运行流程三、2PC 一定能保证数据的一致性吗?四、2PC 的…

软件测试——未来软件测试的5个主要趋势

全球各地的企业每天都在发展变化着,以应对市场挑战,满足日益成熟的客户需求。即使是正在进行的技术进步也会使软件测试专家在实践的过程中更加专注和精确。 2021年给软件测试领域带来了新的技术解决方案,以及质量保证和软件测试的实现。与此同…

Springcloud--异步通信RabbitMq快速入门

RabbitMQ 1.初识MQ 1.1.同步和异步通讯 微服务间通讯有同步和异步两种方式: 同步通讯:就像打电话,需要实时响应。 异步通讯:就像发邮件,不需要马上回复。 两种方式各有优劣,打电话可以立即得到响应&am…

YOLOv5改进系列(8)——添加SOCA注意力机制

【YOLOv5改进系列】前期回顾: YOLOv5改进系列(0)——重要性能指标与训练结果评价及分析 YOLOv5改进系列(1)——添加SE注意力机制

动态查找表

动态查找表 1.二叉排序树1.1. 定义1.2. 查找过程1.3. 插入过程1.4. 创建二叉排序树1.5. 删除操作(1)被移除的结点是叶子结点(2)被移除的结点只有左子树或者只有右子树;(3)被移除的结点既有左子树…

两张图理解MR与XR

我们知道,AR是在现实世界上叠加虚拟信息和图像,VR是完全模拟的虚拟世界,那么对于MR和XR的概念会稍显复杂,本文试图通过2张图来理解它们,如有不对,祈请纠正。 MR 关于MR,先来看看下面第一张图。 …

vue 3 第三十四章:nextTick

nextTick是Vue3中的一个非常有用的函数&#xff0c;它可以在下一次DOM更新循环结束后执行回调函数。这个函数可以用来解决一些异步更新视图的问题&#xff0c;例如在修改数据后立即获取更新后的DOM节点。以下是一个简单的示例&#xff1a; <template><div><p&g…

华硕无畏灵耀破晓原装Windows10/11系统

第一步&#xff1a;下载原装系统文件 第二步&#xff1a;灵耀/无畏/破晓需要自备16g空u盘安装 第三步&#xff1a;创建u盘分区&#xff0c;第一个分区格式为FAT32(存放TLK引导文件)&#xff0c;第二个分区大小为NTFS&#xff08;存放底包&#xff1a;HDI.OFS.SWP.EDN.KIT&…

Unity Package Manager 使用

项目组开发的工具可以托管到远程仓库里&#xff0c;别的项目 也可以使用。 在Unity工程Assets 下 创建自己的插件目录 运行时 代码 和 编辑器代码 &#xff0c;创建 对应的 程序集&#xff0c;以及package.json 文件 package.json内容&#xff1a;可参考官方的&#xff0c;n…

测试人何去何从?2023年测试工程师突破自我,卷出测试圈...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 2023年测试行业现…

被上司问“测得怎么样了?”我心里慌到不行

目录 前言 你测的怎么样了&#xff1f; 这样回答 初入测试职场 结尾&#xff1a; 前言 说实话&#xff0c;我真想从上面去掉"似乎"两个字&#xff0c;软件测试人&#xff0c;就是苦逼&#xff01;有的人曾抱怨过开发很糟糕&#xff0c;但我们没办法要求开发在会写代…

360浏览器如何屏蔽某搜索网站的热搜

1.安装油猴&#xff08;Tampermonkey插件&#xff09; 下载油猴&#xff1a;官网油猴tampermonkey官网_油猴脚本手机版油猴插件下载 安装&#xff1a;360浏览器安装可以参考这边文章。 地址&#xff1a;http://www.xz7.com/article/86938.html 其实就是下载crx文件后&#xff…

linuxOPS基础_linux沾滞位T(sticky bit)

命令&#xff1a;chmod 语法&#xff1a;# chmod [选项] 文件夹 作用&#xff1a;只允许文件的创建者和root用户删除文件 常用选项&#xff1a;ot 添加粘滞位 ​ o-t 去掉粘滞位 ​ 用法&#xff1a;chmod ot 目录名 示例代码&#xff1a; #chmod ot 含义&#xff1a;给…