ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【23】多尺度检测及检测数据集

2021-09-24 20:58:25  阅读:206  来源: 互联网

标签:tensor img 23 检测 尺度 torch bbox 图像 anchor


文章目录

%matplotlib inline
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import torchvision
import torch
import os

1. 多尺度边界框检测

# 测试图像
# imagepath = 'E:\学习\机器学习\数据集\VOC2012\VOCdevkit\VOC2012\JPEGImages\\2007_001423.jpg'
imagepath = 'E:\学习\机器学习\数据集\VOC2012\VOCdevkit\VOC2012\JPEGImages\\2007_001526.jpg'

# 读取并显示图像
image = plt.imread(imagepath)
h, w = image.shape[:2]

定义一些列的显示函数

# 功能: 指定输入图像、尺度列表和宽高比列表,然后此函数将生成以每个像素为中心具有不同形状的锚框,返回所有的锚框
def multibox_prior(data, sizes, ratios):

    device = data.device
    size_tensor = torch.tensor(sizes, device=device)
    ratio_tensor = torch.tensor(ratios, device=device)

#     print(data.shape)
    # 获取图像宽高
    img_height, img_width = data.shape[-2:]

    # 避免anchor太密集,只挑选特定的boxes
    boxes_per_pixel = len(sizes) + len(ratios) - 1

    # 获取每个像素的中心点
    steps_h = 1.0 / img_height  # 高度步长
    steps_w = 1.0 / img_width   # 宽度步长

    # 根据图像像素点位置 * 步长 来实现归一化处理,使得图像尺寸计算为1
    # 0.5 指的是像素点中心位置的偏移量
    center_h = (torch.arange(img_height, device=device) + 0.5) * steps_h
    center_w = (torch.arange(img_width, device=device) + 0.5) * steps_w
    # print(center_h.shape, center_w.shape)   # torch.Size([333]) torch.Size([500])

    # 根据步长位置构建每个像素点的坐标信息
    shift_y, shift_x = torch.meshgrid(center_h, center_w)
    # print(shift_y.shape, shift_x.shape)     # torch.Size([333, 500]) torch.Size([333, 500])

    # 分别转换成列表,方便拼接,其中(shift_x, shift_y)就代表了图像中全部像素点的中心坐标
    shift_y, shift_x = shift_y.reshape(-1), shift_x.reshape(-1)

    # 现在对(shift_x, shift_y)进行拼接,方便一会转换成左上角与右下角的坐标格式,所以需要设置两组坐标
    # 其中参数dim=1表示的是对列进行拼接
    center_point = torch.stack([shift_x, shift_y, shift_x, shift_y], dim=1)
    # 由于每个像素点会生成(n+m−1)个anchor,所以需要对坐标列表重复5次
    # repeat_interleave函数是对每一行分别进行先复制; repeat函数是对每一块分别进行复制
    center_point = center_point.repeat_interleave(boxes_per_pixel, dim=0)
    # print(center_point)

    # 现在构造出了中心点坐标,接着需要构造偏移信息列表,使中心坐标+偏移量就转换成转换成左上角与右下角的坐标格式
    # 其中: anchor_w = s * sqrt(w * h * r)   anchor_h = s * sqrt(w * h / r)
    # 这样使得 anchor_w / anchor_h = r   anchor_w * anchor_h = (ws)*(hs)
    # anchor_w = torch.cat((size_tensor * torch.sqrt(ratio_tensor[0]), size_tensor[0] * torch.sqrt(ratio_tensor[1:]))) \
    #            * math.sqrt(img_width * img_height)
    # anchor_h = torch.cat((size_tensor / torch.sqrt(ratio_tensor[0]), size_tensor[0] / torch.sqrt(ratio_tensor[1:]))) \
    #            * math.sqrt(img_width * img_height)
    # anchor_w, anchor_h:
    # tensor([306.0331, 204.0221, 102.0110, 432.7961, 216.3981])
    # tensor([306.0331, 204.0221, 102.0110, 216.3981, 432.7962])
    # 现在得到的5个anchor是在图像上的像素大小,需要同样对其进行归一化操作

    # 而另一种方法是:
    # 其中size值的是相比原图的大小, ratio值的宽高比
    anchor_w = torch.cat((size_tensor * torch.sqrt(ratio_tensor[0]), size_tensor[0] * torch.sqrt(ratio_tensor[1:])))   \
                * img_height / img_width  # 由于图像一般是矩形的,为了显示出是正方形,这里需要对宽度做一个缩放因子
    anchor_h = torch.cat((size_tensor / torch.sqrt(ratio_tensor[0]), size_tensor[0] / torch.sqrt(ratio_tensor[1:])))
    # anchor_w, anchor_h:
    # tensor([0.4995, 0.3330, 0.1665, 0.7064, 0.3532])
    # tensor([0.7500, 0.5000, 0.2500, 0.5303, 1.0607])
