【小样本分割 2020 ICCV】PANet

news2024/11/19 18:33:33

文章目录

  • 【小样本分割 2020 ICCV】PANet
    • 1. 简介
    • 2. 网络
      • 2.1 整体架构
      • 2.2 原型学习
      • 2.3 非参数度量学习
      • 2.4 原型对齐正则化
    • 3. 代码
      • 3.1 backbone
      • 3.2 模型代码

【小样本分割 2020 ICCV】PANet

论文题目:PANet: Few-Shot Image Semantic Segmentation with Prototype Alignment

中文题目:PANet:基于原型对齐的小样本图像语义分割

论文链接:https://arxiv.org/abs/1908.06391v2

论文代码:https://github.com/kaixin96/PANet

论文团队:新加坡国立大学

发表时间:2019年8月

DOI:

引用:Wang K, Liew J H, Zou Y, et al. Panet: Few-shot image semantic segmentation with prototype alignment[C]//proceedings of the IEEE/CVF international conference on computer vision. 2019: 9197-9206.

引用数:584(截止时间:2023年4月24号)

1. 简介

本文从度量学习的角度来解决小样本分割问题,提出一种新的原型对齐网络来更好地利用支持集信息。

PANet从嵌入空间内的一些支持图像中学习特定类的原型表示,然后通过将每个像素与学习到的原型进行匹配,对查询图像进行分割。

通过非参数度量学习,PANet提供了代表每个语义类的高质量原型,这些原型同时还存在对不同类的判别信息。此外,PANet还引入了一种支持和查询之间的原型对齐正则化。在此基础上,PANet充分利用了来自支持的知识,在少镜头分割中提供了更好的泛化。

目前存在的问题和方案

现有的小样本分割方法通常从少量的支持图像中学习,然后将学习到的知识输入到参数化模块中进行查询分割。然而,这类方案存在两个缺点,推广效果不理想。

  • 首先,它们没有区分知识提取和分割过程,这可能会有问题,因为分割模型的表示与支持集的语义特征混合。因此,我们建议将这部分分为原型提取非参数度量学习两部分。原型被优化为每个语义类的紧凑和鲁棒表示,非参数度量学习通过嵌入空间中的像素级匹配进行分割。
  • 此外,我们不像以前的方法一样使只使用支持图像的标注做masking,我们将这些标注加入了小样本学习的监督过程。为此,我们引入了一种新的原型对齐正则化方法,这是一种反向分割方法。即将查询图像及其预测掩码作为一种新的支持集,用于对之前的支持图像进行分割。通过这种方式,鼓励模型在支持和查询之间生成更一致的原型,从而提供更好的泛化性能
    image-20230424093456851

2. 网络

2.1 整体架构

image-20230424093630233

现有的小样本分割方法将提取的支持特征与查询特征融合,以参数化的方式生成分割结果,我们提出的模型旨在在嵌入空间中学习并对齐每个语义类的紧凑和健壮的原型表示,然后通过非参数度量学习在嵌入空间内进行分割。

PANet首先通过共享的特征提取器将不同的前景对象和背景嵌入到不同的原型中。这样,每一个学习到的原型都是对应类的代表,同时也有足够区别于其它类。然后,将查询图像中的每个像素标记为这个像素的嵌入表示离它最近的类特定原型。

所提出的PANet结构设计有几个优点。

  • 首先,它不引入额外的可学习参数,因此不容易过度拟合。
  • 其次,在PANet中,原型嵌入和预测在计算的特征图上执行,因此分割不需要额外通过网络。
  • 此外,由于正则化仅用于训练,因此不会增加推理的计算成本。

2.2 原型学习

我们的模型基于度量网络,能够有效地学习每个语义类易于分离的原型表示。

PANet不是对整个输入图像进行平均,而是利用支持图像上的掩码注释来分别学习前景和背景原型

有两种方法来利用分割掩码,前期融合后期融合,本文采取的是后期融合,也就是将mask盖在特征图上以产生前景和背景特征(而不是在输入特征提取器前进行融合),这样做有利于保持共享特征提取器的输入一致性。

