DPVO 代码剖析
class PatchLayer(torch.autograd.Function): @staticmethod def forward(ctx, net, coords, radius): """ forward patchify """ ctx.radius = radius ctx.save_for_backward(net, coords) patches, = cuda_corr.patchify_forward(net, coords, radius) return patches @staticmethod def backward(ctx, grad): """ backward patchify """ net, coords = ctx.saved_tensors grad, = cuda_corr.patchify_backward(net, coords, grad, ctx.radius) return grad, None, None def patchify(net, coords, radius, mode='bilinear'): """ extract patches """ patches = PatchLayer.apply(net, coords, radius) if mode == 'bilinear': offset = (coords - coords.floor()).to(net.device) dx, dy = offset[:,:,None,None,None].unbind(dim=-1) # [1, 96, 2] -> [1, 96, 1, 1, 1, 2] -> [1, 96, 1, 1, 1] d = 2 * radius + 1 # 计算了特征块的大小 # 计算了四种加权组合(类似双线性插值的原则)。 x00 = (1-dy) * (1-dx) * patches[...,:d,:d] x01 = (1-dy) * ( dx) * patches[...,:d,1:] x10 = ( dy) * (1-dx) * patches[...,1:,:d] x11 = ( dy) * ( dx) * patches[...,1:,1:] return x00 + x01 + x10 + x11 # 返回这些组合的加和,得到插值后的特征块。 return patches # 直接返回未插值的 patches
问题1: pytorch 如何自定义操作?
torch.autograd.Function
: 是 PyTorch 中创建自定义操作的基本类。需要重写 forward
和 backward
方法来实现前向和反向传播的逻辑。
上下文对象 ctx
: 用于在前向传播中保存反向传播需要的数据。通过 ctx.save_for_backward
保存张量,反向传播时通过 ctx.saved_tensors
取出。
CUDA 操作 cuda_corr.patchify_forward
和 cuda_corr.patchify_backward
: 这两个函数在代码中并未定义,它们是自定义的 CUDA 扩展,用于执行高效的 patch 提取和梯度计算。
在这段代码中,PatchLayer
继承自 torch.autograd.Function
,并定义了自定义的前向和反向传播逻辑。这类函数可以用来创建不使用标准 torch.nn.Module
的自定义操作。
具体解析如下:
1. PatchLayer
类
PatchLayer
类主要实现了一个从网络中提取 patch(图像小块)的自定义操作。它包括 forward
和 backward
两个静态方法。
-
重载
forward
静态方法:ctx
是 PyTorch 提供的上下文对象,用于保存反向传播需要的信息。net
表示输入的特征图。coords
是提取 patch 的中心坐标。radius
表示从中心点开始提取的 patch 半径,决定了 patch 的大小。ctx.radius = radius
和ctx.save_for_backward(net, coords)
是在前向传播中保存必要的信息,以便在反向传播时使用patches, _ = cuda_corr.patchify_forward(net, coords, radius)
是调用 自定义的 CUDA 操作 来进行 patch 提取。只接收函数返回值中的第一个(假定返回的是一个元组或列表)。这是一个CUDA实现的函数,用于高效地提取特征块。
-
重载
backward
静态方法:ctx.saved_tensors
是在forward
方法中保存的net
和coords
。grad
是从上层损失反向传播来的梯度。cuda_corr.patchify_backward
用于计算反向传播的梯度。- 返回的是 grad 与多个 None,表示只有输入的第一个参数有梯度,其他参数没有
cuda_corr.patchify_forward 是 C++ CUDA 实现的。
PatchLayer
类允许开发者在前向和反向传播中插入自定义的 CUDA 操作,从而适应特定的计算需求,同时仍然能够利用 PyTorch 的自动微分能力。
2. patchify
函数
这个函数是用来从输入的 net
张量中提取 patch(小块图像)的。
-
PatchLayer.apply(net, coords, radius)
: 调用自定义的PatchLayer
,提取 patch。返回的patches
是从net
中提取的 patches。 apply 是 PyTorch 自定义函数调用的固定方式,负责调用 forward 方法。 -
双线性插值:
如果mode
设置为'bilinear'
,会对提取的 patch 进行双线性插值操作。这部分代码主要处理:coords - coords.floor()
计算了坐标相对于整数网格点的偏移,用来决定插值的比例。dx, dy
将偏移分成 x 和 y 两个方向。x00, x01, x10, x11
分别表示在 4 个邻近点的插值计算结果。- 最终返回加权后的结果,完成双线性插值。
-
返回值: 如果
mode
为'bilinear'
,返回双线性插值后的 patch;否则直接返回从PatchLayer
提取的 patches。
代码工作流程
patchify
函数被调用,传入net
、coords
、radius
和插值模式。- 如果
mode='bilinear'
,则进行双线性插值,最终得到插值后的 patch。 - 否则,直接返回提取到的 patches。
forward
和backward
方法定义了如何进行前向和反向传播。
总结:这段代码的主要功能是在网络中提取指定半径(radius)的特征图块。这在深度学习的特征匹配、局部特征提取等任务中很有用。通过自定义的 PatchLayer,实现了一个前向(和对应的反向)CUDA操作,使得该操作可以嵌入到PyTorch的自动微分系统中。
参考资料:
- 定义torch.autograd.Function的子类,自己定义某些操作,且定义反向求导函数
- Extending PyTorch
- PyTorch: Defining new autograd functions
问题2:patches, = cuda_corr.patchify_forward(net, coords, radius)
的理解
逐步分解
patches, = cuda_corr.patchify_forward(net, coords, radius)
cuda_corr.patchify_forward
: 这是一个调用 CUDA 实现的前向传播函数,用于从输入特征图中提取局部块(patches)。net
: 输入的特征图,通常是一个四维张量,形状为[batch_size, channels, height, width]
。coords
: 坐标张量,表示要提取的局部块的中心坐标。通常是一个三维张量,形状为[batch_size, num_patches, 2]
,其中2
表示(x, y)
坐标。radius
: 局部块的半径,表示从中心坐标向四周扩展的范围。patches, =
: 这里使用了逗号解包(comma unpacking),表示patchify_forward
函数返回一个元组,我们只取第一个元素(即提取的局部块)。
详细解释
cuda_corr.patchify_forward
: 这个函数是 CUDA 实现的一部分,用于高效地从输入特征图中提取局部块。由于使用了 CUDA,这个操作可以在 GPU 上并行执行,从而加速计算。net
: 输入特征图是整个网络的中间输出,包含了图像的特征信息。我们需要从这个特征图中提取局部块来进行后续的处理。coords
: 这些坐标指定了我们要从特征图中提取局部块的位置。每个坐标对应一个局部块的中心。radius
: 这个参数决定了每个局部块的大小。例如,如果radius
为 1,那么每个局部块的大小将是(2 * radius + 1) x (2 * radius + 1)
,即3x3
。
总结
这段代码的主要功能是从输入特征图中提取指定位置的局部块(patches)。具体来说,它通过调用 CUDA 实现的前向传播函数 patchify_forward
,在 GPU 上高效地完成了这一操作。提取的局部块将用于后续的图像处理任务,如特征匹配或光流估计。
主要功能:从输入特征图中提取指定位置的局部块,并返回这些局部块。
问题3:这个代码好像 C++ 实现的。python 怎么调用的 C++ 代码呢?
在 PyTorch 中,可以通过自定义 C++/CUDA 扩展来实现高性能的计算操作,并将其集成到 Python 代码中。具体来说,patchify_forward
是通过 C++/CUDA 扩展实现的,然后在 Python 中通过 torch.autograd.Function
进行调用。
详细解释
-
C++/CUDA 扩展:
patchify_forward
是一个用 C++ 或 CUDA 编写的函数,位于一个单独的 C++ 文件中(例如correlation_kernel.cu
或correlation.cpp
)。- 这个函数会被编译成一个动态链接库(
.so
或.dll
),供 Python 调用。
-
Python 调用:
- 在 Python 中,通过
torch.autograd.Function
来封装这个 C++/CUDA 函数。 PatchLayer
类继承自torch.autograd.Function
,并在forward
方法中调用cuda_corr.patchify_forward
。
- 在 Python 中,通过
具体步骤
-
C++/CUDA 实现:
- 编写 C++/CUDA 代码,实现
patchify_forward
函数。 - 使用 PyTorch 提供的 C++ API(如
ATen
)来处理张量操作。
- 编写 C++/CUDA 代码,实现
-
编译 C++/CUDA 扩展:
- 使用
setuptools
或CMake
将 C++/CUDA 代码编译成动态链接库。 - 编译后的库文件会被放置在 Python 可以找到的路径中。
- 使用
-
Python 封装:
- 在 Python 中,通过
torch.autograd.Function
来封装 C++/CUDA 函数。 - 在
forward
方法中调用cuda_corr.patchify_forward
,并返回结果。
- 在 Python 中,通过
示例代码
假设 correlation_kernel.cu
文件中实现了 patchify_forward
函数,编译后的库文件为 cuda_corr.so
,那么在 Python 中可以这样调用:
import torch import cuda_corr # 假设这是编译后的库文件 class PatchLayer(torch.autograd.Function): @staticmethod def forward(ctx, net, coords, radius): ctx.radius = radius ctx.save_for_backward(net, coords) patches, = cuda_corr.patchify_forward(net, coords, radius) return patches @staticmethod def backward(ctx, grad): net, coords = ctx.saved_tensors grad, = cuda_corr.patchify_backward(net, coords, grad, ctx.radius) return grad, None, None
总结
patchify_forward
是通过 C++/CUDA 实现的,然后在 Python 中通过 torch.autograd.Function
进行调用。这种方式可以充分利用 GPU 的并行计算能力,提高计算效率。
问题4: setup.py 中的 cuda_corr 解读
cuda_corr
模块的解释
在 setup.py
文件中,cuda_corr
是通过 CUDAExtension
定义的一个 CUDA 扩展模块。这个模块包含了 C++ 和 CUDA 代码,用于实现高性能的计算操作。
详细解释
-
CUDAExtension
:CUDAExtension
是 PyTorch 提供的一个类,用于定义 CUDA 扩展模块。它允许我们将 C++ 和 CUDA 代码编译成一个动态链接库(.so
或.dll
),供 Python 调用。
-
sources
:sources
参数指定了需要编译的源文件列表。对于cuda_corr
模块,源文件包括:dpvo/altcorr/correlation.cpp
: 这是 C++ 代码文件,通常包含一些 CPU 端的逻辑和接口定义。dpvo/altcorr/correlation_kernel.cu
: 这是 CUDA 代码文件,包含 GPU 端的计算逻辑。
-
extra_compile_args
:extra_compile_args
参数指定了编译器额外的编译选项。对于cuda_corr
模块,编译选项包括:cxx
: 用于 C++ 编译器的选项,这里指定了-O3
,表示启用最高级别的优化。nvcc
: 用于 NVCC(NVIDIA CUDA 编译器)的选项,同样指定了-O3
,表示启用最高级别的优化。
-
include_dirs
:include_dirs
参数指定了编译时需要包含的头文件目录。对于cuda_corr
模块,没有指定额外的头文件目录,因此它只会包含默认的头文件路径。
编译过程
-
编译源文件:
correlation.cpp
和correlation_kernel.cu
会被分别编译成目标文件(.o
或.obj
)。correlation.cpp
会被 C++ 编译器(如g++
)编译。correlation_kernel.cu
会被 NVCC 编译器编译成 CUDA 目标文件。
-
链接目标文件:
- 编译后的目标文件会被链接成一个动态链接库(
.so
或.dll
),供 Python 调用。
- 编译后的目标文件会被链接成一个动态链接库(
总结
cuda_corr
是一个通过 CUDAExtension
定义的 CUDA 扩展模块,包含了 C++ 和 CUDA 代码。它通过编译和链接生成一个动态链接库,供 Python 调用。这个模块的主要功能是实现高性能的计算操作,如 patchify_forward
和 patchify_backward
,从而在 GPU 上加速计算。
问题5:解释下 m.def("patchify_forward", &patchify_forward, "PATCHIFY forward");
代码解释
m.def("patchify_forward", &patchify_forward, "PATCHIFY forward");
这行代码使用了 PyBind11 库来定义一个 Python 可调用的函数。具体来说,它将 C++ 函数 patchify_forward
绑定到 Python 模块中,使得在 Python 中可以通过 patchify_forward
这个名字来调用这个函数。
详细解析
-
m.def
方法:m
是 PyBind11 模块对象,通常在PYBIND11_MODULE
宏中定义。def
是m
对象的一个方法,用于定义一个 Python 可调用的函数。
-
第一个参数
"patchify_forward"
:- 这是在 Python 中调用这个函数时使用的名称。
- 例如,在 Python 中可以通过
module.patchify_forward(...)
来调用这个函数。
-
第二个参数
&patchify_forward
:- 这是 C++ 函数的指针,指向实际的 C++ 函数
patchify_forward
。 - 当在 Python 中调用
patchify_forward
时,实际上会调用这个 C++ 函数。
- 这是 C++ 函数的指针,指向实际的 C++ 函数
-
第三个参数
"PATCHIFY forward"
:- 这是一个字符串,通常用于描述这个函数的用途或功能。
- 这个字符串在 Python 中不会直接使用,但可以用于文档生成或调试。
总结
这行代码的作用是将 C++ 函数 patchify_forward
绑定到 Python 模块中,使得在 Python 中可以通过 patchify_forward
这个名字来调用这个函数。这样做的目的是为了在 Python 中方便地调用 C++ 实现的函数,从而利用 C++ 的高性能计算能力。
问题6:patchify_cuda_forward 函数的解读
这个函数的主要功能是实现图像块的前向传播,即将输入的图像特征图(net
)、坐标(coords
)和 图像块半径(radius
) 转换为图像块(patches
)。
参数解释
net
: 输入的图像特征图,形状为[B, C, H, W]
,其中B
是批量大小,C
是通道数,H
和W
是图像的高度和宽度。coords
: 坐标张量,形状为[B, M, 2]
,其中M
是坐标的数量,2
表示每个坐标的(x, y)
值。radius
: 半径,用于确定图像块的大小。
局部变量解释
B
: 批量大小,从coords
的第一个维度获取。M
: 坐标的数量,从coords
的第二个维度获取。C
: 通道数,从net
的第二个维度获取。D
: 图像块的直径,计算公式为2 * radius + 2
。opts
: 张量选项,从net
获取,用于创建新的张量。patches
: 输出的图像块张量,形状为[B, M, C, D, D]
,初始化为零。
函数功能
-
计算图像块的直径:
const int D = 2 * radius + 2; - 根据给定的半径
radius
,计算图像块的直径D
。
- 根据给定的半径
-
创建输出张量
patches
:auto opts = net.options(); auto patches = torch::zeros({B, M, C, D, D}, opts); - 使用
net
的选项创建一个形状为[B, M, C, D, D]
的零张量patches
,用于存储图像块。
- 使用
-
调用 CUDA 内核函数:
AT_DISPATCH_FLOATING_TYPES_AND_HALF(net.type(), "patchify_forward_kernel", ([&] { patchify_forward_kernel<scalar_t><<<BLOCKS(B * M * D * D), THREADS>>>(radius, net.packed_accessor32<scalar_t,4,torch::RestrictPtrTraits>(), coords.packed_accessor32<float,3,torch::RestrictPtrTraits>(), patches.packed_accessor32<scalar_t,5,torch::RestrictPtrTraits>()); })); - 使用
AT_DISPATCH_FLOATING_TYPES_AND_HALF
宏来根据net
的类型分派 CUDA 内核函数。 - 调用
patchify_forward_kernel
CUDA 内核函数,传递参数radius
、net
、coords
和patches
。
- 使用
-
返回结果:
return { patches }; - 返回包含
patches
的向量。
- 返回包含
总结
patchify_cuda_forward
函数的主要功能是将输入的图像特征图和坐标转换为图像块。它通过调用 CUDA 内核函数 patchify_forward_kernel
来实现这一功能,并将结果存储在 patches
张量中。最终,函数返回包含 patches
的向量。
问题7:patchify_forward_kernel 函数的解读
这个函数 patchify_forward_kernel
是一个 CUDA 核函数,用于从输入的特征图中提取指定坐标处的图像块(patch),并将其存储在输出的 patches
张量中。下面逐步解释这个函数的各个部分及其作用:
参数解释:
- R: 半径,用于确定图像块的大小。图像块的直径 $ D = 2 \times R + 2$,包括中心点以及周围的像素点。
- net: 输入的特征图,形状为
[B, C, H, W]
,其中:- $ B$ 是批量大小。
- $ C$ 是通道数。
- $ H$ 和 $ W$ 是图像的高度和宽度。
- coords: 输入的坐标张量,形状为
[B, M, 2]
,其中:- $ B$ 是批量大小。
- $ M$ 是坐标的数量。
- 2 表示每个坐标点的 $ (x, y)$ 值,表示在图像上需要提取 patch 的中心坐标。
- patches: 输出的图像块张量,形状为
[B, M, C, D, D]
,其中:- $ B$ 是批量大小。
- $ M$ 是坐标的数量。
- $ C$ 是通道数。
- $ D$ 是图像块的直径(即 $ 2 \times R + 2$)。
函数分解:
1. 直径 D 的计算
const int D = 2*R + 2;
这一步计算图像块(patch)的直径 $ D$,由给定的半径 $ R$ 决定。
2. 批量大小、坐标数量、通道数、图像高度和宽度的获取
const int B = coords.size(0); const int M = coords.size(1); const int C = net.size(1); const int H = net.size(2); const int W = net.size(3);
- B: 批量大小,表示一批图像的数量。
- M: 每幅图像中坐标的数量,即需要从图像中提取多少个 patch。
- C: 图像特征图中的通道数。
- H 和 W: 图像的高度和宽度。
3. 线程索引的计算
int n = blockIdx.x * blockDim.x + threadIdx.x;
这里通过 CUDA 的 blockIdx.x
和 threadIdx.x
,计算出当前线程的全局索引 $ n$。每个线程处理一个 patches
张量中的元素。
4. 检查索引是否在范围内
if (n < B * M * D * D) {
检查当前线程对应的索引 $ n$ 是否在处理范围内,确保不会越界。
5. 计算 Patch 内的像素位置
const int ii = n % D; n /= D; const int jj = n % D; n /= D; const int m = n % M; n /= M;
- ii 和 jj: 当前线程负责的 patch 内的坐标索引(即在 $ D \times D$ 的 patch 中的坐标)。
- m: 当前线程负责的 patch 在坐标数组中的索引。
6. 获取坐标并计算图像中的像素位置
const float x = coords[n][m][0]; const float y = coords[n][m][1]; const int i = static_cast<int>(floor(y)) + (ii - R); const int j = static_cast<int>(floor(x)) + (jj - R);
- x 和 y: 这是输入的坐标张量
coords
中的中心坐标(表示在特征图net
中需要提取 patch 的中心位置)。 - i 和 j: 通过偏移计算当前 patch 中的每个像素在原始特征图
net
中的位置,使用 $ R$ 来确定相对于中心的偏移量。
7. 检查像素是否在特征图的范围内
if (within_bounds(i, j, H, W)) {
使用辅助函数 within_bounds
检查当前计算出的像素 $ (i, j)$ 是否在图像的有效范围内,防止越界访问。
8. 从输入特征图提取 patch
for (int k=0; k<C; k++) patches[n][m][k][ii][jj] = net[n][k][i][j];
如果像素 $ (i, j)$ 在有效范围内,则从输入特征图 net
中提取对应的值,并将其存储在输出的 patches
张量中。这里的 for
循环用于处理所有通道 $ C$,确保每个通道的数据都正确复制到输出中。
总结:
这个 CUDA 核函数的作用是从输入特征图 net
中根据给定的坐标 coords
提取一组大小为 $ D \times D$ 的 patch。每个 patch 的位置由 coords
中的坐标点确定,半径为 $ R$。提取的 patch 被存储在 patches
张量中,且函数通过并行计算大幅加速了从特征图中提取 patch 的过程。
主要步骤:
- 计算每个线程负责的 patch 中的像素位置。
- 根据中心坐标和半径计算 patch 中每个像素的全局位置。
- 检查像素是否在图像范围内,并从特征图中提取相应值存入输出的
patches
中。
返回的 patches:[batch_size, num_patches, channels, patch_height, patch_width]
问题8:为什么是 2 * R + 2
而不是 2 * R
?
解释
-
半径
R
:R
是给定的半径,表示从中心点到边缘的距离。
-
直径
D
:- 直径
D
是 patches 的总宽度或高度。
- 直径
-
为什么是
2 * R + 2
而不是2 * R
:- 直接使用
2 * R
计算直径时,得到的直径是2 * R
,这意味着图像块的总宽度或高度是2 * R
。 - 但是,考虑到图像块的中心点(即坐标点)周围的像素,通常我们需要包括中心点本身以及其周围的像素。
- 例如,如果
R = 1
,直接使用2 * R
计算直径,得到的直径是2
,这意味着图像块只包括中心点及其左右各一个像素,总共 3 个像素。 - 然而,实际上我们可能希望包括中心点及其周围的像素,总共 5 个像素(中心点 + 上下左右各一个像素)。
- 因此,为了确保图像块包括中心点及其周围的像素,直径需要增加 2,即
2 * R + 2
。
- 直接使用
具体例子
假设 R = 1
:
- 直接使用
2 * R
计算直径,得到的直径是2
,这意味着图像块只包括中心点及其左右各一个像素,总共 3 个像素。 - 使用
2 * R + 2
计算直径,得到的直径是4
,这意味着图像块包括中心点及其上下左右各一个像素,总共 5 个像素。
总结
直径 D
的计算公式 2 * R + 2
是为了确保图像块包括中心点及其周围的像素,而不仅仅是中心点及其左右各一个像素。这样可以更全面地覆盖中心点周围的像素,从而更准确地表示图像块。
问题9: const int i = static_cast<int>(floor(y)) + (ii - R);
这句具体什么意思,怎么理解呢?
这句代码:
const int i = static_cast<int>(floor(y)) + (ii - R);
用于计算提取的图像块(patch)中某个像素在原始特征图中的垂直方向(行方向)上的索引。为了更好地理解,下面分解并详细解释这句代码的含义。
1. y
是什么?
const float y = coords[n][m][1];
y
表示的是从输入的坐标张量 coords
中提取的 $ y$ 坐标。coords
是一个三维张量,形状为 [B, M, 2]
,其中:
B
是批次大小(batch size)。M
是每张图像中指定的坐标点数。- 每个坐标点有两个值,分别是 $ x$ 和 $ y$,其中 $ y$ 表示该 patch 的中心点在特征图
net
中的纵向位置。
2. floor(y)
是什么?
floor(y)
表示对 $ y$ 坐标值进行向下取整操作。由于 $ y$ 是浮点数,可能代表一个不在整数位置上的坐标,例如 $ y = 10.7$。floor(y)
会将这个值向下取整到最接近的整数,即 floor(10.7) = 10
。这通常意味着我们在特征图上选取一个贴近 $ y$ 坐标的整点作为起始位置。
3. static_cast<int>(floor(y))
是什么?
static_cast<int>(floor(y))
表示将浮点数 floor(y)
转换为整数类型。虽然 floor(y)
可能已经是一个整数值,但它的返回类型仍然是 float
,所以需要通过 static_cast<int>()
将其显式地转换为整数类型。
4. ii - R
是什么?
在代码的上方,有如下定义:
const int ii = n % D;
ii
是当前线程负责的 patch 中的像素在垂直方向上的索引。D = 2 * R + 2
是 patch 的直径,其中 $ R$ 是半径。
ii
的取值范围是 [0, D-1]
,表示当前 patch 中像素的垂直位置。R
是半径,ii - R
的作用是将 patch 中的相对坐标偏移到以 patch 中心点为基准的位置。
例如,如果 $ R = 1$,则 $ D = 2 \times 1 + 2 = 4$,则 ii
的取值范围是 0 到 3。此时:
- 当
ii = 0
时,ii - R = 0 - 1 = -1
,表示在 patch 中心点的上方。 - 当
ii = 1
时,ii - R = 1 - 1 = 0
,表示 patch 的中心点。 - 当
ii = 2
时,ii - R = 2 - 1 = 1
,表示 patch 中心点的下方。 - 当
ii = 3
时,ii - R = 3 - 1 = 2
,表示更靠近下方的点。
因此,ii - R
是对当前 patch 中每个像素相对于中心点的垂直偏移量。
5. 组合解释
const int i = static_cast<int>(floor(y)) + (ii - R);
这句代码的完整解释是:
static_cast<int>(floor(y))
:将 $ y$ 坐标向下取整并转换为整数,这是 patch 的中心点在原始特征图中的行(垂直)位置。ii - R
:这是相对于 patch 中心点的垂直偏移量,ii
是当前 patch 中的像素在 patch 中的位置,减去半径 $ R$ 以获得相对偏移。
因此,这段代码的作用是计算 patch 中某个像素在原始特征图中的垂直位置,通过 patch 中心点的 $ y$ 坐标和当前像素相对于 patch 中心的偏移量来确定。
举例:
假设:
- $ y = 10.7$(patch 中心点的 $ y$ 坐标)。
ii = 3
,R = 1
。
那么:
floor(y)
的结果是10
。ii - R = 3 - 1 = 2
,表示当前像素相对于 patch 中心点向下偏移 2 个单位。- 最终结果
i = 10 + 2 = 12
,表示该 patch 中当前像素在原始特征图中的垂直位置为第 12 行。
总结:
这句代码用于计算当前 patch 中某个像素在原始特征图中的垂直位置。它首先根据给定的中心坐标 $ y$ 计算中心点的行索引,然后通过偏移量 $ ii - R$ 调整以找到 patch 中其他像素的位置。这个过程确保了我们能从原始特征图中正确提取出以指定坐标为中心的 patch。
个人总结
这个 cuda 计算其实就是把 特征图中的 patch 块找到,并且 把特征图里面的值 copy 到 pathes 变量中。
因为有两种特征:
- fmap:matching-feature, [1, 1, 128, 120, 160]
- imap:context-feature, [1, 1, 384, 120, 160]
所以经过上述操作得到:
- fmap 的 patches: [1, 96, 128, 4, 4], fmap 的 半径选的是 1,所以 patch 块的 D 为 2x1+2=4, 每个 patch 有 16 个元素
- imap 的 patches: [1, 96, 384, 2, 2], imap 的 半径选的是 0,所以 patch 块的 D 为 2x0+2=2, 每个 patch 有 4 个元素
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现