【Faster R-CNN】之 Resize_and_Padding 代码精读

news2024/11/17 20:21:34

【Faster R-CNN】之 Resize_and_Padding

  • 1、前言:
  • 2、resize_image_and_bbox
    • 1)先对图像做resize处理
    • 2)再对 bounding box 做resize处理
  • 3、padding_images
  • 代码

1、前言:

在上一篇文章 【Faster R-CNN】之 Dataset and Dataloader 代码精读 中,我们重写了 Dataset 和 Dataloader, 可以迭代的 读出 batch 数据了。每个batch 中的数据 包括 image 和 target, 数据形如:
在这里插入图片描述

接下来,batch 中的 images 要传入 backbone 获得 feature map, 但是当前这些 images 存在一个问题,
就是这 batch 中的图像的尺寸 是不一致的
,不能直接喂进 backbone。不论 backbone 是使用 vgg16 还是 resnet 还是其他,其本质都是卷积神经网络结构,需要输入形为(batch_size, channel, height, width)的图片数据。

所以这里,我们需要将 batch 中的图像统一尺寸。怎么做呢? 分为2个步骤: (这里概括,下面举例细讲)

  • resize_image_and_bbox:(1)将batch 中的图像的高和宽全都 resize 到 [800,1333] 这个范围里,800和1333 是超参数,经验值。(2)图像resize了,ground trurh bounding box 的坐标自然也是要做相应的 resize。
  • padding_images:(1)分别找到batch 中图像的最大的 高度height 和 最大的宽度 width。(2)为了内存效率,将最大高度和最大宽度向上取到32 的整数倍,作为这个batch 的最终高和宽(3)将所有 (resize之后的) 图像 padding到 batch 的高和宽,padding方式为:只padding 右边和下面,左边和上边不加padding

下面我们举例细讲:
假设 batch size = 4,
这4张图像尺寸分别为:[3, 375, 500]、[3, 500, 374]、[3, 375, 500]、[3, 400, 500]
第1张图像标注出了 2个 object,bounding box 坐标分别为[ 1., 52., 394., 375.]、 [253., 159., 406., 304.]

2、resize_image_and_bbox

我们使用for 循环,读取batch中的每一张图片和其标注信息,逐张图片做resize处理

for i in range(len(images)):
	image = images[i]
	target_index = targets[i] if targets is not None else None
	image, target_index = self.resize_image_and_bbox(image, target_index)  # resize image and boxes
	images[i] = image
	if targets is not None and target_index is not None:
		targets[i] = target_index

上面代码第4行调用了 resize_image_and_bbox 函数,该函数中包括了 2部分,第一部分,先对 image 做resize,第二部分,对 bounding box 做resize

1)先对图像做resize处理

# ======================= resize image =======================
image_min_size = float(torch.min(im_shape))
image_max_size = float(torch.max(im_shape))
scale_factor = self.min_size / image_min_size  # 按照短边进行缩放,计算缩放比例

# 根据短边的缩放比例来缩放长边,长边缩放后的结果是否超出预设的 max_size
if image_max_size * scale_factor > self.max_size:
	scale_factor = self.max_size / image_max_size  # 若超出预设 max_size, 则按照长边的比例进行缩放

image = torch.nn.functional.interpolate(image[None], scale_factor=scale_factor, mode="bilinear",
                                        recompute_scale_factor=True, align_corners=False)[0]

以第一张图像为例,原图尺寸为 [3, 375, 500], 所以长边为 宽=500, 短边为高=375。
我们的目标是将长和宽都resize 到 [800, 1333] 的范围内。

1)先按照短边进行缩放,将 短边375 缩放到800, 计算出缩放因子 800/375=2.133。
2)将长边按照缩放因子进行缩放,判断缩放后有没有超出 1333上限。 500*2.133=1066.66, 没有超出1333的上限。如果超过了,就按照长边进行缩放,这种情况下,短边超过下限也没关系。
3)采用双线性插值的方式,将图像按照 缩放因子 scale_factor=2.133进行缩放。

2)再对 bounding box 做resize处理

# ======================= resize bbox =======================
boxes = target["boxes"]
ratios_height = torch.as_tensor(image.shape[1] / im_shape[0], dtype=torch.float32, device=boxes.device)
ratios_width = torch.as_tensor(image.shape[2] / im_shape[1], dtype=torch.float32, device=boxes.device)
xmin, ymin, xmax, ymax = boxes.unbind(1)
xmin = xmin * ratios_width
xmax = xmax * ratios_width
ymin = ymin * ratios_height
ymax = ymax * ratios_height
target["boxes"] = torch.stack((xmin, ymin, xmax, ymax), dim=1)

