Transformers 文档

ViTPose

Hugging Face's logo
加入 Hugging Face 社区

并获取增强的文档体验

开始使用

ViTPose

PyTorch

概述

ViTPose 模型由 Yufei Xu、Jing Zhang、Qiming Zhang、Dacheng Tao 在 ViTPose: Simple Vision Transformer Baselines for Human Pose Estimation 中提出。ViTPose 采用标准的、非分层的 Vision Transformer 作为人体关键点估计任务的骨干网络。在其之上添加了一个简单的解码器头,用于从给定的图像预测热图。尽管其结构简单,但该模型在具有挑战性的 MS COCO 关键点检测基准测试中取得了最先进的结果。在 ViTPose++: Vision Transformer for Generic Body Pose Estimation 中,作者在 ViT 骨干网络中采用混合专家 (MoE) 模块,并在更多数据上进行预训练,进一步提高了性能,模型得到了进一步改进。

论文摘要如下

尽管在设计中没有考虑特定的领域知识,但纯视觉 Transformer 在视觉识别任务中表现出了出色的性能。然而,在揭示这种简单结构在姿态估计任务中的潜力方面,人们几乎没有付出努力。在本文中,我们通过一个名为 ViTPose 的简单基线模型,从模型结构的简洁性、模型尺寸的可扩展性、训练范例的灵活性以及模型之间知识的可迁移性等各个方面,展示了纯视觉 Transformer 在姿态估计方面的惊人能力。具体而言,ViTPose 采用纯粹的非分层视觉 Transformer 作为骨干网络,为给定的人体实例提取特征,并采用轻量级解码器进行姿态估计。通过利用 Transformer 的可扩展模型容量和高并行性,它可以从 1 亿参数扩展到 10 亿参数,在吞吐量和性能之间设定了新的帕累托前沿。此外,ViTPose 在注意力类型、输入分辨率、预训练和微调策略以及处理多姿态任务方面非常灵活。我们还通过经验证明,大型 ViTPose 模型的知识可以通过简单的知识令牌轻松转移到小型模型。实验结果表明,我们的基本 ViTPose 模型在具有挑战性的 MS COCO 关键点检测基准测试中优于代表性方法,而最大的模型则创造了新的最先进水平。

drawing ViTPose 架构。摘自原始论文。

此模型由 nielsrsangbumchoi 贡献。原始代码可以在这里找到。

使用技巧

ViTPose 是一种所谓的自上而下的关键点检测模型。这意味着首先使用对象检测器(如 RT-DETR)来检测图像中的人物(或其他实例)。接下来,ViTPose 将裁剪后的图像作为输入,并预测每个图像的关键点。

import torch
import requests
import numpy as np

from PIL import Image

from transformers import AutoProcessor, RTDetrForObjectDetection, VitPoseForPoseEstimation

device = "cuda" if torch.cuda.is_available() else "cpu"

url = "http://images.cocodataset.org/val2017/000000000139.jpg"
image = Image.open(requests.get(url, stream=True).raw)

# ------------------------------------------------------------------------
# Stage 1. Detect humans on the image
# ------------------------------------------------------------------------

# You can choose any detector of your choice
person_image_processor = AutoProcessor.from_pretrained("PekingU/rtdetr_r50vd_coco_o365")
person_model = RTDetrForObjectDetection.from_pretrained("PekingU/rtdetr_r50vd_coco_o365", device_map=device)

inputs = person_image_processor(images=image, return_tensors="pt").to(device)

with torch.no_grad():
    outputs = person_model(**inputs)

results = person_image_processor.post_process_object_detection(
    outputs, target_sizes=torch.tensor([(image.height, image.width)]), threshold=0.3
)
result = results[0]  # take first image results

# Human label refers 0 index in COCO dataset
person_boxes = result["boxes"][result["labels"] == 0]
person_boxes = person_boxes.cpu().numpy()

# Convert boxes from VOC (x1, y1, x2, y2) to COCO (x1, y1, w, h) format
person_boxes[:, 2] = person_boxes[:, 2] - person_boxes[:, 0]
person_boxes[:, 3] = person_boxes[:, 3] - person_boxes[:, 1]

