7、关于LoFTR

news2024/9/22 17:24:40

7、关于LoFTR

LoFTR论文链接:LoFTR

LoFTR的提出,是将Transformer模型的注意力机制在特征匹配方向的应用,Transformer的提取特征的机制,在自身进行,本文提出可以的两张图像之间进行特征计算,非常适合进行特征匹配。

一、传统匹配模式的局限
  1. 首先找到一些关键点(图像梯度较高,角点检测等),然后计算特征相似度来匹配,这种方法很依赖检测到的特征点,一旦点找不到,那就不用说匹配了

在这里插入图片描述

  1. 对于位置不同的两个点,如果它们的背景特征相似(与位置无关了),也无法匹配,两者差别不大的,就无法区分了

在这里插入图片描述

二、LoFTR解决方案
  1. 不需要先得到特征点,第一个问题就得到了解决
  2. 采用end2end(给一个输入,得到结果)方法,使用非常方便

在这里插入图片描述

三、模型架构图

模型进行特征匹配主要分为四步:

  1. Local Feature CNN,就是一个blockbone,对两张图特征图进行卷积,分别得到原来特征图大小的1/8(用来进行粗粒度的匹配)和1/2(用来进行细粒度的匹配) 大小特征图。
  2. Coarse-Level Local Feature Transformer,是对两张1/8的特征图(FA,FB)进行多个串联的self-attention(自己的q和k向量做内积)和cross-attention(FA提供q,FB提供V做内积)来计算自己每个区域内之间的关系和两张图不同区域内的关系。
  3. Matching Module,到目前位置都是进行粗粒度匹配,经过了多层的Coarse-Level Local Feature Transformer,FA,FB已经了解了自身的关系,同时也知道了和对方之间的关系,要进行关系的匹配了,采用互近邻(mutual nearest neighbor (MNN))的方式匹配,简单来说,假设FA对某个点信息概率值大, 但是同时也要某个点对A的概率值也大,必须双向的,源码阈值设置为0.2,也就是互相的结果都大于0.2才能匹配成功,筛选得到符合阈值点传入下一层。
  4. Coarse-to-Fine Module,在经过粗粒度匹配之后,会得到一些候选点区域,这些候选点区域会传到细粒度,细粒度其实就是在匹配的小区域上再做一次Coarse-Level Local Feature Transformer,同样会互相了解对方的特征,但是这里不是采用MNN机制,假设FA的小区域中的某个点有FB对应小区域中的所有点匹配的概率值,才将这写概率值绘制成类似于热力图,再对整个图计算期望值,得到最终的匹配点坐标。

在这里插入图片描述

核心代码
class LoFTR(nn.Module):# ... 省略初始化代码
    def forward(self, data):
        """ 
        前向传播函数:
        参数:
            data (dict): 包含图像和可选掩码的字典
                'image0': (torch.Tensor): (N, 1, H, W) 第一张图像
                'image1': (torch.Tensor): (N, 1, H, W) 第二张图像
                'mask0' (可选): (torch.Tensor): (N, H, W) 第一张图像的掩码,'0' 表示填充位置
                'mask1' (可选): (torch.Tensor): (N, H, W) 第二张图像的掩码
        """
        # 更新数据字典,添加批次大小和图像尺寸
        data.update({
            'bs': data['image0'].size(0),  # 批次大小
            'hw0_i': data['image0'].shape[2:],  # 第一张图像的高度和宽度
            'hw1_i': data['image1'].shape[2:]   # 第二张图像的高度和宽度
        })

        # 如果两张图像的尺寸相同,进行联合处理
        if data['hw0_i'] == data['hw1_i']:
            print("Concatenated Images Shape:", torch.cat([data['image0'], data['image1']], dim=0).shape) # ([2, 1, 480, 640])
            feats_c, feats_f = self.backbone(torch.cat([data['image0'], data['image1']], dim=0))
            print("Feature Coarse Shape:", feats_c.shape) # 1/8  ([2, 256, 60, 80])
            print("Feature Fine Shape:", feats_f.shape) # 1/2  ([2, 128, 240, 320])
            # 分离两张图像的特征
            (feat_c0, feat_c1), (feat_f0, feat_f1) = feats_c.split(data['bs']), feats_f.split(data['bs'])
            print("Feature Coarse Image 0 Shape:", feat_c0.shape) # ([1, 256, 60, 80])
            print("Feature Coarse Image 1 Shape:", feat_c1.shape) # ([1, 256, 60, 80])
            print("Feature Fine Image 0 Shape:", feat_f0.shape) # ([1, 128, 240, 320])
            print("Feature Fine Image 1 Shape:", feat_f1.shape) # ([1, 128, 240, 320])
        else:
            # 处理不同尺寸的图像
            (feat_c0, feat_f0), (feat_c1, feat_f1) = self.backbone(data['image0']), self.backbone(data['image1'])
            print("Feature Coarse Image 0 Shape:", feat_c0.shape)
            print("Feature Coarse Image 1 Shape:", feat_c1.shape)
            print("Feature Fine Image 0 Shape:", feat_f0.shape)
            print("Feature Fine Image 1 Shape:", feat_f1.shape)

        # 更新数据字典,添加特征图的尺寸
        data.update({
            'hw0_c': feat_c0.shape[2:],  # 第一张图像粗特征的高度和宽度
            'hw1_c': feat_c1.shape[2:],  # 第二张图像粗特征的高度和宽度
            'hw0_f': feat_f0.shape[2:],  # 第一张图像细特征的高度和宽度
            'hw1_f': feat_f1.shape[2:]   # 第二张图像细特征的高度和宽度
        })

        # 2. 粗级别局部变换模块
        # 添加位置编码信息
        feat_c0 = rearrange(self.pos_encoding(feat_c0), 'n c h w -> n (h w) c')
        print("Encoded Coarse Feature Image 0 Shape:", feat_c0.shape) # ([1, 4800, 256]) # 总共4800个点,每个点事256维向量
        feat_c1 = rearrange(self.pos_encoding(feat_c1), 'n c h w -> n (h w) c')
        print("Encoded Coarse Feature Image 1 Shape:", feat_c1.shape) # ([1, 4800, 256])
        mask_c0 = mask_c1 = None  # 掩码在训练中有用
        if 'mask0' in data:
            mask_c0, mask_c1 = data['mask0'].flatten(-2), data['mask1'].flatten(-2)
        # 进行粗级别局部变换
        feat_c0, feat_c1 = self.loftr_coarse(feat_c0, feat_c1, mask_c0, mask_c1) 
        print("Transformed Coarse Feature Image 0 Shape:", feat_c0.shape) # ([1, 4800, 256]) # 计算后输出结果不变
        print("Transformed Coarse Feature Image 1 Shape:", feat_c1.shape) # ([1, 4800, 256]) # 计算后输出结果不变

        # 3. 粗级别匹配
        self.coarse_matching(feat_c0, feat_c1, data, mask_c0=mask_c0, mask_c1=mask_c1) 

        # 4. 细级别细化
        feat_f0_unfold, feat_f1_unfold = self.fine_preprocess(feat_f0, feat_f1, feat_c0, feat_c1, data)
        if feat_f0_unfold.size(0) != 0:  # 至少有一个粗级别预测
            feat_f0_unfold, feat_f1_unfold = self.loftr_fine(feat_f0_unfold, feat_f1_unfold)

        # 5. 细级别匹配
        self.fine_matching(feat_f0_unfold, feat_f1_unfold, data)

        return x
Local Feature CNN

Local Feature CNN,就是一个blockbone,对两张图特征图进行卷积,分别得到原来特征图大小的1/8(用来进行粗粒度的匹配)和1/2(用来进行细粒度的匹配) 大小特征图。

# 来源 feats_c, feats_f = self.backbone(torch.cat([data['image0'], data['image1']], dim=0))
def build_backbone(config):
    if config['backbone_type'] == 'ResNetFPN':
        if config['resolution'] == (8, 2):
            return  ResNetFPN_8_2(config['resnetfpn'])
        # ...
class ResNetFPN_8_2(nn.Module):
    """
    ResNet+FPN, output resolution are 1/8 and 1/2.
    Each block has 2 layers.
    """
	# 使用ResNet+FPN获得特征图1/8 和 1/2
    # 关于FPN,FPN(Feature Pyramid Networks)是一种用于目标检测和分割任务的神经网络架构,
    # 特别适用于处理多尺度的图像特征。它通过建立特征金字塔来捕捉不同尺度的图像信息,增强了模型对不同尺度目标的检测能力。
    def __init__(self, config):
     	# ... 具体代码太长不展示
Coarse-Level Local Transformer

Coarse-Level Local Feature Transformer,是对两张1/8的特征图(FA,FB)进行多个串联的self-attention(自己的q和k向量做内积)和cross-attention(FA提供q,FB提供V做内积)来计算自己每个区域内之间的关系和两张图不同区域内的关系。

# 来源核心代码中  feat_c0, feat_c1 = self.loftr_coarse(feat_c0, feat_c1, mask_c0, mask_c1)
class LocalFeatureTransformer(nn.Module):
    def forward(self, feat0, feat1, mask0=None, mask1=None):
    """
    前向传播函数:
    参数:
        feat0 (torch.Tensor): 特征图 0,形状为 [N, L, C]
        feat1 (torch.Tensor): 特征图 1,形状为 [N, S, C]
        mask0 (torch.Tensor): 特征图 0 的掩码,形状为 [N, L](可选)
        mask1 (torch.Tensor): 特征图 1 的掩码,形状为 [N, S](可选)
    """
    
    # 确保特征图的通道数与 transformer 的模型维度一致
    assert self.d_model == feat0.size(2), "the feature number of src and transformer must be equal"
    
    # 迭代处理每一层,根据名称决定操作,总共4个self-cross,两个额为一组
    for layer, name in zip(self.layers, self.layer_names):  # ['self', 'cross', 'self', 'cross', 'self', 'cross', 'self', 'cross']
        if name == 'self':
            # 自注意力机制:feat0 本身计算 q 和 k
            feat0 = layer(feat0, feat0, mask0, mask0)
            print("Self Attention Output feat0 Shape:", feat0.shape) # ([1, 4800, 256])
            # 自注意力机制:feat1 本身计算 q 和 k
            feat1 = layer(feat1, feat1, mask1, mask1)
            print("Self Attention Output feat1 Shape:", feat1.shape) # ([1, 4800, 256])
        elif name == 'cross':
            # 交叉注意力机制:feat0 提供 q,feat1 提供 k 和 v
            feat0 = layer(feat0, feat1, mask0, mask1)
            print("Cross Attention Output feat0 Shape:", feat0.shape) # ([1, 4800, 256])
            # 交叉注意力机制:feat1 提供 q,feat0 提供 k 和 v
            feat1 = layer(feat1, feat0, mask1, mask0)
            print("Cross Attention Output feat1 Shape:", feat1.shape) # ([1, 4800, 256])
        else:
            raise KeyError("Unknown layer type")
    
    print("Final feat0 Shape:", feat0.shape) # ([1, 4800, 256]) 计算后向量维度是不变的
    print("Final feat1 Shape:", feat1.shape) # ([1, 4800, 256])
    
    return feat0, feat1
Matching Module

Matching Module,到目前位置都是进行粗粒度匹配,经过了多层的Coarse-Level Local Feature Transformer,FA,FB已经了解了自身的关系,同时也知道了和对方之间的关系,要进行关系的匹配了,采用互近邻(mutual nearest neighbor (MNN))的方式匹配,简单来说,假设FA对某个点信息概率值大, 但是同时也要某个点对A的概率值也大,必须双向的,源码阈值设置为0.2,也就是互相的结果都大于0.2才能匹配成功,筛选得到符合阈值点传入下一层。

# 来源核心代码中  self.coarse_matching(feat_c0, feat_c1, data, mask_c0=mask_c0, mask_c1=mask_c1) 
class CoarseMatching(nn.Module):
    def forward(self, feat_c0, feat_c1, data, mask_c0=None, mask_c1=None):
    """
    前向传播函数:
    
    参数:
        feat_c0 (torch.Tensor): 特征图 0,形状为 [N, L, C]
        feat_c1 (torch.Tensor): 特征图 1,形状为 [N, S, C]
        data (dict): 额外数据字典
        mask_c0 (torch.Tensor): 特征图 0 的掩码,形状为 [N, L](可选)
        mask_c1 (torch.Tensor): 特征图 1 的掩码,形状为 [N, S](可选)
    """
    
    # 提取批次大小、特征图 0 的长度、特征图 1 的长度和特征图的通道数
    N, L, S, C = feat_c0.size(0), feat_c0.size(1), feat_c1.size(1), feat_c0.size(2)
    print(feat_c0.shape) # ([1, 4800, 256])
    # 对特征图进行归一化
    feat_c0, feat_c1 = map(lambda feat: feat / feat.shape[-1]**.5, [feat_c0, feat_c1])

    if self.match_type == 'dual_softmax':
        # 计算相似度矩阵
        sim_matrix = torch.einsum("nlc,nsc->nls", feat_c0, feat_c1) / self.temperature
        print("Similarity Matrix Shape:", sim_matrix.shape) # ([1, 4800, 4800]) ,feat_c0中的4800和feat_c1进行计算得到([1, 4800, 4800])
        
        if mask_c0 is not None:
            # 应用掩码,将不需要的位置填充为负无穷大
            sim_matrix.masked_fill_(
                ~(mask_c0[..., None] * mask_c1[:, None]).bool(),
                -float('inf')
            )
        
        # 计算置信度矩阵
        conf_matrix = F.softmax(sim_matrix, 1) * F.softmax(sim_matrix, 2)
        print("Confidence Matrix Shape:", conf_matrix.shape) # ([1, 4800, 4800])
        
    # ... 省略部分没走的代码
    
    # 更新数据字典
    data.update({'conf_matrix': conf_matrix})
    
    # 从置信度矩阵中预测粗略匹配
    data.update(**self.get_coarse_match(conf_matrix, data))

再进行下一步细粒度匹配之前,需要将粗粒度匹配出来结果和1/2特征图大小,进行处理,找出所有待细粒度处理的各个区域

# 来源核心代码中  feat_f0_unfold, feat_f1_unfold = self.fine_preprocess(feat_f0, feat_f1, feat_c0, feat_c1, data)
class FinePreprocess(nn.Module):
    def forward(self, feat_f0, feat_f1, feat_c0, feat_c1, data):
    """
    前向传播函数:

    参数:
        feat_f0 (torch.Tensor): 特征图 0,形状为 [N, C, H, W]
        feat_f1 (torch.Tensor): 特征图 1,形状为 [N, C, H, W]
        feat_c0 (torch.Tensor): 粗级特征图 0,形状为 [N, L, C]
        feat_c1 (torch.Tensor): 粗级特征图 1,形状为 [N, L, C]
        data (dict): 包含额外信息的数据字典

    更新:
        data (dict): 更新数据字典,包括:
            'W' (int): 窗口大小
    """
    
    # 获取窗口大小和步幅
    W = self.W
    stride = data['hw0_f'][0] // data['hw0_c'][0]

    # 更新数据字典
    data.update({'W': W})

    # 如果没有有效的批次 ID,返回空张量
    if data['b_ids'].shape[0] == 0:
        feat0 = torch.empty(0, self.W**2, self.d_model_f, device=feat_f0.device)
        feat1 = torch.empty(0, self.W**2, self.d_model_f, device=feat_f0.device)
        return feat0, feat1

    # 1. 展开所有局部窗口
    # 使用 unfold 函数提取特征图中的局部窗口
    feat_f0_unfold = F.unfold(feat_f0, kernel_size=(W, W), stride=stride, padding=W//2)
    feat_f0_unfold = rearrange(feat_f0_unfold, 'n (c ww) l -> n l ww c', ww=W**2)
    
    feat_f1_unfold = F.unfold(feat_f1, kernel_size=(W, W), stride=stride, padding=W//2)
    feat_f1_unfold = rearrange(feat_f1_unfold, 'n (c ww) l -> n l ww c', ww=W**2)

    # 2. 仅选择预测的匹配
    # 根据数据中的批次 ID 和点 ID 选择特定的匹配特征
    feat_f0_unfold = feat_f0_unfold[data['b_ids'], data['i_ids']]  # [n, ww, cf]
    feat_f1_unfold = feat_f1_unfold[data['b_ids'], data['j_ids']]  # [n, ww, cf]

    # 选项:使用粗级别的 Loftr 特征作为上下文信息:连接和线性变换
    if self.cat_c_feat:
        # 从粗级别特征中选择窗口,并进行线性变换
        feat_c_win = self.down_proj(torch.cat([
            feat_c0[data['b_ids'], data['i_ids']],
            feat_c1[data['b_ids'], data['j_ids']]
        ], 0))  # [2n, c]
        
        # 合并特征图
        feat_cf_win = self.merge_feat(torch.cat([
            torch.cat([feat_f0_unfold, feat_f1_unfold], 0),  # [2n, ww, cf]
            repeat(feat_c_win, 'n c -> n ww c', ww=W**2),  # [2n, ww, cf]
        ], -1))
        
        # 将合并后的特征图分为两部分
        feat_f0_unfold, feat_f1_unfold = torch.chunk(feat_cf_win, 2, dim=0)
    
    return feat_f0_unfold, feat_f1_unfold
Coarse-to-Fine Module

Coarse-to-Fine Module,在经过粗粒度匹配之后,会得到一些候选点区域,这些候选点区域会传到细粒度,细粒度其实就是在匹配的小区域上再做一次Coarse-Level Local Feature Transformer,同样会互相了解对方的特征,但是这里不是采用MNN机制,假设FA的小区域中的某个点有FB对应小区域中的所有点匹配的概率值,才将这写概率值绘制成类似于热力图,再对整个图计算期望值,得到最终的匹配点坐标。

# 来源核心代码中 self.fine_matching(feat_f0_unfold, feat_f1_unfold, data)
class FineMatching(nn.Module):
    def forward(self, feat_f0, feat_f1, data):
    """
    前向传播函数:

    参数:
        feat_f0 (torch.Tensor): 特征图 0,形状为 [M, WW, C],其中 M 是匹配的数量,WW 是窗口大小的平方,C 是通道数
        feat_f1 (torch.Tensor): 特征图 1,形状为 [M, WW, C]
        data (dict): 包含额外信息的数据字典
    """
    M, WW, C = feat_f0.shape  # 提取特征图的形状信息
    W = int(math.sqrt(WW))  # 计算窗口的边长
    scale = data['hw0_i'][0] / data['hw0_f'][0]  # 计算缩放因子
    self.M, self.W, self.WW, self.C, self.scale = M, W, WW, C, scale

    # 特殊情况处理:如果没有找到粗级别匹配
    if M == 0:
        assert not self.training, "在训练阶段,M 应始终大于0,请检查 coarse_matching.py"
        # logger.warning('在粗级别没有找到匹配。')
        data.update({
            'expec_f': torch.empty(0, 3, device=feat_f0.device),  # 返回空的期望位置和标准差
            'mkpts0_f': data['mkpts0_c'],  # 使用粗级别的关键点作为回退
            'mkpts1_f': data['mkpts1_c'],
        })
        return

    # 选择特征图中心的特征
    feat_f0_picked = feat_f0[:, WW//2, :]
    print(feat_f0_picked.shape) # ([1541, 128]) 全部待计算的特征区域个数
    # 计算特征之间的相似度矩阵
    sim_matrix = torch.einsum('mc,mrc->mr', feat_f0_picked, feat_f1)
    print(sim_matrix.shape) # ([1541, 128]) 
    
    # 使用 softmax 函数计算热图
    softmax_temp = 1. / C**.5
    heatmap = torch.softmax(softmax_temp * sim_matrix, dim=1).view(-1, W, W)
    print(heatmap.shape) # ([1541, 5, 5]) # 1541 个 5x5的热力图矩阵

    # 从热图中计算坐标,计算期望值
    coords_normalized = dsnt.spatial_expectation2d(heatmap[None], True)[0]  # 计算归一化坐标
    print(coords_normalized.shape) #([1541, 2]) # 1541 个(x,y)坐标,都是小数,表示占图大小比例
    grid_normalized = create_meshgrid(W, W, True, heatmap.device).reshape(1, -1, 2)  # 创建网格
    print(grid_normalized.shape) #([1, 25, 2])
    # 计算标准差
    var = torch.sum(grid_normalized**2 * heatmap.view(-1, WW, 1), dim=1) - coords_normalized**2  # 计算方差
    std = torch.sum(torch.sqrt(torch.clamp(var, min=1e-10)), -1)  # 计算标准差,使用 clamp 以保证数值稳定性
    
    # 更新数据字典以用于精细级别的监督
    data.update({'expec_f': torch.cat([coords_normalized, std.unsqueeze(1)], -1)})

    # 计算绝对的关键点坐标,按图长宽比例还原位置坐标
    self.get_fine_match(coords_normalized, data)

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

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

相关文章

GAMES101(5~6节,光栅化)

光栅化Rasterization 透视投影已知field和近平面,如何推导宽度? 根据三角函数:tan field / 2 (height / 2) / ||n||近平面,从而可以求出高度 因为知道宽高比,所以可以求出宽度,高度 * 宽/高 视口变换 …

Vulkan描述符、描述符Pool、Layout概念

1、DescriptorSetLayout为了组织和管理着色器资源(如缓冲区、纹理、采样器等),多个相同类型的Descriptor放在一个Layout中以优化GPU对资源的访问 //DescriptorSetLayout定义了哪些描述符Descriptor类型(Buffers、Textures、Sample…

四、搭建网站服务器超详细步骤——解决宝塔界面无法登录问题

前言 本篇博客是搭建网站服务器的第四期,也到了中间的一节 先分享一下我在搭建网站时的个人感受,我在这个环节卡住了很久 后来突然醒悟了,然后成功进入了宝塔界面 现在就来分享一下,我所遇到的问题 小伙伴们坐好了 …

LINUX和Windows提权前信息收集的两种方式

信息收集分为两种方式: 手动收集、自动收集; 手动枚举一个系统非常耗时,然而这种方法允许更多的控制,并可以帮助识别自动化工具经常遗漏的更奇特的提权提升方式; 一、手动收集 1、枚举用户 Windows whoami …

【C++】手动实现nique_ptr智能指针

1、自己实现封装了一个智能指针&#xff0c;并且使用了模板 目录 代码实现&#xff1a; 输出结果如下&#xff1a; 代码实现&#xff1a; #include <iostream>using namespace std;template <typename T> class UniquePtr { private:T *ptr;public://默认构造函…

【2024数学建模国赛赛题思路】C题第四套思路已出丨仅供参考

问题一思路分析 假定各种农作物未来的预期销售量、种植成本、亩产量和销售价格相对于2023年保持稳定&#xff0c;每季种植的农作物在当季销售。如果某种作物每季的总产量超过相应的预期销售量&#xff0c;超过部分不能正常销售。请针对以下两种情况&#xff0c;分别给出该乡村…

语法分析例题

答案&#xff1a;A 选项B&#xff0c;选项C&#xff0c;选项D都只有在程序运行后才能看出来是否正确 答案&#xff1a;D 自己写一个C程序&#xff0c;未赋值&#xff0c;就可以看出可以运行&#xff0c;但结果是乱码 #include<stdio.h>int main() {int a;int b;b10;bb…

图形推理-错题集

001 解析&#xff1a; 002 解析&#xff1a; 003 解析&#xff1a; 004 005 006

day09-IO-字符流其它流

一、字符流 字符流&#xff08;只能做文本文件的处理&#xff09;字符输入流 Reader--FileReader字符输出流 Writer--FileWriter ​ 使用文件字符输入流的好处&#xff1a;读取中文不会出现乱码问题 1.1 字符输入流 构造器说明public FileReader (File file)创建字符输入流管道…

软件测试永远的家——银行测试,YYDS

为什么做金融类软件测试举个栗子&#xff0c;银行里的软件测试工程师。横向跟互联网公司里的测试来说&#xff0c;薪资相对稳定&#xff0c;加班少甚至基本没有&#xff0c;业务稳定。实在是测试类岗位中的香饽饽&#xff01; 一、什么是金融行业 金融业是指经营金融商品的特…

VSCode中latex文件(Misplaced alignment tab character .LaTeX

Misplaced alignment tab character &.LaTeX 先给出参考文章1 Misplaced alignment tab character &.LaTeX 把bib文件中的 &改为 and 。删除原有的bbl文件、重新运行 选择这个运行 这个错误在overleaf上并没有遇到、在vscode上遇到了 方法二就是把 &改为…

医用介入导管的种类以及在线检测设备

关键字&#xff1a;介入导管生产线&#xff0c;医用导管检测设备&#xff0c;指引导管测径仪&#xff0c;球囊导管测量仪&#xff0c;医用导管测量&#xff0c;医用导管生产线&#xff0c;溶栓导管测径仪&#xff0c;造影导管测量仪 介入导管在医疗领域&#xff0c;特别是血管内…

机器学习之实战篇——MNIST手写数字0~9识别(全连接神经网络模型)

机器学习之实战篇——Mnist手写数字0~9识别&#xff08;全连接神经网络模型&#xff09; 文章传送MNIST数据集介绍&#xff1a;实验过程实验环境导入模块导入MNIST数据集创建神经网络模型进行训练&#xff0c;测试&#xff0c;评估模型优化 文章传送 机器学习之监督学习&#…

别再羡慕别人啦,四种方法轻松打造自己的IP形象

大家好&#xff0c;我是宇航&#xff0c;10年技术专家&#xff0c;专注于AI绘画&#xff0c;AI视频 做自媒体的小伙伴第一件事儿就是起一个IP名称和制作IP图像。制作图像这件事儿对于很多小伙伴来说都不太容易&#xff0c;有的小伙伴制作了很久还是没有做出自己满意的图像。 …

使用Python本地搭建http.server文件共享服务并实现公网环境远程访问——“cpolar内网穿透”

前言 本文主要介绍如何在Windows系统电脑上使用python这样的简单程序语言&#xff0c;在自己的电脑上搭建一个共享文件服务器&#xff0c;并通过cpolar创建的公网地址&#xff0c;打造一个可以随时随地远程访问的私人云盘。 数据共享作为和连接作为互联网的基础应用&#xff…

Spring Boot项目更改项目名称

背景&#xff1a;新项目开始前&#xff0c;往往需要初始化功能&#xff0c;拿到基础版本后更改项目对应的名称等信息。 更改步骤如下&#xff1a; 1、修改目录名称。 打开本地项目&#xff0c;右键修改项目名称。 2、修改maven项目的pom依赖 修改parent及modules项目名称&…

C++语法知识点合集:7.string类

文章目录 一、标准库中的string类1.string类2.auto和范围for3.string类的常用接口说明 二、string类的模拟实现1. 经典的string类问题2.浅拷贝3.深拷贝 一、标准库中的string类 1.string类 string是表示字符串的字符串类该类的接口与常规容器的接口基本相同&#xff0c;再添加…

鸿蒙 HarmonyOS 下拉控件

✍️作者简介&#xff1a;小北编程&#xff08;专注于HarmonyOS、Android、Java、Web、TCP/IP等技术方向&#xff09; &#x1f433;博客主页&#xff1a; 开源中国、稀土掘金、51cto博客、博客园、知乎、简书、慕课网、CSDN &#x1f514;如果文章对您有一定的帮助请&#x1f…

Verilog和Matlab实现RGB888互转YUV444

文章目录 一、色彩空间1.1 RGB色彩空间1.2 CMYK色彩空间1.3 YUV色彩空间 二、色彩空间转换公式2.1 RGB转CMYK2.2 CMYK转RGB2.3 RGB888转YUV4442.4 YUV444转RGB888 三、MATLAB实现RGB888转YUV4443.1 matlab代码3.2 matlab结果 四、Verilog实现RGB888转YUV444 一、色彩空间 色彩空…

python_openCV_计算图片中的区域的黑色比例

希望对原始图片进行处理,然后计算图片上的黑色和白色的占比 上图, 原始图片 import numpy as np import cv2 import matplotlib.pyplot as pltdef cal_black(img_file):#功能: 计算图片中的区域的黑色比例#取图片中不同的位置进行计算,然后计算器数值#----------------p…