pytorch中创建maskrcnn模型

0.模型输入/输出参数参见

链接: pytorch的mask-rcnn的模型参数解释
核心代码
GeneralizedRCNN(这里以mask-rcnn来解释说明)

# 通过输入图像获取fpn特征图,注意这里的backbone不是直接的resnet,而是fpn化后的
features = self.backbone(images.tensors)
# 由于是mask-rcnn,故而是一个dict,这里是处理非fpn的包装为dict
if isinstance(features, torch.Tensor):
    features = OrderedDict([("0", features)])
# RPN负责生成候选区域(proposals)。它基于前面提取的特征features,以及输入图像images和目标targets(如果有的话,例如在训练阶段)来生成这些候选区域。同时,它也可能返回与候选区域生成相关的损失proposal_losses。
proposals, proposal_losses = self.rpn(images, features, targets)
#ROI Heads(Region of Interest Heads)负责对这些候选区域进行分类和边界框回归。它基于RPN生成的候选区域proposals,前面提取的特征features,以及输入图像的大小images.image_sizes和目标targets来执行这些任务。同时,它返回检测结果detections和与分类和边界框回归相关的损失detector_losses。
detections, detector_losses = self.roi_heads(features, proposals, images.image_sizes, targets)
# 后处理步骤通常包括将检测结果从模型输出格式转换为更易于解释或可视化的格式。例如,它可能包括将边界框坐标从模型使用的格式转换为图像的实际像素坐标,或者对分类得分进行阈值处理以过滤掉低置信度的检测。
detections = self.transform.postprocess(detections, images.image_sizes, original_image_sizes)  # type: ignore[operator]
# 汇总损失
losses = {}
losses.update(detector_losses)
losses.update(proposal_losses)

核心模型类:

MaskRCNN(FasterRCNN)
FasterRCNN(GeneralizedRCNN)
GeneralizedRCNN(nn.Module)
# 处理原始图像
GeneralizedRCNNTransform(nn.Module)
BackboneWithFPN(nn.Module)
IntermediateLayerGetter(nn.ModuleDict)
FeaturePyramidNetwork(nn.Module)
# RPN网络
RegionProposalNetwork(nn.Module)
# 建立anchor
AnchorGenerator(nn.Module)
# rpnhead
RPNHead(nn.Module)

1.提取特征图

通过骨干网络(如ResNet)提取输入图像的特征图

1.1 执行transform

对输入的images,targets执行transform,主要是标准化和resize的合并操作

1.1.1 images执行标准化操作

如果是采用的imagenet权重,则一般采用以下参数执行标准化操作.主要作用屏蔽图像色温/曝光一类的影响.
注意,传入到模型的images是0-1之间的float表示的tensor

image_mean = [0.485, 0.456, 0.406]
image_std = [0.229, 0.224, 0.225]

1.1.2 images执行resize操作

同一个batch的图像需要缩放到同一尺寸,才可以合并.
定义dataloader时的sampler,可以创建一个相似宽高比的图像分配到一组组成batch的sapmler以优化计算.
模型需要指定一个max_size以及一个min_size.过大/小的图像会被缩放至一个合适尺寸.
resize方法可以采用双线性插值法(pytorch的模型是这么干的),或者直接填充.

1.1.3targets的mask执行resize操作

图像resize了,mask也需要同样操作,不然对不上.方法和图像的一致

1.1.4 images执行合并tensor操作

将list(tensor([N,H,W]))[B] 合并为tensor[B,N,H,W]以便传入backbone

1.2 创建backbone,以及backbone_with_fpn

1.2.1 使用backbone网络,提取创建fpn网络

例如restnet50,不需要返回分类信息,而是前面的多层特征图信息,然后组合为fpn数据
调用栈
GeneralizedRCNN.forward->
RegionProposalNetwork.forward->
BackboneWithFPN.forward

