news 2026/9/24 17:22:47

kornia.geometry.keypoints 使用指南:面向对象的 2D/3D 关键点封装 API 详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
kornia.geometry.keypoints 使用指南:面向对象的 2D/3D 关键点封装 API 详解
  • 计算机视觉
  • 人工智能
  • 深度学习
  • 图像处理

【免费下载链接】kornia

🐍 Geometric Computer Vision Library for Spatial AI

项目地址:https://gitcode.com/gh_mirrors/ko/kornia
点击查看免费下载

kornia 在kornia.geometry.keypoints模块中提供了面向对象的 2D/3D 关键点 API,通过KeypointsKeypoints3D两个类将点集张量包装为带类型校验、几何变换与填充能力的一等对象。本文基于该模块的官方文档与仓库源码,完整讲解其数据结构约定、构造校验、张量互转、索引切片、几何变换、填充/去填充等核心能力,并延伸到VideoKeypoints与数据增强容器中的集成用法,帮助你在特征匹配、目标跟踪、三维重建与增强管道中直接落地这套 API。

模块定位:为什么需要关键点封装类

在 Kornia 的几何体系中,kornia.geometry.keypoints模块提供的是关键点的面向对象 API(Object-oriented API)。官方文档对其定位描述为:KeypointsKeypoints3D类包装一个点集张量(a tensor of points),并向外提供**变换(transformations)填充(padding)**能力,适用于物体识别、跟踪与三维重建等场景。

与直接操作原始torch.Tensor相比,这套封装带来几个实际收益:

  • 形状语义明确:构造时强制校验张量布局,杜绝把(N, 3)(B, N, 4)等非法形状当作关键点误用;
  • 类型安全:默认拒绝整数坐标,保证后续几何运算在浮点域内进行;
  • 统一操作入口:变换、填充、索引、克隆等操作都收敛到类方法上,与同模块的 Boxes 等几何容器保持一致的编程模型;
  • 增强管道集成:数据增强容器可以直接把关键点作为一等输入类型处理(见下文"增强容器中的集成")。

核心实现位于 kornia/geometry/keypoints.py,公开导出两个主类:Keypoints(2D)与Keypoints3D(3D),另外模块内还实现了面向视频序列的VideoKeypoints子类。

数据结构与坐标约定

2D 关键点:(N, 2)(B, N, 2)

Keypoints类内部维护一个坐标张量,支持两种布局(源码见 keypoints.py 的构造校验):

布局形状含义
未批处理(unbatched)(N, 2)单幅图像/单帧中的 N 个点
批处理(batched)(B, N, 2)B 个样本,每个含 N 个点

最后一维始终存储(x, y)坐标。构造时通过self._is_batched = False if keypoints.ndim == 2 else True(keypoints.py)记录批处理状态。

3D 关键点:(N, 3)(B, N, 3)

Keypoints3D采用同样的设计,只是最后一维扩展为(x, y, z),布局为(N, 3)(B, N, 3)(keypoints.py)。

形状校验规则

构造器对形状有严格限制(keypoints.py):

if len(keypoints.shape) == 0: # 0 维标量会被 reshape 为 (-1, 2),且使用 reshape 而非 view, # 避免产生与输入无关的新张量而干扰 torch.jit 的追踪 keypoints = keypoints.reshape((-1, 2)) if not (2 <= keypoints.ndim <= 3 and keypoints.shape[-1:] == (2,)): raise ValueError(f"Keypoints shape must be (N, 2) or (B, N, 2). Got {keypoints.shape}.")

即:维度只允许 2 或 3,且最后一维必须严格等于 2(3D 类对应为 3)。测试 tests/geometry/test_keypoints.py 验证了Keypoints(torch.rand(3, 3))Keypoints(torch.rand(3, 4, 2, 2))都会被拒绝。

构造与输入校验

两个类的构造函数签名一致:

Keypoints(keypoints: Union[torch.Tensor, List[torch.Tensor]], raise_if_not_floating_point: bool = True) Keypoints3D(keypoints: Union[torch.Tensor, List[torch.Tensor]], raise_if_not_floating_point: bool = True)