具体来说,给定一个支持集 S i = { ( I c , k , M c , k ) } \mathcal{S}_{i}=\{(I_{c,k},M_{c,k})\} Si={(Ic,k,Mc,k)}, F c , k F_{c,k} Fc,k表示图像 I c , k I_{c,k} Ic,k经过特征提取后得到的特征图。 c c c表示类别, k k k则表示支持图像序号,类 c c c的原型通过掩码平均池化得到:
p c = 1 K ∑ k ∑ x , y F c , k ( x , y ) 1 [ M c , k ( x , y ) = c ] ∑ x , y 1 [ M c , k ( x , y ) = c ] , p_c=\dfrac{1}{K}\sum_k\dfrac{\sum_{x,y}F_{c,k}^{(x,y)}\mathbb{1}[M_{c,k}^{(x,y)}=c]}{\sum_{x,y}\mathbb{1}[M_{c,k}^{(x,y)}=c]}, pc=K1kx,y1[Mc,k(x,y)=c]x,yFc,k(x,y)1[Mc,k(x,y)=c],
背景类的原型则可表示为:
p b g = 1 C K ∑ c , k ∑ x , y F c , k ( x , y ) 1 [ M c , k ( x , y ) ∉ C i ] ∑ x , y 1 [ M c , k ( x , y ) ∉ C i ] . p_{\mathrm{bg}}=\dfrac{1}{CK}\sum_{c,k}\dfrac{\sum_{x,y}F_{c,k}^{(x,y)}\mathbf{1}[M_{c,k}^{(x,y)}\notin\mathcal{C}_i]}{\sum_{x,y}\mathbf{1}[M_{c,k}^{(x,y)}\notin\mathcal{C}_i]}. pbg=CK1c,kx,y1[Mc,k(x,y)/Ci]x,yFc,k(x,y)1[Mc,k(x,y)/Ci].
( x , y ) (x,y) (x,y)表示位置,1表示框内式子为真则值为1,假为0

2.3 非参数度量学习

我们采用非参数度量学习方法来学习最优的原型并进行相应的分割。由于分割可以看作是在每个空间位置的分类,我们计算查询图像在每个空间位置上的特征向量与支持图像原型之间的距离。然后在距离上使用softmax得到语义类(包括背景)的概率映射 M ~ q \tilde{M}_{q} M~q

具体来说,给定一个距离函数 d d d,让 P = { p c ∣ c ∈ C i } ∪ { p b g } \mathcal{P}=\{p_{c}|c\in\mathcal{C}_{i}\}\cup\{p_{\mathrm{bg}}\} P={pccCi}{pbg} F q F_q Fq 表示查询图像特征映射。
M ~ q ; j ( x , y ) = exp ⁡ ( − α d ( F q ( x , y ) , p j ) ) ∑ p j ∈ P exp ⁡ ( − α d ( F q ( x , y ) , p j ) ) . \tilde{M}_{q;j}^{(x,y)}=\frac{\exp(-\alpha d(F_{q}^{(x,y)},p_{j}))}{\sum_{p_{j}\in\mathcal{P}}\exp(-\alpha d(F_{q}^{(x,y)},p_{j}))}. M~q;j(x,y)=pjPexp(αd(Fq(x,y),pj))exp(αd(Fq(x,y),pj)).
距离函数 d d d一般采用余弦距离或平方欧几里得距离,根据经验,我们发现使用余弦距离更稳定,性能更好,可能是因为它是有界的,因此更容易优化。

然后,预测的分割掩码由下式给出:
M ~ q ( x , y ) = arg ⁡ j max ⁡ M ~ q ; j ( x , y ) . \tilde{M}_q^{(x,y)}=\arg\limits_j\max\tilde{M}_{q;j}^{(x,y)}. M~q(x,y)=jargmaxM~q;j(x,y).
分割损失的表达如下:
L s e g = − 1 N ∑ x , y ∑ p j ∈ P 1 [ M q ( x , y ) = j ] log ⁡ M ~ q ; j ( x , y ) , \mathcal{L}_{\mathrm{seg}}=-\frac{1}{N}\sum_{x,y}\sum_{p_j\in\mathcal{P}}1[M_q^{(x,y)}=j]\log\tilde{M}_{q;j}^{(x,y)}, Lseg=N1x,ypjP1[Mq(x,y)=j]logM~q;j(x,y),
M q M_q Mq表示查询图像的ground-truth分割掩码,N表示所有空间位置数量,对上述损失进行优化将为每个类派生出合适的原型。

