基于骨骼关键点的动作识别(OpenMMlab学习笔记,附PYSKL相关代码演示)

news2024/11/20 15:51:44

一、骨骼动作识别

  • 骨骼动作识别视频理解领域的一项任务

1.1 视频数据的多种模态

在这里插入图片描述

  • RGB:使用最广,包含信息最多,从RGB可以得到Flow、Skeleton。但是处理需要较大的计算量

  • Flow:光流,主要包含运动信息,处理方式与RGB相同,一般用3D卷积

  • Audio:使用不多

  • Skeleton:骨骼关键点序列数据,即人体关键点坐标。与动作识别密切相关,信息密度大。

1.2 骨骼动作识别含义、条件、适用场景与优点

  • 关键点:通过关键点坐标进行动作识别,例如用Face Landmarks来识别表情,用Hand Landmarks来识别手势。
  • 骨骼动作识别前提条件

动作可以仅通过Skeleton序列来识别。

(1)动作类型要有合适的定义不适合Skeleton识别的例子:有许多个动作类别为eating sth,那就只能通过识别sth来进行动作识别,此时与Skeleton无关。、

(2)视频中要存在质量比较好的骨骼数据不适合Skeleton识别的例子:视频中没有人,或者只出现人的一小部分。

  • 适用于骨骼动作识别的场景

(1)训练数据稀缺、训练数据与测试数据存在较大bias的情况下,比如背景颜色或者环境等因素会导致RGB模型质量不佳。此时如果训练骨骼动作模型的话,就会有更好地泛化性。因为骨骼模型的话就只基于关键点坐标,从而进行动作识别。

(2)轻量型。可以用较小的计算量来进行Action Recognition的任务。

  • 骨骼动作识别的轻量型

使用Skeleton的计算量 < 使用RGB的计算量

这里的RGB(3D-CNN)方法可见MMAction2

在这里插入图片描述

1.3 如何获得Skeleton序列,作为骨骼动作识别模型的输入

(1)Kinect Sensor(RGBD)

在构建RGBD数据集时,用带有RGBD深度相机的Kinect传感器,来估计3D的Skeleton。但是得到的关键点坐标噪声较多,质量较差。

在这里插入图片描述

(2)★2D的人体姿态估计

通过估计出2D人体姿态关键点,来预测动作。

在这里插入图片描述

(3)3D的人体姿态估计——Motion Capture

使用动捕设备。这种情况一般使用的网络较少。

在这里插入图片描述

二、基于GCN的技术路线——ST-GCN++

2.1 GCN——Key Points

(1)GCN以骨骼关键点序列作为输入,输入形状为T × V × C

T:序列长度

V:一个Skeleton里关键点的个数

C: 维度。2D或者3D。

(2)存在多个人时,GCN取所有人特征的平均作为特征,如图所示

在这里插入图片描述

(3)GCN网络由多个GCN Block堆叠而成。类似于Bottleneck→ResNet。

2.2 ST-GCN结构

随着网络的加深,特征的通道数(C)不断加深,时序维度(T)上不断地降采样。

最后一层的输出会经过一个global average pooling得到输出特征。

最后,经过线性层分类器,得到分类结果。

在这里插入图片描述

  • 关于GCN Block的设计

GCN Block的组成:1个GCN Layer和1个TCN Layer

GCN Layer:使用系数矩阵A,对同一帧内部的关键点进行特征融合

TCN Layer: 使用1D卷积为每个关键点进行时序建模

(1)GCN Block的forward函数:

def forward(self, x, A = None):
    x = self.tcn(self.gcn(x, A)) + self.residual(x)  # GCN Layer和TCN Layer
    return self.relu(x)

(2)TCN Layer:是由时序维度上的1D卷积和一个bn组成

选择了size为9的大kernel,大量增加了计算量的消耗,增加了参数量。

class unit_tcn(nn.Module):
    def __init__(self,
                in_channels,
                out_channels,
                kernel_size = 9,
                stride = 1):
        super(unit_tcn, self).__init__()
        pad = (kernel_size - 1) // 2
        self.conv = nn.Conv2d(
        	in_channels,
        	out_channels,
            kernel_size = (kernel_size, 1),
            padding = (pad, 0),
            stride = (stride, 1))
        self.bn = nn.BatchNorm2d(out_channels)
        
    def forward(self, x):
        x = self.bn(self.conv(x))
        return x
        