#     print(anchor_w)
#     print(anchor_h)

    # 获得偏移量
    anchor_offset = torch.stack((-anchor_w, -anchor_h, anchor_w, anchor_h))
    anchor_offset = anchor_offset.T.repeat(img_height * img_width, 1) / 2    # 先转置再按偏移块来重复

    # 更加中心点坐标与偏移量,获取anchor
    anchors = center_point + anchor_offset

    return anchors.unsqueeze(0)


# 将边界框 (左上x, 左上y, 右下x, 右下y) 格式转换成 matplotlib 格式:
# ((左上x, 左上y), 宽, 高)
def bbox_to_rect(bbox, color, linewidth=2):
    
    # 注意,这里输入的是单个边界框
    xy = (bbox[0], bbox[1])   # 左上角坐标
    width = bbox[2]-bbox[0]   # 右下角的x坐标 - 左上角的x坐标
    height = bbox[3]-bbox[1]  # 右下角的y坐标 - 左上角的y坐标
    
    # 返回matplotlib 的边界框格式
    # fill=False: 取消填充功能,否则不是边界框而是一个色块
    # edgecolor: 边界框颜色
    # linewidth:  边界框的宽度
    return plt.Rectangle(xy, width, height, fill=False, edgecolor=color, linewidth=linewidth)


# 功能: 显示一个像素点上的所有边界框(这里设置了一个像素点上会有5个anchor)
def show_bboxes(axes, bboxes, labels=None, colors=None):

    # 如果没有传入颜色设置,这里会进行颜色一个初始化设置
    if colors is None:
        colors = ['blue', 'red', 'green', 'gray', 'pink']

    # 如果没有传入标签设置,这里会进行标签一个初始化设置
    if labels is None:
        labels = [i for i in range(len(bboxes))]
    
    
    # print(labels)
    # 以增加补丁的方式在原图上绘制矩形框
    for i, bbox in enumerate(bboxes):
        color = colors[i % len(colors)]
        rect = bbox_to_rect(bbox, color)  # 循环采用列表中的5种颜色
        # 增加矩形框补丁
        axes.add_patch(rect)
        # 增加文本补丁
        axes.text(rect.xy[0], rect.xy[1], labels[i], fontsize=8, color='white',
                  va='center', ha='center', bbox=dict(facecolor=color, edgecolor="black"))


def display_anchors(fmap_w, fmap_h, s):
#     d2l.set_figsize()
    # 前两个维度上的值不影响输出
    fmap = torch.zeros((1, 10, fmap_h, fmap_w))
    anchors = multibox_prior(fmap, sizes=s, ratios=[1, 2, 0.5])
    bbox_scale = torch.tensor((w, h, w, h))
    show_bboxes(plt.imshow(image).axes, anchors[0] * bbox_scale)

使用不同的尺度构建anchor

display_anchors(fmap_w=4, fmap_h=4, s=[0.15])
torch.Size([1, 10, 4, 4])
tensor([0.1500, 0.2121, 0.1061])
tensor([0.1500, 0.1061, 0.2121])

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5BVvNldI-1632488032613)(output_6_1.png)]

display_anchors(fmap_w=2, fmap_h=2, s=[0.4])
torch.Size([1, 10, 2, 2])
tensor([0.4000, 0.5657, 0.2828])
tensor([0.4000, 0.2828, 0.5657])

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tO5NAUad-1632488032627)(output_7_1.png)]

display_anchors(fmap_w=1, fmap_h=1, s=[0.8])
torch.Size([1, 10, 1, 1])
tensor([0.8000, 1.1314, 0.5657])
tensor([0.8000, 0.5657, 1.1314])

在这里插入图片描述

假设此处的 c 张特征图是 CNN 基于输入图像的正向传播算法获得的中间输出。 既然每张特征图上都有 hw 个不同的空间位置,那么相同空间位置可以看作含有 c 个单元。感受野的定义,特征图在相同空间位置的 c 个单元在输入图像上的感受野相同: 它们表征了同一感受野内的输入图像信息。 因此,我们可以将特征图在同一空间位置的 c 个单元变换为使用此空间位置生成的 a 个锚框类别和偏移量。 本质上,我们用输入图像在某个感受野区域内的信息,来预测输入图像上与该区域位置相近的锚框类别和偏移量