backbone = resnet50(weights=weights_backbone, progress=progress, norm_layer=norm_layer)
backbone = _resnet_fpn_extractor(backbone, trainable_backbone_layers)

输出结果形如:
在这里插入图片描述

1.2.2 提取fpn特征数据

现在我们来提取输出features
一共分2步,body和fpn
body从backbone提取原始特征层数据 (pytorch 定义为 IntermediateLayerGetter 类)
fpn将其处理打包为fpn结构数据 (pytorch 定义为 FeaturePyramidNetwork 类)

def forward(self, x: Tensor) -> Dict[str, Tensor]:
     x = self.body(x)
     x = self.fpn(x)
     return x
1.2.2.1 body

resnet的网络结构如下,我们需要提取layer1-4的输出结果来构建fpn,
提取所有return_layers最后一个层,即layer4之前的所有层,即抛弃掉无用的avgpool和fc层
resnet50所有层
参考精简代码如下:

import copy
from collections import OrderedDict

import torch
from PIL import Image
from torchvision.models import resnet50, ResNet50_Weights
from torchvision import transforms

if __name__ == '__main__':
    model = resnet50(weights=ResNet50_Weights.DEFAULT)
    original_img = Image.open("a.png").convert('RGB')
    ts = transforms.Compose([transforms.ToTensor()])
    img = ts(original_img)
    img = torch.unsqueeze(img, dim=0)
    model.eval()

    # 需要返回用以构建fpn层的名字
    return_layers = {'layer1': '0', 'layer2': '1', 'layer3': '2', 'layer4': '3'}
    # 创建return_layers的副本
    return_layers_copy = copy.deepcopy(return_layers)
    # 存储有效的层
    layers = OrderedDict()
    # 提取所有return_layers最后一个层,即layer4之前的所有层,即抛弃掉无用的avgpool和fc层
    for name, module in model.named_children():
        layers[name] = module
        if name in return_layers_copy:
            del return_layers_copy[name]
        # 如果指示的层被删光了,说明遍历到最后一个了,跳出循环
        if not return_layers_copy:
            break
    # 创建结果集合
    out = OrderedDict()
    for name, module in layers.items():
        img = module(img)
        if name in return_layers:
            out_name = return_layers[name]
            out[out_name] = img
    # rs = model(img)
    print(out)

输出入下图
提取有用的层,并且获取数据

1.2.2.2 fpn

对body输出的out的4个结果分别执行1x1的卷积操作(共4个卷积核,输出均为256,输入是256~2048)得到结果
精简代码如下:

o2 = list()
in_channels_list = [256, 512, 1024, 2048]
out_channels = 256
# 使用一个1x1的卷积核处理为同样深度的特征图
inner_blocks = nn.ModuleList()
for index, in_channels in enumerate(in_channels_list):
	inner_block_module = nn.Conv2d(in_channels, out_channels, kernel_size=1, padding=0)
	inner_blocks.append(inner_blocks)
	# 等效于不设定激活函数和归一化层的Conv2d,注意Conv2dNormActivation会自动计算padding,以使之尺寸不变
	# inner_block_module2 = Conv2dNormActivation(in_channels, out_channels, kernel_size=1, padding=0, norm_layer=None,
	#                                            activation_layer=None)
	x = out.get(str(index))
	x = inner_block_module(x)
	o2.append(x)

 print(o2)

输出如下
在这里插入图片描述对同一深度的结果(o2)操作上采样以及相加,即:
注:此处方便理解,C代表代码中的o2,C2代表o2的下标2,所以有0,和论文中的不太一致
C3=P3
P3上采样+C2=P2
P2上采样+C1=P1
P1上采样+C0=P0