参数语义如下:

  • keypoints:原始点集张量;也支持传入List[torch.Tensor](此时内部会调用_merge_keypoint_list合并列表,但目前该辅助函数在源码中raise NotImplementedError,keypoints.py,因此实际使用时以单个张量为准);
  • raise_if_not_floating_point:默认True,若输入张量不是浮点类型则直接抛出ValueError(提示Coordinates must be in floating point);若设为False,则自动调用.float()转换后继续使用。

构造过程的校验链(两个类完全一致):

  1. 类型校验:非张量输入抛出TypeError(测试 test_keypoints.py);
  2. 浮点校验:整数张量默认抛出ValueError(test_keypoints.py 同时验证了raise_if_not_floating_point=False时 int32 会被转为 float32);
  3. 形状校验:见上一节。
import torch from kornia.geometry.keypoints import Keypoints # 合法构造:Nx2 与 BxNx2 kp_unbatched = Keypoints(torch.rand(10, 2)) kp_batched = Keypoints(torch.rand(3, 10, 2)) # 非法输入:非张量 / 整数坐标 / 错误形状 # Keypoints("not a tensor") # TypeError # Keypoints(torch.ones(5, 2, dtype=torch.int32)) # ValueError(默认) # Keypoints(torch.rand(3, 3)) # ValueError # 关闭浮点校验:整数自动转 float32 kp = Keypoints(torch.ones(5, 2, dtype=torch.int32), raise_if_not_floating_point=False) assert kp.dtype == torch.float32

基础属性与张量互转

属性访问

