Pytorch中的仿射变换(F.affine_grid)

news2024/11/16 7:31:55

目录

1、平移操作实现

2、缩放操作

3、旋转操作

4、转置操作


在pytorch框架中, F.affine_grid 与 F.grid_sample(torch.nn.functional as F)联合使用来对图像进行变形。

F.affine_grid 根据形变参数产生sampling grid,F.grid_sample根据sampling grid对图像进行变形。

需要注意,pytorch中的F.grid_sample是反向采样,这就导致了形变参数与直觉是相反的(后面有实验验证)(例如放射矩阵中的缩放因子是0.5,会使目标图像扩大两倍;平移为正会使目标图像往左上角移动)

【反向采样】:定义的sampling grid尺寸即输出图像的尺寸,而sampling grid其中每一个位置内的数值x,y,例如sampling_grid[i,j]=[x,y], 则表示输出图像在(i,j)点的像素值应该在原图(x,y)处取值,若x,y都恰好为整数且在原图范围内,则直接取原图在x,y点的像素值;若x,y不是都为整数但都在原图范围内,则需要用插值算法计算该点的像素值;超出图像范围的则为0。

 torch.nn.functional.affine_grid(theta,size):

给定一组仿射矩阵(theta),生成一个2d的流场.通常与 grid_sample() 结合使用,用于空间变换网络.

参数:

  • theta(Tensor 类型):输入的一组仿射矩阵(N*2*3)
  • size(torch.Size 类型):要输出的图像的size,(N*C*H*W),比如:torch.Size((32,3,24,24))
  • 其中 N 是指 batch_size

回忆一下仿射变换,需要一个3*3的矩阵。而affine grid需要的theta是N*2*3的,其中的这个2*3就是仿射矩阵的前两行(因为第三行是涉及到透视变换的,和仿射变换无关,pytorch维护者就不管最后一行)。
再回忆一下,前两行都是控制什么的:

  • 前两列涉及到旋转和缩放
  • 最后一列涉及到平移
  • 这6个自由度,就组成了平面2d图像的仿射变换需要的变换矩阵。而affine grid,就是通过用户设置仿射变换的参数(theta),并且指定仿射变换之后得到的图像的size,得到了一个grid。这个grid输入到grid sample中,就完成了仿射变换。grid用来指定,新图像的每个位置,需要去源图像的哪个位置采样。

———————————————————————————————————————————

假设我们有这么一张图片:

 利用python进行显示:

from torchvision import transforms
from PIL import Image
import matplotlib.pyplot as plt

img_path = "图片文件路径"
img_torch = transforms.ToTensor()(Image.open(img_path))

plt.imshow(img_torch.numpy().transpose(1, 2, 0))
plt.show()

1、平移操作实现

  • 利用普通方式

例如我们需要向右平移50px,向下平移100px。

import numpy as np
import torch
 
theta = np.array([
    [1,0,50],
    [0,1,100]
])
# 变换1:可以实现缩放/旋转,这里为 [[1,0],[0,1]] 保存图片不变
t1 = theta[:,[0,1]]
# 变换2:可以实现平移
t2 = theta[:,[2]]
 
_, h, w = img_torch.size()
new_img_torch = torch.zeros_like(img_torch, dtype=torch.float)
for x in range(w):
    for y in range(h):
        pos = np.array([[x], [y]])
        npos = t1@pos+t2
        nx, ny = npos[0][0], npos[1][0]
        if 0<=nx<w and 0<=ny<h:
            new_img_torch[:,ny,nx] = img_torch[:,y,x]
plt.imshow(new_img_torch.numpy().transpose(1,2,0))
plt.show()

 

  • 利用pytorch 方式

向右移动0.2,向下移动0.4:

from torch.nn import functional as F
 
theta = torch.tensor([
    [1,0,-0.2],
    [0,1,-0.4]
], dtype=torch.float)
grid = F.affine_grid(theta.unsqueeze(0), img_torch.unsqueeze(0).size())
output = F.grid_sample(img_torch.unsqueeze(0), grid)
new_img_torch = output[0]
plt.imshow(new_img_torch.numpy().transpose(1,2,0))
plt.show()

