Faster RCNN网络源码解读(Ⅴ) --- GeneralizedRCNNTransform图像初始化代码解析

news2024/11/15 15:55:39

目录

一、代码作用(transform.py)

​编辑

二、代码解析 

2.1 GeneralizedRCNNTransform类

2.1.1 初始化函数__init__

2.1.2 normalize标准化处理 

2.1.3 将图像以及bndbox进行缩放resize

2.1.4 batch_images 

2.1.5 正向传播 forward

2.1.6 后处理模块postprocess 

三、ImageList类 


一、代码作用(transform.py)

        前面我们已经说过怎样生成自定义数据集的部分了,我们通过自定义数据集会生成图像信息以及对应的标注信息,在这部分我们会对图像进行标准化处理以及对bndbox信息进行一个resize处理把他打包成batch放入网络中进行正向传播。

二、代码解析 

2.1 GeneralizedRCNNTransform类

2.1.1 初始化函数__init__

    #定义了输入进网络的最小边长,最大边长,均值,方差
    def __init__(self, min_size, max_size, image_mean, image_std):
        super(GeneralizedRCNNTransform, self).__init__()

		#如果min_size是不是list或者tuple类型
        if not isinstance(min_size, (list, tuple)):
            min_size = (min_size,)
        self.min_size = min_size      # 指定图像的最小边长范围
        self.max_size = max_size      # 指定图像的最大边长范围
        self.image_mean = image_mean  # 指定图像在标准化处理中的均值
        self.image_std = image_std    # 指定图像在标准化处理中的方差

        定义了输入进网络的最小边长,最大边长,均值,方差。

        即对于每张图片我们要给他缩放到最小值min_size和最大值max_size之间。还要将图像进行标准化处理,其均值和方差为传入参数。

2.1.2 normalize标准化处理 

    def normalize(self, image):
        """标准化处理"""
        dtype, device = image.dtype, image.device
		
		#将均值方差转换成pytorch的tensor格式
		#mean传进来是个list,其中有三个元素,我们转换成tensor应该是  # [:, None, None]: shape [3] -> [3, 1, 1]种形式
        mean = torch.as_tensor(self.image_mean, dtype=dtype, device=device)
        std = torch.as_tensor(self.image_std, dtype=dtype, device=device)
        # [:, None, None]: shape [3] -> [3, 1, 1]
        return (image - mean[:, None, None]) / std[:, None, None]

2.1.3 将图像以及bndbox进行缩放resize

    def resize(self, image, target):
        # type: (Tensor, Optional[Dict[str, Tensor]]) -> Tuple[Tensor, Optional[Dict[str, Tensor]]]
        """
        将图片缩放到指定的大小范围内,并对应缩放bboxes信息
        Args:
            image: 输入的图片
            target: 输入图片的相关信息(包括bboxes信息)

        Returns:
            image: 缩放后的图片
            target: 缩放bboxes后的图片相关信息
        """
        # image shape is [channel, height, width]
		#获取图像的高度,宽度信息
        h, w = image.shape[-2:]

        if self.training:
			#如果在训练模式,取得我们传入的参数,即图像最小边长
            size = float(self.torch_choice(self.min_size))  # 指定输入图片的最小边长,注意是self.min_size不是min_size
        else:
            # FIXME assume for now that testing uses the largest scale
            size = float(self.min_size[-1])    # 指定输入图片的最小边长,注意是self.min_size不是min_size

        if torchvision._is_tracing():
            image = _resize_image_onnx(image, size, float(self.max_size))
        else:
			#传入参数:最小尺度、最大尺度
            image = _resize_image(image, size, float(self.max_size))

        if target is None:
            return image, target


		#取出box信息
        bbox = target["boxes"]
        # 根据图像的缩放比例来缩放bbox,参数为标注框,原来的图像尺寸,经过缩放后的图像尺寸
        bbox = resize_boxes(bbox, [h, w], image.shape[-2:])
        target["boxes"] = bbox

        return image, target

        首先获取图像的宽度和高度信息h, w

        将图像高度宽度信息转化成tensor信息,如果在训练模式,取得我们传入的参数,即图像最小边长size。将最小边长size和最大边长self.max_size输入到_resize_image函数对图像进行缩放。