对P0-3分别做一次3x3的卷积,对P3做最大池化得pool
P0->3x3卷积=out(0)
P1->3x3卷积=out(1)
P2->3x3卷积=out(2)
P3->3x3卷积=out(3)
P3->maxpool=pool
代码简化示意如下:

 # 执行上采样以及合并操作,以及结果再次卷积
    o3 = list()
    # 最后一个直接丢到结果集上,作为p4
    last_inner = o2[-1]
    o3.append(last_inner)

    # 倒着遍历o2,从倒数第3个开始
    # 使用一个3x3的卷积核再次处理结果, 减少上采样的混叠效应
    layer_blocks = nn.ModuleList()
    for idx in range(len(o2) - 2, -1, -1):
        # 获取当前这个,以及形状
        inner_lateral = o2[idx]
        feat_shape = inner_lateral.shape[-2:]
        # 对上层的那个执行上采样
        inner_top_down = nn.functional.interpolate(last_inner, size=feat_shape)
        # 相加作为P3~P1
        last_inner = inner_lateral + inner_top_down

        # 使用一个3x3的卷积核再次处理结果,减少上采样的混叠效应
        layer_block_module = nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=0)
        layer_blocks.append(layer_block_module)

        # 倒序存储到结果上
        o3.insert(0, layer_block_module(last_inner))

    # 取出最小特征图做一次池化,池化核1x1,步长2
    tm = nn.functional.max_pool2d(o3[-1], kernel_size=1, stride=2, padding=0)
    o3.append(tm)

    names = ["0", "1", "2", "3", "pool"]
    # make it back an OrderedDict
    out = OrderedDict([(k, v) for k, v in zip(names, o3)])
    print(o3)

在这里插入图片描述以上全部演示代码

import copy
from collections import OrderedDict

import torch
from PIL import Image
from torchvision.models import resnet50, ResNet50_Weights
from torchvision import transforms
from torchvision.ops import Conv2dNormActivation
from torch import nn

if __name__ == '__main__':
    model = resnet50(weights=ResNet50_Weights.DEFAULT)
    original_img = Image.open("a.png").convert('RGB')
    ts = transforms.Compose([transforms.ToTensor()])
    img = ts(original_img)
    img = torch.unsqueeze(img, dim=0)
    model.eval()

    # 需要返回用以构建fpn层的名字
    return_layers = {'layer1': '0', 'layer2': '1', 'layer3': '2', 'layer4': '3'}
    # 创建return_layers的副本
    return_layers_copy = copy.deepcopy(return_layers)
    # 存储有效的层
    layers = OrderedDict()
    # 提取所有return_layers最后一个层,即layer4之前的所有层,即抛弃掉无用的avgpool和fc层
    for name, module in model.named_children():
        layers[name] = module
        if name in return_layers_copy:
            del return_layers_copy[name]
        # 如果指示的层被删光了,说明遍历到最后一个了,跳出循环
        if not return_layers_copy:
            break
    # 创建结果集合
    out = OrderedDict()
    for name, module in layers.items():
        img = module(img)
        if name in return_layers:
            out_name = return_layers[name]
            out[out_name] = img
    print(out)

    in_channels_list = [256, 512, 1024, 2048]
    out_channels = 256

    # 创建1x1卷积,并执行卷积操作
    o2 = list()
    # 使用一个1x1的卷积核处理为同样深度的特征图
    inner_blocks = nn.ModuleList()
    for index, in_channels in enumerate(in_channels_list):
        # 用以执行处理同一深度的卷积核
        inner_block_module = nn.Conv2d(in_channels, out_channels, kernel_size=1, padding=0)
        inner_blocks.append(inner_blocks)
        # 等效于不设定激活函数和归一化层的Conv2d
        # inner_block_module2 = Conv2dNormActivation(in_channels, out_channels, kernel_size=1, padding=0,
        #                                            norm_layer=None,activation_layer=None)
        x = out.get(str(index))
        x = inner_block_module(x)
        o2.append(x)

    # 执行上采样以及合并操作,以及结果再次卷积
    o3 = list()
    # 最后一个直接丢到结果集上,作为p4
    last_inner = o2[-1]
    o3.append(last_inner)

    # 倒着遍历o2,从倒数第3个开始
    # 使用一个3x3的卷积核再次处理结果, 减少上采样的混叠效应
    layer_blocks = nn.ModuleList()
    for idx in range(len(o2) - 2, -1, -1):
        # 获取当前这个,以及形状
        inner_lateral = o2[idx]
        feat_shape = inner_lateral.shape[-2:]
        # 对上层的那个执行上采样
        inner_top_down = nn.functional.interpolate(last_inner, size=feat_shape)
        # 相加作为P3~P1
        last_inner = inner_lateral + inner_top_down

        # 使用一个3x3的卷积核再次处理结果,减少上采样的混叠效应
        layer_block_module = nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=0)
        layer_blocks.append(layer_block_module)

        # 倒序存储到结果上
        o3.insert(0, layer_block_module(last_inner))

    # 取出最小特征图做一次池化,卷积核1x1,步长2
    tm = nn.functional.max_pool2d(o3[-1], kernel_size=1, stride=2, padding=0)
    o3.append(tm)

    names = ["0", "1", "2", "3", "pool"]
    # make it back an OrderedDict
    out = OrderedDict([(k, v) for k, v in zip(names, o3)])
    print(o3)