2.4 原型对齐正则化

直觉上来说,如果模型能够使用从支持集中提取的原型来预测出一个好的查询分割掩码,那么这个预测出的查询图像分割掩码应该能够很好地分割支持图像。因此PAR鼓励最终的分割模型反向进行few-shot learning,即以查询和预测的掩码作为新的支持样本来学习分割原始支持图像(也就是这里的查询图像和预测得到的查询图像掩码作为新的支持图像–掩码对)。这将在支持原型和查询图像之间形成对齐,并从支持集中学习到更丰富的知识。

支持图像的分割概率图为
M ~ c , k ; j ( x , y ) = exp ⁡ ( − α d ( F c , k ( x , y ) , p ˉ j ) ) ∑ p ˉ j ∈ { p ˉ c , p ˉ b g } exp ⁡ ( − α d ( F c , k ( x , y ) , p ˉ j ) ) , \tilde{M}_{c,k;j}^{(x,y)}=\frac{\exp(-\alpha d(F_{c,k}^{(x,y)},\bar{p}_{j}))}{\sum_{\bar{p}_{j}\in\{\bar{p}_{c},\bar{p}_{b_{g}}\}}\exp(-\alpha d(F_{c,k}^{(x,y)},\bar{p}_{j}))}, M~c,k;j(x,y)=pˉj{pˉc,pˉbg}exp(αd(Fc,k(x,y),pˉj))exp(αd(Fc,k(x,y),pˉj)),

使用查询图像和其掩码预测支持图像掩码的损失函数如下:

L P A R = − 1 C K N ∑ c , k , x , y ∑ p j ∈ P 1 [ M q ( x , y ) = j ] log ⁡ M ~ q ; j ( x , y ) . \mathcal{L}_{\mathrm{PAR}}=-\dfrac{1}{CKN}\sum_{c,k,x,y}\sum_{p_j\in\mathcal{P}}1[M_q^{(x,y)}=j]\log\tilde{M}_{q;j}^{(x,y)}. LPAR=CKN1c,k,x,ypjP1[Mq(x,y)=j]logM~q;j(x,y).

3. 代码

3.1 backbone

import torch
import torch.nn as nn
import torch.nn.functional as F


__all__ = ['ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101', 'resnet152']


def conv3x3(in_planes, out_planes, stride=1, groups=1, dilation=1):
    return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,
                     padding=dilation, groups=groups, bias=False, dilation=dilation)


def conv1x1(in_planes, out_planes, stride=1):
    return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False)


class BasicBlock(nn.Module):
    expansion = 1

    def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1,
                 base_width=64, dilation=1, norm_layer=None):
        super(BasicBlock, self).__init__()
        if norm_layer is None:
            norm_layer = nn.BatchNorm2d
        if groups != 1 or base_width != 64:
            raise ValueError('BasicBlock only supports groups=1 and base_width=64')
        if dilation > 1:
            raise NotImplementedError("Dilation > 1 not supported in BasicBlock")

        self.conv1 = conv3x3(inplanes, planes, stride)
        self.bn1 = norm_layer(planes)
        self.relu = nn.ReLU(inplace=True)
        self.conv2 = conv3x3(planes, planes)
        self.bn2 = norm_layer(planes)
        self.downsample = downsample
        self.stride = stride

    def forward(self, x):
        identity = x

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)

        out = self.conv2(out)
        out = self.bn2(out)

        if self.downsample is not None:
            identity = self.downsample(x)

        out += identity
        out = self.relu(out)

        return out


class Bottleneck(nn.Module):
    expansion = 4

    def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1,
                 base_width=64, dilation=1, norm_layer=None, last_relu=True):
        super(Bottleneck, self).__init__()
        if norm_layer is None:
            norm_layer = nn.BatchNorm2d
        width = int(planes * (base_width / 64.)) * groups

        self.conv1 = conv1x1(inplanes, width)
        self.bn1 = norm_layer(width)
        self.conv2 = conv3x3(width, width, stride, groups, dilation)
        self.bn2 = norm_layer(width)
        self.conv3 = conv1x1(width, planes * self.expansion)
        self.bn3 = norm_layer(planes * self.expansion)
        self.relu = nn.ReLU(inplace=True)
        self.downsample = downsample
        self.stride = stride
        self.last_relu = last_relu

    def forward(self, x):
        identity = x

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)

        out = self.conv2(out)
        out = self.bn2(out)
        out = self.relu(out)

        out = self.conv3(out)
        out = self.bn3(out)

        if self.downsample is not None:
            identity = self.downsample(x)

        out += identity
        if self.last_relu:
            out = self.relu(out)

        return out


