Faster-RCNN代码解读5:主要文件解读-上

news2024/11/25 4:21:29

Faster-RCNN代码解读5:主要文件解读-上

前言

​ 因为最近打算尝试一下Faster-RCNN的复现,不要多想,我还没有厉害到可以一个人复现所有代码。所以,是参考别人的代码,进行自己的解读。

代码来自于B站的UP主(大佬666),其把代码都放到了GitHub上了,我把链接都放到下面了(应该不算侵权吧,毕竟代码都开源了_):

b站链接:https://www.bilibili.com/video/BV1of4y1m7nj/?vd_source=afeab8b555e5eb1bfa1e7f267262cbf2

GitHub链接:https://github.com/WZMIAOMIAO/deep-learning-for-image-processing

目的

​ 其实UP主已经做了很好的视频讲解了他的代码,只是有时候我还是喜欢阅读博客来学习,另外视频很长,6个小时,我看的时候容易睡着_,所以才打算写博客记录一下学习笔记。

目前完成的内容

第一篇:VOC数据集详细介绍

第二篇:Faster-RCNN代码解读2:快速上手使用

第三篇:Faster-RCNN代码解读3:制作自己的数据加载器

第四篇:Faster-RCNN代码解读4:辅助文件解读

第五篇: Faster-RCNN代码解读5:主要文件解读-上(本文)

目录结构

文章目录

    • Faster-RCNN代码解读5:主要文件解读-上
      • 1. 前言:
      • 2. boxes.py文件解读:
        • 2.1 nms函数:
        • 2.2 batched_nms函数:
        • 2.3 remove_small_boxes函数:
        • 2.4 clip_boxes_to_image函数:
        • 2.5 box_area函数:
        • 2.6 box_iou函数:
      • 3. image_list.py文件解读:
      • 4. transform.py文件解读:
        • 4.1 GeneralizedRCNNTransform类:
        • 4.2 _resize_image函数:
        • 4.3 resize_boxes函数:
      • 5. 总结:

1. 前言:

​ 本文主要介绍Faster-RCNN项目下的network_files文件夹下的内容。这个文件夹是该项目的核心文件夹,主要就是关于Faster-RCNN构建。

​ 由于内容比较多,所以我打算分为上中下三篇来讲解。本篇主要讲解的文件为:

boxes.py
image_list.py
transform.py

​ 在讲解文件之前,要说明一下,在代码中经常出现的一个现象:

在这里插入图片描述

​ 就是相同功能的函数,有时候会出现两次,其中的一个上面会带有@torch.jit.unusedonnx表示将模型转为通用开放的神经网络模型,此模型与pytorch、tensorflow等框架无关,两者也是可以互相转换的。

2. boxes.py文件解读:

​ 这个文件主要的作用是:里面定义许多和边界框有关的功能函数,比如nms、计算面积、计算iou等等。下面一一来讲解。

2.1 nms函数:

​ 这个函数是对一张图像的边界框进行NMS处理。

​ 输入参数:

参数意义
boxes图像的边界框值,格式为Tensor[N, 4]
其中N表示N个边界框,4表示左上角和右下角的4个坐标值
scores上述边界框对应的置信度值,格式为Tensor[N]
iou_threshold阈值,用于NMS处理时判断的依据,即丢弃所有IOU大于阈值的框

​ 这个函数的内容只有一句,即:

return torch.ops.torchvision.nms(boxes, scores, iou_threshold)

可见,这个函数其实调用的时pytorch官方实现的nms方法。

2.2 batched_nms函数:

​ 这个函数是以批处理的形式进行NMS操作

​ 输入参数有三个和上面的相同,就不多说了,只有一个新增的参数,即:

idxs,其是每个边界框的类别索引值,格式为Tensor[N]

​ 函数的内容如下(看注释):

# boxes.numel()返回元素个数
if boxes.numel() == 0:
    # 如果boxes中没有值了,就可以结束函数了
    return torch.empty((0,), dtype=torch.int64, device=boxes.device)

# 获取所有boxes中最大的坐标值(xmin, ymin, xmax, ymax)
max_coordinate = boxes.max()