(3)GCN Layer:

使用系数矩阵A对不同关键点进行特征融合。

系数矩阵A的来源:预定义的系数矩阵 × 数据驱动的稀疏mask

class unit_gcn(nn.Module):
    def __init__(self,
                in_channels,
                out_channels,
                s_kernel = 3):
        super().__init__()
        
        self.s_kernel = s_kernel
        self.conv = nn.Conv2d(
        	in_channels,
            out_channles * s_kernel,
            kernel_size = 1)
        
	def forward(self, x, A):
        # the shape of A is (s_kernel, V, V)
        assert A.size(0) == self.s_kernel     # 使用系数矩阵A对不同关键点进行特征融合
        x = self.conv(x)
        
        n, kc, t, v = x.size()
        x = x.view(n, self.s_kernel, kc // self.s_kernel, t, v)
        x = torch.einsum('nkctv, kvw->nctw', (x. A))
        return x.contiguous()
        

2.3 ST-GCN++

  • 在此基础上对GCN再进行改进的空间不大

(1)TCN的改进

舍弃的单个大kernel的1D卷积,换成使用多分支的时域卷积。采用多分支的结构可以增强网络的时序建模能力,同时还会节省计算量和参数量。

在这里插入图片描述

(2)GCN的改进

  • 老版本的GCN是用预定义的系数矩阵 × 数据驱动的稀疏mask得到系数矩阵A,通过A来进行不同关键点的特征融合。但是这种稀疏的结构不利于关键点的建模,因为只局限于相邻的关键点进行连接。

  • GCN改进后,使用预训练的系数矩阵A作为初始化,在训练过程中不断地训练和更新A。同时,在GCN中添加residual结构。

(3)数据预处理及超参的改进

  • 在ST-GCN中,构建输入过程中,将所有样本进行zeropad(0填充)到最长的长度(300帧),

  • 在ST-GCN++中,对每个骨骼的关键点序列进行了归一化操作,对于skeleton减去首帧的中心点,再将skeleton旋转至固定角度,得到更干净的输入。在构建输入的过程中,对每一个样本进行uniformsample(均匀采样)得到长为100帧的输入,节省了计算量,也会有较强的数据驱动。

  • 在超参设置方面,ST-GCN++使用了CosineAnnEaling Scheduler,及通过cos函数来调节学习率。此外,还使用了Large Weight Decay(更大的权重衰减),防止模型过拟合。

2.4 基于骨骼的动作识别模型精度对比

在这里插入图片描述

2.5 GCN的缺点

(1)鲁棒性:输入的扰动容易对 GCN 造成较大影响,使其难以处理关键点缺失或训练测试时使用骨骼数据存在分布差异(例如出自不同姿态提取器)等情形。

(2)兼容性:GCN 使用图序列表示骨架序列,这一表示很难与其他基于 3D-CNN 的模态(RGB, Flow 等)进行特征融合。

(3)可扩展性:GCN 所需计算量随视频中人数线性增长,很难被用于群体动作识别等应用

三、基于2D-CNN的技术路线——PoTion

将多张heatmap图以color coding算法压缩为一张图片。再用2D-CNN进行处理。尽管color coding在一定形式上保留运动的形式,但在编码过程中会存在一定的信息损失

在这里插入图片描述

四、基于3D-CNN的解决方案——PoseC3D

  • PoseC3D 是一种基于 3D-CNN 的骨骼行为识别框架。不同于传统的基于人体 3 维骨架的 GCN 方法,PoseC3D 仅使用 2 维人体骨架热图堆叠作为输入,就能达到更好的识别效果。

  • 项目地址:posec3d

  • 相关论文:Revisiting Skeleton-based Action Recognition

在这里插入图片描述

(1)提取2D Skeleton

  • 通过Top-Down进行2D人体姿态估计,获取关键点坐标(x, y, c),其中,c是置信度。直接存储关键点热图会消耗大量磁盘空间,因此将每个 2D 关键点存储为坐标。
  • 提取2D关键点的质量通常优于3D关键点的质量。所以PoseC3D采用2D关键点作为输入。即使基于轻量主干网络(MobileNetV2)所预测的二维姿态,用于动作识别时,效果也好于任何来源的三维人体姿态。

在这里插入图片描述

(2)生成3D heatmap volume(热图堆叠)

  • 基于获取的2D关键点坐标,生成3D的heatmap volume
  • 一张heatmap(K × H × W),K 为关键点个数
  • 3D heatmap volume(K × T × H × W),K为关键点个数。T为时序维度,即连续帧heatmap的张数。
  • 在空间上,采用subject-centered cropping;在时间上,使用uniform sampling,以建模更长的视频。

在这里插入图片描述

(3)用3D-CNN对获取的3D heatmap volume进行分类,得到动作类别

设计了两种3D-CNN

  • Pose-SlowOnly:仅以骨骼模态作为输入

在这里插入图片描述

  • RGBPose-SlowFast:骨骼 + RGB 模态

在这里插入图片描述

五、各模型的比较

5.1 PoseC3D的优势

(1)鲁棒性

每帧去drop一个关键点,p表示drop关键点的概率。可以看到3D-CNN在这种情况下几乎没受影响。

在这里插入图片描述

(2)可扩展性

随着视频中人数的增多,3D-CNN并不需要额外的计算量与参数量。但GCN每多一个人就会多一份计算量。

在这里插入图片描述

5.2 PoseC3D与2D-CNN的比较

PoseC3D计算量与参数量更小,准确率更高。在使用Kinetic-400预训练后,PoseC3D优势更显著。

在这里插入图片描述

5.3 3D-CNN与GCN比较

  • 其中,PoseC3D的输入大小17 * 48 * 56 *56 (K * T * H * W)
  • 所有模型都使用HRNet提取出的2D skeleton
  • 3D-CNN在性能,计算量,参数量方面都略优于GCN

在这里插入图片描述

六、PYSKL(附相关代码演示)

  • 如果应用的话,推荐用ST-GCN++模型。如果想做一些改进,用PoseConv3D模型。

  • PYSKL是一个开源项目。是使用pytorch基于骨骼关键点的数据进行动作识别

  • 项目地址:PYSKL

6.1 可视化skeleton

(1)下载依赖库

import glob
from pyskl.smp import *
from pyskl.utils.visualize import Vis3DPose, Vis2DPose
from mmcv import load, dump

(2) Download annotations

download_file('http://download.openmmlab.com/mmaction/pyskl/demo/annotations/ntu60_samples_hrnet.pkl', 'ntu60_2d.pkl')
download_file('http://download.openmmlab.com/mmaction/pyskl/demo/annotations/ntu60_samples_3danno.pkl', 'ntu60_3d.pkl')

(3)可视化2D skeleton

annotations = load('ntu60_2d.pkl')
index = 0
anno = annotations[index]
vid = Vis2DPose(anno, thre=0.2, out_shape=(540, 960), layout='coco', fps=12, video=None)
vid.ipython_display()

在这里插入图片描述

(4)可视化2D skeleton融合RGB video

annotations = load('ntu60_2d.pkl')
index = 0
anno = annotations[index]
frame_dir = anno['frame_dir']
video_url = f"http://download.openmmlab.com/mmaction/pyskl/demo/nturgbd/{frame_dir}.avi"
download_file(video_url, frame_dir + '.avi')
vid = Vis2DPose(anno, thre=0.2, out_shape=(540, 960), layout='coco', fps=12, video=frame_dir + '.avi')
vid.ipython_display()

在这里插入图片描述

(5)可视化 3D Skeletons

from pyskl.datasets.pipelines import PreNormalize3D
annotations = load('ntu60_3d.pkl')
index = 0
anno = annotations[index]
anno = PreNormalize3D()(anno)  # * Need Pre-Normalization before Visualization
vid = Vis3DPose(anno, layout='nturgb+d', fps=12, angle=(30, 45), fig_size=(8, 8), dpi=80)
vid = vid.vis()
vid.ipython_display()

在这里插入图片描述

6.2 可视化skeleton+ heatmap + 行为识别

(1)安装依赖项

  • mmpose安装文档见MMPose安装,我安装的版本是0.28.1

  • pyskl安装说明见PYSKL

import os
import cv2
import os.path as osp
import decord
import numpy as np
import matplotlib.pyplot as plt
import urllib
import moviepy.editor as mpy
import random as rd
from pyskl.smp import *
from mmpose.apis import vis_pose_result
from mmpose.models import TopDown
from mmcv import load, dump

(2)准备经过预处理的注释文件

  1. 关于相关预处理的annotation文件地址:annotations

  2. 经过预处理的骨架annotation链接:

  • GYM [2D Skeleton]: https://download.openmmlab.com/mmaction/pyskl/data/gym/gym_hrnet.pkl

  • NTURGB+D [2D Skeleton]: https://download.openmmlab.com/mmaction/pyskl/data/nturgbd/ntu60_hrnet.pkl

  1. 关于pickle文件的格式:

​ 每个pickle文件对应于一个动作识别数据集。pickle文件的内容是一个字典,由两个字段组成:split、annotations。

  • split:该字段的值是一个字典。key是split的名称,value是属于特定片段的视频标识列表。

  • annotations:该字段的值是骨架标注列表。每个骨架标注是一个字典,包含以下字段:

    • frame_dir (str): 对应视频的标识符

    • total_frames (int): 该视频的总帧数

    • img_shape (tuple[int]): (针对于2D骨架)视频帧的shape,即(H,W)

    • original_shape (tuple[int]): 同上

    • label (int): 动作标签

    • keypoint (np.ndarray, with shape [M x T x V x C]): M代表人数。T代表帧数。V代表关键点个数。C代表关键点坐标维度数(2D or 3D)

    • keypoint_score (np.ndarray, with shape [M x T x V]): (针对于2D骨架)关键点的置信度得分

# 准备经过预处理的 annotation
gym_ann_file = './data/gym/gym_hrnet.pkl'
ntu60_ann_file = './data/nturgbd/ntu60_hrnet.pkl'

(3)定义可视化效果(包括动作标签可视化、skeleton可视化)

# 设置字体与线条
FONTFACE = cv2.FONT_HERSHEY_DUPLEX
FONTSCALE = 0.6
FONTCOLOR = (255, 255, 255)
BGBLUE = (0, 119, 182)
THICKNESS = 1
LINETYPE = 1
# 定义可视化动作标签函数
def add_label(frame, label, BGCOLOR=BGBLUE):
    threshold = 30
    def split_label(label):
        label = label.split()
        lines, cline = [], ''
        for word in label:
            if len(cline) + len(word) < threshold:
                cline = cline + ' ' + word
            else:
                lines.append(cline)
                cline = word
        if cline != '':
            lines += [cline]
        return lines
    
    if len(label) > 30:
        label = split_label(label)
    else:
        label = [label]
    label = ['Action: '] + label
    
    sizes = []
    for line in label:
        sizes.append(cv2.getTextSize(line, FONTFACE, FONTSCALE, THICKNESS)[0])
    box_width = max([x[0] for x in sizes]) + 10
    text_height = sizes[0][1]
    box_height = len(sizes) * (text_height + 6)
    
    cv2.rectangle(frame, (0, 0), (box_width, box_height), BGCOLOR, -1)
    for i, line in enumerate(label):
        location = (5, (text_height + 6) * i + text_height + 3)
        cv2.putText(frame, line, location, FONTFACE, FONTSCALE, FONTCOLOR, THICKNESS, LINETYPE)
    return frame
    
# 定义可视化skeleton函数
def vis_skeleton(vid_path, anno, category_name=None, ratio=0.5):
    vid = decord.VideoReader(vid_path)
    frames = [x.asnumpy() for x in vid]
    
    h, w, _ = frames[0].shape
    new_shape = (int(w * ratio), int(h * ratio))
    frames = [cv2.resize(f, new_shape) for f in frames]
    
    assert len(frames) == anno['total_frames']
    # The shape is N x T x K x 3
    kps = np.concatenate([anno['keypoint'], anno['keypoint_score'][..., None]], axis=-1)
    kps[..., :2] *= ratio
    # 转换为 T x N x K x 3
    kps = kps.transpose([1, 0, 2, 3])
    vis_frames = []

    # 我们需要一个Topdown模型的实例,所以构建一个最小的实例
    model = TopDown(backbone=dict(type='ShuffleNetV1'))

    for f, kp in zip(frames, kps):
        bbox = np.zeros([0, 4], dtype=np.float32)
        result = [dict(keypoints=k) for k in kp]
        
        vis_frame = vis_pose_result(model, f, result)
        
        if category_name is not None:
            vis_frame = add_label(vis_frame, category_name)
        
        vis_frames.append(vis_frame)
    return vis_frames

(4)定义获取heatmap

keypoint_pipeline = [
    dict(type='PoseDecode'),
    dict(type='PoseCompact', hw_ratio=1., allow_imgpad=True),
    dict(type='Resize', scale=(-1, 64)),
    dict(type='CenterCrop', crop_size=64),
    dict(type='GeneratePoseTarget', with_kp=True, with_limb=False)
]

limb_pipeline = [
    dict(type='PoseDecode'),
    dict(type='PoseCompact', hw_ratio=1., allow_imgpad=True),
    dict(type='Resize', scale=(-1, 64)),
    dict(type='CenterCrop', crop_size=64),
    dict(type='GeneratePoseTarget', with_kp=False, with_limb=True)
]

from pyskl.datasets.pipelines import Compose
def get_pseudo_heatmap(anno, flag='keypoint'):
    assert flag in ['keypoint', 'limb']
    pipeline = Compose(keypoint_pipeline if flag == 'keypoint' else limb_pipeline)
    return pipeline(anno)['imgs']

def vis_heatmaps(heatmaps, channel=-1, ratio=8):
    # 如果通道为-1,则在同一张图上绘制所有关键点
    import matplotlib.cm as cm
    heatmaps = [x.transpose(1, 2, 0) for x in heatmaps]
    h, w, _ = heatmaps[0].shape
    newh, neww = int(h * ratio), int(w * ratio)
    
    if channel == -1:
        heatmaps = [np.max(x, axis=-1) for x in heatmaps]
    cmap = cm.viridis
    heatmaps = [(cmap(x)[..., :3] * 255).astype(np.uint8) for x in heatmaps]
    heatmaps = [cv2.resize(x, (neww, newh)) for x in heatmaps]
    return heatmaps

(5)进行动作识别与可视化(gym与ntu60数据集)

  • gym.txt标签文件地址:https://github.com/kennymckormick/pyskl/blob/main/tools/data/label_map/gym.txt

首先是GYM数据集

# Load GYM annotations
lines = mrlines('./tools/data/label_map/gym.txt')    # 加载动作标签txt文件
gym_categories = [x.strip().split('; ')[-1] for x in lines]
gym_annos = load(gym_ann_file)['annotations']
# download sample videos of GYM
!wget https://download.openmmlab.com/mmaction/posec3d/gym_samples.tar
!tar -xf gym_samples.tar
gym_root = 'gym_samples/'    # 50个体操视频
gym_vids = os.listdir(gym_root)
#  index in 0 - 49.
# 这里选择文件夹中第3个视频
idx = 2
vid = gym_vids[idx]

frame_dir = vid.split('.')[0]
vid_path = osp.join(gym_root, vid)
anno = [x for x in gym_annos if x['frame_dir'] == frame_dir][0]
# 可视化 Skeleton,并且进行动作识别,可视化动作标签
vis_frames = vis_skeleton(vid_path, anno, gym_categories[anno['label']])
vid = mpy.ImageSequenceClip(vis_frames, fps=24)
vid.ipython_display()

在这里插入图片描述

# 可视化 heatmap keypoint
keypoint_heatmap = get_pseudo_heatmap(anno)
keypoint_mapvis = vis_heatmaps(keypoint_heatmap)
keypoint_mapvis = [add_label(f, gym_categories[anno['label']]) for f in keypoint_mapvis]
vid = mpy.ImageSequenceClip(keypoint_mapvis, fps=24)
vid.ipython_display()

在这里插入图片描述

# 可视化 heatmap limb
limb_heatmap = get_pseudo_heatmap(anno, 'limb')
limb_mapvis = vis_heatmaps(limb_heatmap)
limb_mapvis = [add_label(f, gym_categories[anno['label']]) for f in limb_mapvis]
vid = mpy.ImageSequenceClip(limb_mapvis, fps=24)
vid.ipython_display()

在这里插入图片描述

接下来是NTU60数据集

# ntu_60数据集的标签
ntu_categories = ['drink water', 'eat meal/snack', 'brushing teeth', 'brushing hair', 'drop', 'pickup', 
                  'throw', 'sitting down', 'standing up (from sitting position)', 'clapping', 'reading', 
                  'writing', 'tear up paper', 'wear jacket', 'take off jacket', 'wear a shoe', 
                  'take off a shoe', 'wear on glasses', 'take off glasses', 'put on a hat/cap', 
                  'take off a hat/cap', 'cheer up', 'hand waving', 'kicking something', 
                  'reach into pocket', 'hopping (one foot jumping)', 'jump up', 
                  'make a phone call/answer phone', 'playing with phone/tablet', 'typing on a keyboard', 
                  'pointing to something with finger', 'taking a selfie', 'check time (from watch)', 
                  'rub two hands together', 'nod head/bow', 'shake head', 'wipe face', 'salute', 
                  'put the palms together', 'cross hands in front (say stop)', 'sneeze/cough', 
                  'staggering', 'falling', 'touch head (headache)', 'touch chest (stomachache/heart pain)', 
                  'touch back (backache)', 'touch neck (neckache)', 'nausea or vomiting condition', 
                  'use a fan (with hand or paper)/feeling warm', 'punching/slapping other person', 
                  'kicking other person', 'pushing other person', 'pat on back of other person', 
                  'point finger at the other person', 'hugging other person', 
                  'giving something to other person', "touch other person's pocket", 'handshaking', 
                  'walking towards each other', 'walking apart from each other']
# Load ntu60 annotations
ntu_annos = load(ntu60_ann_file)['annotations']
# download sample videos of NTU-60
!wget https://download.openmmlab.com/mmaction/posec3d/ntu_samples.tar
!tar -xf ntu_samples.tar
# !rm ntu_samples.tar
ntu_root = 'ntu_samples/'       # 50个室内动作视频
ntu_vids = os.listdir(ntu_root)
# index in 0 - 49.
# 这里选择文件夹中第42个视频
idx = 41
vid = ntu_vids[idx]

frame_dir = vid.split('.')[0]
vid_path = osp.join(ntu_root, vid)
anno = [x for x in ntu_annos if x['frame_dir'] == frame_dir.split('_')[0]][0]
# 可视化 Skeleton,并且进行动作识别,可视化动作标签
vis_frames = vis_skeleton(vid_path, anno, ntu_categories[anno['label']])
vid = mpy.ImageSequenceClip(vis_frames, fps=24)
vid.ipython_display()

在这里插入图片描述

# 可视化 heatmap keypoint
keypoint_heatmap = get_pseudo_heatmap(anno)
keypoint_mapvis = vis_heatmaps(keypoint_heatmap)
keypoint_mapvis = [add_label(f, ntu_categories[anno['label']]) for f in keypoint_mapvis]
vid = mpy.ImageSequenceClip(keypoint_mapvis, fps=24)
vid.ipython_display()

在这里插入图片描述

# 可视化 heatmap limb
limb_heatmap = get_pseudo_heatmap(anno, 'limb')
limb_mapvis = vis_heatmaps(limb_heatmap)
limb_mapvis = [add_label(f, ntu_categories[anno['label']]) for f in limb_mapvis]
vid = mpy.ImageSequenceClip(limb_mapvis, fps=24)
vid.ipython_display()

在这里插入图片描述

七、相关参考地址

  1. 关于PoseC3D更详细的解释参考:PoseC3D: 基于人体姿态的动作识别新范式
  2. PoseC3D项目地址:posec3d
  3. PoseC3D相关论文: Revisiting Skeleton-based Action Recognition
  4. PYSKL项目地址:PYSKL
  5. MMPose安装文档:MMPose安装
  6. 关于相关预处理的annotation文件地址:annotations
  7. GYM的annotation文件:GYM_annotation
  8. NTU60的annotation文件:NTU60_annotation
  9. gym.txt标签文件地址:gym.txt

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

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

相关文章

面向对象分析与设计 UML2.0 学习笔记

一、认识UML UML-Unified Modeling Language 统一建模语言&#xff0c;又称标准建模语言。是用来对软件密集系统进行可视化建模的一种语言。UML的定义包括UML语义和UML表示法两个元素。 UML是在开发阶段&#xff0c;说明、可视化、构建和书写一个面向对象软件密集系统的制品的…

Hyper-V虚拟机在wifi环境下的外网连接配置

目录 什么是虚拟交换机管理器了解虚拟交换机中的三个概念通过无线网卡创建虚拟交换机遇到的问题wifi环境下虚拟机外网连接方法 前面我们已经安装好了Hyper-V虚拟机和liunx操作系统&#xff0c;但是我们没有给虚拟机配置网络&#xff0c;本来我以为是一件很简单的事情&#xff0…

【Spring Boot学习】怎么配置文件,配置文件有什么用

前言&#xff1a; &#x1f49e;&#x1f49e;今天我们依然是学习Spring Boot&#xff0c;这里我们会更加了解Spring Boot的知识&#xff0c;知道Spring Boot的配置文件是什么样子的。有什么用&#xff1f;怎么使用Spring Boot的配置文件。 &#x1f49e;&#x1f49e;路漫漫&a…

Apache James 同时开启25、587、465端口

前提&#xff1a; 可以参考如下两篇文章在window或者linux上&#xff0c;先部署好Apache James Apache James邮件服务器搭建&#xff08;linux&#xff09;_Steven-Russell的博客-CSDN博客 Apache James邮件服务器搭建&#xff08;windows&#xff09;_Steven-Russell的博客…

Qt6.2教程——6.QT常用控件QLineEdit

一&#xff0c;QLineEdit简介 QLineEdit是Qt库中的一个控件&#xff0c;它提供了一个单行的文本输入框。用户可以在这个输入框中输入和编辑文本。它可以设置占位符文本&#xff08;Placeholder Text&#xff09;&#xff0c;当QLineEdit为空的时候显示这个占位符文本。它还可以…

JDK8新特性之方法引用【 ::】

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于方法引用的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.是什么 二.为什么要用 三.什么时候…

chrome录制保存网络请求

有时候&#xff0c;需要与同事共同查看网络请求&#xff0c;但是现场往往环境限制&#xff0c;导致无法访问环境。在这里推荐一种利用chrome保存网络请求的方法。 准备&#xff1a; 1. chrome浏览器&#xff08;最好版本号是62以上&#xff09; chrome浏览器越新越好(最新稳定…

rsarsa

数学很酷&#xff01;使用RSA算法对秘密消息进行解码&#xff0c;c&#xff0c;p&#xff0c;q&#xff0c;e是RSA算法的参数。 RSA算法还不太了解&#xff0c;经过这段时间的学习&#xff0c;得知 q和p是最开始选择的两个质数&#xff0c;主要是为了计算出钥匙n e是在1到φ(n)…

服务器编程:数据库连接池

引言&#xff1a; 数据库连接池和线程池的思想一样&#xff0c;是为了避免频繁创建和销毁数据库连接导致的性能开销。如果一个项目频繁的需要访问数据库&#xff0c;那么它就有可能需要频繁的创建/销毁数据库连接&#xff0c;那么我们可以采用数据库连接池的技术&#xff0c;在…

Docker中搭建RabbitMQ集群

Docker中搭建RabbitMQ集群 1、启动三个RabbitMQ容器2、为容器设置节点2.1、设置Erlang Cookie2.2、设置节点12.3、设置节点22.4、设置节点32.5、预览结果 3、配置镜像队列3.1、配置镜像的原因3.2、搭建步骤 1、启动三个RabbitMQ容器 服务器IP端口hostname管理界面地址192.168.…

IoC容器的设计(利用反射、注解和工厂模式实现)

1.实验要求 利用注解、反射和工厂模式设计一个简单的IoC容器该IoC容器包含3个注解和一个IoC容器类&#xff08;AnnotationConfigApplicationContext&#xff09;&#xff0c;其定义如下&#xff1a; 注解&#xff1a; 注解含义Component标注BeanAutowired标注需要被注入的对…

如何写好一份企业直播主题策划?

写一份好的直播主题策划&#xff0c;需要考虑包括目标受众、目的、内容、形式、互动等&#xff0c;下面是写企业直播主题策划的一些关注点&#xff0c;希望能帮到您。 定位您直播的目标受众 明确你的直播主题适合的目标受众是谁&#xff0c;他们的兴趣、需求和期望是什么。了解…

OAuth2,jwt,springsecurity之间的区别和联系

OAuth 2.0、JWT (JSON Web Token) 和 Spring Security 是安全相关的概念和技术&#xff0c;它们有着不同的功能和用途。 OAuth 2.0&#xff08;开放授权&#xff09;&#xff1a; OAuth 2.0 是一种授权框架&#xff0c;用于授权第三方应用程序访问用户资源&#xff0c;而无需共…

【OpenCV DNN】Flask 视频监控目标检测教程 10

欢迎关注『OpenCV DNN Youcans』系列&#xff0c;持续更新中 【OpenCV DNN】Flask 视频监控目标检测教程 10 3.10 OpenCV DNNFlask实时监控目标检测1、加载MobileNet SSD模型2、导入分类名称文件3、处理视频帧进行目标检测4、新建一个Flask项目5、Python 程序文件6、视频流的网…

linux系统addr ip以及ifconfig查询不到ip地址解决方法,没有ens33

先看使用情况 网上一堆垃圾博文解决方案都是你抄我我抄你&#xff0c;一点用没有&#xff0c;都说使用 vi /etc/sysconfig/network-scripts/ifcfg-ens33 来更改配置ONBOOT为yes&#xff0c;改个屁&#xff0c;给你们看看我目前的配置&#xff0c;劳资本身就是yes&#xff0c;还…

Elasticsearch 基本使用(二)简单查询 嵌套查询

查询数据 简单查询按id查询单条记录查询所有数据设置排序filter 过滤查询数组内的值查询 嵌套查询查询一个外层字段 内的嵌套字段查询多个字段&#xff0c;其中有嵌套字段 简单查询 按id查询单条记录 GET bank/_doc/1查询所有数据 默认只查询10条记录 GET bank/_search {&q…

Linux任务调度、磁盘分区、挂载

一、任务调度介绍 任务调度是指系统在某个时间执行的特定的命令或程序 任务调度分为两类&#xff1a; 1.系统工作&#xff1a;有些重要的工作必须周而复始的执行&#xff0c;比如病毒扫描 2&#xff0c;个别用户工作&#xff1a;个别用户可能希望执行某些程序&#xff0c;比如…

canvas自定义绘制顺序解决遮挡问题

canvas自定义绘制顺序解决遮挡问题 1. 问题场景2. 解决思路3. 实现代码 1. 问题场景 使用canvas绘制进行要素叠加时&#xff0c;往往会出现不是按照先画的在下面&#xff0c;后画的在最上面这样的顺序进行叠加显示。原因就是由于图片大小不同导致绘制或加载的时间不一样&#…

合宙Air724UG Cat.1模块硬件设计指南--LCD专用SPI接口

概述 Air724UG支持一路LCD专用SPI接口&#xff0c;用于驱动SPI LCD屏幕&#xff0c;不能作为通用SPI使用 特性&#xff1a; 最大支持320240分辨率&#xff0c;30帧 内置图像处理单元GOUDA 支持格式&#xff1a; YUV4 : 2 : 0 ;YUV4 : 2 : 2;RGB565; ARGB8888 目前只支持4线8bi…