class ResNet(nn.Module):

    def __init__(self, block, layers, zero_init_residual=False, groups=1,
                 width_per_group=64, replace_stride_with_dilation=None, norm_layer=None):
        super(ResNet, self).__init__()

        self.out_channels = block.expansion * 256

        if norm_layer is None:
            norm_layer = nn.BatchNorm2d
        self._norm_layer = norm_layer

        self.inplanes = 128
        self.dilation = 1
        if replace_stride_with_dilation is None:
            replace_stride_with_dilation = [False, False, False]
        if len(replace_stride_with_dilation) != 3:
            raise ValueError("replace_stride_with_dilation should be None "
                             "or a 3-element tuple, got {}".format(replace_stride_with_dilation))
        self.groups = groups
        self.base_width = width_per_group

        self.conv1 = nn.Sequential(
            conv3x3(3, 64, stride=2),
            norm_layer(64),
            nn.ReLU(inplace=True),
            conv3x3(64, 64),
            norm_layer(64),
            nn.ReLU(inplace=True),
            conv3x3(64, 128)
        )
        self.bn1 = norm_layer(128)
        self.relu = nn.ReLU(inplace=True)

        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        self.layer1 = self._make_layer(block, 64, layers[0])
        self.layer2 = self._make_layer(block, 128, layers[1], stride=2,
                                       dilate=replace_stride_with_dilation[0])
        self.layer3 = self._make_layer(block, 256, layers[2], stride=2,
                                       dilate=replace_stride_with_dilation[1], last_relu=False)

        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
            elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):
                nn.init.constant_(m.weight, 1)
                nn.init.constant_(m.bias, 0)

        if zero_init_residual:
            for m in self.modules():
                if isinstance(m, Bottleneck):
                    nn.init.constant_(m.bn3.weight, 0)
                elif isinstance(m, BasicBlock):
                    nn.init.constant_(m.bn2.weight, 0)

    def _make_layer(self, block, planes, blocks, stride=1, dilate=False, last_relu=True):
        """
        :param last_relu: in metric learning paradigm, the final relu is removed (last_relu = False)
        """
        norm_layer = self._norm_layer
        downsample = None
        previous_dilation = self.dilation
        if dilate:
            self.dilation *= stride
            stride = 1
        if stride != 1 or self.inplanes != planes * block.expansion:
            downsample = nn.Sequential(
                conv1x1(self.inplanes, planes * block.expansion, stride),
                norm_layer(planes * block.expansion),
            )

        layers = list()
        layers.append(block(self.inplanes, planes, stride, downsample, self.groups,
                            self.base_width, previous_dilation, norm_layer))
        self.inplanes = planes * block.expansion
        for i in range(1, blocks):
            use_relu = True if i != blocks - 1 else last_relu
            layers.append(block(self.inplanes, planes, groups=self.groups,
                                base_width=self.base_width, dilation=self.dilation,
                                norm_layer=norm_layer, last_relu=use_relu))

        return nn.Sequential(*layers)

    def base_forward(self, x):
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        x = self.maxpool(x)

        c1 = self.layer1(x)
        c2 = self.layer2(c1)
        c3 = self.layer3(c2)

        return c3


def _resnet(arch, block, layers, pretrained, **kwargs):
    model = ResNet(block, layers, **kwargs)
    if pretrained:
        state_dict = torch.load(pretrained)
        msg=model.load_state_dict(state_dict, strict=False)
        print(msg)
    return model


def resnet18(pretrained=False):
    return _resnet('resnet18', BasicBlock, [2, 2, 2, 2], pretrained)


def resnet34(pretrained=False):
    return _resnet('resnet34', BasicBlock, [3, 4, 6, 3], pretrained)