def _resize_image(image, self_min_size, self_max_size):
	#比如一张图片我们是10*20 我们想让他控制在5*5间
	#我们用 5/10 得到缩放因子0.5 0.5*20大于5 不行
	#我们用 5/20 得到缩放因子0.25 0.25*20=5可以

    # type: (Tensor, float, float) -> Tensor
    im_shape = torch.tensor(image.shape[-2:])
    min_size = float(torch.min(im_shape))    # 获取高宽中的最小值
    max_size = float(torch.max(im_shape))    # 获取高宽中的最大值
    scale_factor = self_min_size / min_size  # 根据指定最小边长和图片最小边长计算缩放比例

    # 如果使用该缩放比例计算的图片最大边长大于指定的最大边长
    if max_size * scale_factor > self_max_size:
        scale_factor = self_max_size / max_size  # 将缩放比例设为指定最大边长和图片最大边长之比

    # interpolate利用插值的方法缩放图片
    # image[None]操作是在最前面添加batch维度[C, H, W] -> [1, C, H, W]
    # bilinear双线性插值只支持4D Tensor
    image = torch.nn.functional.interpolate(
        image[None], scale_factor=scale_factor, mode="bilinear", recompute_scale_factor=True,
        align_corners=False)[0]

    return image

        比如一张图片我们是10*20 我们想让他控制在5*5间,我们用 5/10 得到缩放因子0.5 0.5*20大于5 不行,我们用 5/20 得到缩放因子0.25 0.25*20=5可以。

        经过此函数之后,我们的图像以及缩放到了初始化函数的最小图像大小到最大图像大小之间了。

        随后我们取出bbox信息对标注图像框进行等比例缩放。

def resize_boxes(boxes, original_size, new_size):
    # type: (Tensor, List[int], List[int]) -> Tensor
    """
    将boxes参数根据图像的缩放情况进行相应缩放

    Arguments:
        original_size: 图像缩放前的尺寸
        new_size: 图像缩放后的尺寸
    """
	#将原来图片的尺寸和现在图片的尺寸转换为tensor格式
    ratios = [
        torch.tensor(s, dtype=torch.float32, device=boxes.device) /
        torch.tensor(s_orig, dtype=torch.float32, device=boxes.device)
        for s, s_orig in zip(new_size, original_size)
    ]
    ratios_height, ratios_width = ratios
    # Removes a tensor dimension, boxes [minibatch, 4]
    # Returns a tuple of all slices along a given dimension, already without it.
	#将边界框按索引值为1的方向展开
	# [minibatch, 4] 当前图片有几个box信息  他们的坐标
    xmin, ymin, xmax, ymax = boxes.unbind(1)
    xmin = xmin * ratios_width
    xmax = xmax * ratios_width
    ymin = ymin * ratios_height
    ymax = ymax * ratios_height
    return torch.stack((xmin, ymin, xmax, ymax), dim=1)

        经过resize函数后,我们将图片大小限制在了(min_size, max_size)之间,并且完成了对bndbox的缩放。

2.1.4 batch_images 

        将图像打包成一个batch输入到一个网络中,我们经过标准化处理和resize之后我们的图像大小并不是标准的大小每张图片都不一样的,我们要Resize。

        我们经过resize函数后的图像并不是统一尺寸的图像,我们要通过这个函数进行图像打包处理(即让图片处于同一尺寸中)。 

    def batch_images(self, images, size_divisible=32):
        # type: (List[Tensor], int) -> Tensor
        """
        将一批图像打包成一个batch返回(注意batch中每个tensor的shape是相同的)
        Args:
            images: 输入的一批图片
            size_divisible: 将图像高和宽调整到该数的整数倍

        Returns:
            batched_imgs: 打包成一个batch后的tensor数据
        """
		#不会执行的
        if torchvision._is_tracing():
            # batch_images() does not export well to ONNX
            # call _onnx_batch_images() instead
            return self._onnx_batch_images(images, size_divisible)

        # 分别计算一个batch中所有图片中的最大channel, height, width,输出的是一个batch图片的最大高度、最大宽度和最大通道数
        max_size = self.max_by_axis([list(img.shape) for img in images])

		#传入的参数size_divisible=32 这样会使我们的长度宽度取整到离32最大的尺寸
        stride = float(size_divisible)
        # max_size = list(max_size)
        # 将height向上调整到stride的整数倍
        max_size[1] = int(math.ceil(float(max_size[1]) / stride) * stride)
        # 将width向上调整到stride的整数倍
        max_size[2] = int(math.ceil(float(max_size[2]) / stride) * stride)

        # [batch, channel, height, width]
		#len(images)是图片个数
        batch_shape = [len(images)] + max_size

        # 创建shape为batch_shape且值全部为0的tensor,tensor的
        batched_imgs = images[0].new_full(batch_shape, 0)
        
		for img, pad_img in zip(images, batched_imgs):
            # 将输入images中的每张图片复制到新的batched_imgs的每张图片中,对齐左上角,保证bboxes的坐标不变
            # 这样保证输入到网络中一个batch的每张图片的shape相同
            # copy_: Copies the elements from src into self tensor and returns self
			#通道数目 长 宽
            pad_img[: img.shape[0], : img.shape[1], : img.shape[2]].copy_(img)

        return batched_imgs

        这里的思想是假如我们要将八张图片打包成一个batch,首先获取这八张图片宽的最大值和宽的最大值,假设图中蓝色的矩阵,全部用0来填充。紧接着把每张图片的左上角与蓝色矩形框的左上角进行对齐。达到了输入图片都是同一size,这保持了图像的正常比例。

        size_divisible=32这个参数的含义是将尺寸全部向上取整到32的整数倍方便系统进行优化。

        batch_shape = [len(images)] + max_size

        上局代码中,max_size是三维的,存储的一个batch图片的最大通道数,最大长度以及最大宽度, [len(images)]对应着图片的个数即batch的个数,这样构建了四维向量,[batch, channel, height, width]。

        创建shape为batch_shape且值全部为0的tensor batched_imgs

