12 Transformers - 使用Pipeline处理计算机视觉

文章目录

Transformers 是一个采用当下技术最新、表现最佳( State-of-the-artSoTA)的模型和技术在预训练 自然语言处理计算机视觉音频多模态 模型方面提供推理和训练的的开源库;旨在快速易用,以便每个人都可以开始使用 transformer 模型进行学习或构建。该库不仅包含 Transformer 模型,还包括用于计算机视觉任务的现代卷积网络等非 Transformer 模型。

计算机视觉

计算机视觉任务中最早成功之一是使用卷积神经网络(Convolutional Neural NetworksCNN)识别图像中的邮政编码。图像由像素组成,每个像素都有一个数值,使得将图像用像素值矩阵表示变得容易。每个像素值组合描述了图像的颜色。

计算机视觉任务可以通过以下两种方式解决:

  1. 使用卷积来学习图像的层次特征,从低级特征到高级抽象特征。
  2. 将图像分成块,并使用 Transformer 逐步学习每个图像块如何相互关联以形成图像。与 CNN 偏好的自底向上方法不同,该方法有点像从一个模糊的图像开始,然后逐渐将其聚焦清晰。

图像分类

图像分类(Image classification)将分析整个图像特征并从预定义的类别集合中标签化。和大多数分类任务一样,图像分类有许多实际用例,包括:

  • 医疗保健:标记医学图像以检测疾病或监测患者健康状况
  • 环境:标记卫星图像以监测森林砍伐、提供野外管理信息或检测野火
  • 农业:标记农作物图像以监测植物健康或用于土地使用监测的卫星图像
  • 生态学:标记动物或植物物种的图像以监测野生动物种群或跟踪濒危物种

首先实例化 Pipeline ,实例化时通过输入参数指定任务标识 image-classification,并使用任务默认的训练模型:

复制代码
from transformers import pipeline

classifier = pipeline(task="image-classification")
preds = classifier(
     "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg"
 )
result = [{"score": round(pred["score"], 4), "label": pred["label"]} for pred in preds]
print(*result, sep="\n")

结果:

复制代码
{'score': 0.4335, 'label': 'lynx, catamount'}
{'score': 0.0348, 'label': 'cougar, puma, catamount, mountain lion, painter, panther, Felis concolor'}
{'score': 0.0324, 'label': 'snow leopard, ounce, Panthera uncia'}
{'score': 0.0239, 'label': 'Egyptian cat'}
{'score': 0.0229, 'label': 'tiger cat'}

零样本图像分类

零样本图像分类(Zero-shot image classification)是一项用于既有分类还没有可用特征的模型上将图像划分类别的任务。

传统上,图像检测是通过已经分类好的图片集合训练出的模型上对给定的图像提取特征并根据特征指派到这个模型的既有分类上。如果需要将这种模型用于引入到新的一组标签的分类任务时,需要微调以重新校准模型。

相比之下,零样本图像分类模型通常是在大量图像和相关描述数据集上训练的多模态模型。这些模型通过学习对齐的视觉语言表示,可用于许多下游任务。这是一种更灵活的图像分类方法,它允许模型在不需要额外训练数据的情况下泛化到新的和未见过的类别,并使用户能够通过自由格式文本描述的图像信息查找具有目标对象图片。

初始化 Pipeline 并通过输入参数指定任务标识 zero-shot-image-classification,并传入所用的模型 openai/clip-vit-large-patch14

复制代码
from transformers import pipeline
 
detector = pipeline(model="openai/clip-vit-large-patch14", task="zero-shot-image-classification")

接下来准备待分类的图片:

复制代码
from PIL import Image
import requests

url = "https://huggingface.co/datasets/Narsil/image_dummy/raw/main/parrots.png"
image = Image.open(requests.get(url, stream=True).raw)

image