def resnet50(pretrained=False):
    return _resnet('resnet50', Bottleneck, [3, 4, 6, 3], pretrained,
                   replace_stride_with_dilation=[False, True, True])


def resnet101(pretrained=False):
    return _resnet('resnet101', Bottleneck, [3, 4, 23, 3], pretrained,
                   replace_stride_with_dilation=[False, True, True])


def resnet152(pretrained=False):
    return _resnet('resnet152', Bottleneck, [3, 8, 36, 3], pretrained,
                   replace_stride_with_dilation=[False, True, True])


    

3.2 模型代码

from .resnet import resnet50, resnet101

import torch
from torch import nn
import torch.nn.functional as F
import pdb


class PANet(nn.Module):
    def __init__(self, backbone, shot=1, pretrained=True):
        super(PANet, self).__init__()
        backbone = eval(backbone)(pretrained=pretrained)  # 创建backbone-resnet50。backbone 取前几层。
        self.layer0 = nn.Sequential(backbone.conv1, backbone.bn1, backbone.relu, backbone.maxpool)
        self.layer1, self.layer2, self.layer3 = backbone.layer1, backbone.layer2, backbone.layer3
        self.shot = shot

    def forward(self, img_s_list, mask_s_list, img_q, mask_q):
        """

        Args:
            img_s_list: support images
                        List   shape=shot x [batch size,3,473,473]
            mask_s_list: masks for support images
                    List  shape=shot x [batch size,473,473]
            img_q:  query images
                    [batch_size,3,473,473]
            mask_q:  query images
                    [batch_size,473,473]
        """
        h, w = img_q.shape[-2:]

        # feature maps of support images
        feature_s_list = []
        #  获取支持集的特征
        for k in range(len(img_s_list)):
            with torch.no_grad():
                s_0 = self.layer0(img_s_list[k])
                s_0 = self.layer1(s_0)
            s_0 = self.layer2(s_0)
            s_0 = self.layer3(s_0)
            feature_s_list.append(s_0)
            del s_0

        # 获取查询集图像的特征
        with torch.no_grad():
            q_0 = self.layer0(img_q)
            q_0 = self.layer1(q_0)
        q_0 = self.layer2(q_0)
        feature_q = self.layer3(q_0)  # [4,1024,60,60]

        # foreground(target class) and background prototypes pooled from K support features
        feature_fg_list = []
        feature_bg_list = []

        for k in range(len(img_s_list)):
            feature_fg = self.masked_average_pooling(feature_s_list[k], (mask_s_list[k] == 1).float())[None, :]  # feature_fg=[1,4,1024]
            feature_bg = self.masked_average_pooling(feature_s_list[k], (mask_s_list[k] == 0).float())[None, :]  # feature_bg=[1,4,1024]
            feature_fg_list.append(feature_fg)
            feature_bg_list.append(feature_bg)

        # 对shot个图片进行平均,计算原型 [4,1024,1,1]
        FP = torch.mean(torch.cat(feature_fg_list, dim=0), dim=0).unsqueeze(-1).unsqueeze(-1)
        # 背景原型 [4,1024,1,1]
        BP = torch.mean(torch.cat(feature_bg_list, dim=0), dim=0).unsqueeze(-1).unsqueeze(-1)

        # 计算查询特征和前景和背景的原型 之间的相似度。计算出初步的分割掩码
        out_0 = self.similarity_func(feature_q, FP, BP)  # [4,2,60,60]
        out_0 = F.interpolate(out_0, size=(h, w), mode="bilinear", align_corners=True)  # [4,2,473,473]

        # 如果是训练阶段,不需要对齐
        # Prototype alignment regularization (PAR) 原型对齐阶段
        if self.training:
            out_ls = []
            # 通过 查询集的输出的特征,根据掩码,输出查询集的前景和背景的原型
            fg_q = self.masked_average_pooling(feature_q, (mask_q == 1).float())[None, :].squeeze(0)  # [4,1024]
            bg_q = self.masked_average_pooling(feature_q, (mask_q == 0).float())[None, :].squeeze(0)  # [4,1024]

            for i in range(self.shot):
                # 根据查询集的原型,计算出支持集的分割图
                self_out = self.similarity_func(feature_s_list[i], fg_q[..., None, None], bg_q[..., None, None])
                self_out = F.interpolate(self_out, size=(h, w), mode="bilinear", align_corners=True)
                out_ls.append(self_out)

            return out_0, out_ls

        return out_0

    def similarity_func(self, feature_q, fg_proto, bg_proto):
        """
         通过计算相似度来进行分割
         Args:
            feature_q: [4,1024,60,60] 查询集特征
            fg_proto: [4,1024,1,1] 前景原型
            bg_proto: [4,1024,1,1] 背景原型
         return: [4,2,60,60] 初步的分割结果
        """
        similarity_fg = F.cosine_similarity(feature_q, fg_proto, dim=1)
        similarity_bg = F.cosine_similarity(feature_q, bg_proto, dim=1)
        out = torch.cat((similarity_bg[:, None, ...], similarity_fg[:, None, ...]), dim=1) * 10.0  # [4,2,60,60]
        return out

    def masked_average_pooling(self, feature, mask):
        """
        通过mask_pool操作获取对应特征的原型。
        Args:
            feature: [4,1024,60,60]
            mask:  [4,473,473]
        return prototype: [4,1024] 掩码后对应特征的原型
        """
        mask = F.interpolate(mask.unsqueeze(1), size=feature.shape[-2:], mode='bilinear', align_corners=True)
        masked_feature = torch.sum(feature * mask, dim=(2, 3)) / (mask.sum(dim=(2, 3)) + 1e-5)
        return masked_feature