属性返回内容源码位置
shape底层坐标张量的形状,即(N, 2)(B, N, 2)(3D 为(N, 3)/(B, N, 3)keypoints.py
data原始坐标张量((..., 2),最后一维为(x, y)keypoints.py
device坐标张量所在设备keypoints.py
dtype坐标张量的数据类型keypoints.py

与张量互转:from_tensor/to_tensor

# 类方法:校验并包装张量 kp = Keypoints.from_tensor(torch.rand(5, 2)) # 返回 Keypoints 实例 kp3d = Keypoints3D.from_tensor(torch.rand(5, 3)) # 返回 Keypoints3D 实例 # 实例方法:还原为原始张量 (B, N, 2)/(B, N, 3) raw = kp.to_tensor()

to_tensoras_padded_sequence参数用于"关键点来自列表"的场景,当前实现中该路径会抛出NotImplementedError(keypoints.py),默认直接返回内部self._data。测试 test_keypoints.py 验证了to_tensor()与输入数据逐元素一致。

克隆与类型转换

kp2 = kp.clone() # 深度拷贝:修改 kp2.data 不影响 kp.data kp.type(torch.float64) # 原地转换坐标 dtype,返回 self

clone()通过Keypoints(self._data.clone(), False)实现(keypoints.py),测试验证了克隆后两者互不影响(test_keypoints.py)。

索引、切片与定点写入

Keypoints/Keypoints3D支持与张量类似的索引操作:

# 切片:返回新的 Keypoints 实例(不共享修改) sub = kp[:5] # 取出前 5 个点,sub.shape == (5, 2) sub = kp[torch.tensor([0, 2, 4])] # 也支持张量索引 # 赋值:把另一组 Keypoints 的值写回 new_kp = Keypoints(torch.zeros(5, 2)) kp[:5] = new_kp # 底层执行 self._data[key] = value._data # 定点写入(类似 Tensor.index_put_) idx = (torch.tensor([0, 1, 2]),) kp2 = kp.index_put(idx, torch.ones(3, 2)) # 非原地:克隆后写入,返回新对象 kp.index_put(idx, torch.ones(3, 2), inplace=True) # 原地:直接修改 self 并返回

index_put的签名(keypoints.py)接受indices(被Tensor.index_put_接受的索引元组/列表)、values(原始张量或另一个Keypoints对象)以及inplace开关;非原地模式下先clone()再写回,保证原对象不变。对应测试见 test_keypoints.py。

几何变换:transform_keypoints

用法与矩阵约定

transform_keypoints是 2D 关键点封装中最核心的几何能力(keypoints.py):

def transform_keypoints(self, M: torch.Tensor, inplace: bool = False) -> "Keypoints":
  • M:变换矩阵,形状为(3, 3)(单组)或(B, 3, 3)(每组对应一个矩阵),要求2 <= M.ndim <= 3M.shape[-2:] == (3, 3),否则抛ValueError
  • inplace:为True时直接改写self._data并返回self;否则返回一个新的Keypoints对象;
  • 便捷方法transform_keypoints_(M)等价于transform_keypoints(M, inplace=True)(keypoints.py)。

示例(平移变换,与测试 test_keypoints.py 中的期望值一致):

data = torch.tensor([[[1.0, 0.0], [0.0, 1.0]]]) # (1, 2, 2):B=1, N=2 kp = Keypoints(data) M = torch.eye(3).unsqueeze(0) # (1, 3, 3) M[0, 0, 2] = 2.0 # 平移 x 方向 +2 M[0, 1, 2] = 3.0 # 平移 y 方向 +3 kp_t = kp.transform_keypoints(M) # kp_t.data == tensor([[[3., 3.], [2., 4.]]]) # 每个点 (x+2, y+3) # 原地版本 kp.transform_keypoints_(M)

底层实现:transform_points

transform_keypoints内部实际调用的是 kornia/geometry/linalg.py 中的transform_points(trans_01, points_1)。该底层函数:

  • 输入变换矩阵形状为(B, D+1, D+1),点集为(B, N, D),输出(B, N, D)(此处 D=2);
  • 校验批大小必须一致或为 1(允许广播),且矩阵最后一维比点集最后一维大 1;
  • 对空点集(points_1.shape[-2] == 0)提前返回,避免 reshape 推断-1时崩溃——这意味着可以安全地对"无标注的空关键点集"做变换;
  • 内部会先 reshape 到统一的BxNxD再执行批矩阵乘法(bmm)。

因此transform_keypoints的梯度可以一直回传到输入点集:测试 test_keypoints.py 使用torch.float64Keypoints(x).transform_keypoints(M).data做了gradcheck验证;同一测试文件中的test_dynamo也验证了该操作可被torch.compile/dynamo 优化路径正确执行。

批处理注意点

当关键点为(B, N, 2)时,M可以是(B, 3, 3)(每样本独立变换),也可以是(1, 3, 3)(广播到所有样本)。测试 test_keypoints.py 展示了用(1, 3, 3)矩阵对(2, 4, 2)批关键点整体平移的场景。

填充与去填充:pad/unpad

在图像增强(如随机裁剪、填充)后,关键点坐标需要随图像坐标系一起平移。Keypoints.pad/unpad即为此设计(keypoints.py):

def pad(self, padding_size: torch.Tensor) -> "Keypoints": ... def unpad(self, padding_size: torch.Tensor) -> "Keypoints": ...
  • padding_size:形状必须为(B, 4),按(left, right, top, bottom)顺序组织,否则抛RuntimeError
  • pad 逻辑x += left_paddingy += top_padding(只有 left 与 top 会改变坐标原点);
  • unpad 逻辑x -= left_paddingy -= top_padding,即 pad 的逆操作。

这一约定与同仓库 Boxes 的pad完全一致("padding_size is ordered as(left, right, top, bottom). Onlyleftandtopchange the coordinate origin"),保证同一增强管道中"框"与"点"使用同一套填充语义。

data = torch.zeros(2, 4, 2) # (B=2, N=4, 2) kp = Keypoints(data) padding = torch.tensor([[1.0, 0.0, 2.0, 0.0], # 样本0:left=1, top=2 [0.0, 0.0, 3.0, 0.0]]) # 样本1:left=0, top=3 kp.pad(padding) # 样本0: x += 1, y += 2 # 样本1: x += 0, y += 3 kp.unpad(padding) # 撤销填充,回到原始坐标

对应测试见 test_keypoints.py,其中padkp.data[0, :, 0]全为 1.0(x 加了 left=1),kp.data[0, :, 1]全为 2.0(y 加了 top=2)。

Keypoints3D:3D 关键点的现状与边界

Keypoints3D提供了与Keypoints对齐的构造校验、形状约束((N, 3)/(B, N, 3))、shape/data属性、from_tensor/to_tensorclone、索引与切片能力,可直接用于三维点集管理。

但需要特别说明的是,从当前源码看,3D 关键点的部分几何操作尚未实现(keypoints.py):

方法当前状态
pad/unpadraise NotImplementedError(签名中padding_size规划为(B, 6),对应三维 left/right/top/bottom 及深度方向的填充语义)
transform_keypoints/transform_keypoints_raise NotImplementedError(docstring 中矩阵形状仍沿用 2D 的(3, 3)/(B, 3, 3),实现待完善)

测试 test_keypoints.py 明确断言了这些方法会抛出NotImplementedError。因此在实际项目中,目前应把Keypoints3D定位为"3D 关键点的结构化容器"(负责布局校验、属性访问、克隆与张量互转),几何变换部分可自行结合transform_points(其 D 维可扩展到 3)等底层函数处理,并关注 Kornia 后续版本对Keypoints3D变换/填充能力的补齐。

VideoKeypoints:视频序列关键点

kornia.augmentation.container.augment等视频增强场景需要处理(B, T, N, 2)布局(B=批大小,T=帧数/时间通道,N=点数)。VideoKeypointsKeypoints的子类(keypoints.py),用于封装这类时间序列关键点:

from kornia.geometry.keypoints import VideoKeypoints data = torch.rand(2, 5, 10, 2) # (B, T, N, 2) vkp = VideoKeypoints.from_tensor(data) # 内部 reshape 为 (B*T, N, 2) assert vkp.temporal_channel_size == 5 # 时间维度被记录 out = vkp.to_tensor() # 还原为 (B, T, N, 2)

关键实现点:

  • from_tensor只接受(B, T, N, 2)张量,其余输入抛ValueError(keypoints.py),内部以boxes.size(1)记录temporal_channel_size,并将数据view(batch * time, -1, 2)存储——这样即可复用父类的单帧操作逻辑;
  • to_tensor重写为out.view(-1, self.temporal_channel_size, ...),把内部形状还原成(B, T, N, 2)
  • transform_keypointsclone均会保留temporal_channel_size,保证变换/克隆后仍能正确还原时间维(keypoints.py)。

测试见 test_keypoints.py,覆盖了构造、temporal_channel_size记录、to_tensor往返一致性与异常输入。

在数据增强容器中的集成

Keypoints/VideoKeypoints并非孤立类,它们已被 Kornia 增强体系作为一等数据类型接入。在 kornia/augmentation/container/augment.py 的_preproc_keypoints中可以看到完整的数据流:

  1. 视频序列:当容器处于contains_video_sequential模式时,输入张量(或张量列表)先统一转为浮点,再通过VideoKeypoints.from_tensor包装,必要时用.type(dtype)还原原始 dtype;
  2. 2D 单帧:输入若已是Keypoints实例则直接透传;否则用Keypoints.from_tensor包装,非浮点输入先转float()
  3. 3D 增强:当前路径会抛出NotImplementedError("3D keypoint handlers are not yet supported.")——从源码看,3D 增强容器对关键点的支持仍在规划中,使用时需注意这一限制。

增强结束后,_postproc_keypoint(augment.py)负责把Keypoints还原为用户传入的原始类型(张量或Keypoints对象)。此外,2D 增强基类(如 kornia/augmentation/_2d/base.py)与各类几何增强算子(如 crop.py)也都声明了接受Keypoints输入的接口。这意味着你可以把关键点与其他数据类型(图像、掩膜、框)一起送入增强容器,实现"图像变换的同时关键点同步变换"。

正确性保障:测试与验证

模块的完整行为由 tests/geometry/test_keypoints.py 覆盖,可作为使用时的行为参考:

  • 冒烟与基数test_smoketest_cardinalitytest_batched/test_unbatched验证构造与形状记录;
  • 互转test_from_tensor/test_to_tensor验证与张量往返一致;
  • 索引test_getitem/test_setitem/test_index_put(含原地模式)验证切片与定点写入;
  • 变换test_transform_keypoints/test_transform_keypoints_inplace/test_transform_keypoints_batched验证平移结果,test_gradcheck验证可微性,test_dynamo验证编译兼容;
  • 填充test_pad/test_unpad验证 left/top 语义;
  • 异常路径test_exception/test_transform_exception/test_pad_exception验证非法输入均被拦截;
  • 整数坐标test_int_input_raises_by_defaulttest_int_input_converted_when_not_raising验证raise_if_not_floating_point两种行为;
  • 3D 边界test_not_implemented明确记录Keypoints3D.pad/unpad/transform_keypoints当前未实现。

由于Keypoints并非nn.Module,测试中test_smoke_jittest_module均为跳过状态(test_keypoints.py),即该类不参与 JIT 模块化与nn.Module的常规测试流程。

使用建议小结

  1. 优先用封装类而非裸张量:在涉及关键点的检测、匹配、跟踪与增强流程中,用Keypoints.from_tensor包装一次,即可获得形状/类型校验与统一的transform_keypointspad/unpad操作入口;
  2. 牢记坐标与填充约定:坐标最后一维按(x, y)(3D 为(x, y, z))排列;padding_size(left, right, top, bottom)排列且只有 left/top 平移坐标,与Boxes语义一致;
  3. 3D 能力边界Keypoints3D目前适合做容器与张量互转,几何变换与填充需等待实现补齐或自行调用底层transform_points
  4. 视频场景使用VideoKeypoints(B, T, N, 2)布局请走VideoKeypoints.from_tensor,变换与克隆后仍可通过to_tensor还原时间维;
  5. 增强管道联动:把关键点直接传给增强容器(或其中的几何增强算子),图像与关键点会同步经历同一变换,减少手工同步坐标的出错概率。

参考资料:官方 API 文档见 docs/source/geometry.keypoints.rst;核心实现见 kornia/geometry/keypoints.py;底层点变换见 kornia/geometry/linalg.py;行为测试见 tests/geometry/test_keypoints.py。

  • 计算机视觉
  • 人工智能
  • 深度学习
  • 图像处理

【免费下载链接】kornia

🐍 Geometric Computer Vision Library for Spatial AI

项目地址:https://gitcode.com/gh_mirrors/ko/kornia
点击查看免费下载

相关推荐

上一篇:CopilotKit 语音输入 Demo 的声音素材指南:为 CrewAI Conversational Flows 准备 sample.wav
下一篇:Metabase 原生 SQL 可选变量(Optional Variables)完全指南:用 `[[ ]]` 让查询子句智能显隐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 17:22:23

GEO服务商能力构成全景:七种定位类型与企业匹配逻辑

2026年&#xff0c;AI搜索正在改变品牌可见性的竞争方式。企业决策者在获取行业信息时&#xff0c;越来越多地直接向AI提问并采纳答案&#xff0c;这个变化使得"品牌是否出现在AI的答案里"成为一个具体问题。市场上的GEO服务商数量在一年内明显增长&#xff0c;类型也…

作者头像 李华
网站建设 2026/9/24 17:19:41

【MATLAB例程】三维RRT路径规划与TOA-AOA-TDOA融合定位算法。附下载链接,代码有中文注释、包运行成功

原创代码&#xff0c;请勿翻卖 文章目录程序简介路径规划模型量测模型运行结果MATLAB源代码程序简介 本程序实现三维RRT避障路径规划与TOA、AOA、TDOA融合定位&#xff0c;并对三维轨迹及定位误差进行分析。地图范围、三维障碍物、起终点、锚节点位置、RRT规划参数及量测噪声等…

作者头像 李华