YOLOv8改进 | 卷积模块 | SAConv可切换空洞卷积
秋招面试专栏推荐 :深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转
💡💡💡本专栏所有程序均经过测试,可成功执行💡💡💡
专栏目录 :《YOLOv8改进有效涨点》专栏介绍 & 专栏目录 | 目前已有40+篇内容,内含各种Head检测头、损失函数Loss、Backbone、Neck、NMS等创新点改进——点击即可跳转
许多现代目标检测器通过使用“观察和思考两次”的机制展现了卓越的性能。我们在目标检测的主干设计中探讨了这种机制。在宏观层面上,提出了递归特征金字塔(Recursive Feature Pyramid),它将额外的反馈连接从特征金字塔网络整合到底向上的主干层中。在微观层面上,提出了可切换空洞卷积(Switchable Atrous Convolution),它用不同的空洞率对特征进行卷积,并使用切换函数收集结果。将它们结合起来就得到了DetectoRS,它显著提高了目标检测的性能。 文章在介绍主要的原理后,将手把手教学如何进行模块的代码添加和修改,并将修改后的完整代码放在文章的最后,方便大家一键运行,小白也可轻松上手实践。以帮助您更好地学习深度学习目标检测YOLO系列的挑战。
目录
1. 原理
2. 将PConv添加到YOLOv8代码
2.1 PConv代码实现
2.2 更改init.py文件
2.3 新增yaml文件
2.4 注册模块
2.5 执行程序
3. 完整代码分享
4. GFLOPs
5. 进阶
6. 总结
1. 原理

论文地址:DetectoRS: Detecting Objects with Recursive Feature Pyramid and Switchable Atrous Convolutio——点击即可跳转
官方代码:官方代码仓库——点击即可跳转
可切换空洞卷积 (SAC) 是一种复杂的卷积技术,用于增强深度学习模型的性能,特别是在对象检测和分割等任务中。以下是根据提供的文档对其主要原理的细分:
空洞卷积
空洞卷积,也称为扩张卷积,用于增加过滤器的视野,而不会增加参数数量或计算量。这是通过在连续的过滤器值之间引入零来实现的。插入这些零的速率称为空洞速率 ( r )。对于大小为 的过滤器,有效内核大小变为 ( k + (k-1)(r-1) )。这允许网络捕获多尺度信息,这对于检测不同大小的物体特别有用。
可切换空洞卷积 (SAC)
SAC 以空洞卷积为基础,引入了一种在卷积操作期间动态切换不同空洞率的机制。其核心思想是允许卷积层根据输入特征自适应地选择适当的空洞率。这种适应性有助于模型更好地处理同一图像中不同尺度的对象。
SAC 的组件
SAC 由三个主要组件组成:主 SAC 组件和两个附加在 SAC 组件之前和之后的全局上下文模块。
-
主 SAC 组件:这是实际发生可切换空洞卷积的核心部分。它涉及两个具有不同空洞率的卷积操作的加权组合。
SAC 的数学公式如下: 其中:
-
( x ) 是输入。
-
和
是卷积运算的权重。
-
( r ) 是空洞率。
-
是一个开关函数,实现为一个平均池化层,具有 5x5 内核,后跟一个 1x1 卷积层,确定两个卷积运算之间的平衡。
-
全局上下文模块:这些模块在 SAC 操作前后为特征添加了图像级上下文,增强了模型理解图像全局结构的能力。
SAC的优势
-
多尺度检测:通过动态切换不同的空洞率,SAC 使模型能够更好地检测各种尺寸的物体。
-
参数效率:SAC 在不增加额外参数的情况下增加了感受野,同时保持了计算效率。
-
适应性:切换功能允许卷积层根据输入进行适应,使模型更加灵活,能够处理不同的图像和物体尺度。
实现示例
骨干网络(例如 ResNet)中的每个 3x3 卷积层都转换为 SAC,然后执行具有不同空洞率的卷积的加权组合。
综上所述,可切换空洞卷积结合了空洞卷积和动态适应性的优点,增强了深度学习模型,提高了其处理不同物体尺度图像的能力,同时保持参数和计算方面的效率。
2. 将PConv添加到YOLOv8代码
2.1 PConv代码实现
关键步骤一:将下面代码粘贴到在/ultralytics/ultralytics/nn/modules/conv.py中,并在该文件的__all__中添加“SAConv”
class ConvAWS2d(nn.Conv2d):def __init__(self,in_channels,out_channels,kernel_size,stride=1,padding=0,dilation=1,groups=1,bias=True):super().__init__(in_channels,out_channels,kernel_size,stride=stride,padding=padding,dilation=dilation,groups=groups,bias=bias)self.register_buffer('weight_gamma', torch.ones(self.out_channels, 1, 1, 1))self.register_buffer('weight_beta', torch.zeros(self.out_channels, 1, 1, 1))def _get_weight(self, weight):weight_mean = weight.mean(dim=1, keepdim=True).mean(dim=2,keepdim=True).mean(dim=3, keepdim=True)weight = weight - weight_meanstd = torch.sqrt(weight.view(weight.size(0), -1).var(dim=1) + 1e-5).view(-1, 1, 1, 1)weight = weight / stdweight = self.weight_gamma * weight + self.weight_betareturn weightdef forward(self, x):weight = self._get_weight(self.weight)return super()._conv_forward(x, weight, None)def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict,missing_keys, unexpected_keys, error_msgs):self.weight_gamma.data.fill_(-1)super()._load_from_state_dict(state_dict, prefix, local_metadata, strict,missing_keys, unexpected_keys, error_msgs)if self.weight_gamma.data.mean() > 0:returnweight = self.weight.dataweight_mean = weight.data.mean(dim=1, keepdim=True).mean(dim=2,keepdim=True).mean(dim=3, keepdim=True)self.weight_beta.data.copy_(weight_mean)std = torch.sqrt(weight.view(weight.size(0), -1).var(dim=1) + 1e-5).view(-1, 1, 1, 1)self.weight_gamma.data.copy_(std)class SAConv2d(ConvAWS2d):def __init__(self,in_channels,out_channels,kernel_size,s=1,p=None,g=1,d=1,act=True,bias=True):super().__init__(in_channels,out_channels,kernel_size,stride=s,padding=autopad(kernel_size, p),dilation=d,groups=g,bias=bias)self.switch = torch.nn.Conv2d(self.in_channels,1,kernel_size=1,stride=s,bias=True)self.switch.weight.data.fill_(0)self.switch.bias.data.fill_(1)self.weight_diff = torch.nn.Parameter(torch.Tensor(self.weight.size()))self.weight_diff.data.zero_()self.pre_context = torch.nn.Conv2d(self.in_channels,self.in_channels,kernel_size=1,bias=True)self.pre_context.weight.data.fill_(0)self.pre_context.bias.data.fill_(0)self.post_context = torch.nn.Conv2d(self.out_channels,self.out_channels,kernel_size=1,bias=True)self.post_context.weight.data.fill_(0)self.post_context.bias.data.fill_(0)self.bn = nn.BatchNorm2d(out_channels)self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())def forward(self, x):# pre-contextavg_x = torch.nn.functional.adaptive_avg_pool2d(x, output_size=1)avg_x = self.pre_context(avg_x)avg_x = avg_x.expand_as(x)x = x + avg_x# switchavg_x = torch.nn.functional.pad(x, pad=(2, 2, 2, 2), mode="reflect")avg_x = torch.nn.functional.avg_pool2d(avg_x, kernel_size=5, stride=1, padding=0)switch = self.switch(avg_x)# sacweight = self._get_weight(self.weight)out_s = super()._conv_forward(x, weight, None)ori_p = self.paddingori_d = self.dilationself.padding = tuple(3 * p for p in self.padding)self.dilation = tuple(3 * d for d in self.dilation)weight = weight + self.weight_diffout_l = super()._conv_forward(x, weight, None)out = switch * out_s + (1 - switch) * out_lself.padding = ori_pself.dilation = ori_d# post-contextavg_x = torch.nn.functional.adaptive_avg_pool2d(out, output_size=1)avg_x = self.post_context(avg_x)avg_x = avg_x.expand_as(out)out = out + avg_xreturn self.act(self.bn(out))
可切换空洞卷积 (SAconv) 通过动态调整卷积层中的空洞率来增强图像处理,从而实现更有效的多尺度特征提取。 SAconv 处理图像的主要工作流程概述:
SAconv 的主要工作流程
-
输入图像: 该过程从输入到神经网络的输入图像开始。
-
初始卷积层: 图像首先经过几个标准卷积层,提取边缘、纹理和基本形状等低级特征。这些层通常具有固定的内核大小和步长值。
-
可切换空洞卷积 (SAC) 模块: 核心组件 SAC 模块取代了网络主干中的传统卷积层(例如 ResNet)。以下是 SAC 操作的详细分解:
-
特征提取: 输入特征 ( x ) 首先通过两个不同的卷积操作进行处理,权重分别为 ( w ) 和
,其中
表示对权重的调整。这些卷积具有不同的空洞率,使它们能够捕获多个尺度的特征。
-
切换函数: 切换函数 S(x) 确定两个卷积之间的平衡。它通常实现为具有 5x5 内核的平均池化层,后跟 1x1 卷积层。切换函数的输出是一组权重,用于平衡两个卷积的贡献。
-
加权组合: SAC 的最终输出是两个卷积的加权组合:
这里,
表示标准卷积,
表示速率为 ( r ) 的空洞卷积。
-
全局上下文模块: 在 SAC 模块之前和之后,使用全局上下文模块来合并图像级上下文。这些模块通常涉及全局平均池化和全连接层等操作,以捕获图像的整体结构。这有助于细化特征并为后续层提供更好的上下文。
-
中间层: SAC 模块和全局上下文模块的输出被馈送到网络的进一步层,其中可能包括额外的卷积、规范化和激活函数。这些层继续细化和处理 SAC 模块提取的特征。
-
输出层: 最后,处理后的特征到达输出层,生成所需的结果。这可能是分类标签、分割掩码或用于对象检测的边界框,具体取决于网络设计用于的任务。
工作流程说明
在提供的文档中,图 4 显示了集成到 ResNet 架构中的 SAC 模块的示例。在此图中,ResNet 主干中的每个 3x3 卷积层都被 SAC 模块替换。此修改允许网络根据输入特征动态调整其感受野,从而提高需要多尺度特征检测的任务的性能。

2.2 更改init.py文件
关键步骤二:修改modules文件夹下的__init__.py文件,先导入函数

然后在下面的__all__中声明函数

2.3 新增yaml文件
关键步骤三:在 \ultralytics\ultralytics\cfg\models\v8下新建文件 yolov8_SAConv.yaml并将下面代码复制进去
# Ultralytics YOLO 🚀, AGPL-3.0 license
# YOLOv8 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect# Parameters
nc: 80 # number of classes
scales: # model compound scaling constants, i.e. 'model=yolov8n.yaml' will call yolov8.yaml with scale 'n'# [depth, width, max_channels]n: [ 0.33, 0.25, 1024 ] # YOLOv8n summary: 225 layers, 3157200 parameters, 3157184 gradients, 8.9 GFLOPs# YOLOv8.0n backbone
backbone:# [from, repeats, module, args]- [ -1, 1, SAConv2d, [ 64, 3, 2 ] ] # 0-P1/2- [ -1, 1, SAConv2d, [ 128, 3, 2 ] ] # 1-P2/4- [ -1, 3, C2f, [ 128, True ] ]- [ -1, 1, SAConv2d, [ 256, 3, 2 ] ] # 3-P3/8- [ -1, 6, C2f, [ 256, True ] ]- [ -1, 1, SAConv2d, [ 512, 3, 2 ] ] # 5-P4/16- [ -1, 6, C2f, [ 512, True ] ]- [ -1, 1, SAConv2d, [ 1024, 3, 2 ] ] # 7-P5/32- [ -1, 3, C2f, [ 1024, True ] ]- [ -1, 1, SPPF, [ 1024, 5 ] ] # 9# YOLOv8.0n head
head:- [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ]- [ [ -1, 6 ], 1, Concat, [ 1 ] ] # cat backbone P4- [ -1, 3, C2f, [ 512 ] ] # 12- [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ]- [ [ -1, 4 ], 1, Concat, [ 1 ] ] # cat backbone P3- [ -1, 3, C2f, [ 256 ] ] # 15 (P3/8-small)- [ -1, 1, SAConv2d, [ 256, 3, 2 ] ]- [ [ -1, 12 ], 1, Concat, [ 1 ] ] # cat head P4- [ -1, 3, C2f, [ 512 ] ] # 18 (P4/16-medium)- [ -1, 1, SAConv2d, [ 512, 3, 2 ] ]- [ [ -1, 9 ], 1, Concat, [ 1 ] ] # cat head P5- [ -1, 3, C2f, [ 1024 ] ] # 21 (P5/32-large)- [ [ 15, 18, 21 ], 1, Detect, [ nc ] ] # Detect(P3, P4, P5)
温馨提示:因为本文只是对yolov8基础上添加模块,如果要对yolov8n/l/m/x进行添加则只需要指定对应的depth_multiple 和 width_multiple。
# YOLOv8n
depth_multiple: 0.33 # model depth multiple
width_multiple: 0.25 # layer channel multiple
max_channels: 1024 # max_channels# YOLOv8s
depth_multiple: 0.33 # model depth multiple
width_multiple: 0.50 # layer channel multiple
max_channels: 1024 # max_channels# YOLOv8l
depth_multiple: 1.0 # model depth multiple
width_multiple: 1.0 # layer channel multiple
max_channels: 512 # max_channels# YOLOv8m
depth_multiple: 0.67 # model depth multiple
width_multiple: 0.75 # layer channel multiple
max_channels: 768 # max_channels# YOLOv8x
depth_multiple: 1.33 # model depth multiple
width_multiple: 1.25 # layer channel multiple
max_channels: 512 # max_channels
2.4 注册模块
关键步骤四:在parse_model函数中进行注册,添加SAConv,

2.5 执行程序
在train.py中,将model的参数路径设置为yolov8_SAConv.yaml的路径
建议大家写绝对路径,确保一定能找到
from ultralytics import YOLO# Load a model
# model = YOLO('yolov8n.yaml') # build a new model from YAML
# model = YOLO('yolov8n.pt') # load a pretrained model (recommended for training)model = YOLO(r'/projects/ultralytics/ultralytics/cfg/models/v8/yolov8_PConv.yaml') # build from YAML and transfer weights# Train the model
model.train(batch=16)
🚀运行程序,如果出现下面的内容则说明添加成功🚀

3. 完整代码分享
https://pan.baidu.com/s/1z2KWa9guFsyHTETM9gXdPA?pwd=kbu5
提取码: kbu5
4. GFLOPs
关于GFLOPs的计算方式可以查看:百面算法工程师 | 卷积基础知识——Convolution
未改进的YOLOv8nGFLOPs

改进后的GFLOPs

5. 进阶
可以与其他的注意力机制或者损失函数等结合,进一步提升检测效果
6. 总结
可切换空洞卷积 (SAConv) 可动态调整卷积层中的空洞率,通过基于输入特征平衡其贡献的切换函数组合不同的空洞卷积,使网络能够更有效地捕获多尺度特征。这种适应性增强了网络检测不同大小物体的能力,并提高了物体检测和分割等任务的性能,而无需添加额外的参数。
相关文章:
YOLOv8改进 | 卷积模块 | SAConv可切换空洞卷积
秋招面试专栏推荐 :深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 💡💡💡本专栏所有程序均经过测试,可成功执行💡💡💡 专栏目录 :《YOLOv8改进有效…...
使用Python下载并合并HLS视频片段
下载和合并视频片段的实用方法 在日常工作中,我们经常会遇到需要从网上下载视频并将其合并成一个完整视频的需求。本文将介绍如何使用 Python 下载多个视频片段,并使用 ffmpeg 将这些片段合并成一个完整的视频文件。以下是具体步骤和代码实现。 完整代…...
常见的九种二极管
常见的九种二极管 文章目录 常见的九种二极管1、普通二极管2、光电二极管(LED)3、变容二级管4、发光二极管5、恒流二极管6、快恢复二极管(FRD)7、肖特基二极管8、瞬态电压抑制二极管(TVS)9、齐纳二极管(稳压࿰…...
竞赛选题 python的搜索引擎系统设计与实现
0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 python的搜索引擎系统设计与实现 🥇学长这里给一个题目综合评分(每项满分5分) 难度系数:3分工作量:5分创新点:3分 该项目较为新颖ÿ…...
大模型技术方向夏令营1期-对话分角色要素提取挑战赛
#AI夏令营 #Datawhale #夏令营 一、 baseline 跑通 Baseline 本身挑战性有限,关键是熟悉 LLM-centric 相关任务 coding 层面的流程方法,比如: 大模型 API(这里为科大讯飞 Spark)调用token消耗的理解如何调用大模型实现针对给定…...
类和对象(封装、继承、多态、友元)
c面相对象的三大特性为:封装、继承、多态 c 认为万事万物都皆为对象,对象上有其属性和行为 一、类和对象(封装) (一)封装的意义 封装是c面相对象的三大特性之一 封装的意义: 将属性和行为…...
关于Yolov8我踩过的那些坑
按照报错频次梳理: 致命反斜杠‘\’ 调用模型时,我喜欢‘copy relative location’,然后win系统默认反斜杠! 就导致路径读取错误!各种报错!! debug到崩溃然后发现是斜杠的问题,本吗喽…...
Linux——shell原理和文件权限
1.shell原理 在我们使用云服务器时,需要通过shell进行使用,而shell则是一种外壳程序。 我们提到过,大部分的指令实际上就是文件,当用户需要执行某种功能时,由于用户不擅长和操作系统直接交互(操作复杂&…...
网络工程师需要熟悉Docker吗?我觉得不需要精通,但是得懂基础
你好,这里是网络技术联盟站,我是瑞哥。 Docker,这个字眼大家不陌生吧,不过作为网络工程师可能平时接触不到,如果在看文章的是运维人员,那么70%以上的运维人员都会跟Docker打交道。即使网工用不到ÿ…...
c++初级-2-引用
文章目录 引用一、引用的定义二、引用做函数参数三、引用作为返回对象四、引用的本质五、常量引用 引用 即给一个变量起别名。 一、引用的定义 int a 10;//引用int& b a;cout << "a " << a << endl;cout << "b " <&l…...
如何清理电脑内存?让电脑运行如飞!
电脑内存(RAM)的清理对于维持系统的流畅运行至关重要。随着使用时间的增加,系统内存会被各种应用程序和后台进程占用,导致系统响应变慢,甚至出现卡顿现象。通过有效地清理内存,可以提升电脑的性能ÿ…...
[数据集][目标检测]人员状态跑睡抽烟打电话跌倒检测数据集4943张5类别
数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):4943 标注数量(xml文件个数):4943 标注数量(txt文件个数):4943 标注…...
Java8 - Stream API 处理集合数据
Java 8的Stream API提供了一种功能强大的方式来处理集合数据,以函数式和声明式的方式进行操作。Stream API允许您对元素集合执行操作,如过滤、映射和归约,以简洁高效的方式进行处理。 下面是Java 8 Stream API的一些关键特性和概念ÿ…...
漫步5G-A City,一份独属于上海的浪漫
作家亨利詹姆斯曾写道,“城市漫步,让我接触到了这个世界上最好的东西”。 用漫无目的地行走,来体验和观察一座城市,上海凭借丰富多元的文化特质,成为citywalk这种浪漫生活方式的流行地。 无论你是漫步在美术馆、画廊林…...
SpringBoot 如何处理跨域请求?你说的出几种方法?
引言:在现代的Web开发中,跨域请求(Cross-Origin Resource Sharing,CORS)是一个常见的挑战。随着前后端分离架构的流行,前端应用通常运行在一个与后端 API 不同的域名或端口上,这就导致了浏览器的…...
OV SSL证书年度成本概览:为企业安全护航的经济之选
在当今数字化时代,企业网站不仅是品牌展示的窗口,更是与客户沟通的桥梁。然而,随着网络威胁的不断升级,保护网站安全成为了企业不可忽视的任务。SSL证书,特别是OV SSL证书,因其对企业身份的严格验证&#x…...
歌尔气压计SPA06-003在无人机的创新应用
随着科技的不断进步,各类智能设备的功能日益强大,其中气压计作为一种能够测量大气压力的传感器,已被广泛应用于多种领域。歌尔气压计以其高精度、低功耗的特点,在无人机和智能手表上的应用尤为突出,为这两个领域的产品…...
python3多文件操作
1 介绍 有两个.py文件,分别为main.py和util.py,执行main.py时,调用util.py当中的函数。 main.py内容如下, import util if __name__ "__main__":a [3.0,4.0]length util.get_length_from_vec(a)print(f"leng…...
312. 戳气球
312. 戳气球 题目链接:312. 戳气球 代码如下: //参考链接:https://leetcode.cn/problems/burst-balloons/solutions/336390/chuo-qi-qiu-by-leetcode-solution class Solution { public:int maxCoins(vector<int>& nums) {int nnums.size()…...
深入理解C++中的锁
目录 1.基本互斥锁(std::mutex) 2.递归互斥锁(std::recursive_mutex) 3.带超时机制的互斥锁(std::timed_mutex) 4.带超时机制的递归互斥锁(std::recursive_timed_mutex) 5.共享…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