通过 candidate_labels 设置新的标签集合,然后调用detector并传入图片和新标签集合。在本示例中,我们直接传递图像;其他合适的选项包括图像的本地路径或图像的URL。 候选标签可以是简单的单词,就像这个示例中一样,也可以更详细描述。

复制代码
predictions = detector(image, candidate_labels=["animals", "humans", "landscape"])
predictions

结果:

复制代码
[{'score': 0.965, 'label': 'animals'}, {'score': 0.03, 'label': 'humans'}, {'score': 0.005, 'label': 'landscape'}]

对象检测

与图像分类不同,对象检测(Object detection)从给定的图像中识别多个目标对象以及这些对象在图像中的位置(由边界框定义)。目标检测的一些示例应用包括:

  • 自动驾驶工具:检测日常交通对象,如其他车辆、行人和红绿灯
  • 遥感:灾害监测、城市规划和天气预报
  • 缺陷检测:检测建筑物中的裂缝或结构损坏,以及制造业产品缺陷

首先初始化 Pipeline 并指定任务为 object-detection,这里不指定模型即直接使用该任务的默认模型:

复制代码
from transformers import pipeline

detector = pipeline(task="object-detection")

然后调用实例 detector开始检测:

复制代码
preds = detector(
     "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg"
 )
result = [{"score": round(pred["score"], 4), "label": pred["label"], "box": pred["box"]} for pred in preds]
result

结果:

复制代码
[{'score': 0.9865,
  'label': 'cat',
  'box': {'xmin': 178, 'ymin': 154, 'xmax': 882, 'ymax': 598}}]

零样本对象检测

传统上,用于对象检测(Object detection)的模型都是使用已经分好类的图像集合训练出来的;对象检测的结果仅限于在既有的分类基础上指派合适的分类。

零样本对象检测(Zero-shot object detection)由 OWL-ViT 模型支持,它使用了区别于对象检测的方法。OWL-ViT 是一个开放词汇的对象检测器,这意味着它可以基于自定义文本检测图像中的对象,而无需对标记数据集的模型进行微调。

OWL-ViT 利用多模态表示来执行开放词汇表检测,它将 CLIP 与轻量级对象分类和定位头相结合。开放词汇检测是通过在 CLIP 的文本编码器中嵌入自定义文本,并将其作为对象分类和定位头的输入来实现的;目标分类和定位头将图像与其相应的文本描述相关联,而ViT 则将图像块作为输入来处理。OWL-ViT 的作者首先从头开始训练 CLIP,然后在标准目标检测数据集上使用二部匹配损失对OWL-ViT 进行端到端微调。

使用这种方法,模型可以根据文本描述检测对象,而无需事先对标记数据集进行训练。首先在实例化 Pipeline时指定任务 zero-shot-object-detection 和使用的模型 google/owlvit-base-patch32:

复制代码
from transformers import pipeline

detector = pipeline(model="google/owlvit-base-patch32", task="zero-shot-object-detection")

通过参数 candidate_labels 设置新标签集合,随同图片传给实例 detector 。 这里我们直接传递了图片;其他合适的选项包括图像的本地路径或图像URL。我们还传递了要查询图像的所有物体的文本描述。

复制代码
'''
输入参数 image 是一个复合类型,可以是字符串如本地图片路径或者图片http地址;也可以是"Image.Image",也可以是一组图片地址的列表,列表的值由字典组成
如果是列表,则每个字典值格式为:
{
"image": "http://images.cocodataset.org/val2017/000000039769.jpg",
"candidate_labels":["cat", "couch"]
}
'''
predictions = detector(
     "http://images.cocodataset.org/val2017/000000039769.jpg",
     candidate_labels=["cat", "couch"],
 )
predictions

结果:

复制代码
[
  {'score': 0.287, 'label': 'cat', 'box': {'xmin': 324, 'ymin': 20, 'xmax': 640, 'ymax': 373}}, 
  {'score': 0.254, 'label': 'cat', 'box': {'xmin': 1, 'ymin': 55, 'xmax': 315, 'ymax': 472}}, 
  {'score': 0.121, 'label': 'couch', 'box': {'xmin': 4, 'ymin': 0, 'xmax': 642, 'ymax': 476}}
]