batched_imgs = images[0].new_full(batch_shape, 0)

        batched_imgs最终存储着尺寸大小相同的image。

2.1.5 正向传播 forward

    def forward(self,
                images,       # type: List[Tensor]
                targets=None  # type: Optional[List[Dict[str, Tensor]]]
                ):
        # type: (...) -> Tuple[ImageList, Optional[List[Dict[str, Tensor]]]]
        images = [img for img in images]

		#遍历图像列表获取每张图片 
        for i in range(len(images)):
            image = images[i]
			#判断target是否为空,若不为空,则将索引为i的target赋值给target_index
            target_index = targets[i] if targets is not None else None

            if image.dim() != 3:
                raise ValueError("images is expected to be a list of 3d tensors "
                                 "of shape [C, H, W], got {}".format(image.shape))
			
			#标准化处理, 对图像和对应的bboxes缩放到指定范围
            image = self.normalize(image)                
			
			#resize处理
            image, target_index = self.resize(image, target_index)   

			#替换图像
            images[i] = image
            if targets is not None and target_index is not None:
                targets[i] = target_index

        # 记录resize后的图像尺寸
		# 这时debug,每张图片的大小不一样
        image_sizes = [img.shape[-2:] for img in images]
		# 将images打包成一个batch(8*3*1216*1088)数量 通道 长宽
        images = self.batch_images(images)  

		#是个list 每个元素都是一个(int int)的tuple,记录resize之后的尺寸
        image_sizes_list = torch.jit.annotate(List[Tuple[int, int]], [])

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

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

        输入的参数是未经初始化的图片image以及标注信息target。

        将所有图片存放在images列表中。

        遍历所有图片,将遍历到的图片放到image变量中,判断这个图片的target信息是否为空,若为空target_index的值赋予为none否则赋予target_index的值为该张图片的标注信息。        

        检查完图像合理化后:

        ①对图像进行标准化处理image = self.normalize(image) 

        ②对图像进行resize处理 image, target_index = self.resize(image, target_index)   

        ③替换图像 

images[i] = image
if targets is not None and target_index is not None:
    targets[i] = target_index

        ④记录resize之后的图像尺寸

        image_sizes = [img.shape[-2:] for img in images]

        这里每张图片的大小是不一样的。

         ⑤将images打包成一个batch(8*3*1216*1088)数量 通道 长宽

images = self.batch_images(images)  

         ⑥定义了一个image_sizes_list,它是一个list,每个元素是一个(int,int)的tuple,记录resize之后batch之前的尺寸。

        我们来缕清一下思路:

        输入到本类之前的图像是大小不一的,我们经过resize函数将图像进行缩放到了一定范围内,最后经过batch处理将图片处理到统一尺寸,由于resize之后进行预测的边界框信息是resize之后的,但是我们绘制我们的预测结果时其实要在原图上进行绘制。

        image_list 含两个尺寸信息,返回给调用函数。image_list和target数据即即将输入backbone中的数据。

