YOLOv5算法改进(8)— 替换主干网络之MobileNetV3

news2024/11/22 10:21:52

前言:Hello大家好,我是小哥谈。MobileNetV3是由Google团队在2019年提出的一种卷积神经网络结构,其目标是在保持高性能的同时减少计算延时。MobileNetV3相比于前一版本(MobileNetV2)在性能上有明显的提升。根据原论文,在ImageNet分类任务中,MobileNetV3的正确率提升了3.2%,同时计算延时降低了20%。MobileNetV3通过使用NAS搜索参数重新设计了耗时层结构,这也是其与之前版本的主要区别之一。🌈 

 前期回顾:

             YOLOv5算法改进(1)— 如何去改进YOLOv5算法

             YOLOv5算法改进(2)— 添加SE注意力机制

             YOLOv5算法改进(3)— 添加CBAM注意力机制

             YOLOv5算法改进(4)— 添加CA注意力机制

             YOLOv5算法改进(5)— 添加ECA注意力机制

             YOLOv5算法改进(6)— 添加SOCA注意力机制

             YOLOv5算法改进(7)— 添加SimAM注意力机制

            目录

🚀1.论文

🚀2.MobileNetV3网络架构

🚀3.YOLOv5结合MobileNetV3_small

💥💥步骤1:在common.py中添加MobileNetV3模块

​💥💥步骤2:在yolo.py文件中加入类名 

​💥💥步骤3:创建自定义yaml文件 

💥💥步骤4:验证是否加入成功

💥💥步骤5:修改train.py中的'--cfg'默认参数

🚀4.YOLOv5结合MobileNetV3_large

💥💥步骤3:创建自定义yaml文件 

🚀1.论文

MobileNetV3 是由 Google 团队在 2019 年提出的轻量化网络模型。传统的卷积神经网络,内容需求大、运算量大,无法在移动设备以及嵌入式设备上运行,为了解决这一问题,MobileNet网络应运而生。MobileNetV3在移动端图像分类、目标检测、语义分割等任务上均取得了优秀的表现。MobileNetV3采用了很多新的技术,包括针对通道注意力的Squeeze-and-Excitation模块、NAS搜索方法等,这些方法都有利于进一步提升网络的性能。🌱

MobileNetV3的整体架构基本沿用了MobileNetV2的设计,采用了轻量级的深度可分离卷积和残差块等结构,依然是由多个模块组成,但是每个模块得到了优化和升级,包括瓶颈结构、SE模块和NL模块。MobileNetV3在ImageNet 分类任务中正确率上升了 3.2%,计算延时还降低了20%。🌱

整体来说MobileNetV3有两大创新点:

  • 互补搜索技术组合:由资源受限的NAS执行模块级搜索,NetAdapt执行局部搜索。
  • 网络结构改进:将最后一步的平均池化层前移并移除最后一个卷积层,引入h-swish激活函数。

MobileNetV3 有两个版本,MobileNetV3-Small 与 MobileNetV3-Large 分别对应对计算和存储要求低和高的版本。🌺

论文题目:《Searching for MobileNetV3》

论文地址:  https://arxiv.org/abs/1905.02244

代码实现:  mirrors / xiaolai-sqlai / mobilenetv3 · GitCode


🚀2.MobileNetV3网络架构

下图为MobileNetV3的网络结构图,large和small的整体结构一致,区别就是基本单元bneck的个数以及内部参数上,主要是通道数目。🌳

MobileNetV3-Large版本参数: 

 MobileNetV3-Small版本参数:

说明:♨️♨️♨️

上表为具体的参数设置,其中bneck是网络的基本结构。SE代表是否使用通道注意力机制。NL代表激活函数的类型,包括HS(h-swish),RE(ReLU)。NBN 代表没有BN操作。s 是stride的意思,网络使用卷积stride操作进行降采样,没有使用pooling操作。

MobileNetV3 相较于 MobileNetV2,主要有以下改进和优化:

🍀更高的准确率:MobileNetV3在ImageNet数据集上的Top-1准确率达到了75.2%,比MobileNetV2的72.0%要高。

🍀更快的推理速度:MobileNetV3通过增加Squeeze-and-Excitation模块和hard-swish非线性激活函数,提高了模型的计算效率,加快了推理速度。

🍀更少的参数和计算量:MobileNetV3在保持准确率不变的情况下,参数数量和计算量比MobileNetV2都要少。

🍀支持多种模型结构:MobileNetV3除了提供标准版模型外,还提供了Small模型和Large模型,可以根据不同的场景和需求选择合适的模型。

🍀支持自适应网络:MobileNetV3加入了Squeeze-and-Excite模块和自适应网络结构,可以根据输入图像的尺寸和分辨率自适应地调整模型的结构和参数,从而实现更好的模型泛化能力和适应性。

🍀支持权重重要性筛选:MobileNetV3提供了权重重要性筛选工具,可以定量地筛选出对模型性能影响最大的参数,从而实现更高效的模型压缩和优化。

总的来说,MobileNetV3 相对于 MobileNetV2,在准确率、推理速度、参数数量、计算量、模型结构和适应性等方面都有了显著的提升和改进。🌴


🚀3.YOLOv5结合MobileNetV3_small

💥💥步骤1:在common.py中添加MobileNetV3模块

将下面MobileNetV3模块的代码复制粘贴到common.py文件的末尾。

# Mobilenetv3Small
# ——————MobileNetV3——————
 
class h_sigmoid(nn.Module):
    def __init__(self, inplace=True):
        super(h_sigmoid, self).__init__()
        self.relu = nn.ReLU6(inplace=inplace)
 
    def forward(self, x):
        return self.relu(x + 3) / 6
 
 
class h_swish(nn.Module):
    def __init__(self, inplace=True):
        super(h_swish, self).__init__()
        self.sigmoid = h_sigmoid(inplace=inplace)
 
    def forward(self, x):
        return x * self.sigmoid(x)
 
 
class SELayer(nn.Module):
    def __init__(self, channel, reduction=4):
        super(SELayer, self).__init__()
        # Squeeze操作
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        # Excitation操作(FC+ReLU+FC+Sigmoid)
        self.fc = nn.Sequential(
            nn.Linear(channel, channel // reduction),
            nn.ReLU(inplace=True),
            nn.Linear(channel // reduction, channel),
            h_sigmoid()
        )
 
    def forward(self, x):
        b, c, _, _ = x.size()
        y = self.avg_pool(x)
        y = y.view(b, c)
        y = self.fc(y).view(b, c, 1, 1)  # 学习到的每一channel的权重
        return x * y
 
 
class conv_bn_hswish(nn.Module):
    """
    This equals to
    def conv_3x3_bn(inp, oup, stride):
        return nn.Sequential(
            nn.Conv2d(inp, oup, 3, stride, 1, bias=False),
            nn.BatchNorm2d(oup),
            h_swish()
        )
    """
 
    def __init__(self, c1, c2, stride):
        super(conv_bn_hswish, self).__init__()
        self.conv = nn.Conv2d(c1, c2, 3, stride, 1, bias=False)
        self.bn = nn.BatchNorm2d(c2)
        self.act = h_swish()
 
    def forward(self, x):
        return self.act(self.bn(self.conv(x)))
 
    def fuseforward(self, x):
        return self.act(self.conv(x))
 
 
class MobileNetV3(nn.Module):
    def __init__(self, inp, oup, hidden_dim, kernel_size, stride, use_se, use_hs):
        super(MobileNetV3, self).__init__()
        assert stride in [1, 2]
 
        self.identity = stride == 1 and inp == oup
 
        # 输入通道数=扩张通道数 则不进行通道扩张
        if inp == hidden_dim:
            self.conv = nn.Sequential(
                # dw
                nn.Conv2d(hidden_dim, hidden_dim, kernel_size, stride, (kernel_size - 1) // 2, groups=hidden_dim,
                          bias=False),
                nn.BatchNorm2d(hidden_dim),
                h_swish() if use_hs else nn.ReLU(inplace=True),
                # Squeeze-and-Excite
                SELayer(hidden_dim) if use_se else nn.Sequential(),
                # pw-linear
                nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
                nn.BatchNorm2d(oup),
            )
        else:
            # 否则 先进行通道扩张
            self.conv = nn.Sequential(
                # pw
                nn.Conv2d(inp, hidden_dim, 1, 1, 0, bias=False),
                nn.BatchNorm2d(hidden_dim),
                h_swish() if use_hs else nn.ReLU(inplace=True),
                # dw
                nn.Conv2d(hidden_dim, hidden_dim, kernel_size, stride, (kernel_size - 1) // 2, groups=hidden_dim,
                          bias=False),
                nn.BatchNorm2d(hidden_dim),
                # Squeeze-and-Excite
                SELayer(hidden_dim) if use_se else nn.Sequential(),
                h_swish() if use_hs else nn.ReLU(inplace=True),
                # pw-linear
                nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
                nn.BatchNorm2d(oup),
            )
 
    def forward(self, x):
        y = self.conv(x)
        if self.identity:
            return x + y
        else:
            return y

具体如下图所示:

​💥💥步骤2:在yolo.py文件中加入类名 

首先在yolo.py文件中找到parse_model函数这一行,加入h_sigmoidh_swishSELayerconv_bn_hswishMobileNetV3这五个模块。

​💥💥步骤3:创建自定义yaml文件 

models文件夹中复制yolov5s.yaml,粘贴并重命名为yolov5s_MobileNetV3.yaml

然后根据 MobileNetV3的网络架构来修改配置文件。

根据网络结构,我们可以看到MobileNetV3模块包含六个参数,即[out_ch,hidden_ch,kernel_size,stride,use_se,use_hs]

这六个参数的含义如下:

out_ch:输出通道

hidden_ch:表示在Inverted residuals中的扩张通道数。

kernel_size:卷积核大小

stride:步长

use_se:表示是否使用了SELayer,使用了是1,不使用是0。

use_hs:表示使用h_swish还是ReLU,使用h_swish是1,使用ReLU是0。

同样的,head部分这几个concat的层也要做修改:

yaml文件修改后的完整代码如下:

# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
 
# Parameters
nc: 80  # number of classes
depth_multiple: 1.0  # model depth multiple
width_multiple: 1.0  # layer channel multiple
anchors:
  - [10,13, 16,30, 33,23]  # P3/8
  - [30,61, 62,45, 59,119]  # P4/16
  - [116,90, 156,198, 373,326]  # P5/32
 
   # Mobilenetv3-small backbone
   # MobileNetV3_InvertedResidual [out_ch, hid_ch, k_s, stride, SE, HardSwish]
backbone:
  # [from, number, module, args]
  [[-1, 1, conv_bn_hswish, [16, 2]],             # 0-p1/2   320*320
   [-1, 1, MobileNetV3, [16,  16, 3, 2, 1, 0]],  # 1-p2/4   160*160
   [-1, 1, MobileNetV3, [24,  72, 3, 2, 0, 0]],  # 2-p3/8   80*80
   [-1, 1, MobileNetV3, [24,  88, 3, 1, 0, 0]],  # 3        80*80
   [-1, 1, MobileNetV3, [40,  96, 5, 2, 1, 1]],  # 4-p4/16  40*40
   [-1, 1, MobileNetV3, [40, 240, 5, 1, 1, 1]],  # 5        40*40
   [-1, 1, MobileNetV3, [40, 240, 5, 1, 1, 1]],  # 6        40*40
   [-1, 1, MobileNetV3, [48, 120, 5, 1, 1, 1]],  # 7        40*40
   [-1, 1, MobileNetV3, [48, 144, 5, 1, 1, 1]],  # 8        40*40
   [-1, 1, MobileNetV3, [96, 288, 5, 2, 1, 1]],  # 9-p5/32  20*20
   [-1, 1, MobileNetV3, [96, 576, 5, 1, 1, 1]],  # 10       20*20
   [-1, 1, MobileNetV3, [96, 576, 5, 1, 1, 1]],  # 11       20*20
  ]
 
# YOLOv5 v6.0 head
head:
  [[-1, 1, Conv, [96, 1, 1]],  # 12                         20*20
   [-1, 1, nn.Upsample, [None, 2, 'nearest']], # 13         40*40
   [[-1, 8], 1, Concat, [1]],  # cat backbone P4            40*40
   [-1, 3, C3, [144, False]],  # 15                         40*40
 
   [-1, 1, Conv, [144, 1, 1]], # 16                         40*40
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],# 17          80*80
   [[-1, 3], 1, Concat, [1]],  # cat backbone P3            80*80
   [-1, 3, C3, [168, False]],  # 19 (P3/8-small)            80*80
 
   [-1, 1, Conv, [168, 3, 2]], # 20                         40*40
   [[-1, 16], 1, Concat, [1]], # cat head P4                40*40
   [-1, 3, C3, [312, False]],  # 22 (P4/16-medium)          40*40
 
   [-1, 1, Conv, [312, 3, 2]], # 23                         20*20
   [[-1, 12], 1, Concat, [1]], # cat head P5                20*20
   [-1, 3, C3, [408, False]],  # 25 (P5/32-large)           20*20
 
   [[19, 22, 25], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)
  ]

💥💥步骤4:验证是否加入成功

yolo.py文件里,配置我们刚才自定义的yolov5s_MobileNetV3.yaml

然后运行yolo.py,得到结果。

 我们和原始的yolov5s.yaml进行对比。

可以看到替换主干网络为MobileNetV3之后层数变多了,可以学习到更多的特征,参数量由原来的700多万减少到500多万,大幅度减少了,GFLOPS也由16.4变为12.1。💯💯💯

💥💥步骤5:修改train.py中的'--cfg'默认参数

train.py文件中找到 parse_opt函数,然后将第二行'--cfg'的default改为 'models/yolov5s_MobileNetV3.yaml',然后就可以开始进行训练了。🎈🎈🎈


 🚀4.YOLOv5结合MobileNetV3_large

MobileNetV3_large 和 MobileNetV3_small的区别在于 yaml 文件中 head 中的 concat 连接不同,即深度因子和宽度因子不同。

步骤1和步骤2与上面的相同,我们直接开始步骤3,修改yaml文件部分。

💥💥步骤3:创建自定义yaml文件 

models文件夹中复制yolov5s.yaml,粘贴并重命名为yolov5s_MobileNetV3_large.yaml

然后根据 MobileNetV3的网络架构来修改配置文件。

yaml文件修改后的完整代码如下:

# Parameters
nc: 20  # number of classes
depth_multiple: 1.0  # model depth multiple
width_multiple: 1.0  # layer channel multiple
anchors:
  - [10,13, 16,30, 33,23]  # P3/8
  - [30,61, 62,45, 59,119]  # P4/16
  - [116,90, 156,198, 373,326]  # P5/32
 
# YOLOv5 v6.0 backbone
backbone:
 
  [[-1, 1, conv_bn_hswish, [16, 2]],                  # 0-p1/2
   [-1, 1, MobileNetV3, [ 16,  16, 3, 1, 0, 0]],  # 1-p1/2
   [-1, 1, MobileNetV3, [ 24,  64, 3, 2, 0, 0]],  # 2-p2/4
   [-1, 1, MobileNetV3, [ 24,  72, 3, 1, 0, 0]],  # 3-p2/4
   [-1, 1, MobileNetV3, [ 40,  72, 5, 2, 1, 0]],  # 4-p3/8
   [-1, 1, MobileNetV3, [ 40, 120, 5, 1, 1, 0]],  # 5-p3/8
   [-1, 1, MobileNetV3, [ 40, 120, 5, 1, 1, 0]],  # 6-p3/8
   [-1, 1, MobileNetV3, [ 80, 240, 3, 2, 0, 1]],  # 7-p4/16
   [-1, 1, MobileNetV3, [ 80, 200, 3, 1, 0, 1]],  # 8-p4/16
   [-1, 1, MobileNetV3, [ 80, 184, 3, 1, 0, 1]],  # 9-p4/16
   [-1, 1, MobileNetV3, [ 80, 184, 3, 1, 0, 1]],  # 10-p4/16
   [-1, 1, MobileNetV3, [112, 480, 3, 1, 1, 1]],  # 11-p4/16
   [-1, 1, MobileNetV3, [112, 672, 3, 1, 1, 1]],  # 12-p4/16
   [-1, 1, MobileNetV3, [160, 672, 5, 1, 1, 1]],  # 13-p4/16
   [-1, 1, MobileNetV3, [160, 960, 5, 2, 1, 1]],  # 14-p5/32   原672改为原算法960
   [-1, 1, MobileNetV3, [160, 960, 5, 1, 1, 1]],  # 15-p5/32
  ]
# YOLOv5 v6.0 head
head:
  [ [ -1, 1, Conv, [ 256, 1, 1 ] ],
    [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
    [ [ -1, 13], 1, Concat, [ 1 ] ],  # cat backbone P4
    [ -1, 1, C3, [ 256, False ] ],  # 13
 
    [ -1, 1, Conv, [ 128, 1, 1 ] ],
    [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
    [ [ -1, 6 ], 1, Concat, [ 1 ] ],  # cat backbone P3
    [ -1, 1, C3, [ 128, False ] ],  # 17 (P3/8-small)
 
    [ -1, 1, Conv, [ 128, 3, 2 ] ],
    [ [ -1, 20 ], 1, Concat, [ 1 ] ],  # cat head P4
    [ -1, 1, C3, [ 256, False ] ],  # 20 (P4/16-medium)
 
    [ -1, 1, Conv, [ 256, 3, 2 ] ],
    [ [ -1, 16 ], 1, Concat, [ 1 ] ],  # cat head P5
    [ -1, 1, C3, [ 512, False ] ],  # 23 (P5/32-large)
 
    [ [ 23, 26, 29 ], 1, Detect, [ nc, anchors ] ],  # Detect(P3, P4, P5)
  ]

 网络运行结果:

由结果可以看到,MobileNetV3_large模型比MobileNetV3_small多了更多的MobileNet_Block结构,参差倒置结构中通道数维度也增大了许多,速度比YOLOv5s慢近一半,但是参数更少,效果介乎MobileNetV3_smallYOLOv5s之间,可以作为模型对比,凸显自己模型的优势。🍃🍃🍃

说明:♨️♨️♨️

如果训练之后发现掉点纯属正常现象,因为轻量化网络在提速减少计算量的同时,也会降低精度。

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

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

相关文章

证明arcsinx+arccosx=π/2,并且为什么arcsinx-arccosx=π/2不成立

下面我们先直接用代数式来证明一下: 设 y 1 arcsin ⁡ x , y 2 arccos ⁡ x ,求 y 1 y 2 由于 x sin ⁡ y 1 cos ⁡ y 2 ,而 cos ⁡ y 2 sin ⁡ ( y 2 π 2 ) 那么就得到 y 1 y 2 π 2 ,即 y 1 − y 2 π 2 …

ViewPager+ Fragment结合的setUserVisibleHint()调用时机

最近的项目使用到了ViewPager Fragment的模式,要求在每次Fragment获取显示的时候来刷新数据,该项目下ViewPager有5个子fragment,在onCreateView及fragment的**setUserVisibleHint(bool isVisibleToUser)**中的isVisibleToUser为t…

Jmeter(二十七):BeanShell PostProcessor跨线程全局变量使用

在性能测试中,两个相关联的接口不一定都在同一个线程组,遇见这种情况时,我们要进行跨线程组传参,此处用登录和查询配送单两个请求举例; 1、登录请求中配置json提取器,将接口返回的token保存在变量中&#…

Linux 指令心法(二)`cd` 更改当前目录

文章目录 命令的概述和用途命令的用法命令行选项和参数的详细说明命令的示例命令的注意事项或提示 命令的概述和用途 cd 是 “Change Directory” 的缩写。这是一个 shell 内建命令,用于在 Linux 和 Unix 系统中改变当前工作目录。通过使用 cd 命令,用户…

Python小知识 - Python中的多线程

Python中的多线程 线程是进程中的一个执行单元,是轻量级的进程。一个进程可以创建多个线程,线程之间共享进程的资源,比如内存、文件句柄等。 在Python中,使用threading模块实现线程。 下面的代码创建了两个线程,一个输…

【数学建模】清风数模正课5 相关性分析

相关系数 相关性分析的关键是计算相关系数,在本节课中将会介绍两种常用的相关系数:皮尔逊相关系数(Pearson)和斯皮尔曼相关系数(Spearman)。 它们可以用来衡量两个变量间相关性的大小,对于不同…

后端面试话术集锦第三篇:spring cloud 面试话术

🚗后端面试集锦目录 💖后端面试话术集锦第一篇:spring面试话术💖 💖后端面试话术集锦第二篇:spring boot面试话术💖 💖后端面试话术集锦第三篇:spring cloud面试话术💖 💖后端面试话术集锦第四篇:ElasticSearch面试话术💖 💖后端面试话术集锦第五篇:r…

面试现场表现:展示你的编程能力和沟通技巧

🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…

【力扣】55、跳跃游戏

var canJump function(nums){let cover 0;for(let i0;i<nums.length;i){if(i<cover){cover Math.max(nums[i]i,cover);if(cover >nums.length-1){return true;}}}}

春秋云镜 CVE-2018-7448

春秋云镜 CVE-2018-7448 CMS Made Simple 2.1.6 RCE 靶标介绍 CMS Made Simple 2.1.6版本存在代码注入漏洞&#xff0c;可以通过 timezone 参数执行任意代码。 启动场景 漏洞利用 1、目的文件夹选择/var/www/html/cms 2、next到第四步&#xff0c;数据库名&#xff1a;cms…

2023-8-29 有向图的拓扑排序

题目链接&#xff1a;有向图的拓扑排序 #include <cstring> #include <iostream> #include <algorithm>using namespace std;const int N 100010;int n, m; int h[N], e[N], ne[N], idx;int q[N], d[N];void add(int a, int b) {e[idx] b, ne[idx] h[a]…

Android11.0 Launcher3定制化功能之抽屉式(双层)app列表排序功能实现

1.概述 在11.0的系统开发中,在定制Launcher3的开发中,对于抽屉式即双层桌面的workspace的app列表排序的功能,也是常有的需求,把常用的app图标放在前面,其他的可以放在列表后面做个整体的排序,这就需要了解app列表排序的流程,然后根据需求来实现功能 如图: 2.Launche…

面试中的代码写作:如何撰写清晰、高效的示例代码

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

代码随想录算法训练营之JAVA|第四十二天|70. 爬楼梯

今天是第 天刷leetcode&#xff0c;立个flag&#xff0c;打卡60天&#xff0c;如果做不到&#xff0c;完成一件评论区点赞最高的挑战。 算法挑战链接 70. 爬楼梯https://leetcode.cn/problems/climbing-stairs/ 第一想法 这是一个动态规划的入门题目&#xff0c;在看完完全背…

Flutter(九)Flutter动画和自定义组件

目录 1.动画简介2.动画实现和监听3. 自定义路由切换动画4. Hero动画5.交织动画6.动画切换7.Flutter预置的动画过渡组件自定义组件1.简介2.组合组件3.CustomPaint 和 RenderObject 1.动画简介 Animation、Curve、Controller、Tween这四个角色&#xff0c;它们一起配合来完成一个…

操作系统真题

操作系统真题 考点前驱图真题分页存储管理索引文件结构分段存储管理进程的状态进程的同步和互斥 考点 考试只会考察选择题 前驱图真题 c 这是常考题型 b 分页存储管理 将程序分页 --逻辑地址 将内存分为页框&#xff08;物理块&#xff09; --物理地址 程序页的大小和页框的大小…

ros2官方文档(基于humble版本)学习笔记

ros2官方文档&#xff08;基于humble版本&#xff09;学习笔记&#xff08;一&#xff09; 一、安装ROS2二、按教程学习1.CLI 工具配置环境 由于市面上专门讲ROS2开发的书籍不多&#xff0c;近期看完了《ROS机器人开发实践》其中大部分内容还是基于ROS1写的&#xff0c;涉及top…

图像扭曲之波浪扭曲

源码&#xff1a; void wave_sine(cv::Mat& src,cv::Mat& dst,double amplitude,double wavelength) {dst.create(src.rows, src.cols, CV_8UC3);dst.setTo(0);double xAmplitude amplitude;double yAmplitude amplitude;double xWavelength wavelength;double yWa…

【Linux】深入理解文件缓冲区

文章目录 问题引入如何理解缓冲区缓冲区刷新策略问题解释模拟一个文件缓冲区 问题引入 首先看一段代码&#xff1a; #include <stdio.h> #include <string.h> int main() {const char *msg0"hello printf\n";const char *msg1"hello fwrite\n&quo…

Ae 效果:CC Image Wipe

过渡/CC Image Wipe Transition/CC Image Wipe CC Image Wipe&#xff08;CC 图像擦除&#xff09;效果的主要用途是通过使用图像的某个属性&#xff08;如红通道、绿通道、蓝通道、Alpha 通道、亮度、明度、色相或饱和度&#xff09;来创建一个独特的过渡效果&#xff0c;可以…