参考资料

小样本图像分割PANet: Few-Shot Image Semantic Segmentation with Prototype Alignment_few-shot image semantic seg- mentation with protot_XL_Dylan的博客-CSDN博客

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

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

相关文章

IDEA下SpringBoot指定配置文件启动项目

目录 一. idea下的SpringBoot启动:指定配置文件 二. 项目已打包,运行配置 1).使用java -jar启动基于(一)下的配置文件启动 2)指定项目内其它配置文件application-pro.yml启动项目 3) Linux服务器上启动基于(三)的…

OneFlow源码解析:Eager模式下Tensor的存储管理

作者|郑建华 1 不同Tensor类型的存储管理方式 Lazy Tensor 的存储是由 Runtime 和 Actor 等对象管理的。静态图完成编译后,需要多少个对象、多少存储空间都是确定的,Runtime 等在初始化时会分配存储,在退出时回收资源。 Eager 模…

SpringBoot--图片验证码kaptcha

本文介绍如何在SpringBoot中整合kaptcha,以及如何配置kaptcha,生成验证码和校验等 文章目录 前言环境搭建项目结构添加依赖 代码实现KaptchaConfigKnife4jConfigdomainServiceutilcontroller 测试生成验证码校验验证码 前言 参考链接: Gith…

设计模式:结构型模式 - 适配器模式

文章目录 1.概述2.结构3.类适配器模式4.对象适配器模式5.应用场景6.JDK源码解析 - Reader 与 InputStream 1.概述 如果去欧洲国家去旅游的话,他们的插座如下图最左边,是欧洲标准。而我们使用的插头如下图最右边的。因此我们的笔记本电脑,手机…

【Android车载系列】第9章 车载通信-Socket实现IPC通信机制(实现仿FdBus效果)

1 FDBus简介 FDBus 基于 Socket (TCP 和 Unix domain) 之上的IPC机制, 采用 Google protobuf 做序列化和反序列化。 FDBus还支持字符串形式的名字作为server地址。通过 name server 自动为 server 分配Unix domain 地址和 TCP 端口号, 实现 client 和server 之间用服务名字寻址…

【C++】哈希的应用:位图和布隆过滤器

目录 1. 位图1.1 位图的概念1.2 位图的结构1.3 位图的实现 2. 布隆过滤器2.1 概念2.2 结构2.3 布隆过滤器的实现 1. 位图 1.1 位图的概念 💭位图(bitset)是一种基于哈希思想设计的数据结构,其功能主要用于判断数据是否已存在。适…

【设计模式】模板方法模式--让你的代码更具灵活性与可扩展性

文章目录 前言模板方法模式的定义核心组成模板方法模式与其他设计模式的区别 代码实现抽象类具体类Client 经典类图spring中的例子 总结 前言 在软件开发中,设计模式是一种经过实践检验的、可复用的解决方案,它们可以帮助我们解决某一特定领域的典型问题…