如果想看看结果的可视化情况,使用以下案例:

复制代码
from PIL import ImageDraw

draw = ImageDraw.Draw(image)

for prediction in predictions:
     box = prediction["box"]
     label = prediction["label"]
     score = prediction["score"]

     xmin, ymin, xmax, ymax = box.values()
     draw.rectangle((xmin, ymin, xmax, ymax), outline="red", width=1)
     draw.text((xmin, ymin), f"{label}: {round(score,2)}", fill="white")

image

图像分割

图像分割(image-segmentation)是一项像素级任务,将图像中的每个像素分配给一个类别。它区别于"对象检测",对象检测是使用边界框标记和预测图像中的对象;而图像分割将图像中的每个像素标记到某一个标签或类别,会更加精细。有几种类型的图像分割:

  • 语义分割:语义分割为图像中的每个像素分配标签或类别;语义分割只能判断类别,无法区分个体。
  • 实例分割:除了标记对象的类别外,还标记每个对象的不同实例(dog-1dog-2);实例分割方式有点类似于"对象检测",不过"对象检测"一般输出的是边界框,实例分割输出的是一个蒙版(Mask),即每个对象实例。
  • 全景分割:语义分割和实例分割的组合; 它使用语义类为每个像素标记并标记每个对象的不同实例。

举个例子来看看他们的区别。语义分割完成后,对从给定图像检测出的狗会同一归类到 dog 类别上,但是使用实例分割,将检测到的 dog,还可细分为 dog-1dog-2,即能识别出他们细微差别。

分割任务对于自动驾驶十分有用,可以根据周围创建像素级地图,以便在行人和其他交通工具周围安全导航;它也有助于医学成像,更精细颗粒度可以帮助识别异常细胞或器官特征;图像分割也可以用于电子商务,如实现虚拟穿衣,或利用相机在现实世界中覆盖物体增强现实体验。

首先实例化 Pipeline并传入指定任务标识 image-segmentation;这里不指定模型而使用任务默认的模型:

复制代码
from transformers import pipeline

segmenter = pipeline(task="image-segmentation")

然后调用实例 segmenter 来分割图片:

复制代码
preds = segmenter(
     "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg"
)
result = [{"score": round(pred["score"], 4), "label": pred["label"]} for pred in preds]
print(*result, sep="\n")

结果:

复制代码
{'score': 0.9879, 'label': 'LABEL_184'}
{'score': 0.9973, 'label': 'snow'}
{'score': 0.9972, 'label': 'cat'}

图生图

图生图(Image-to-Image)任务是接受一个输入图像并输出另一个图像的任务。它有各种子任务,包括图像增强(超分辨率,弱光增强,脱模等),图像修复等。

注意 :写本文时,Pipeline类只支持超分辨率子任务。

实例化 Pipeline 并初始化 [Swin2SR] 模型并完成超分辨率处理。目前 Pipeline 只支持[Swin2SR] 模型。

复制代码
from transformers import pipeline
import torch
from accelerate.test_utils.testing import get_backend
# 自动检测设备类型 (CUDA, CPU, XPU, MPS, etc.)
device, _, _ = get_backend()
pipe = pipeline(task="image-to-image", model="caidas/swin2SR-lightweight-x2-64", device=device)

然后载入要处理的猫的图片:

复制代码
from PIL import Image
import requests

url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/cat.jpg"
image = Image.open(requests.get(url, stream=True).raw)

print(image.size)

原始图片尺寸:

复制代码
# (532, 432)

现在,对输入的图片放大,得到结果图片:

复制代码
upscaled = pipe(image)
print(upscaled.size)

结果图片尺寸:

复制代码
# (1072, 880)

关键点检测