# ------------------------------------------------------------------------
# Stage 2. Detect keypoints for each person found
# ------------------------------------------------------------------------

image_processor = AutoProcessor.from_pretrained("usyd-community/vitpose-base-simple")
model = VitPoseForPoseEstimation.from_pretrained("usyd-community/vitpose-base-simple", device_map=device)

inputs = image_processor(image, boxes=[person_boxes], return_tensors="pt").to(device)

with torch.no_grad():
    outputs = model(**inputs)

pose_results = image_processor.post_process_pose_estimation(outputs, boxes=[person_boxes])
image_pose_result = pose_results[0]  # results for first image

ViTPose++ 模型

最好的检查点ViTPose++ 论文中的那些。ViTPose++ 模型为 ViT 骨干网络采用了所谓的 混合专家 (MoE) 架构,从而获得更好的性能。

ViTPose+ 检查点使用 6 个专家,因此可以传递 6 个不同的数据集索引。下面提供了各种数据集索引的概述

在模型的前向传播中传递 dataset_index 参数,以指示要为批次中的每个示例使用哪些专家。下面显示了示例用法

image_processor = AutoProcessor.from_pretrained("usyd-community/vitpose-plus-base")
model = VitPoseForPoseEstimation.from_pretrained("usyd-community/vitpose-plus-base", device=device)

inputs = image_processor(image, boxes=[person_boxes], return_tensors="pt").to(device)

dataset_index = torch.tensor([0], device=device) # must be a tensor of shape (batch_size,)

with torch.no_grad():
    outputs = model(**inputs, dataset_index=dataset_index)

ViTPose+ 检查点使用 6 个专家,因此可以传递 6 个不同的数据集索引。下面提供了各种数据集索引的概述

可视化

要可视化各种关键点,可以使用 supervision (需要 pip install supervision

import supervision as sv

xy = torch.stack([pose_result['keypoints'] for pose_result in image_pose_result]).cpu().numpy()
scores = torch.stack([pose_result['scores'] for pose_result in image_pose_result]).cpu().numpy()

key_points = sv.KeyPoints(
    xy=xy, confidence=scores
)

edge_annotator = sv.EdgeAnnotator(
    color=sv.Color.GREEN,
    thickness=1
)
vertex_annotator = sv.VertexAnnotator(
    color=sv.Color.RED,
    radius=2
)
annotated_frame = edge_annotator.annotate(
    scene=image.copy(),
    key_points=key_points
)
annotated_frame = vertex_annotator.annotate(
    scene=annotated_frame,
    key_points=key_points
)

或者,也可以使用 OpenCV 可视化关键点(需要 pip install opencv-python

import math
import cv2

def draw_points(image, keypoints, scores, pose_keypoint_color, keypoint_score_threshold, radius, show_keypoint_weight):
    if pose_keypoint_color is not None:
        assert len(pose_keypoint_color) == len(keypoints)
    for kid, (kpt, kpt_score) in enumerate(zip(keypoints, scores)):
        x_coord, y_coord = int(kpt[0]), int(kpt[1])
        if kpt_score > keypoint_score_threshold:
            color = tuple(int(c) for c in pose_keypoint_color[kid])
            if show_keypoint_weight:
                cv2.circle(image, (int(x_coord), int(y_coord)), radius, color, -1)
                transparency = max(0, min(1, kpt_score))
                cv2.addWeighted(image, transparency, image, 1 - transparency, 0, dst=image)
            else:
                cv2.circle(image, (int(x_coord), int(y_coord)), radius, color, -1)

def draw_links(image, keypoints, scores, keypoint_edges, link_colors, keypoint_score_threshold, thickness, show_keypoint_weight, stick_width = 2):
    height, width, _ = image.shape
    if keypoint_edges is not None and link_colors is not None:
        assert len(link_colors) == len(keypoint_edges)
        for sk_id, sk in enumerate(keypoint_edges):
            x1, y1, score1 = (int(keypoints[sk[0], 0]), int(keypoints[sk[0], 1]), scores[sk[0]])
            x2, y2, score2 = (int(keypoints[sk[1], 0]), int(keypoints[sk[1], 1]), scores[sk[1]])
            if (
                x1 > 0
                and x1 < width
                and y1 > 0
                and y1 < height
                and x2 > 0
                and x2 < width
                and y2 > 0
                and y2 < height
                and score1 > keypoint_score_threshold
                and score2 > keypoint_score_threshold
            ):
                color = tuple(int(c) for c in link_colors[sk_id])
                if show_keypoint_weight:
                    X = (x1, x2)
                    Y = (y1, y2)
                    mean_x = np.mean(X)
                    mean_y = np.mean(Y)
                    length = ((Y[0] - Y[1]) ** 2 + (X[0] - X[1]) ** 2) ** 0.5
                    angle = math.degrees(math.atan2(Y[0] - Y[1], X[0] - X[1]))
                    polygon = cv2.ellipse2Poly(
                        (int(mean_x), int(mean_y)), (int(length / 2), int(stick_width)), int(angle), 0, 360, 1
                    )
                    cv2.fillConvexPoly(image, polygon, color)
                    transparency = max(0, min(1, 0.5 * (keypoints[sk[0], 2] + keypoints[sk[1], 2])))
                    cv2.addWeighted(image, transparency, image, 1 - transparency, 0, dst=image)
                else:
                    cv2.line(image, (x1, y1), (x2, y2), color, thickness=thickness)


# Note: keypoint_edges and color palette are dataset-specific
keypoint_edges = model.config.edges

palette = np.array(
    [
        [255, 128, 0],
        [255, 153, 51],
        [255, 178, 102],
        [230, 230, 0],
        [255, 153, 255],
        [153, 204, 255],
        [255, 102, 255],
        [255, 51, 255],
        [102, 178, 255],
        [51, 153, 255],
        [255, 153, 153],
        [255, 102, 102],
        [255, 51, 51],
        [153, 255, 153],
        [102, 255, 102],
        [51, 255, 51],
        [0, 255, 0],
        [0, 0, 255],
        [255, 0, 0],
        [255, 255, 255],
    ]
)

link_colors = palette[[0, 0, 0, 0, 7, 7, 7, 9, 9, 9, 9, 9, 16, 16, 16, 16, 16, 16, 16]]
keypoint_colors = palette[[16, 16, 16, 16, 16, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0]]

numpy_image = np.array(image)

for pose_result in image_pose_result:
    scores = np.array(pose_result["scores"])
    keypoints = np.array(pose_result["keypoints"])

    # draw each point on image
    draw_points(numpy_image, keypoints, scores, keypoint_colors, keypoint_score_threshold=0.3, radius=4, show_keypoint_weight=False)

    # draw links
    draw_links(numpy_image, keypoints, scores, keypoint_edges, link_colors, keypoint_score_threshold=0.3, thickness=1, show_keypoint_weight=False)

pose_image = Image.fromarray(numpy_image)
pose_image
drawing

资源

以下是官方 Hugging Face 和社区(🌎 表示)资源列表,可帮助您开始使用 ViTPose。如果您有兴趣提交资源以包含在此处,请随时打开 Pull Request,我们将进行审核!该资源最好能展示一些新的东西,而不是重复现有资源。

  • 可以在此处找到 ViTPose 在图像和视频上的演示。
  • 可以在此处找到说明推理和可视化的 notebook。

VitPoseImageProcessor

class transformers.VitPoseImageProcessor

< >

( do_affine_transform: bool = True size: typing.Dict[str, int] = None do_rescale: bool = True rescale_factor: typing.Union[int, float] = 0.00392156862745098 do_normalize: bool = True image_mean: typing.Union[float, typing.List[float], NoneType] = None image_std: typing.Union[float, typing.List[float], NoneType] = None **kwargs )

参数

  • do_affine_transform (bool, 可选, 默认为 True) — 是否对输入图像应用仿射变换。
  • size (Dict[str, int] 可选, 默认为 {"height" -- 256, "width": 192}): 应用 affine_transform 后图像的分辨率。仅在 do_affine_transform 设置为 True 时生效。可以被 preprocess 方法中的 size 覆盖。
  • do_rescale (bool, 可选, 默认为 True) — 是否应用缩放因子(使像素值变为 0. 和 1. 之间的浮点数)。
  • rescale_factor (intfloat, 可选, 默认为 1/255) — 如果要缩放图像,则使用的缩放因子。可以被 preprocess 方法中的 rescale_factor 覆盖。
  • do_normalize (bool, 可选, 默认为 True) — 是否使用均值和标准差对输入进行归一化。
  • image_mean (List[int], 默认为 [0.485, 0.456, 0.406], 可选) — 用于归一化图像时,每个通道的均值序列。
  • image_std (List[int], 默认为 [0.229, 0.224, 0.225], 可选) — 用于归一化图像时,每个通道的标准差序列。

构建 VitPose 图像处理器。

preprocess

< >

( images: typing.Union[ForwardRef('PIL.Image.Image'), numpy.ndarray, ForwardRef('torch.Tensor'), list['PIL.Image.Image'], list[numpy.ndarray], list['torch.Tensor']] boxes: typing.Union[typing.List[typing.List[float]], numpy.ndarray] do_affine_transform: bool = None size: typing.Dict[str, int] = None do_rescale: bool = None rescale_factor: float = None do_normalize: bool = None image_mean: typing.Union[float, typing.List[float], NoneType] = None image_std: typing.Union[float, typing.List[float], NoneType] = None return_tensors: typing.Union[str, transformers.utils.generic.TensorType, NoneType] = None data_format: typing.Union[str, transformers.image_utils.ChannelDimension] = <ChannelDimension.FIRST: 'channels_first'> input_data_format: typing.Union[str, transformers.image_utils.ChannelDimension, NoneType] = None ) BatchFeature

参数

  • images (ImageInput) — 要预处理的图像。 期望是像素值范围从 0 到 255 的单张或批量图像。如果传入像素值在 0 到 1 之间的图像,请设置 do_rescale=False
  • boxes (List[List[List[float]]]np.ndarray) — 每张图像的边界框列表或数组。每个框应为一个包含 4 个浮点数的列表,表示 COCO 格式的边界框坐标(top_left_x,top_left_y,宽度,高度)。
  • do_affine_transform (bool, 可选, 默认为 self.do_affine_transform) — 是否对输入图像应用仿射变换。
  • size (Dict[str, int] 可选, 默认为 self.size) — 字典格式为 {"height": h, "width": w},指定调整大小后输出图像的尺寸。
  • do_rescale (bool, 可选, 默认为 self.do_rescale) — 是否将图像值缩放到 [0 - 1] 之间。
  • rescale_factor (float, 可选, 默认为 self.rescale_factor) — 如果 do_rescale 设置为 True,则用于缩放图像的缩放因子。
  • do_normalize (bool, 可选, 默认为 self.do_normalize) — 是否对图像进行归一化。
  • image_mean (floatList[float], 可选, 默认为 self.image_mean) — 如果 do_normalize 设置为 True,则使用的图像均值。
  • image_std (floatList[float], 可选, 默认为 self.image_std) — 如果 do_normalize 设置为 True,则使用的图像标准差。
  • return_tensors (strTensorType, 可选, 默认为 'np') — 如果设置,将返回特定框架的张量。可接受的值为:

    • 'tf':返回 TensorFlow tf.constant 对象。
    • 'pt':返回 PyTorch torch.Tensor 对象。
    • 'np':返回 NumPy np.ndarray 对象。
    • 'jax':返回 JAX jnp.ndarray 对象。

返回

BatchFeature

一个 BatchFeature,包含以下字段

  • pixel_values — 要馈送到模型的像素值,形状为 (batch_size, num_channels, height, width)。

预处理单张或批量图像。

post_process_pose_estimation

< >

( outputs: VitPoseEstimatorOutput boxes: typing.Union[typing.List[typing.List[typing.List[float]]], numpy.ndarray] kernel_size: int = 11 threshold: float = None target_sizes: typing.Union[transformers.utils.generic.TensorType, typing.List[typing.Tuple]] = None ) List[List[Dict]]

参数

  • outputs (VitPoseEstimatorOutput) — VitPoseForPoseEstimation 模型输出。
  • boxes (List[List[List[float]]]np.ndarray) — 每张图像的边界框列表或数组。每个框应为一个包含 4 个浮点数的列表,表示 COCO 格式的边界框坐标(top_left_x,top_left_y,宽度,高度)。
  • kernel_size (int, 可选, 默认为 11) — 用于调制的高斯核大小 (K)。
  • threshold (float, 可选, 默认为 None) — 保留对象检测预测的分数阈值。
  • target_sizes (torch.TensorList[Tuple[int, int]], 可选) — 形状为 (batch_size, 2) 的张量或元组列表 (Tuple[int, int]),包含批次中每张图像的目标尺寸 (height, width)。 如果未设置,预测将使用默认值调整大小。

返回

List[List[Dict]]

字典列表,每个字典包含模型预测的批次中图像的关键点和框。

将热图转换为关键点预测,然后将其转换回图像。

VitPoseConfig

class transformers.VitPoseConfig

< >

( backbone_config: PretrainedConfig = None backbone: str = None use_pretrained_backbone: bool = False use_timm_backbone: bool = False backbone_kwargs: dict = None initializer_range: float = 0.02 scale_factor: int = 4 use_simple_decoder: bool = True **kwargs )

参数

  • backbone_config (PretrainedConfigdict, 可选, 默认为 VitPoseBackboneConfig()) — 主干模型的配置。目前,仅支持 model_typevitpose_backbonebackbone_config
  • backbone (str, 可选) — 当 backbone_configNone 时,要使用的主干网络的名称。如果 use_pretrained_backboneTrue,这将从 timm 或 transformers 库加载相应的预训练权重。如果 use_pretrained_backboneFalse,这将加载主干网络的配置,并使用该配置以随机权重初始化主干网络。
  • use_pretrained_backbone (bool, 可选, 默认为 False) — 是否使用主干网络的预训练权重。
  • use_timm_backbone (bool, 可选, 默认为 False) — 是否从 timm 库加载 backbone。如果为 False,则从 transformers 库加载主干网络。
  • backbone_kwargs (dict, 可选) — 从检查点加载时要传递给 AutoBackbone 的关键字参数,例如 {'out_indices': (0, 1, 2, 3)}。如果设置了 backbone_config,则无法指定此项。
  • initializer_range (float, 可选, 默认为 0.02) — 用于初始化所有权重矩阵的 truncated_normal_initializer 的标准差。
  • scale_factor (int, 可选, 默认为 4) — 用于放大来自 ViT 主干网络的特征图的因子。
  • use_simple_decoder (bool, 可选, 默认为 True) — 是否使用 VitPoseSimpleDecoder 将来自主干网络的特征图解码为热图。否则,将使用 VitPoseClassicDecoder

这是用于存储 VitPoseForPoseEstimation 配置的配置类。它用于根据指定的参数实例化 VitPose 模型,定义模型架构。使用默认值实例化配置将产生与 VitPose usyd-community/vitpose-base-simple 架构类似的配置。

配置对象继承自 PretrainedConfig,可用于控制模型输出。有关更多信息,请阅读 PretrainedConfig 的文档。

示例

>>> from transformers import VitPoseConfig, VitPoseForPoseEstimation

>>> # Initializing a VitPose configuration
>>> configuration = VitPoseConfig()

>>> # Initializing a model (with random weights) from the configuration
>>> model = VitPoseForPoseEstimation(configuration)

>>> # Accessing the model configuration
>>> configuration = model.config

VitPoseForPoseEstimation

class transformers.VitPoseForPoseEstimation

< >

( config: VitPoseConfig )

参数

  • config (VitPoseConfig) — 具有模型所有参数的模型配置类。使用配置文件初始化不会加载与模型关联的权重,仅加载配置。查看 from_pretrained() 方法以加载模型权重。

带有姿势估计头的 VitPose 模型。此模型是 PyTorch torch.nn.Module 子类。将其用作常规 PyTorch 模块,并查阅 PyTorch 文档以了解与常规用法和行为相关的所有事项。

forward

< >

( pixel_values: Tensor dataset_index: typing.Optional[torch.Tensor] = None flip_pairs: typing.Optional[torch.Tensor] = None labels: typing.Optional[torch.Tensor] = None output_attentions: typing.Optional[bool] = None output_hidden_states: typing.Optional[bool] = None return_dict: typing.Optional[bool] = None ) transformers.models.vitpose.modeling_vitpose.VitPoseEstimatorOutputtuple(torch.FloatTensor)

参数

  • pixel_values (torch.FloatTensor,形状为 (batch_size, num_channels, height, width)) — 像素值。像素值可以使用 VitPoseImageProcessor 获得。 有关详细信息,请参阅 VitPoseImageProcessor.call()
  • dataset_index (torch.Tensor,形状为 (batch_size,)) — 用于主干网络的混合专家 (MoE) 块的索引。

    这对应于训练期间使用的数据集索引,例如,对于单个数据集索引 0 指的是相应的数据集。对于多个数据集索引 0 指的是数据集 A(例如 MPII),索引 1 指的是数据集 B(例如 CrowdPose)。

  • flip_pairs (torch.tensor, 可选) — 是否镜像关键点对(例如,左耳 — 右耳)。
  • output_attentions (bool, 可选) — 是否返回所有注意力层的注意力张量。 有关更多详细信息,请参阅返回张量下的 attentions
  • output_hidden_states (bool, 可选) — 是否返回所有层的隐藏状态。 有关更多详细信息,请参阅返回张量下的 hidden_states
  • return_dict (bool, 可选) — 是否返回 ModelOutput 而不是普通元组。

返回

transformers.models.vitpose.modeling_vitpose.VitPoseEstimatorOutputtuple(torch.FloatTensor)

一个 transformers.models.vitpose.modeling_vitpose.VitPoseEstimatorOutputtorch.FloatTensor 的元组(如果传递了 return_dict=False 或当 config.return_dict=False 时),其中包括各种元素,具体取决于配置 (VitPoseConfig) 和输入。

  • loss (torch.FloatTensor,形状为 (1,)可选,当提供 labels 时返回) — 目前不支持损失。 有关更多详细信息,请参阅 https://github.com/ViTAE-Transformer/ViTPose/tree/main/mmpose/models/losses

  • heatmaps (torch.FloatTensor,形状为 (batch_size, num_keypoints, height, width)) — 模型预测的热图。

  • hidden_states (tuple(torch.FloatTensor), 可选, 当传递 output_hidden_states=True 或当 config.output_hidden_states=True 时返回) — torch.FloatTensor 的元组(如果模型具有嵌入层,则为嵌入的输出 + 每个阶段的输出),形状为 (batch_size, sequence_length, hidden_size)。 模型在每个阶段输出的隐藏状态(也称为特征图)。

  • attentions (tuple(torch.FloatTensor), 可选, 当传递 output_attentions=True 或当 config.output_attentions=True 时返回) — torch.FloatTensor 的元组(每层一个),形状为 (batch_size, num_heads, patch_size, sequence_length)

    注意力 softmax 之后的注意力权重,用于计算自注意力头中的加权平均值。

VitPoseForPoseEstimation 的 forward 方法覆盖了 __call__ 特殊方法。

虽然 forward 传递的配方需要在该函数中定义,但应该在之后调用 Module 实例而不是此函数,因为前者负责运行预处理和后处理步骤,而后者则会默默地忽略它们。

示例

>>> from transformers import AutoImageProcessor, VitPoseForPoseEstimation
>>> import torch
>>> from PIL import Image
>>> import requests

>>> processor = AutoImageProcessor.from_pretrained("usyd-community/vitpose-base-simple")
>>> model = VitPoseForPoseEstimation.from_pretrained("usyd-community/vitpose-base-simple")

>>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
>>> image = Image.open(requests.get(url, stream=True).raw)
>>> boxes = [[[412.8, 157.61, 53.05, 138.01], [384.43, 172.21, 15.12, 35.74]]]
>>> inputs = processor(image, boxes=boxes, return_tensors="pt")

>>> with torch.no_grad():
...     outputs = model(**inputs)
>>> heatmaps = outputs.heatmaps
< > 在 GitHub 上更新