在这个过程中,可以学习的参数分布在
1.backbone网络,可以锁定一些层不更新,pytorch默认是更新3个层
2.处理特征图为同一深度的1x1核的卷积层
3.最后输出前的3x3的卷积层

2 构建RPN网络

上文我们获取了5张尺寸各异,深度为256的特征图,下面我们对他进行RPN即
(Region Proposal Network)区域生成网络构建.
注意 : 后面的原图,均指被transform处理过的图像,而不是真的原原本本的图像

        features = self.backbone(images.tensors)
        if isinstance(features, torch.Tensor):
            features = OrderedDict([("0", features)])
        proposals, proposal_losses = self.rpn(images, features, targets)
        detections, detector_losses = self.roi_heads(features, proposals, images.image_sizes, targets)
        detections = self.transform.postprocess(detections, images.image_sizes, original_image_sizes)  # type: ignore[operator]

2.1 创建anchors

fpn创建了5张特征图(下图示例),我们取最大尺寸的为例,(其他的做法同理):
请添加图片描述

  1. 特征图尺寸为: 1x256x200x272 (batch_size=1) ->记作A
  2. 原始图像尺寸 800x1066 (实际尺寸为1848, 2464)->记作B
  3. 可以看出 A的尺寸是B的4倍(大约) .
  4. 做一个映射,一个A的点,映射到B上就是4个像素点.
  5. 我给A的每一个点,创建9个候选框,长宽比为(0.5,1,2).长为(16,32,64)
  6. 那么总计就是 200x272x9 = 489600个候选框.
  7. 下面计算出每一个候选框的实际坐标,按照(x1,y1,x2,y2)返回一个(489600,4)的tensor

2.1.1 基础cell_anchor构建

  1. 示例代码,计算出9个候选框的基础尺寸,其中scales代表是面积的开方,也可以理解为,在长宽比为1时,边长就是scales值.(注意实际使用的代码应该返回一个list,以适应5个特征图)
    调用栈
    GeneralizedRCNN.forward->
    RegionProposalNetwork.forward->
    AnchorGenerator.forward
import torch
from torch import Tensor


def generate_anchors(
        scales,
        aspect_ratios,
        dtype: torch.dtype = torch.float32,
        device: torch.device = torch.device("cpu"),
) -> Tensor:
    scales = torch.as_tensor(scales, dtype=dtype, device=device)
    aspect_ratios = torch.as_tensor(aspect_ratios, dtype=dtype, device=device)
    h_ratios = torch.sqrt(aspect_ratios)
    w_ratios = 1 / h_ratios

    ws = (w_ratios[:, None] * scales[None, :]).view(-1)
    hs = (h_ratios[:, None] * scales[None, :]).view(-1)

    base_anchors = torch.stack([-ws, -hs, ws, hs], dim=1) / 2
    return base_anchors.round()