# 为每一个类别/每一层生成一个很大的偏移量
# 这里的to只是让生成tensor的dytpe和device与boxes保持一致
offsets = idxs.to(boxes) * (max_coordinate + 1)
# boxes加上对应层的偏移量后,保证不同类别/层之间boxes不会有重合的现象
boxes_for_nms = boxes + offsets[:, None]
# 进行nms操作
keep = nms(boxes_for_nms, scores, iou_threshold)
return keep

2.3 remove_small_boxes函数:

​ 这个函数的主要作用是:删除那些宽高小于阈值的边界框。

​ 输入参数:

参数意义
boxes:边界框对象
min_size :宽高最小值,即阈值

​ 函数内容如下:

ws, hs = boxes[:, 2] - boxes[:, 0], boxes[:, 3] - boxes[:, 1]  # 预测boxes的宽和高
# keep = (ws >= min_size) & (hs >= min_size)  # 当满足宽,高都大于给定阈值时为True
# torch.logical_and即逻辑与,当都为真时才为真
# torch.ge(value,threshold)逐元素比较,当value大于threshold时为True
keep = torch.logical_and(torch.ge(ws, min_size), torch.ge(hs, min_size))
keep = torch.where(keep)[0]
return keep

2.4 clip_boxes_to_image函数:

​ 这个函数的功能是:针对那些坐标超过图像范围的预测框进行裁剪,并将越界的坐标定义为图像边界。

​ 输入参数:

参数意义
boxes预测边界框,和上面的都一样
size图像的宽和高,格式为Tuple[height, width]

​ 函数内容如下,看注释:

# 获取boxes的维度
dim = boxes.dim()
# 获取box的坐标信息
# boxes_x = [N,2],boxes_y=[N,2]
boxes_x = boxes[..., 0::2]  # x1, x2
boxes_y = boxes[..., 1::2]  # y1, y2
# 获取图像宽高
height, width = size

# torchvision._is_tracing 与 torch._C._get_tracing_state()
# 是否为JIT跟踪模型,具体我也不是很懂,涉及到了torch的源码内容
if torchvision._is_tracing():
    # 首先和图像左上角(0,0)坐标比较,如果边界框x轴比左上角小,则取0
    boxes_x = torch.max(boxes_x, torch.tensor(0, dtype=boxes.dtype, device=boxes.device))
    # 然后和图像宽度比,即如果坐标比宽度大,则直接取宽度的值
    boxes_x = torch.min(boxes_x, torch.tensor(width, dtype=boxes.dtype, device=boxes.device))
    # y同x原理一样
    boxes_y = torch.max(boxes_y, torch.tensor(0, dtype=boxes.dtype, device=boxes.device))
    boxes_y = torch.min(boxes_y, torch.tensor(height, dtype=boxes.dtype, device=boxes.device))
else:
	# boxes_x.clamp : 返回限制范围内的数据
    boxes_x = boxes_x.clamp(min=0, max=width)   # 限制x坐标范围在[0,width]之间
    boxes_y = boxes_y.clamp(min=0, max=height)  # 限制y坐标范围在[0,height]之间

# 将过滤后的x、y拼接在一起,dim指定维度
clipped_boxes = torch.stack((boxes_x, boxes_y), dim=dim)
return clipped_boxes.reshape(boxes.shape)

2.5 box_area函数:

​ 这个函数的作用,通过坐标来计算边界框的面积,只要用于计算IOU。

​ 输入参数只有一个,即boxes对象。

​ 然后代码也很简单,只有一句:

return (boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1])

​ boxes的shape为[N,4],其中4表示左上角和右下角四个坐标值。面积,肯定为长乘宽,即x坐标差值和y坐标差值之积。

2.6 box_iou函数:

​ 这个函数的作用就是计算两个边界框的IOU值。

​ 输入参数:两个边界框对象,格式都为Tensor[N, 4]

​ 函数内容如下:

# 计算面积
area1 = box_area(boxes1)
area2 = box_area(boxes2)

# 寻找两个边界框中:
# 左上角最大的坐标 ---- 相交部分左上角
# 右下角最小的坐标 ---- 相交部分右下角
lt = torch.max(boxes1[:, None, :2], boxes2[:, :2])  # left-top [N,M,2]
rb = torch.min(boxes1[:, None, 2:], boxes2[:, 2:])  # right-bottom [N,M,2]