1)原图像的尺寸为 [3, 375, 500], 按照缩放因子 2.1333 进行双线性插值后得到的图像尺寸为 [3, 800, 1066]
所以,高的缩放比例为800/375=2.1333, 宽的缩放比例为1066/500=2.1320

2)将图像中的bounding box 的坐标解析出来,
第1个 bounding box 的坐标为 [xmin, ymin, xmax, ymax] = [ 1., 52., 394., 375.]
第2个 bounding box 的坐标为 [xmin, ymin, xmax, ymax] = [253., 159., 406., 304.]

将 xmin 和 xmax 按照 宽的缩放比例 2.1320 进行缩放
将 ymin 和 ymax 按照 高的缩放比例 2.1333 进行缩放

得:
第1个 bounding box resize后的坐标为 [xmin, ymin, xmax, ymax] = [ 2.1320, 110.9333, 840.0081, 799.9999]
第2个 bounding box resize后的坐标为 [xmin, ymin, xmax, ymax] = [ 539.3961, 339.2000, 865.5921, 648.5333]

** 这里求得的 bounding box 的坐标数据类型是 float32

3)batch 中所有图片都 resize 后,图片尺寸分别为
[3, 800, 1066]
[3, 1069, 800]
[3, 800, 1066]
[3, 800, 1000]


batch 中每张图片都 resize 到 限定的范围之后,就要把他们都padding到统一的尺寸了

3、padding_images

def padding_images(self, images, size_divisible=32):
    image_size = [img.shape for img in images]
    max_size = torch.max(torch.tensor(image_size), dim=0)[0]
    stride = float(size_divisible)
    max_size[1] = int(math.ceil(float(max_size[1]) / stride) * stride)
    max_size[2] = int(math.ceil(float(max_size[2]) / stride) * stride)

    batch_shape = [len(images)] + [v.item() for v in max_size]
    '''以images[0]为基,创建带 padding 的 新tensor,
    是为了新创建的 tensor 在相同的 device 上'''
    batched_imgs = images[0].new_full(batch_shape, 0)

    for img, pad_img in zip(images, batched_imgs):
        pad_img[: img.shape[0], : img.shape[1], : img.shape[2]].copy_(img)
    return batched_imgs

1)分别找到 这4张图中最大的 高度 和 最大的宽度。
4张图片resize 后的尺寸分别为:[3, 800, 1066]、[3, 1069, 800]、[3, 800, 1066]、[3, 800, 1000]
所以,最大高度为 1069,最大宽度为 1066

2)将最大高度和最大宽度 向上取到32 的整数倍 (这是为了内存的使用效率)
最大高度为 int( math.ceil(1069. / 32.) * 32)=1088
最大宽度为 int( math.ceil(1069. / 32.) * 32)=1088
所以,[1088, 1088] 就是 batch 中所有图像将要 padding 到的统一尺寸。

3)padding 成统一尺寸
将batch 中的所有图像都 padding 到 [1088, 1088] 尺寸
padding方式如下,左上角对齐,只在 右边和下边padidng。
在这里插入图片描述
至此,我们就已经得到了 shape 为 [batch_size, channel, height, width] = [4, 3, 1088, 1088] 的 batch 了。


代码

import torch
import math
import torch.nn as nn