if __name__ == '__main__':
    size = (16, 32, 64)
    aspect_ratio = (0.5, 1.0, 2.0)
    print(generate_anchors(size, aspect_ratio))


输出结果

tensor([[-11.,  -6.,  11.,   6.],
        [-23., -11.,  23.,  11.],
        [-45., -23.,  45.,  23.],
        [ -8.,  -8.,   8.,   8.],
        [-16., -16.,  16.,  16.],
        [-32., -32.,  32.,  32.],
        [ -6., -11.,   6.,  11.],
        [-11., -23.,  11.,  23.],
        [-23., -45.,  23.,  45.]])

2.1.2 计算中心点相加cell_anchor

  1. 使用基础cell_anchor,A的所有点坐标,以及和原图的缩放关系,计算所有anchor.
    cell_anchor为(9x4),即A的每个点坐标都有9和候选框
    A尺寸为(200x272),即共有54400个坐标
    即最终结果应该是(54400x9x4) = (489600x4)的tensor
    其中4是由cell_anchor+缩放过比例的A坐标得到
    简化代码如下
import torch

from test2 import generate_anchors

if __name__ == '__main__':
    grid_height = 200
    grid_width = 272
    # 和原图的缩放比例
    stride_height = torch.tensor(4)
    stride_width = torch.tensor(4)
    device = torch.device("cpu")
    # 依照缩放比例,即步幅,将特征图像素点缩放到原始图上
    shifts_x = torch.arange(0, grid_width, dtype=torch.int32, device=device) * stride_width
    shifts_y = torch.arange(0, grid_height, dtype=torch.int32, device=device) * stride_height
    # 将(200x272)=54400个中心点数据(cx,cy) 变为54400x4的数据,即54400x(x1,y1,x2,y2)=(cx,cy,cx,cy)
    shift_y, shift_x = torch.meshgrid(shifts_y, shifts_x, indexing="ij")
    shift_x = shift_x.reshape(-1)
    shift_y = shift_y.reshape(-1)
    # shifts表示共有54400个中心点,每个中心点的坐标为(x1,y1,x2,y2);
    shifts = torch.stack((shift_x, shift_y, shift_x, shift_y), dim=1)
    # 获取cell_anchors(9,4) 表示每个中心点都有9中可能,坐标距离(x1,y1,x2,y2)偏移分别是(a1,b1,a2,b2)(例如:[-11.,  -6.,  11.,   6.])
    size = (16, 32, 64)
    aspect_ratio = (0.5, 1.0, 2.0)
    cell_anchor = generate_anchors(size, aspect_ratio)
    # 只需要将shifts(54400x4) + cell_anchors(9x4)相加即可的得到结果(489600x4),即shifts,cell_anchors的第一维分别扩展9和54400倍即可
    anchor = shifts.view(-1, 1, 4) + cell_anchor.view(1, -1, 4)
    # 将 54400x9x4 转为 489600x4 ,代表共有489600的anchor,每个含1个坐标(x1,y1,x2,y2)
    anchor = anchor.view(-1, 4)
    print(anchor.shape)
  1. 对5个特征图执行相同操作,注意size的选用.在不同特征图上,size可以设置为不同形状的不同值,以适应在不同特征图尺寸的表现.
  2. 分辨率越大的特征图,缩放比例就越小,中心点间距小,故而size的设定就应越小
  3. 分辨率越小的特征图,缩放比例就越大,中心点间距大,故而size的设定就应越大
  4. 例如size可以设定为((16, 32, 64),(32, 64, 128),(64,128,256),(128,256,512),(256,512,1024)),当然也可以设定为一样的,看需求如何,同理aspect_ratio 也可以按照实际的需求自行设定,比如存在细长棍状的物体时,可以比例设置为(0.25, 0.5, 1.0, 2.0, 4.0)
  5. 按照每个scles=3.aspect_ratio =3来计算.总共获得个尺寸候选框分别为,最后加起来就好
    200 × 272 × 9 = 489600 100 × 136 × 9 = 122400 50 × 68 × 9 = 30600 25 × 34 × 9 = 7650 13 × 19 × 9 = 2223 s u m ( ) = 652473 \begin{align} 200 \times272 \times9&=489600\\ 100 \times136 \times9&=122400\\ 50 \times68 \times9&=30600\\ 25 \times34 \times9&=7650\\ 13 \times19 \times9&=2223\\ sum()&=652473 \end{align}\\ 200×272×9100×136×950×68×925×34×913×19×9sum()=489600=122400=30600=7650=2223=652473