# 计算相交的值
wh = (rb - lt).clamp(min=0)  # [N,M,2]
inter = wh[:, :, 0] * wh[:, :, 1]  # [N,M]

# IOU = 交集 / 并集
# 交集 = 两个面积相交的部分
# 并集 = 两个面积相加 减去 交集
iou = inter / (area1[:, None] + area2 - inter)
return iou

​ 这里说一下,上面相交部分坐标为什么找最大和最小(自己画的图):

在这里插入图片描述

3. image_list.py文件解读:

​ 该文件只定义了一个ImageList类,目的是将图像尺寸变换前后的数据关联在一起,方便使用。

__init__方法

​ 输入参数:

参数意义
tensorspadding后的图像数据
image_sizespadding前的图像尺寸,格式为:list[tuple[int, int]]

​ 初始化方法很简单,就是把传入的参数定义为类变量:

self.tensors = tensors
self.image_sizes = image_sizes

to方法

​ 就是把数据放入指定设备中,和我们平时将模型放入指定设备的to方法类似。

4. transform.py文件解读:

​ 该文件的主要作用是:**定义数据的前处理和后处理方法。**前处理方法比如标准化处理、将尺寸不同的大小图片打包为一个batch等等。后处理方法比如将边界框映射回原尺寸上(因为为了不同尺寸图像可以打包在一起,改变了图像尺寸,所以需要映射回去)。另外,正是因为改变了图像尺寸,所以需要定义上面的ImageList类。

4.1 GeneralizedRCNNTransform类:

​ 首先,看GeneralizedRCNNTransform类,是该文件的核心部件。

__init__方法

​ 输入参数:

参数意义
min_size指定图像的最小边长范围
max_size指定图像的最大边长范围
image_mean指定图像在标准化处理中的均值
image_std指定图像在标准化处理中的方差

​ 代码内容很简单,首先判断输入的值是不是列表或者元组,如果不是,则转为元组:

if not isinstance(min_size, (list, tuple)):
	min_size = (min_size,)

​ 然后,把输入参数转为类变量即可:

self.min_size = min_size      # 指定图像的最小边长范围
self.max_size = max_size      # 指定图像的最大边长范围
self.image_mean = image_mean  # 指定图像在标准化处理中的均值
self.image_std = image_std    # 指定图像在标准化处理中的方差

normalize方法:

​ 这个方法的就是对图像进行标准化处理

​ 具体内容看注释:

# 获取数据类型和设备信息 cpu or gpu
dtype, device = image.dtype, image.device
# 均值 + 方差,转为tensor格式
mean = torch.as_tensor(self.image_mean, dtype=dtype, device=device)
std = torch.as_tensor(self.image_std, dtype=dtype, device=device)
# 进行标准化
# [:, None, None]: shape [3] -> [3, 1, 1],让维度和图像保持相同【channel、w、h】
return (image - mean[:, None, None]) / std[:, None, None]

resize方法:

​ 这个方法的作用就是将图片缩放到指定大小范围内,并同时缩放其boxes信息。

​ 传入的参数:

参数意义
image输入的图片
target输入图片的相关信息(包括bboxes信息)
这个就是我们定义dataset的时候返回的值之一

​ 首先,获取图像宽和高:

# image = [channel, height, width]
# 获取宽高
h, w = image.shape[-2:]

​ 判断处于训练状态还是验证状态,因为不同的状态传入的min_size参数格式不同:

# 判断是训练还是验证
if self.training:
    # 指定输入图片的最小边长,注意是self.min_size不是min_size
    # min_size: int ; self.min_size: tuple
    size = float(self.torch_choice(self.min_size))
else:
    # FIXME assume for now that testing uses the largest scale
    # 指定输入图片的最小边长,注意是self.min_size不是min_size
    size = float(self.min_size[-1])

​ 然后将图片送入_resize_image函数中(具体方法看下面的4.2内容,这里不说),改变图像的尺寸:

# if 条件不用管,两者的目的都是相同的
if torchvision._is_tracing():
    image = _resize_image_onnx(image, size, float(self.max_size))
else:
	image = _resize_image(image, size, float(self.max_size))

​ 缩放完图片后,肯定需要缩放一下图片对应的box了。但在此之前,首先判断target是否为空,为空表示这个图像根本没有对象,直接跳过即可。否则,就可以缩放box信息了:

# 如果target为空,则结束函数
if target is None:
    return image, target

# 从target获取boxes值
bbox = target["boxes"]
# 根据图像的缩放比例来缩放bbox
# 原来: [h, w] ; 缩放的要求: image.shape[-2:]
bbox = resize_boxes(bbox, [h, w], image.shape[-2:])
# 添加缩放后的值
target["boxes"] = bbox

​ 其中,缩放box的函数叫做resize_boxes,其接收三个参数:

待缩放的box
缩放前尺寸
缩放后尺寸

​ 该函数内容看下面的4.3内容。

batch_images方法:

​ 该方法的作用是**将大小不同的图像打包一个batch。**这里值得一提的是,在图像分类中,我们的输入图像大小是固定的,因此可以直接打包为一个batch,不需要我们单独实现,而在检测中,输入图像大小不同,因此需要将图像变为同一尺寸再打包。

​ 这里,作者选择的打包思路为:获取一批图像的最大尺寸,以它为模板,其余图像与它左上角对齐,然后其余图像与最大图像相比少的部分填充为0。见下图示意:

在这里插入图片描述

​ 作者就是按照这个思路进行实现的:

# 分别计算一个batch中所有图片中的最大channel(3), height, width
max_size = self.max_by_axis([list(img.shape) for img in images])

# 将刚刚获得的size取离32最近的整数倍值,一种玄学,就是32的倍数更利于训练,比如2的n次方
stride = float(size_divisible)
# 将height向上调整到stride的整数倍
max_size[1] = int(math.ceil(float(max_size[1]) / stride) * stride)
# 将width向上调整到stride的整数倍
max_size[2] = int(math.ceil(float(max_size[2]) / stride) * stride)

# 构建训练batch:[batch, channel, height, width]
batch_shape = [len(images)] + max_size

# 创建shape为batch_shape且值全部为0的tensor
# images[0]中的索引无所谓,只是利用new_full方法,即以batch_shape为shape创建一个tensor,其值全为0
batched_imgs = images[0].new_full(batch_shape, 0)
for img, pad_img in zip(images, batched_imgs):
    # 将输入images中的每张图片复制到新的batched_imgs的每张图片中,对齐左上角,保证bboxes的坐标不变
    # 这样保证输入到网络中一个batch的每张图片的shape相同
    # 0 : img.shape[0],channel维度 ; : img.shape[1] : 图像高 ; : img.shape[2],图像高
    pad_img[: img.shape[0], : img.shape[1], : img.shape[2]].copy_(img)

​ 其中,还有一个方法就是max_by_axis,见下面。

max_by_axis方法:

​ 这个方法的作用,就是找出图像中通道数、宽、高最大的值。当然,通道数都是一样的,为3。

​ 它这个方法用两层循环实现,实现的思路有点类似排序算法中的冒泡排序,就是先找一个临时变量作为最大值,然后不断比较,谁大谁替代:

# 将索引为0的值给maxes
maxes = the_list[0]
# 从索引为1的开始遍历
for sublist in the_list[1:]:
    # 将当前索引的值与前一个最大的值比较,如果比它大,则重新设置最大值
    for index, item in enumerate(sublist):
        maxes[index] = max(maxes[index], item)
# 返回最大值
return maxes

forward方法:

​ 这个方法是该类的前向传播过程

​ 输入参数:

参数意义
images输入图像列表(一个batch的数量)
targets图像对应的信息

​ 首先,将输入图像参数转为列表:

# 获取图像列表
images = [img for img in images]

​ 然后,遍历图像,对每个图像进行标准化和缩放处理:

# 遍历图像
for i in range(len(images)):
    # 复制一份图像
    image = images[i]
    # 判断target是否为空,
    target_index = targets[i] if targets is not None else None

    # 判断图片维度是否为3
    if image.dim() != 3:
        raise ValueError("images is expected to be a list of 3d tensors "
                         "of shape [C, H, W], got {}".format(image.shape))
	# 对图像进行标准化处理
    image = self.normalize(image)
    # 对图像和对应的bboxes缩放到指定范围
    image, target_index = self.resize(image, target_index)
    # 替换图像
    images[i] = image
    # 判断图像是否为空
    if targets is not None and target_index is not None:
        # 替换target,即替换resize后的box
        targets[i] = target_index