关键点检测(Keypoint detection)识别和定位图像中的兴趣点。关键点也被称为地标,代表物体的有意义的特征(如面部特征或物体部位)。这些模型接受图像输入并返回以下输出:

  • 关键点和得分:兴趣点和他们的信心得分。
  • 描述符:每个关键点周围的图像区域表示,捕获其纹理,梯度,方向和其他属性。

关键点检测具有广泛的应用基础如目标检测、跟踪、识别、姿态估计等:在人脸识别中,关键点检测可以帮助定位人脸特征,提高识别准确率;在人体姿态估计中,可以识别出人体各关节位置,进而分析人体的运动状态等。

⚠️ 注意!

该特定功能未实现特定任务支持,需使用基础模型提供的实现来处理。

下面使用一个关键点检测的基础模型 [SuperPoint]来演示如何从图像中提取关键点。

复制代码
from transformers import AutoImageProcessor, SuperPointForKeypointDetection
processor = AutoImageProcessor.from_pretrained("magic-leap-community/superpoint")
model = SuperPointForKeypointDetection.from_pretrained("magic-leap-community/superpoint")

现在我们开始载入检测图片:

复制代码
import torch
from PIL import Image
import requests
import cv2

url_image_1 = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg"
image_1 = Image.open(requests.get(url_image_1, stream=True).raw)
url_image_2 = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/cats.png"
image_2 = Image.open(requests.get(url_image_2, stream=True).raw)

images = [image_1, image_2]

接着对输入图片进行预处理后进行推理:

复制代码
inputs = processor(images,return_tensors="pt").to(model.device, model.dtype)
outputs = model(**inputs)

输出结果包含批处理中每项的关键点坐标、描述、蒙版和得分。蒙版突出显示图像中存在关键点的区域,需要使用蒙版属性来检索相应的信息。