2.2 计算每个anchor分类和回归值

这么多的候选框,首先进行一次判断,过绝大部分无用的anchor.
在这里插入图片描述

  1. 需要判断这个anchor是否保留,即判断他存在物体的可能性有多高,例如设定可能性大于70%即保留.
  2. 那这个存在物体就是一个二分类问题,即上图的2k scores
  3. 预测框和实际物体边框(GT)的差距有多少,就是一个回归问题.即上图的4k coordinates
  4. 注意,这个是需要对每一个候选框进行的操作

方案:
使用所有的特征图作为输入,创建一个模型RPNHead,计算预测,返回2个结果,分类结果与回归结果
即:使用一个3x3的卷积处理后分别接上分类卷积和回归卷积,输出通道数量应为每个点的可能的anchor数,即K,计算应得到2个结果

objectness(252473x1) = (
200x272x9x1+
100x136x9x1

)
pred_bbox_deltas (252473x4)= (
200x272x9x4+
100x136x9x4

)
注意 pred_bbox_deltas 并不是直接返回的直接边界值,而是和GT的偏差,因为图像尺寸不同原因,且是参数化后值,故有_deltas后缀
模型代码如下
调用栈
GeneralizedRCNN.forward->
RegionProposalNetwork.forward->
RPNHead.forward

class RPNHead(nn.Module):
    def __init__(self, in_channels: int, num_anchors: int, conv_depth=1) -> None:
        super().__init__()
        convs = []
        for _ in range(conv_depth):
            convs.append(Conv2dNormActivation(in_channels, in_channels, kernel_size=3, norm_layer=None))
        self.conv = nn.Sequential(*convs)
        self.cls_logits = nn.Conv2d(in_channels, num_anchors, kernel_size=1, stride=1)
        self.bbox_pred = nn.Conv2d(in_channels, num_anchors * 4, kernel_size=1, stride=1)

        for layer in self.modules():
            if isinstance(layer, nn.Conv2d):
                torch.nn.init.normal_(layer.weight, std=0.01)  # type: ignore[arg-type]
                if layer.bias is not None:
                    torch.nn.init.constant_(layer.bias, 0)  # type: ignore[arg-type]

		# 省略若干方法
    def forward(self, x: List[Tensor]) -> Tuple[List[Tensor], List[Tensor]]:
        logits = []
        bbox_reg = []
        for feature in x:
            t = self.conv(feature)
            logits.append(self.cls_logits(t))
            bbox_reg.append(self.bbox_pred(t))
        return logits, bbox_reg

2.3过滤无用候选框

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何快速搭建nginx服务

华子目录 nginx简介概念特点nginx框架nginx关键工作机制 nginx正向代理功能nginx反向代理功能nginx反向代理的工作流程代理本质 nginx负载均衡部署nginx常用命令systemctl系列nginx自带命令 nginx配置文件主配置文件/etc/nginx/nginx.conf内容结构模块分析配置分析注意示例 ngi…

Android创建快捷方式到桌面