​ 接着,将图片打包为一个batch,而由于最后需要将box映射回原图像,因此需要记录resize后的尺寸(尺寸可能不同)和打包后的尺寸(尺寸都相同了):

# 记录resize后的图像尺寸。此时图片的尺寸可能不同
image_sizes = [img.shape[-2:] for img in images]
# 将images打包成一个batch,此时图像大小尺寸相同了
images = self.batch_images(images)
# 指定image_list格式
image_sizes_list = torch.jit.annotate(List[Tuple[int, int]], [])

# 遍历image_sizes
for image_size in image_sizes:
    # 如果image_size长度为2,报错
    assert len(image_size) == 2
    # 将值打包为一个tuple,传入一个list中,符合image_sizes_list格式
    image_sizes_list.append((image_size[0], image_size[1]))

# ImageList类:将两者关联一起
#  images :打包后一个独立的tensor变量
# image_sizes_list: 打包前的图像尺寸信息
# 主要方便后期绘图,因为图像原尺寸就在xml文件中,所以需要记录一下resiz后的尺寸和打包后的尺寸
image_list = ImageList(images, image_sizes_list)
# 得到即将输入backbone的信息
return image_list, targets

postprocess方法:

​ 这个方法就是对网络的预测结果进行后处理(主要将bboxes还原到原图像尺度上)

​ 输入参数:

参数意义
result网络的预测结果(bbox信息和类别信息)
image_shapes图像预处理缩放后(即打包处理后)的尺寸
original_image_sizes图像的原始尺寸(即缩放到指定范围后的尺寸)

​ 首先,判断是否为训练模式,如果是,则不需要后处理:

# 如果为训练模式,不需要后处理
if self.training:
	return result

​ 然后,进行缩放映射,还是使用resize_boxes方法,这个很简单:

# 遍历每张图片的预测信息,将boxes信息还原回原尺度
for i, (pred, im_s, o_im_s) in enumerate(zip(result, image_shapes, original_image_sizes)):
    # 获取预测结果的boxes信息
    boxes = pred["boxes"]
    # 通过resize_boxes(box信息,原始尺寸,要转为的尺寸)
    boxes = resize_boxes(boxes, im_s, o_im_s)  # 将bboxes缩放回原图像尺度上
    # 替换
    result[i]["boxes"] = boxes

4.2 _resize_image函数:

​ 作用:改变图像大小尺寸。

​ 输入参数:

参数意义
image输入图像
self_min_size允许的最小尺寸
self_max_size允许的最大尺寸

​ 这个函数的实现思路为:首先获取图像宽高,并设定宽高中的最大值和最小值;接着,用允许的最小尺寸除以图像宽高的最小值,得到缩放因子;如果图像最大尺寸乘以缩放因子小于允许的最大尺寸,那么可以进行图像的缩放工作了,否则还需要修改缩放因子。

​ 代码具体内容看注释:

# 获取图像原尺寸信息
im_shape = torch.tensor(image.shape[-2:])
min_size = float(torch.min(im_shape))    # 获取高宽中的最小值
max_size = float(torch.max(im_shape))    # 获取高宽中的最大值
scale_factor = self_min_size / min_size  # 根据指定最小边长和图片最小边长计算缩放比例

# 如果使用该缩放比例计算的图片最大边长大于指定的最大边长
if max_size * scale_factor > self_max_size:
    # 重新指定缩放因子
    scale_factor = self_max_size / max_size  # 将缩放比例设为指定最大边长和图片最大边长之比

# interpolate利用插值的方法缩放图片
# image[None]操作是在最前面添加batch维度[C, H, W] -> [1, C, H, W]
# 原因:bilinear(双线性插值)只支持4D Tensor
image=torch.nn.functional.interpolate(image[None],scale_factor=scale_factor,mode="bilinear",recompute_scale_factor=True,align_corners=False)[0]

4.3 resize_boxes函数:

​ 作用:将boxes参数根据图像的缩放情况进行相应缩放

​ 输入参数:

参数意义
boxes待缩放的box
original_size图像缩放前的尺寸
new_size图像缩放后的尺寸

​ 首先,获取宽、高的缩放因子:

# 获取宽度和高度方向的缩放因子
# 下面的/为除号
ratios = [
    torch.tensor(s, dtype=torch.float32, device=boxes.device) /
    torch.tensor(s_orig, dtype=torch.float32, device=boxes.device)
    for s, s_orig in zip(new_size, original_size)
]
ratios_height, ratios_width = ratios

​ 接着,获取box的四个坐标信息,并乘以缩放因子,得到新的坐标值,最后再将其拼接返回即可:

# boxes [minibatch, 4],
# boxes.unbind(1)表示在4这个维度展开,得到坐标值
xmin, ymin, xmax, ymax = boxes.unbind(1)
# 缩放
xmin = xmin * ratios_width
xmax = xmax * ratios_width
ymin = ymin * ratios_height
ymax = ymax * ratios_height
# 将坐标拼接,然后返回去
return torch.stack((xmin, ymin, xmax, ymax), dim=1)

5. 总结:

​ 从这一篇开始就是讲解Faster-RCNN的具体架构实现了,本篇主要讲解了其中的辅助文件功能和作用。其中比较重要的文件是transform.py

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

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

相关文章

算法模板(3):搜索(2):bfs与图论基础

bfs 在搜索题中,一般来讲,bfs和dfs都有一个最优选择。 基础bfs 走迷宫 注:这个模板具有还原路径的功能。其实,还可以反向搜(从终点走到起点),就不用 reverse数组了。其实,bfs是不…

Qt Quick - Dialog

Dialog使用总结一、概述二、对话框标题和按钮三、模态对话框四、非模态的对话框五、standardButtons 属性一、概述 对话框是一种弹出式对话框,主要用于短期任务和与用户的简短交流。与ApplicationWindow和Page类似,Dialog被组织成三个部分:header、cont…

PyTorch深度学习实战 | 基于线性回归、决策树和SVM进行鸢尾花分类

鸢尾花数据集是机器学习领域非常经典的一个分类任务数据集。它的英文名称为Iris Data Set,使用sklearn库可以直接下载并导入该数据集。数据集总共包含150行数据,每一行数据由4个特征值及一个标签组成。标签为三种不同类别的鸢尾花,分别为&…

【AI帮我写代码,上班摸鱼不是梦】手摸手图解CodeWhisperer的安装使用

IDEA插件 除了借助ChatGPT通过问答的方式生成代码,也可以通过IDEA插件在写代码是直接帮助我们生成代码。 目前,IDEA插件有CodeGeeX、CodeWhisperer、Copilot。其中,CodeGeeX和CodeWhisperer是完全免费的,Copilot是收费的&#x…

数据分析:麦当劳食品营养数据探索并可视化

系列文章目录 作者:i阿极 作者简介:Python领域新星作者、多项比赛获奖者:博主个人首页 😊😊😊如果觉得文章不错或能帮助到你学习,可以点赞👍收藏📁评论📒关注…

c++模板整理

目录 一.泛型编程​​​​​​​ 二.函数模板 2.1 函数模板概念 2.2函数模板格式 2.3 函数模板的原理 2.4 函数模板的实例化 2.5 模板参数的匹配原则 三.类模板 3.1 类模板的定义格式 3.2 类模板的实例化 3.3模板类 一.泛型编程​​​​​​​ 如何实现一个通用的交…

【前端之旅】快速上手Echarts

一名软件工程专业学生的前端之旅,记录自己对三件套(HTML、CSS、JavaScript)、Jquery、Ajax、Axios、Bootstrap、Node.js、Vue、小程序开发(Uniapp)以及各种UI组件库、前端框架的学习。 【前端之旅】Web基础与开发工具 【前端之旅】手把手教你安装VS Code并附上超实用插件…

「高并发业务必读」深入剖析 Java 并发包中的锁机制

故事 程序员小张: 刚毕业,参加工作1年左右,日常工作是CRUD 架构师老李: 多个大型项目经验,精通各种屠龙宝术; 小张和老李一起工作已有数月,双方在技术上也有了很多的交流,但是却总是…

GB28181 协议 SIP

2、注册信令 2.1基本注册 2.1.1 抓包过程 2.1.2 详细步骤 2.1.2.1、REGISTER REGISTER sip:34020000002000000001192.168.9.186:15060 SIP/2.0Via: SIP/2.0/TCP 192.168.9.186:42860;rport;branchz9hG4bK1557586049From: <sip:30514805331320000140192.168.9.186:5060>…