【2023 · CANN训练营第一季】应用开发深入讲解——第一章 模型转换(1)

学习目标 学习资源 1.模型转换 模型转换基础 在线课程 文档AIPP功能说明 文档 应用开发(初级) 课程目标: 了解AscendCL的使用场景、基本概念、基本开发流程。 学会使用AscendCL接口开发基础推理应用,应用中包含对图片的基本处理…

javaScript常见面试题(二)

一、浅拷贝和深拷贝 浅拷贝:(1)对于基本数据类型,拷贝的是值,修改的时候数据互不影响;(2)对于引用数据类型,拷贝的是在堆内存中存储的内存地址,修改的时候&a…

mapreduce基础: 手写本地wordcount案例

文章目录 一、源代码1. WordCountMapper类2. WordCountReducer类3. WordCountDriver类 二、运行截图 一、源代码 1. WordCountMapper类 package org.example.wordcount;import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.LongWritable; import org.apac…

yum库和nfs共享服务

yum库 redhat,centos用的是.rpm的包,用yum解决依赖包关系下载 ubuntu,debian用的是.deb的包,用apt解决依赖包的关系下载 yum软件仓库的提供方式 FTP服务: ftp://… HTTP服务: http://…或者https://… 本地目录: fi…

母婴品牌内容输出怎么做?“四板斧”送你

新媒体时代,信息大爆炸,人们的注意力有限,有噱头和亮点的内容才能博得注意,成为用户关注的焦点。 母婴行业重视品牌效益和产品的质量,毕竟类似“三聚氰胺”的惨剧谁也不希望再发生。母婴产品的质量依赖技术和生产线支…

C语言从入门到精通第9天(循环结构的使用)

循环结构的使用 while语句do-while语句for语句嵌套循环 循环结构可以重复的执行一段代码块,在C语言中提供了三种不同类型的循环结构:for、while和do-while。 while语句 语法: while(表达式){ 语句; } 如果表达式为真则执行结构体…

并发编程学习笔记

为什么学? 只要去做一个技术含量稍微好点的系统,并发包下面的东西是很容易会要用到的 synchronized(Object){ } 主要是用来给对象加锁 synchronized底层原理 首先监视器的计数器是0,然后线程一访问,会将值改为1,…

[Gitops--4] OpenELB

OpenELB OpenELB是一个开源的负载均衡器,功能和metalLB类似 OpenELB主要两种工作模式: Layer2和BGP模式.目前OpenELB的BGP不支持ipv6 OpenELB核心思想就是通过某种方式将特定的VIP的流量引导k8s集群中,然后通过Kube-proxy将流量转发到后面的特定服务. 1. OpenELB介绍 1.1 La…

对于python文件,敲下回车后发生了什么

引言 我们常说 Python 一是门解释型语言,只需要敲下 python code.py就可以运行编写的代码,而无需使用类似于 javac 或者 gcc 进行编译。那么,Python 解释器是真的一行一行读取 Python 源代码而后执行吗? 实际上,Python 在执行程序…

为什么要进行倾斜摄影三维模型的顶层合并?

为什么要进行倾斜摄影三维模型的顶层合并? 1、倾斜摄影三维模型顶层合并的重要性 倾斜摄影三维模型的顶层合并是指将拍摄同一区域的多个倾斜角度的影像进行融合,生成一个连续的、完整的三维地理信息数据。其原因主要有以下几点: &#xff0…

关于倾斜摄影三维模型轻量化数据大小和质量关系分析

关于倾斜摄影三维模型轻量化数据大小和质量关系分析 倾斜摄影三维模型轻量化是一种常用的技术,通过对原始三维模型数据进行压缩和简化,减小其数据大小,从而提高数据传输和展示效率。然而,轻量化过程中可能会对数据质量产生影响。以…

性能测试——安装Loadrunner11.0的详细步骤

一、下载Loadrunner11.0版本 去相关网站下载即可 二、安装 (windows与虚拟机上安装操作大相径庭) 1、将ISO文件导入,打开光驱,运行“setup.exe“ 2、点击安装,部分机器会提示缺少“Microsoft Visual C 2005 SP1运行组…

有效的字母异位词

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。 来源:力扣(LeetCode) 链接:https://leetco…