效果图 参考 https://blog.51cto.com/u_16175498/8811197https://blog.51cto.com/u_16175498/8811197 权限 <uses-permission android:name"com.android.launcher.permission.INSTALL_SHORTCUT" /> 实现 if (Build.VERSION.SDK_INT > Build.VERSION_C…

【已解决】Python Selenium chromedriver Pycharm闪退的问题

概要 根据不同的业务场景需求&#xff0c;有时我们难免会使用程序来打开浏览器进行访问。本文在pycharm中使用selenium打开chromedriver出现闪退问题&#xff0c;根据不断尝试&#xff0c;最终找到的问题根本是版本问题。 代码如下 # (1) 导入selenium from selenium import …

C++ stack、queue以及deque

1、stack和queue常用接口 严格来说栈和队列的实现是容器适配器 1、常用接口&#xff1a; 栈&#xff1a;top、push、pop、size、emptystack - C Reference (cplusplus.com) 队列&#xff1a;top、push、pop、swap、size、emptyqueue - C Reference (cplusplus.com) 2、deque&a…

Android手势识别面试问题及回答

问题 1: 如何在Android中实现基本的手势识别&#xff1f; 答案: 在Android中&#xff0c;可以通过使用GestureDetector类来实现基本的手势识别。首先需要创建一个GestureDetector的实例&#xff0c;并实现GestureDetector.OnGestureListener接口来响应各种手势事件&#xff0c…

ubuntu安装mysql时候修改root密码

前情&#xff1a; 使用set password for rootlocalhost ‘passwd’&#xff1b; set password for ‘root’‘localhost’‘passwd’&#xff1b; update user set passwordpassword(‘passwd’) where user‘root’ and host ‘localhost’; flush privileges; 以上方法均报…

定制开发AI智能名片商城小程序:玩转积分制度的成功案例

在数字化浪潮席卷而来的今天&#xff0c;企业营销方式不断创新&#xff0c;力求在众多竞争对手中脱颖而出。其中&#xff0c;积分制度以其直观、有效的特点&#xff0c;成为了众多企业的营销利器。某时尚品牌“潮流前线”便是其中的佼佼者。他们通过定制一款AI智能名片商城小程…

德国著名自动化公司Festo设计了一款仿生蜜蜂,仅重34g,支持多只蜜蜂编队飞行!...

德国著名的气动元件研发及自动化解决方案供应商Festo公司近日展示了一款仿生蜜蜂&#xff08;BionicBee&#xff09;&#xff0c;重量只有34g&#xff0c;却完全可以实现自主飞行&#xff0c;还支持多只相同的蜜蜂机器人编队飞行。 BionicBee 重约 34 克&#xff0c;长 22 厘米…

二叉树的前序,中序,后序遍历

二叉树可以分为左子树&#xff0c;右子树和根节点。同时左子树和右子树又可以分为新的左子树和右子树加上新的根节点&#xff0c;以此类推。 二叉树的前序&#xff0c;中序&#xff0c;后序遍历也叫前根遍历&#xff0c;中根遍历&#xff0c;后根遍历或者前序遍历&#xff0c;…

【Vue 2.x】学习vue之三路由

文章目录 Vue三路由第十章1、vue中的路由vue的应用分为a、多页面应用b、单页面应用 2、路由的基本应用1、基础2、使用3、加载 3、vue组件的分类1、普通组件2、路由组件 4、路由的嵌套5、路由传递Query参数1、拼接参数传递2、路由传递对象 6、简化路由1、命名路由 7、parms传递参…

控制台主机不能运行,切换终端实现RPG运行

鄙人转载&#xff0c;主要是移植过程中使用小熊猫C2.25.1 过程中&#xff0c;字符集不同&#xff0c;导致某些空格 从bilibili专栏粘贴导致出现符号不匹配&#xff0c;但是编辑器不能替换 用原来的devc 5.11 发现问题&#xff0c;读出额外的英文&#xff1f; 使用文件替换&…