手写Spring框架-前奏-反射获取Annotation

目录 所谓反射 反射机制的作用 反射依赖reflect和Class 反射依赖的Class Class类的特点 获取Class对象的三种方式 获取类的构造方法并使用 获取类的成员变量并使用 获取类的成员方法并使用 问题引入 解析类的注解 解析成员变量的注解标签 解析方法上的注解 注解获…

Java类加载

类加载的时机 一个类型从被加载到虚拟机内存中开始&#xff0c;到卸载出内存为止&#xff0c;它的整个生命周期将会经历加载、验证、准备、解析、初始化、使用和卸载七个阶段。其中验证、准备、解析三个阶段统称为连接。 图中加载、验证、准备、初始化和卸载这五个阶段的顺序是…

CDGP数据治理专家认证含金量如何?值得考一个吗?

CDGP&#xff08;Certified Data Governance Professional&#xff09;数据治理专家认证的含金量非常高。该认证证明了持有人拥有数据治理方面的专业知识和技能&#xff0c;能够有效地管理和保护组织的数据资产。 CDGP认证考试内容涵盖数据治理的各个方面&#xff0c;包括数据…

看这家在线教育企业如何通过DHTMLX Scheduler,实现培训管理系统优化

“我们公司目前有一套培训管理系统&#xff0c;用于管理培训学员。目前学员越来越多&#xff0c;老旧的系统已经没法满足需求&#xff0c;导致我们经常需要手动记录学员出勤培训情况&#xff0c;除此之外&#xff0c;系统课程安排只展示时间&#xff0c;没法展示诸如主题&#…

macOS Big Sur 11.7.6 (20G1231) 正式版 ISO、PKG、DMG、IPSW 下载

本站下载的 macOS 软件包&#xff0c;既可以拖拽到 Applications&#xff08;应用程序&#xff09;下直接安装&#xff0c;也可以制作启动 U 盘安装&#xff0c;或者在虚拟机中启动安装。另外也支持在 Windows 和 Linux 中创建可引导介质。 2023 年 4 月 10 日&#xff08;北京…

【Vue全家桶】Pinia状态管理

【Vue全家桶】Pinia状态管理 文章目录【Vue全家桶】Pinia状态管理写在前面一、认识Pinia1.1 认识Pinia1.2 为什么使用Pinia&#xff1f;二、 Store2.1 定义Store2.2 Option对象2.3 setup函数2.4 使用定义的Store三、Pinia核心概念State3.1 定义State3.2 操作State3.3 使用选项式…

基于小生境粒子群优化算法的考虑光伏波动性的主动配电网有功无功协调优化(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密…

C语言基础——指针

文章目录一、指针1.指针的意义2.指针类型表示3.一些操作3.1打印1个变量地址3.2通过地址查看改地址的内容以及修改改地址的内容3.3操作某个空间 -- 4个字节,给他赋值为100&#xff0c;只知道该空间的地址0x8000 00004.指针变量的定义5.指针类型的大小6.指针变量的使用6.1 指针变…

python数据分析-matplotlib折线图知识总结01

python绘图库matplotlib的知识总结一.matplotlib是什么二.matplotlib的安装与导入三.matplotlib的常用函数四.matplotlib绘制折线图的使用方法1.设置图形大小2. 利用数据绘图3.调整x,y轴的刻度,旋转角度,显示描述信息,绘制网格,添加图例4.图形的样式5.绘制多条折线6.显示绘制的…

python知识记录:灵活使用numpy提高python数据分析效率!

NumPy是Python语言的一个第三方库&#xff0c;其支持大量高维度数组与矩阵运算。 作为python科学计算领域的三剑客之一&#xff0c;numpy在数据分析处理方面有着独特的魅力&#xff01; numpy模块的出现更多的是在数组处理的操作上面&#xff0c;并且支持和python常用的数据结…

Transformer在时序预测的应⽤第一弹——Autoformer

Transformer在时序预测的应⽤第一弹——Autoformer 原文地址&#xff1a;Autoformer: Decomposition Transformers with Auto-Correlation for Long-Term Series Forecasting&#xff08;NIPS 2021&#xff09; 做长时间序列的预测 Decomposition把时间序列做拆分&#xff0c…