当不同层的特征图在输入图像上分别拥有不同大小的感受野时,它们可以用于检测不同大小的目标。 例如,我们可以设计一个神经网络,其中靠近输出层的特征图单元具有更宽的感受野,这样它们就可以从输入图像中检测到较大的目标。

2. 目标检测数据集

这里代码提供的下载数据集方式我好像无法成功,只能手动下载,同时自己实现一下加载此数据集

from torch.utils.data import DataLoader, Dataset
# 一个用于加载香蕉检测数据集的自定义数据集
class BananasDataset(Dataset):
   
    def __init__(self, is_train):
        
        # 加载图像与标签信息
        self.images, self.labels = self.read_data_bananas(is_train)

        # 根据训练集或测试集打印出数量
        print('read ' + str(len(self.images)) + (' training examples' if is_train else ' validation examples'))

    def __getitem__(self, idx):
        
        # 对每一张图像的处理, 这里转变为了浮点数
        return (self.images[idx].float(), self.labels[idx])

    def __len__(self):
        
        # 返回图像数量
        return len(self.images)
    
    def read_data_bananas(self, is_train=True):
        
        # 保存测试集与训练集的图像是一致的
        datapath = 'E:\学习\机器学习\数据集\\banana-detection'
        csv_file = os.path.join(datapath, 'bananas_train' if is_train else 'bananas_val', 'label.csv')
        csv_data = pd.read_csv(csv_file)
        csv_data = csv_data.set_index('img_name')
        
        images, targets = [], []
        # 迭代每一行,添加信息
        for img_name, target in csv_data.iterrows():
            
            # 每张图像的路径
            imagepath = os.path.join(datapath, 'bananas_train' if is_train else 'bananas_val', 'images', img_name)
            # 读取图片数据, 类似于Image.open(x).convert('RGB')
            images.append(torchvision.io.read_image(imagepath))
            # target中包含了类别与边界框信息(左上角与右下角组成),eg:[0, 104, 20, 143, 58]组成的一个列表
            targets.append(list(target))
        
        # 对于边界框与类别信息在第2个维度前做了增维处理, 并且进行归一化处理, 图像的尺寸是256
        # labels.shape: torch.Size([1000, 1, 5])
        return images, torch.tensor(targets).unsqueeze(1) / 256

batch_size = 16

# 对于测试集,无须按随机顺序读取
train_iter = DataLoader(BananasDataset(is_train=True), batch_size, shuffle=True)
val_iter = DataLoader(BananasDataset(is_train=False), batch_size)
read 1000 training examples
read 100 validation examples

展示 10 幅带有真实边界框的图像

def bbox_to_rect(bbox, color, linewidth=2):

    # 注意,这里输入的是单个边界框
    xy = (bbox[0], bbox[1])  # 左上角坐标
    width = bbox[2] - bbox[0]  # 右下角的x坐标 - 左上角的x坐标
    height = bbox[3] - bbox[1]  # 右下角的y坐标 - 左上角的y坐标

    # 返回matplotlib 的边界框格式
    # fill=False: 取消填充功能,否则不是边界框而是一个色块
    # edgecolor: 边界框颜色
    # linewidth:  边界框的宽度
    return plt.Rectangle(xy, width, height, fill=False, edgecolor=color, linewidth=linewidth)

需要注意,这里对图像数值进行了归一化处理; 而且label的信息也是经过相对处理的,所以一会如果想要显示label需要对其乘上原图像大小

images, labels = next(iter(train_iter))
images = images.permute(0, 2, 3, 1) / 255
images.shape
torch.Size([16, 256, 256, 3])

根据图像与标签显示数据

index = 1
plt.figure(figsize=(9, 8), dpi=100)

for image, label in zip(images, labels):
    
    plt.subplot(4, 4, index)
    plt.axis('off')
    # 显示图像
    fig = plt.imshow(image)
    
    # 注意这里的label是归一化处理后的,需要乘上原数值
    bbox = label[0,1:] * 255
    
    # 添加矩形框
    rect = bbox_to_rect(bbox, 'w')
#     rect = plt.Rectangle((bbox[0], bbox[1]), bbox[2] - bbox[0], bbox[3] - bbox[1], 
#                          fill=False, edgecolor='red', linewidth=2)
    fig.axes.add_patch(rect)
    
    index += 1
    

在这里插入图片描述
这是一个比较小的数据集,方便调试测试使用,利于学习。

标签:tensor,img,23,检测,尺度,torch,bbox,图像,anchor
来源: https://blog.csdn.net/weixin_44751294/article/details/120462988

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有