C语言贪吃蛇项目

今天给大家带来一款简单的贪吃蛇游戏&#xff0c;一起随我来看看吧 游戏效果&#xff1a; 实现基本的功能&#xff1a; • 贪吃蛇地图绘制 • 蛇吃⻝物的功能&#xff1a;&#xff08;上、下、左、右⽅向键控制蛇的动作&#xff09; • 蛇撞墙死亡 • 蛇撞⾃⾝死亡 • 计算得分…

5.C++动态内存管理(超全)

目录 1 .C/C 内存分布 2. C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free 3. C内存管理方式 3.1 new/delete操作内置类型 3.2 new和delete操作自定义类型 3.3 operator new函数 3.4 定位new表达式(placement-new) &#xff08;了解&#xff09; 4. 常…

【ARMv8/v9 系统寄存 3 -- system counter CNTPCT_EL0】

文章目录 ARMv8/v9 system countersystem counter读取函数实现 ARMv8/v9 system counter 所有使用Arm处理器的系统中都会包含一个标准化的通用定时器&#xff08;Generic Timer&#xff09;框架。这个通用定时器系统提供了一个系统计数器&#xff08;System Counter&#xff0…

ps科研常用操作,制作模式图 扣取想要的内容元素photoshop

复制想要copy的图片&#xff0c; 打开ps---file-----new &#xff0c;ctrolv粘贴图片进入ps 选择魔棒工具&#xff0c;点击想要去除的白色区域 然后&#xff0c;cotrol shift i&#xff0c;反选&#xff0c; ctrol shiftj复制&#xff0c;复制成功之后&#xff0c;一定要改…

UnityWebGL获取话筒实时数据

看了木子李大佬的数字人https://digital.lkz.fit/之后&#xff0c;我也想搞一个&#xff0c;于是开始研究起来&#xff0c;先从WebGL录音开始&#xff0c;一共试了三个插件&#xff0c;个个都有问题…… 1、UnityWebGLMicrophone 用起来没啥问题&#xff0c;但是只能录音&#…

大型企业总分支多区域数据传输,效率为先还是安全为先?

大型企业为了业务拓展需要&#xff0c;会在全国乃至全球各地设立分公司和办事机构&#xff0c;以便更好地处理当地事务&#xff0c;并进行市场的开拓和客户维护&#xff0c;此时&#xff0c;企业内部就衍生出了新的业务需求&#xff0c;即多区域数据传输。 多区域很难准确定义&…

【大语言模型LLM】-基于ChatGPT搭建客服助手(1)

&#x1f525;博客主页&#xff1a;西瓜WiFi &#x1f3a5;系列专栏&#xff1a;《大语言模型》 很多非常有趣的模型&#xff0c;值得收藏&#xff0c;满足大家的收集癖&#xff01; 如果觉得有用&#xff0c;请三连&#x1f44d;⭐❤️&#xff0c;谢谢&#xff01; 长期不…

2024年五一杯高校数学建模竞赛(A题)|钢板切割问题 | 建模解析,小鹿学长带队指引全代码文章与思路

我是鹿鹿学长&#xff0c;就读于上海交通大学&#xff0c;截至目前已经帮200人完成了建模与思路的构建的处理了&#xff5e; 本篇文章是鹿鹿学长经过深度思考&#xff0c;独辟蹊径&#xff0c;通过路径优化解决钢板切割问题。结合贪心算法&#xff0c;Floyd-Warshall等多元算法…

STM32G030F6P6TR 芯片TSSOP20 MCU单片机微控制器芯片

STM32G030F6P6TR 在物联网&#xff08;IoT&#xff09;设备中的典型应用案例包括但不限于以下几个方面&#xff1a; 1. 环境监测系统&#xff1a; 使用传感器来监测温度、湿度、气压等环境因素&#xff0c;并通过无线通信模块将数据发送到中央服务器或云端平台进行分析和监控。…
最新文章