总结:

  • 要使用 pytorch 的平移操作,只需要两步:
    • 创建 grid:grid = torch.nn.functional.affine_grid(theta, size),其实我们可以通过调节 size 设置所得到的图像的大小(相当于resize);
    • grid_sample 进行重采样:outputs = torch.nn.functional.grid_sample(inputs, grid, mode='bilinear')
  • theta 的第三列为平移比例,向右为负,向下为负;

 我们通过设置 size 可以将图像resize:

from torch.nn import functional as F
 
theta = torch.tensor([
    [1,0,-0.2],
    [0,1,-0.4]
], dtype=torch.float)
# 修改size
N, C, W, H = img_torch.unsqueeze(0).size()
size = torch.Size((N, C, W//2, H//3))
grid = F.affine_grid(theta.unsqueeze(0), size)
output = F.grid_sample(img_torch.unsqueeze(0), grid)
new_img_torch = output[0]
plt.imshow(new_img_torch.numpy().transpose(1,2,0))
plt.show()

 

 

2、缩放操作

  • 利用普通方式

放大1倍:

import numpy as np
import torch
 
theta = np.array([
    [2,0,0],
    [0,2,0]
])
t1 = theta[:,[0,1]]
t2 = theta[:,[2]]
 
_, h, w = img_torch.size()
new_img_torch = torch.zeros_like(img_torch, dtype=torch.float)
for x in range(w):
    for y in range(h):
        pos = np.array([[x], [y]])
        npos = t1@pos+t2
        nx, ny = npos[0][0], npos[1][0]
        if 0<=nx<w and 0<=ny<h:
            new_img_torch[:,ny,nx] = img_torch[:,y,x]
plt.imshow(new_img_torch.numpy().transpose(1,2,0))
plt.show()

由于没有使用插值算法,所以中间有很多部分是黑色的。

  • 利用pytorch 方式

from torch.nn import functional as F
 
theta = torch.tensor([
    [0.5, 0  , 0],
    [0  , 0.5, 0]
], dtype=torch.float)
grid = F.affine_grid(theta.unsqueeze(0), img_torch.unsqueeze(0).size())
output = F.grid_sample(img_torch.unsqueeze(0), grid)
new_img_torch = output[0]
plt.imshow(new_img_torch.numpy().transpose(1,2,0))
plt.show()

结论:可以看到,affine_grid 的放大操作是以图片中心为原点的。

3、旋转操作

  • 利用普通操作

将图片旋转30度:

import numpy as np
import torch
import math
 
angle = 30*math.pi/180
theta = np.array([
    [math.cos(angle),math.sin(-angle),0],
    [math.sin(angle),math.cos(angle) ,0]
])
t1 = theta[:,[0,1]]
t2 = theta[:,[2]]
 
_, h, w = img_torch.size()
new_img_torch = torch.zeros_like(img_torch, dtype=torch.float)
for x in range(w):
    for y in range(h):
        pos = np.array([[x], [y]])
        npos = t1@pos+t2
        nx, ny = int(npos[0][0]), int(npos[1][0])
        if 0<=nx<w and 0<=ny<h:
            new_img_torch[:,ny,nx] = img_torch[:,y,x]
plt.imshow(new_img_torch.numpy().transpose(1,2,0))
plt.show()

  • 利用pytorch 方式

from torch.nn import functional as F
import math
 
angle = -30*math.pi/180
theta = torch.tensor([
    [math.cos(angle),math.sin(-angle),0],
    [math.sin(angle),math.cos(angle) ,0]
], dtype=torch.float)
grid = F.affine_grid(theta.unsqueeze(0), img_torch.unsqueeze(0).size())
output = F.grid_sample(img_torch.unsqueeze(0), grid)
new_img_torch = output[0]
plt.imshow(new_img_torch.numpy().transpose(1,2,0))
plt.show()

 pytorch 以图片中心为原点进行旋转,并且在旋转过程中会发生图片缩放,如果选择角度变为 90°,图片为:

4、转置操作

  • 利用普通操作

import numpy as np
import torch
 
theta = np.array([
    [0,1,0],
    [1,0,0]
])
t1 = theta[:,[0,1]]
t2 = theta[:,[2]]
 
_, h, w = img_torch.size()
new_img_torch = torch.zeros_like(img_torch, dtype=torch.float)
for x in range(w):
    for y in range(h):
        pos = np.array([[x], [y]])
        npos = t1@pos+t2
        nx, ny = npos[0][0], npos[1][0]
        if 0<=nx<w and 0<=ny<h:
            new_img_torch[:,ny,nx] = img_torch[:,y,x]
plt.imshow(new_img_torch.numpy().transpose(1,2,0))
plt.show()

  • 利用pytorch 方式

我们可以通过size大小,保存图片不被压缩:

from torch.nn import functional as F
 
theta = torch.tensor([
    [0, 1, 0],
    [1, 0, 0]
], dtype=torch.float)
N, C, H, W = img_torch.unsqueeze(0).size()
grid = F.affine_grid(theta.unsqueeze(0), torch.Size((N, C, W, H)))
output = F.grid_sample(img_torch.unsqueeze(0), grid)
new_img_torch = output[0]
plt.imshow(new_img_torch.numpy().transpose(1,2,0))
plt.show()

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

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

相关文章

深入浅出openGauss的执行器基础

目录 火山模型 Tuple 数据结构设计 条件计算 Expr 和 Var 示例1 filter 示例2 join 示例3 index scan & index only scan 火山模型 执行器各个算子解耦合的基础。对于每个算子来说&#xff0c;只有三步&#xff1a; 1、向自己的孩子拿一个 tuple。即调用孩子节点…

C++初阶之缺省参数

目录 前言 缺省参数 1.缺省参数的概念 2.缺省参数的分类 全缺省参数 半缺省参数 前言 今天小编继续给大家带来C的内容&#xff0c;那么今天小编给大家讲解的就是有关C中缺省参数的介绍。 缺省参数 1.缺省参数的概念 缺省参数是声明或定义函数时为函数的参数指定一个缺省…

分布式互联网——Web3.0

文章目录 前言一、什么是 Web3.0?二、Web3.0 技术1.分布式账本技术(DLT)2. 区块链3. 智能合约4. 加密算法2.操作 三、Web3.0 的未来发展 前言 Web3.0&#xff0c;也被称为“分布式互联网”&#xff0c;是当前互联网的下一代版本。它是一种未来的互联网&#xff0c;它没有中心…

ROS学习第十一节——常用命令

1.概述 机器人系统中启动的节点少则几个&#xff0c;多则十几个、几十个&#xff0c;不同的节点名称各异&#xff0c;通信时使用话题、服务、消息、参数等等都各不相同&#xff0c;一个显而易见的问题是: 当需要自定义节点和其他某个已经存在的节点通信时&#xff0c;如何获取…

实践分享:如何在自己的App 中引入AI 画图

最近AIGC 简直是杀疯了&#xff0c;领导动不动就让我们在APP 里引入大语言模型&#xff0c;引入AI画图……说搞就搞&#xff01;本期基于最近在app 里引入AI画图小程序的操作&#xff0c;给大家做一波实践分享。 Scribble Diffusion 是一个简单的在线服务&#xff0c;它使用 A…

定制自己的文档问答机器人

近期ChatGPT很火爆&#xff0c;功能很强大&#xff0c;其具有强大的逻辑推理能力和数据背景。但是如果我们想要使用ChatGPT聊一些它没有训练过的知识&#xff0c;或者我们自己的一些数据时&#xff0c;由于ChatGPT没有学习过这些知识&#xff0c;所以回答结果不准确。 下文就介…

十、切分织物起球和非起球区域以便于计算毛球对比度

一、通过训练的模型可以将织物的起球区域进行识别区分 原图&#xff1a; 模型识别&#xff1a; 二、对比度的计算 为了对织物起球等级进行评定&#xff0c;需要这边不同的参数特征来构建模型的评级系统 通过查阅相关文献&#xff0c;确定最终的特征参数为&#xff1a;织物起…

11.面向对象概述,类的创建,对象的创建

一.面向对象程序设计概述 1.知识点面向对象程序设计的目的 &#xff08;1&#xff09;从程序设计的角度来看&#xff0c;事物的属性就可以用变量来表示&#xff0c;行为则可以用方法来反映。 &#xff08;2&#xff09;客观世界中事物的属性和行为可以进行传递&#xff0c;当…

汇编与内联 x86-64

机器字长 x86是32位系统 64是64位系统 这里的32和64&#xff0c;指的都是机器字长 机器字长是 能直接进行整数/位运算的大小指针的大小(索引内存的范围) 8位机 由于空间大小限制&#xff0c;想要把集成电路做到个人主机里&#xff0c;只能用8位字长的 16位机 8086 IBMP…

如何破除增长的未知性?火山引擎交出了答卷

4月18日&#xff0c;由火山引擎主办的2023春季火山引擎“FORCE原动力”大会在上海召开。本次大会主要围绕云计算和数字化领域&#xff0c;全方位地展示火山引擎在云技术、云服务和云场景方面的最新探索、应用与实践&#xff0c;呈现创新发展的战略蓝图。 曾经&#xff0c;增长是…

Flink高手之路5-Table API SQL

文章目录 Flink 中的Table API & SQL一、Table API & SQL 介绍1. 为什么要Table API和SQL2. Table API & SQL的特点3. Table API& SQL发展历程3.1 架构升级3.2 查询处理器的选择3.3 了解-Blink planner和Flink Planner具体区别如下&#xff1a;3.4 了解-Blink …

神采PromeAI 2.0版本上线,助你释放创作超能力

上个月&#xff0c;我们推出神采PromeAI 1.0版本&#xff0c;让用户可以免费体验AI草图渲染功能。神采作为设计师的提效工具和灵感源泉&#xff0c;深受用户的广大好评。于是&#xff0c;在经过算法优化后&#xff0c;神采PromeAI 2.0版本终于在本周上线了&#xff01; 我们提供…

【Vulnhub】之Symfonos2

一、 部署方法 在官网上下载靶机ova环境&#xff1a;https://download.vulnhub.com/symfonos/symfonos2.7z使用VMware搭建靶机环境攻击机使用VMware上搭建的kali靶机和攻击机之间使用NAT模式&#xff0c;保证靶机和攻击机放置于同一网段中。 二、 靶机下载安装 靶机下载与安…

ETCD(四)读请求处理过程

客户端通过etcdctl执行get命令 etcdctl get name --endpoints localhost:12379,192.158.00.32:12379client端 首先是client会解析这条命令&#xff0c;包括其中的get API方法&#xff0c;key值&#xff0c;请求server地址。解析完之后etcdctl会创建一个clientv3库对象&#xf…

Ubantu docker学习笔记(七)容器网络

文章目录 一、容器网络管理1.1查看容器网络1.2创建容器网络1.3 删除容器网络1.4 容器网络详细信息1.5 配置容器网络1.6 断开容器网络连接 二、none网络三、host网络四、bridge网络五、container网络六、容器连接外部网络6.1创建Overlay网络6.2创建Macvlan网络 一、容器网络管理…

研0进阶式学习---数据库配置

目录 最开始的问题&#xff1a;不同的连接名下面的数据库信息完全一样尝试新建用户名和密码&#xff0c;以此来建立新的连接 但这样建立的连接下面的数据库仍然是和之前的一模一样尝试改变xampp端口号&#xff0c;以此来建立新的连接 结论MySQL实例的数据库文件是与实例绑定的&…

完美解决丨+# TypeError: ‘dict_keys‘ object does not support indexing

结构 - 标题 - 问题描述 - 代码栗子 - 总结 目录 TypeError: dict_keys object does not support indexing 如何实现&#xff1f; python a {a: 1} b a.keys() c b[0] 异常描述 TypeError Traceback (most recent call last) <ipython-input-9-9dceb06f3f…

信号完整性分析基本概念之Retimer和Redriver

一两句话讲清楚版&#xff1a; Retimer 通过 其 Rx 端 CTLE/DFE (连续时间线性均衡/判断反馈均衡) 、CDR (时钟数据恢复) 及 Tx 端 EQ (均衡)&#xff0c;来够补偿信道损耗&#xff0c;消除信号抖动&#xff0c;提升信号完整性&#xff0c;从而增加传输距离。 Redriver 是放大…

多线程拉取+kafka推送

多线程拉取kafka推送 1 多线程 在本次需求中&#xff0c;多线程部分我主要考虑了一个点&#xff0c;就是线程池的配置如何最优。因为数据量级比较大&#xff0c;所以这个点要着重处理&#xff0c;否则拉取的时间会非常长或者是任务失败会比较频繁&#xff1b; 因为数据的量级…

Spring Security OAuth2.0(一)-----前言-授权码模式及代码实例

什么是 OAuth2 OAuth 是一个开放标准&#xff0c;该标准允许用户让第三方应用访问该用户在某一网站上存储的私密资源&#xff08;如头像、照片、视频等&#xff09;&#xff0c;而在这个过程中无需将用户名和密码提供给第三方应用。实现这一功能是通过提供一个令牌&#xff08…