复制代码
SuperPointKeypointDescriptionOutput(loss=None, keypoints=tensor([[[0.0437, 0.0167],
         [0.0688, 0.0167],
         [0.0172, 0.0188],
         ...,
         [0.5984, 0.9812],
         [0.6953, 0.9812]]]), 
         scores=tensor([[0.0056, 0.0053, 0.0079,  ..., 0.0125, 0.0539, 0.0377],
        [0.0206, 0.0058, 0.0065,  ..., 0.0000, 0.0000, 0.0000]],
       grad_fn=<CopySlices>), descriptors=tensor([[[-0.0807,  0.0114, -0.1210,  ..., -0.1122,  0.0899,  0.0357],
         [-0.0807,  0.0114, -0.1210,  ..., -0.1122,  0.0899,  0.0357],
         [-0.0807,  0.0114, -0.1210,  ..., -0.1122,  0.0899,  0.0357],
         ...],
       grad_fn=<CopySlices>), mask=tensor([[1, 1, 1,  ..., 1, 1, 1],
        [1, 1, 1,  ..., 0, 0, 0]], dtype=torch.int32), hidden_states=None)

为了在图像中绘制实际关键点,需要对输出进行后处理。为此,不得不将实际图像大小与结果通过 post_process_keypoint_detection 一起输出。

复制代码
[{'keypoints': tensor([[ 226,   57],
          [ 356,   57],
          [  89,   64],
          ...,
          [3604, 3391]], dtype=torch.int32),
  'scores': tensor([0.0056, 0.0053, ...], grad_fn=<IndexBackward0>),
  'descriptors': tensor([[-0.0807,  0.0114, -0.1210,  ..., -0.1122,  0.0899,  0.0357],
          [-0.0807,  0.0114, -0.1210,  ..., -0.1122,  0.0899,  0.0357]],
         grad_fn=<IndexBackward0>)},
    {'keypoints': tensor([[ 46,   6],
          [ 78,   6],
          [422,   6],
          [206, 404]], dtype=torch.int32),
  'scores': tensor([0.0206, 0.0058, 0.0065, 0.0053, 0.0070, ...,grad_fn=<IndexBackward0>),
  'descriptors': tensor([[-0.0525,  0.0726,  0.0270,  ...,  0.0389, -0.0189, -0.0211],
          [-0.0525,  0.0726,  0.0270,  ...,  0.0389, -0.0189, -0.0211]}]

现在就可以绘制关键点了:

复制代码
import matplotlib.pyplot as plt
import torch

for i in range(len(images)):
  keypoints = outputs[i]["keypoints"]
  scores = outputs[i]["scores"]
  descriptors = outputs[i]["descriptors"]
  keypoints = outputs[i]["keypoints"].detach().numpy()
  scores = outputs[i]["scores"].detach().numpy()
  image = images[i]
  image_width, image_height = image.size

  plt.axis('off')
  plt.imshow(image)
  plt.scatter(
      keypoints[:, 0],
      keypoints[:, 1],
      s=scores * 100,
      c='cyan',
      alpha=0.4
  )
  plt.show()
  # 或者保存
  plt.savefig(f"a.png")

深度估计

深度估计(depth estimation)预测图像中每个像素到相机的距离。这个计算机视觉任务对于场景理解和重建尤为重要。例如,在自动驾驶汽车中,车辆需要了解行人、交通标志和其他车辆等物体的距离,避免被阻挡或碰撞。深度信息还有助于将二维图像构建三维平面表示,并可用于创建生物学结构或建筑物的高质量三维平面表示。

有两种方法可以进行深度估计:

  • stereo:通过比较同一图像的两个略微不同角度的图像来估计深度
  • monocular:从单个图像中估计深度

实例化 Pipeline 并指定任务为 depth-estimation 和 指定模型 LiheYoung/depth-anything-base-hf

复制代码
from transformers import pipeline

depth_estimator = pipeline(task="depth-estimation", model="LiheYoung/depth-anything-base-hf")

然后调用实例 depth_estimator 来估计深度:

复制代码
output = depth_estimator("http://images.cocodataset.org/val2017/000000039769.jpg")
# 每个张量值是以米为单位表示每个像素的深度
output["predicted_depth"].shape

输出:

复制代码
torch.Size([1, 384, 384])

单目深度估计

单目深度估计(Monocular depth estimation)是一项计算机视觉任务,涉及从单个图像中预测场景的深度信息。换句话说,它是从单个摄像机视点估计场景中物体距离的过程。

单目深度估计有各种各样的应用,包括3D重建、增强现实、自动驾驶和机器人等。它需要模型理解场景中物体之间的复杂关系以及相应的深度信息,这些信息可能受到照明条件、遮挡和纹理等因素的影响,是一项具有挑战性的任务。

有两个主要的深度估计类别:

  • 绝对深度估计:此任务变体旨在从相机提供精确的深度测量。该估计可与公制深度估计交替使用,其中深度以米或英尺作为精确测量单位。绝对深度估计模型输出深度图与数值表示现实世界的距离。

  • 相对深度估计 :相对深度估计旨在预测场景中物体或点的深度顺序而不提供精确的测量。这些模型输出一个深度图,表明场景的哪些部分相对于彼此更近或更远,而不包括到 A A A 和 B B B 的实际距离。

下面开始使用相对深度估计模型 [Depth Anything V2] 和绝对深度估计模型 [ZoeDepth]来演示如何推断。

相对深度估计

相对深度估计已由任务 depth-estimation 实现,已在"深度估计"中讲解,为了完整,这里再重新讲解。首先,实例化 [Pipeline] 载入深度估计任务 depth-estimation

复制代码
from transformers import pipeline
import torch
from accelerate.test_utils.testing import get_backend
# 自动检测设备类型 (CUDA, CPU, XPU, MPS, etc.)
device, _, _ = get_backend()
pipe = pipeline("depth-estimation", model="depth-anything/Depth-Anything-V2-base-hf", device=device)

然后准备需要分析的图片:

复制代码
from PIL import Image
import requests

url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg"
image = Image.open(requests.get(url, stream=True).raw)
image

接着将待处理的图片通过参数传入:

复制代码
predictions = pipe(image)

pipeline实例 返回一个包含两个属性键的字典。一个为 predicted_depth 节点,其值表示每个像素的深度张量,以米为单位表示;另一个是 depth,值为已转换成 PIL 图像的深度估计结果。下面将结果打印出来看看效果:

复制代码
predictions["depth"]

绝对深度估计

⚠️ 注意!

该特定功能未实现特定任务支持,需使用基础模型提供的实现来处理。

首先从[Hugging Face Hub]上加载模型和对应处理器。本例还是使用之前相同的检查点:

复制代码
from transformers import AutoImageProcessor, AutoModelForDepthEstimation

checkpoint = "Intel/zoedepth-nyu-kitti"

image_processor = AutoImageProcessor.from_pretrained(checkpoint)
model = AutoModelForDepthEstimation.from_pretrained(checkpoint).to(device)

使用 image_processor 为传入输入图像,先完成必要的图像转换(如调整大小和归一化):

复制代码
pixel_values = image_processor(image, return_tensors="pt").pixel_values.to(device)

将准备好的输入通过 model 实例传入:

复制代码
import torch

with torch.no_grad():
    outputs = model(pixel_values)

然后对结果执行后处理,删除任何填充并调整深度图的大小以匹配原始图像的大小。通过 [post_process_depth_estimation] 方法输出一个包含 predicted_depth 属性节点的字典列表。

复制代码
# ZoeDepth会自动对输入图像填充,因此传入原始图片的大小
# 使用`post_process_depth_estimation`方法移除填充和重设为原始维度
post_processed_output = image_processor.post_process_depth_estimation(
    outputs,
    source_sizes=[(image.height, image.width)],
)

predicted_depth = post_processed_output[0]["predicted_depth"]
depth = (predicted_depth - predicted_depth.min()) / (predicted_depth.max() - predicted_depth.min())
depth = depth.detach().cpu().numpy() * 255
depth = Image.fromarray(depth.astype("uint8"))

视频分类

视频分类(Video classification)是为整个视频分配标签或类别的任务,预期每个视频只能有一个分类。视频分类模型将视频作为输入,并预测视频属于指定的哪个类。视频分类的一个实际应用是动作/行为的识别,这对健身应用和视力受损的人都很有用。

假定已使用预训练数据集通过微调训练后得到结果模型为 my_awesome_video_cls_model(或者通过官网查询视频分类支持哪些模型,然后选择其中的模型来使用),实例化 Pipeline并传入模型和任务加载模型并推理:

复制代码
from transformers import pipeline

video_cls = pipeline(task="video-classification", model="my_awesome_video_cls_model")

然后开始分类:

复制代码
# 参数input是复合类型即字符串或由字符串组成的列表,也就是说,可以处理单个视频,也可以同时处理多个视频
video_cls("https://huggingface.co/datasets/sayakpaul/ucf101-subset/resolve/main/v_BasketballDunk_g14_c06.avi")

结果:

复制代码
[{'score': 0.9272987842559814, 'label': 'BasketballDunk'},
 {'score': 0.017777055501937866, 'label': 'BabyCrawling'},
 {'score': 0.01663011871278286, 'label': 'BalanceBeam'},
 {'score': 0.009560945443809032, 'label': 'BandMarching'},
 {'score': 0.0068979403004050255, 'label': 'BaseballPitch'}]
相关推荐
NAGNIP5 小时前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
冬奇Lab6 小时前
一天一个开源项目(第36篇):EverMemOS - 跨 LLM 与平台的长时记忆 OS,让 Agent 会记忆更会推理
人工智能·开源·资讯
冬奇Lab6 小时前
OpenClaw 源码深度解析(一):Gateway——为什么需要一个"中枢"
人工智能·开源·源码阅读
AngelPP10 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年10 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼10 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS10 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区11 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈11 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang12 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx