金字塔模型

        金字塔模型(Pyramid Model)在深度学习中是一种用于多尺度图像分析和处理的模型结构,它基于金字塔的概念,即从图像的不同尺度或分辨率上提取信息。金字塔模型特别适用于处理需要在不同尺度上捕捉信息的任务,例如物体检测、分割和图像检索。

        金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似。将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低。 

        金字塔模型的核心思想是通过创建图像的多个尺度版本来捕捉不同层次的信息。这些尺度版本通常称为金字塔层级,每一层都包含图像在不同分辨率下的表示。金字塔模型通常包括以下几个关键步骤:

1. 图像金字塔

        创建图像的不同尺度(分辨率)的版本。通常有两种类型的金字塔:

        高斯金字塔:通过不断地应用高斯模糊和下采样生成不同分辨率的图像。

        拉普拉斯金字塔:在高斯金字塔的基础上,通过计算每一层与上层的差异来捕捉细节信息。

 2. 特征金字塔

        特征金字塔是在网络的不同层级上提取特征图,这些特征图代表了不同尺度的特征。特征金字塔通常利用多层卷积层来捕捉不同层次的特征。

金字塔模型的应用示例

        FPN(Feature Pyramid Networks)是一种经典的特征金字塔模型,广泛应用于目标检测和分割任务中。FPN的关键思想是利用卷积神经网络的不同层级提取多尺度特征,并在这些特征之间进行融合,以获得更丰富的特征表示。

2.1 FPN的结构

        骨干网络(Backbone Network)

        使用一个标准的卷积神经网络(如ResNet)作为骨干网络,从中提取不同层级的特征图。

        金字塔特征图生成

        从骨干网络的多个层级提取特征图,并将这些特征图用于构建特征金字塔。通常,FPN会提取多个层级的特征图(如高层特征图、中层特征图、低层特征图)。

        特征融合(Feature Fusion)

        通过上采样和卷积操作,将低层特征图与高层特征图融合。具体地,FPN会对低层特征图进行上采样,并与高层特征图进行相加,结合高层的语义信息和低层的细节信息。

        预测层

        在融合后的特征图上进行目标检测或分割预测。这些预测可以结合不同尺度的特征信息,从而提高检测精度和鲁棒性。

2.2 金字塔模型代码示例

# 定义一个简单的卷积网络作为骨干网络
class SimpleBackbone(nn.Module):
    def __init__(self):
        super(SimpleBackbone, self).__init__()
        self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=2, padding=1)  # 64x64
        self.conv2 = nn.Conv2d(16, 32, kernel_size=3, stride=2, padding=1) # 32x32
        self.conv3 = nn.Conv2d(32, 64, kernel_size=3, stride=2, padding=1) # 16x16
        self.conv4 = nn.Conv2d(64, 128, kernel_size=3, stride=2, padding=1) # 8x8

    def forward(self, x):
        x1 = F.relu(self.conv1(x))  # 64x64
        x2 = F.relu(self.conv2(x1)) # 32x32
        x3 = F.relu(self.conv3(x2)) # 16x16
        x4 = F.relu(self.conv4(x3)) # 8x8
        return x1, x2, x3, x4

        骨干网络

         一个简单的卷积网络,用于从输入图像中提取不同层级的特征图。它依次经过四个卷积层,生成四个不同尺度的特征图。

# 定义特征金字塔网络(FPN)
class FeaturePyramidNetwork(nn.Module):
    def __init__(self):
        super(FeaturePyramidNetwork, self).__init__()
        # 1x1卷积用于减少通道数
        self.reduce_conv1 = nn.Conv2d(16, 16, kernel_size=1)
        self.reduce_conv2 = nn.Conv2d(32, 16, kernel_size=1)
        self.reduce_conv3 = nn.Conv2d(64, 16, kernel_size=1)
        self.reduce_conv4 = nn.Conv2d(128, 16, kernel_size=1)

        # 上采样卷积
        self.upsample_conv1 = nn.Conv2d(16, 16, kernel_size=3, padding=1)
        self.upsample_conv2 = nn.Conv2d(16, 16, kernel_size=3, padding=1)

    def forward(self, x1, x2, x3, x4):
        # 使用1x1卷积减少通道数
        p4 = self.reduce_conv4(x4)  # 8x8
        p3 = self.reduce_conv3(x3)  # 16x16
        p2 = self.reduce_conv2(x2)  # 32x32
        p1 = self.reduce_conv1(x1)  # 64x64

        # 上采样
        p3_up = F.interpolate(p4, scale_factor=2, mode='bilinear', align_corners=False) + p3
        p2_up = F.interpolate(p3_up, scale_factor=2, mode='bilinear', align_corners=False) + p2
        p1_up = F.interpolate(p2_up, scale_factor=2, mode='bilinear', align_corners=False) + p1

        return p1_up, p2_up, p3_up, p4

特征金字塔网络(FeaturePyramidNetwork

  • 通过1x1卷积减少特征图的通道数。
  • 使用上采样将特征图上采样到较大的尺寸,并与其他层级的特征图进行相加,融合不同尺度的信息。

         F.interpolate 是 PyTorch 中的一个函数,用于对张量进行插值操作,也就是改变张量的大小

        p4 是输入特征图,它来自于网络中较深层的卷积层。这个特征图通常具有较低的空间分辨率

       scale_factor=2 表示将特征图的大小扩大 2 倍,指的是空间尺寸(宽度和高度)的上采样

      mode='bilinear' 指定了插值的方式。使用的双线性插值(bilinear interpolation),这是一种常见的图像插值方法,适合处理连续值的图像数据

       align_corners=False 是一个控制插值方法的参数。它决定了插值时是否对齐输入和输出图像的角点。通常,align_corners=False 是推荐的设置,它在多数情况下能够生成更自然的插值结果。

        通过将低分辨率的深层特征图 p4 上采样到与高分辨率的浅层特征图 p3 相同的分辨率,然后将二者相加,可以结合深层特征的语义信息和浅层特征的细节信息。

一个简单的训练过程

# 定义一个简单的训练过程
def train(model, input_image):
    backbone = SimpleBackbone()
    fpn = FeaturePyramidNetwork()
    backbone.eval()
    fpn.eval()

    # 通过骨干网络提取特征
    x1, x2, x3, x4 = backbone(input_image)

    # 通过FPN提取多尺度特征
    p1, p2, p3, p4 = fpn(x1, x2, x3, x4)

    return p1, p2, p3, p4

加载示例图像

# 加载一个示例图像
def load_image(image_path):
    transform = transforms.Compose([
        transforms.Resize((256, 256)),
        transforms.ToTensor(),
    ])
    image = Image.open(image_path).convert('RGB')
    image = transform(image)
    image = image.unsqueeze(0)  # 添加批次维度
    return image

主程序

# 主程序
if __name__ == "__main__":
    # 加载图像
    input_image = load_image('example.jpg')  # 请替换为实际图像路径

    # 初始化模型
    model = nn.Sequential(
        SimpleBackbone(),
        FeaturePyramidNetwork()
    )

    # 训练
    p1, p2, p3, p4 = train(model, input_image)

    # 显示特征图
    fig, axes = plt.subplots(1, 4, figsize=(20, 5))
    axes[0].imshow(p1[0].detach().numpy().mean(axis=0), cmap='gray')
    axes[0].set_title('Feature Map 1')
    axes[1].imshow(p2[0].detach().numpy().mean(axis=0), cmap='gray')
    axes[1].set_title('Feature Map 2')
    axes[2].imshow(p3[0].detach().numpy().mean(axis=0), cmap='gray')
    axes[2].set_title('Feature Map 3')
    axes[3].imshow(p4[0].detach().numpy().mean(axis=0), cmap='gray')
    axes[3].set_title('Feature Map 4')
    plt.show()

   fig 用于控制图形的整体属性,如大小、标题等;

        axes 是一个包含子图轴对象的数组,在这里它有 4 个元素,每个元素对应一个子图。

        axes[0] 表示选择第一个子图(第 0 个位置的轴对象)

   imshow(...) 是 Matplotlib 的一个函数,用于在指定的轴对象上显示图像数据;p1[0] 选择第一个样本(第 0 个批次),即从这个批次中选择第一个图像的特征图。.detach()指的是将张量从计算图中分离出来,使其不再需要梯度计算。mean(axis=0):对特征图的通道维度(channels)进行平均操作。

        假设特征图 p1[0] 的形状为 [channels, height, width],那么 mean(axis=0) 会对 channels 维度进行平均,得到形状为 [height, width] 的二维矩阵。这个二维矩阵表示将所有通道的特征平均后得到的图像。

cmap='gray''gray' 表示使用灰度图显示,即图像将以黑白(灰度)方式展示。

图像的金字塔: import cv2 as cv import numpy as np #降采样:将源图片尺寸缩小一倍,称为源图片尺寸的一半 def pyr_down_demo(image): dst = cv.pyrDown(image) cv.imshow("pyr_down_demo",dst) print(dst.shape) #高斯金字塔 def pyramid_demo(image): temp = image.copy() level = 3 pyramid_image = [] for i in range(3): dst = cv.pyrDown(temp) cv.imshow("pyramid_image"+str(i),dst) pyramid_image.append(dst) temp = dst.copy() return pyramid_image #拉普拉斯金字塔 def lapalian_demo(image): pyramid_images = pyramid_demo(image) level = len(pyramid_images) for i in range(level-1,-1,-1): if(i-1)<0: expand =cv.pyrUp(pyramid_images[i],dstsize= image.shape[:2]) lpls =cv.subtract(image,expand) cv.imshow("lapalian_down_"+str(i),lpls) else: expand = cv.pyrUp(pyramid_images[i],dstsize=pyramid_images[i-1].shape[:2]) lps = cv.subtract(pyramid_images[i-1],expand) cv.imshow("lapalian_down_"+str(i),lps) src =cv.imread("E:/opencv/picture/lena.jpg") pyr_down_demo(src) dst =cv.resize(src,(256,256)) cv.imshow("inital_window",src) cv.imshow("Resize_demo",dst) lapalian_demo(src) pyramid_demo(src) #print(src.shape) cv.waitKey(0) cv.destroyAllWindows() 分析: 图像金字塔是图像中多尺度表达的一种,最主要用于图像的分割,是一种以多分辨率来解释图像的有效但概念简单的结构,简单来说,图像金字塔就是用来进行图像缩放的。 进行图像缩放可以用图像金字塔,也可以使用resize函数进行缩放,后者效果更好。这里只是对图像金字塔做一些简单了解。 两种类型的金字塔: 1, 高斯金字塔:用于下采样。高斯金字塔是最基本的图像塔。原理:首先将原图像作为最底层图像G0(高斯金字塔的第0层),利用高斯核(5*5)对其进行卷积,然后对卷积后的图像进行下采样(去除偶数行和列)得到上一层图像G1,将此图像作为输入,重复卷积和下采样操作得到更上一层图像,反复迭代多次,形成一个金字塔形的图像数据结构,即高斯金字塔。 2, 拉普拉斯金字塔:用于重建图像,也就是预测残差,对图像进行最大程度的还原。比如一幅小图像重建为一幅大图,原理:用高斯金字塔的每一层图像减去其上一层图像上采样并高斯卷积之后的预测图像,得到一系列的差值图像即为LP分解图像。 两种类型的采用: 1) 上采样:就是图片放大(所谓上就是变大),使用PryUp函数。 步骤:先将图像在每个方向放大为原来的两倍,新增的行和列用0填充,再使用先前同样的内核与放大后的图像卷积,获得新增像素的近似值。 2)下采样:就是图片缩小(所谓下嘛,就是变小),使用PryDown函数。下采样将步骤:先对图像进行高斯内核卷积 ,再将所有偶数行和列去除。 总之,上、下采样都存在一个严重的问题,那就是图像变模糊了,因为缩放的过程中发生了信息丢失的问题。要解决这个问题,就得用拉普拉斯金字塔 对于源码分析: 1.高斯金字塔 def pyramid_demo(image): level = 3 pyramid =[] for i in range(level): dst = cv.pyrDown(image) cv.imshow("pyramid_demo"+str(i),dst) pyramid.append(dst) image = dst return pyramid 高斯金字塔实质上就是由大变小,对源图像进行PyrDown(即先高斯模糊,后降采样)得到dst1,然后将dst1作为输入图像再进行PyrDown(即先高斯模糊,后降采样)得到dst2,这个过程循环n次,就会得到n曾金字塔了。源码中n=3. 2.拉普拉斯金字塔 def lapalace_demo(image): pyramid = pyramid_demo(image) levels = len(pyramid) #range(2,-1,-1):计数从2开始,到-1结束(但不包含-1),步长为-1.得到2,1,0 for i in range(levels-1,-1,-1): if i=1时,使用pyrUP指令,输入图像为pyramid[i],输出图像的大小为pyramid[i-1](因为高斯金字塔的pyramid链表前一个元素是后一个元素的两倍),得到结果expand,再用图像相减命令cv.subtract将expand与pyramid[i-1]做差,即可得到残差图像了。 当I<1,即i=0时,此时pyramid[i]是链表中元素最大的了,大小是image图像的一半。我们用pyrUP指令,输入图像为pyramid[i],输出图像的大小为image的尺寸大小,得到残差图像。 最后将各个残差图像显示出来。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值