2.1.6 后处理模块postprocess 

	#对网络的预测结果进行后处理(主要将bboxes还原到原图像尺度上)
	#result : 是网络的最终预测结果 包括bndbox信息及每个bndbox对应的位置信息,标签值以及对应的概率
	#image_shapes :将图像经过resize之后的每一个图像的高度和宽度
	#original_image_sizes :每张图片在缩放前的高度和宽度
    def postprocess(self,
                    result,                # type: List[Dict[str, Tensor]]
                    image_shapes,          # type: List[Tuple[int, int]]
                    original_image_sizes   # type: List[Tuple[int, int]]
                    ):
        # type: (...) -> List[Dict[str, Tensor]]
        """
        对网络的预测结果进行后处理(主要将bboxes还原到原图像尺度上)
        Args:
            result: list(dict), 网络的预测结果, len(result) == batch_size
            image_shapes: list(torch.Size), 图像预处理缩放后的尺寸, len(image_shapes) == batch_size
            original_image_sizes: list(torch.Size), 图像的原始尺寸, len(original_image_sizes) == batch_size

        Returns:

        """
        if self.training:
            return result

		#i是对应的索引,(pred, im_s, o_im_s)对应result, image_shapes, original_image_sizes
        # 遍历每张图片的预测信息,将boxes信息还原回原尺度
		# im_s 缩放后的图像尺度 o_im_s图像原始尺度
        for i, (pred, im_s, o_im_s) in enumerate(zip(result, image_shapes, original_image_sizes)):
            boxes = pred["boxes"]
            boxes = resize_boxes(boxes, im_s, o_im_s)  # 将bboxes缩放回原图像尺度上
            result[i]["boxes"] = boxes
        return result

三、ImageList类 

from typing import List, Tuple
from torch import Tensor


class ImageList(object):
    """
    Structure that holds a list of images (of possibly
    varying sizes) as a single tensor.
    This works by padding the images to the same size,
    and storing in a field the original sizes of each image
    """

    def __init__(self, tensors, image_sizes):
        # type: (Tensor, List[Tuple[int, int]]) -> None
        """
        Arguments:
            tensors (tensor) padding后的图像数据
            image_sizes (list[tuple[int, int]])  padding前的图像尺寸
        """
        self.tensors = tensors
        self.image_sizes = image_sizes

    def to(self, device):
        # type: (Device) -> ImageList # noqa
        cast_tensor = self.tensors.to(device)
        return ImageList(cast_tensor, self.image_sizes)

        输入参数tensor :batch处理后的图像。

        输入参数image_sizes :与batch处理前的尺寸,这里每张图片大小不一样。

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

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

相关文章

分布式系统共识机制:一致性算法设计思想

分布式系统共识机制:一致性算法设计思想Paxos 算法节点角色算法流程Raft 算法节点角色核心机制leader选举日志复制PBFTHotstuff门限签名核心机制二阶段提交协议三阶段提交协议这次以一个宏观的角度去总结 自己学习过的一致性算法。一致性算法的目标就是让分布式系统…

Java开发 - 树(二叉树,二叉排序树,红黑树)

目录 前言 你好,认识一下,我是树 二叉树与二叉排序树 二叉排序树特点 为什么说二叉排序树查询效率要高于链表呢? 元素的类型 比较器 手写二叉排序树 定义一棵二叉树 增加元素 查询元素 修改元素 删除元素 遍历二叉树 重写toS…

JAVA注解处理API实战

简介 ​ 插件化注解处理(Pluggable Annotation Processing)API JSR 269提供一套标准API来处理Annotations( JSR 175),实际上JSR 269不仅仅用来处理Annotation,它建立了Java 语言本身的一个模型,它把method、package、constructor、type、variable、enum、annotatio…

工业远程I/O模块 CANopen 通讯

1.对象字典OD 对象字典是每个CANopen设备必须具有的功能,它包含了设备所有可以被访问的参数,客户端使用索引(Index)和子索引(Sub-index)来读写对象字典里的参数。 对象字典主要用来设定设备组态及进行非即时的通讯。每个对象采用16位索引的方式来寻址&…

01Editor最新破解

文章目录01Editor最新版注册算法逆向1.定位注册算法代码2.整体注册算法3.Check1算法分析4.Check2算法分析5.获得正确的任意用户名对应的序列号01Editor最新版注册算法逆向 1.定位注册算法代码 【版本】13.0.164bit\textcolor{green}{【版本】13.0.1\ 64bit}【版本】13.0.1 64b…

Redis集群系列七 —— 散列插槽分析

集群状态日志分析 Redis 分片集群引入了一个逻辑上的插槽或哈希槽概念,将集群划分为16384(0~16383)个槽位,集群中的每个节点占据一部分槽位数,在逻辑上将集群中的所有节点构成了一块完整的内存空间。 这个日志中可以通…

对象定义-解构-枚举属性遍历以及对象内函数

属性名表达式 定义对象的属性有两种方式 1、直接使用标识符作为属性名 obj.name 2、以表达式作为属性名 obj[ab] 10 let obj {} obj.name 孙悟空 // 孙悟空 obj[a b] 10 // 10 console.log(obj); // {name: 孙悟空, ab: 10}es5中字面量定义对象只能使用一种方法 var …

4.3.3、划分子网的 IPv4 地址