class Resize_and_Padding(nn.Module):
    def __init__(self, min_size, max_size):
        super(Resize_and_Padding, self).__init__()
        self.min_size = min_size  # 指定图像的最小边长范围
        self.max_size = max_size

    def forward(self, images, targets=None):
        for i in range(len(images)):
            image = images[i]
            target_index = targets[i] if targets is not None else None
            image, target_index = self.resize_image_and_bbox(image, target_index)   # resize image and boxes
            images[i] = image
            if targets is not None and target_index is not None:
                targets[i] = target_index

        image_sizes = [img.shape[-2:] for img in images]
        images = self.padding_images(images)  # 将batch 中的图像padding 到相同的尺寸

        image_sizes_list = []
        for image_size in image_sizes:
            image_sizes_list.append((image_size[0], image_size[1]))

        image_list = ImageList(images, image_sizes_list)
        return image_list, targets

    def resize_image_and_bbox(self, image, target):
        im_shape = torch.tensor(image.shape[-2:])
        
        # ======================= resize image =======================
        image_min_size = float(torch.min(im_shape))
        image_max_size = float(torch.max(im_shape))
        scale_factor = self.min_size / image_min_size  # 按照短边进行缩放,计算缩放比例

        # 根据短边的缩放比例来缩放长边,长边缩放后的结果是否超出预设的 max_size
        if image_max_size * scale_factor > self.max_size:
            scale_factor = self.max_size / image_max_size  # 若超出预设 max_size, 则按照长边的比例进行缩放

        image = torch.nn.functional.interpolate(image[None], scale_factor=scale_factor, mode="bilinear",
                                                recompute_scale_factor=True, align_corners=False)[0]
        # ======================= resize bbox =======================
        boxes = target["boxes"]
        ratios_height = torch.as_tensor(image.shape[1] / im_shape[0], dtype=torch.float32, device=boxes.device)
        ratios_width = torch.as_tensor(image.shape[2] / im_shape[1], dtype=torch.float32, device=boxes.device)
        xmin, ymin, xmax, ymax = boxes.unbind(1)
        xmin = xmin * ratios_width
        xmax = xmax * ratios_width
        ymin = ymin * ratios_height
        ymax = ymax * ratios_height
        target["boxes"] = torch.stack((xmin, ymin, xmax, ymax), dim=1)
        return image, target

    def padding_images(self, images, size_divisible=32):
        image_size = [img.shape for img in images]
        max_size = torch.max(torch.tensor(image_size), dim=0)[0]
        stride = float(size_divisible)
        max_size[1] = int(math.ceil(float(max_size[1]) / stride) * stride)
        max_size[2] = int(math.ceil(float(max_size[2]) / stride) * stride)

        batch_shape = [len(images)] + [v.item() for v in max_size]
        '''以images[0]为基,创建带 padding 的 新tensor,
        是为了新创建的 tensor 在相同的 device 上'''
        batched_imgs = images[0].new_full(batch_shape, 0)

        for img, pad_img in zip(images, batched_imgs):
            pad_img[: img.shape[0], : img.shape[1], : img.shape[2]].copy_(img)
        return batched_imgs

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

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

相关文章

Linux网络:传输层之UDPTCP协议

文章目录一、端口号1.端口号范围划分2.常用命令二、UDP 协议1.格式2.特点3. UDP 的缓冲区4. UDP 使用注意事项5.基于 UDP 的应用层协议三、TCP 协议1.格式2.确认应答机制3.超时重传机制4.连接管理机制三次握手四次挥手5.滑动窗口6.流量控制7.拥塞控制8.延迟应答9.捎带应答10.面…

PyQt5利用Qt Designer制作一个可以拖动获取文件信息的页面

前言 本篇在讲什么 用pyqt5制作一个简单的程序,拖动文件或脚本可以读取文件信息 本篇适合什么 适合初学PyQt5的小白 本篇需要什么 对Python语法有简单认知 对Qt有简单认知 依赖Pycharm编辑器 本篇的特色 具有全流程的图文教学 重实践,轻理论&…

[Golang实战]整理Golang忽略的问题

整理Golang忽略的问题参考资料1.WaitGroup与GoRoutine的竞速2.Mutex互斥锁和RWMutex互斥读写锁3.poll,select,epoll4.何时栈和堆?5.GoRoutine合理使用6.GoRoutine优雅退出6.1data channel关闭通知退出6.2exit channel关闭通知退出6.3context超时或取消通知退出6.4WaitGroup/Er…

IPWorks EDI 2022.0.8381 for NET Crack

