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

简介: 【23】多尺度检测及检测数据集
%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])

image.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])

image.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])

image.png


假设此处的 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

image.png

 

这是一个比较小的数据集,方便调试测试使用,利于学习。


目录
相关文章
|
算法 自动驾驶 数据挖掘
3D检测:DETR3D
3D检测:DETR3D
445 0
3D检测:DETR3D
|
1月前
|
JSON 计算机视觉 数据格式
数据集学习笔记(一):常用检测、行为检测数据集
这篇文章是关于常用目标检测和行为检测数据集的介绍,包括CIFAR系列、COCO、VOC系列、TT100K和UCF101等数据集的详细信息和使用说明。
104 0
数据集学习笔记(一):常用检测、行为检测数据集
|
6月前
|
机器学习/深度学习 人工智能 图计算
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
202 0
|
机器学习/深度学习 传感器 算法
【图像分割】图像检测(分割、特征提取)、各种特征(面积等)的测量和过滤(Matlab代码实现)
【图像分割】图像检测(分割、特征提取)、各种特征(面积等)的测量和过滤(Matlab代码实现)
|
机器学习/深度学习 存储 运维
使用单类全卷积数据描述异常检测网络检测药丸图像上的缺陷
使用单类全卷积数据描述 (FCDD) 异常检测网络检测药丸图像上的缺陷。
169 0
|
存储 机器学习/深度学习 编解码
使用训练分类网络预处理多分辨率图像
说明如何准备用于读取和预处理可能不适合内存的多分辨率全玻片图像 (WSI) 的数据存储。肿瘤分类的深度学习方法依赖于数字病理学,其中整个组织切片被成像和数字化。生成的 WSI 具有高分辨率,大约为 200,000 x 100,000 像素。WSI 通常以多分辨率格式存储,以促进图像的高效显示、导航和处理。 读取和处理WSI数据。这些对象有助于使用多个分辨率级别,并且不需要将图像加载到核心内存中。此示例演示如何使用较低分辨率的图像数据从较精细的级别有效地准备数据。可以使用处理后的数据来训练分类深度学习网络。
320 0
|
机器学习/深度学习 传感器 自动驾驶
FCOS升级 | FCOS在3D检测中应该如何使用呢?FCOS3D就是最好的验证(一)
FCOS升级 | FCOS在3D检测中应该如何使用呢?FCOS3D就是最好的验证(一)
295 0
|
计算机视觉
FCOS升级 | FCOS在3D检测中应该如何使用呢?FCOS3D就是最好的验证(二)
FCOS升级 | FCOS在3D检测中应该如何使用呢?FCOS3D就是最好的验证(二)
462 0
|
机器学习/深度学习 自动驾驶 大数据
3D检测涨点Trick | 2D检测居然可以教BEV进行3D目标检测
3D检测涨点Trick | 2D检测居然可以教BEV进行3D目标检测
572 0
|
机器学习/深度学习 传感器 文字识别
【图像检测】基于计算机实现交通标志图像检测提取附matlab代码和报告
【图像检测】基于计算机实现交通标志图像检测提取附matlab代码和报告