若有一个大型的局域网需要连接到因特网 若需要申请一个 C 类网络地址,其可分配的 IP 地址数量只有 254254254 个,不够使用 因此申请了一个 B 类网络地址,其可分配的 IP 地址数量达到了 655346553465534 个 给每台计算机和路由器的接口分配一…

实验九、消除互补输出级交越失真方法的研究

一、题目 互补输出级交越失真消除方法的研究。 二、仿真电路 基本互补电路和消除交越失真互补输出级如图1所示。晶体管采用 NPN 型晶体管 2N3904 和 PNP 型晶体管 2N3906。二极管采用 1N4009。 在实际的实验中,几乎不可能得到具有理想对称性的 NPN 型和 PNP 型管…

网络编程套接字----UDP协议

文章目录前言一、理解源IP地址和目的IP地址二、认识端口号理解"端口号"和"进程ID"理解源端口号和目的端口号三、认识TCP协议四、认识UDP协议五、网络字节序六、socket编程接口socket常见APIsockaddr结构sockaddr结构sockaddr_in 结构in_addr结构七、地址转…

第三方软件测试▏有效保障软件产品质量的关键性步骤

软件测试作为软件产品生命周期中不可或缺的重要步骤,被许多软件企业所重视。主要是通过对软件产品进行全面的测试,确保软件质量以及满足用户需求。但软件测试不仅仅是个简单的检测工作,而是一个系统性的、有组织性的测试过程,包含…

Linux:安装 telnet 命令

我是 ABin-阿斌:写一生代码,创一世佳话,筑一览芳华。如果小伙伴们觉得不错就一键三连吧~ 声明:原文地址:https://www.pudn.com/news/6332b44a272bb74d44053074.html 其他参考文章:https://www.cnblogs.com…

尚医通-上传医院接口-需求准备(十七)

目录: (1)数据接口-上传医院接口-需求准备 (1)数据接口-上传医院接口-需求准备 在医院接口设置的时候说过,我们做的是预约挂号平台,里面有数据的显示,挂号等等相关业务,…

计算机组成原理【by王道考研计算机】

文章目录第一章1. 什么是计算机系统2. 硬件的发展3. 计算机硬件的基本组成冯诺依曼结构现代计算机结构主存储器运算器控制器工作过程实例4. 计算机系统的层次结构五层结构三种级别的语言5. 计算机的性能指标存储器的容量CPU其他常用时间单位第二章1. 进制转换2. 字符与字符串3.…

下一代,容器工具Podman

一、简介 Kubernetes 团队发布了最新的 1.20 版本时,有一枚重磅炸弹:正式宣布弃用 Docker 支持的功能。弃用 Docker 之后,开发者们对其替代品的讨论逐渐热烈,其中 Containerd 和 Podman 倍受期待。 Podman 是一个开源的容器运行…

云原生、20.3k Star......时序数据库 TDengine 的 2022 年精彩纷呈

日月其迈,时盛岁新 2022 的进度条已经加载至“100%” 疫情肆虐下,毫无疑问 这仍然是头顶风雪攀登山峰的一年 好在如今曙光已现 回望这一年,TDengine 也硕果满满 2022 年是 TDengine 创立的第六个年头 我们付出着,也在收获着…

英伟达Orin芯片平台使用TensorRT加速部署YOLO

前言 自动驾驶行业想要在开发板上部署一套算法还是需要花费很大功夫的,因为计算资源的限制以及实时性要求高。并不像在服务器端部署算法那样粗犷。接下来就记录一下YOLO系列部署的过程,后期会把开发板上的问题都记录在此。 一、部署环境 计算平台&…

11.简单的CSS按钮悬停特效

效果 源码 <!DOCTYPE html> <html> <head><title>Button Hover Effects</title><link rel="stylesheet" type="text/css" href="style.css"> </head> <body><a href="#"><…

信贷产品年终总结之风控评分模型

叮咚&#xff0c;信贷年终总结的又一个专题来了&#xff0c;作为报告总结类的系列型文章&#xff0c;近期我们番茄知识星球平台陆续发布了相关年终总结专题&#xff0c;依次为客群特征画像、贷中行为分析、贷后逾期表现等&#xff0c;以上文章可详见之前陆续发布的内容。该业务…

nacos配置部署与管理

nacos配置部署与管理配置文件配置获取顺序&#xff1a;nacos配置热部署方式一&#xff1a;RefreshScope方式二&#xff1a;ConfigurationProperties配置文件 首先新建配置文件 Data ID&#xff1a;配置文件的名称&#xff08;唯一&#xff09;命名规范&#xff1a;服务名称-运…