IPWorks EDI基于用于安全 EDI 通信(AS2、SFTP、OFTP、RosettaNet、MLLP 等)的领先 EDI-INT 协议,IPWorks EDI 库包含促进安全 EDI 消息传递以及 EDI 映射、翻译和验证(X12、 EDIFACT、HL7、TRADACOMS、VDA、XML 和 JSON&#xff0…

golang/安装

golang中文官网 https://golang.google.cn/ golang下载 安装 一路next 配置 配置值说明GOROOTD:\ProgramFiles\golanggolang安装目录PATHD:\ProgramFiles\golang\bingolang命令路径GO111MODULEon开启go.mod功能,go.mod是go官方依赖包管理工具GOPROXYhttps://go…

【FPGA笔记系列7】时序逻辑电路基础D触发器

时序逻辑电路 组合逻辑与时序逻辑电路的本质区别:时序逻辑电路的输出和前一时刻的状态有关,组合逻辑电路的输出只和当前的输入有关 与非门RS锁存器的缺陷:当SR从00变到11时,状态不稳定! 电路中小圈圈表示低电平有效! 透明锁存器 R=1当En=1时,Q=S当En=0时,后面为RS触发器…

使用git合并两个不同项目代码

使用git合并两个不同项目代码 前言, 这里解决的是两个不同的项目, 因为不同项目那必然是两个不同的git仓库 都是不同的git仓库了那就更不可能是相同的分支了(即使分支名相同) 至于为什么会有这种业务情况出现, 我也不知道, 反正先学干就完了 这里图形化界面演示用的是idea自带的…

人工智能时代八大类算法你了解吗?(包邮送书6本)

文章目录本文导读1. 关联规则分析2. 回归分析3. 分类分析4. 聚类分析5. 集成学习6. 自然语言处理7. 图像处理8. 深度学习9. 书籍推荐(包邮送书6本)本文导读 从零带你了解人工智能时代需要掌握的8大类算法,包括基础理论、关联规则分析、回归分…

Java-基础-4.IO流

一:为什么有IO流? 在显示生产中,我们的数据,都是不停的往过输入和输出,我们将这种模式称作为流。并且在输入和输出的过程中,我们包装了一些其他类。 二:什么是IO流? 1. 按照数据处理…

Linux学习之常用基本命令【1】

文章目录前言一 Linux系统简介二 补充知识Unix和Minix三 开关机命令四 系统目录结构五 树形显示文件目录结构六 目录管理6.0 目录操作常用命令6.1 ls(列出目录)【常用命令】6.2 cd(切换目录)6.3 pwd( 显示当前所在的目录 )6.4 mkdir(make directory创建目录&#xf…

Power BI折线图

如果要展现数据的趋势变化,折线图应该是不二之选,并且它更擅长于展现时间序列下的数据,根据折线斜率的不同展现变化的速率。 本文使用PowerBI Desktop来轻松生成一个折线图。 案例数据:2006-2015年各省市的三个产业的产值&#…

【SAP Abap】X档案:SAP 快速提供基础数据给第三方系统访问的几种方法

SAP 快速提供基础数据给第三方系统访问的几种方法1、数据封装2、开放RFC访问3、开放接口服务4、开放DB访问1、数据封装 在企业信息系统建设过程中,少不了的就是系统集成数据对接。 尤其是SAP系统中大量的基础数据集成,如各种字段值域,需要提…

Vue2笔记02 表单数据,过滤器,常见指令,生命周期,组件

表单数据 过滤器 过滤器:将数据进行简单处理后再使用 好用的第三方库的网站:BootCDN - Bootstrap 中文网开源项目免费 CDN 加速服务 显示当前时间计算属性的写法 过滤器的写法 多个过滤器可以串联 👆这里的过滤器是局部过滤器&#xff0c…

day03_java基本语法

今日内容 零、复习昨日 一、开发工具 二、Eclipse使用 三、程序解读 四、输出语句 五、常量 六、变量 七、数据类型 零、 复习昨日 见晨考.txt 一、开发工具 开发工具: Eclipse(免费),IDEA(收费) 1.1 Eclipse安装 Eclipse是绿色安全的,直接解压即可使用 1.2 启动Eclipse ps:…

原型和原型链

什么是原型? 因为每一个函数都有一个属性,这个属性名就是prototype,(即为显式原型) 这个属性的值是一个对象 每一个实例对象都有一个__proto__(即为隐式原型) 原型就是函数的prototype属性,…

excel数据比较:如何做一个完美的多条件排名方案

排名,简单;但如果有多个项目类别,并且可能存在业绩相同,怎么快速找出各个分享排名第一的人物呢?这就要通过多条件去匹配,才能找出需要的排名第一者。这里提供了两个方案,但都不够完美&#xff0…

Unity-TCP-网络聊天功能(三): 公共/私人聊天、添加好友、好友上下线、新消息提醒、点击好友聊天、退出登录

7.多人公共聊天、私人聊天、添加好友、好友上下线、新消息提醒、点击好友开始聊天、退出登录搭建好ChatView的UI和ChatItem编写Unity-Scripts/View/ChatView.cs脚本,加入了私人聊天和公共聊天逻辑,chatView界面处理接收(ChatHandle委托&#…

在 Linux 中创建用户 and 给用户设置密码相关操作

目录 一、创建用户 二、给用户设置密码相关操作 ②.①、修改用户密码 ②.②、设置用户密码 ②.③、删除用户密码 一、创建用户 语法: useradd [-g 用户组名 -d home目录] 用户名 在 Linux 中“[]”里面的内容都代表可选,可以不写。 useradd&…

【数据结构】归并排序、快速排序(递归法和非递归法)

文章目录 一、归并排序 递归法 思想 程序代码 时间复杂度 非递归法 思想 程序代码 二、快速排序(挖坑法) 思想 程序代码 时间复杂度 三、快速排序(hoare法) 思想 程序代码 hoare法错误集锦 死循环 越界 四、快…

kubernetes pod内容器状态OOMKilled和退出码137全流程解析

kubernetes pod内容器状态OOMKilled和退出码137全流程解析 - 简书 使用event_control监听memory cgroup的oom事件 - 简书 kubernetes/k8s CRI分析-kubelet删除pod分析 - 良凯尔 - 博客园 在kubernetes的实际生产实践中,经常会看到pod内的容器因为内存使用超限被内…