在本文中,你将学习关于这种类型的变压器模型。你还将学习使用Python、一个默认的变压器模型和HuggingFace Transformers库创建自己的对象检测管道。
什么是对象检测?
环顾四周。你很可能会看到很多东西——可能是一个计算机显示器、一个键盘和鼠标,或者当你在移动浏览器中浏览时,一个智能手机。
这些都是对象,一个特定类别的实例。例如,在下面的图片中,我们看到了人类这个类别的一个实例。我们还看到了许多瓶子类的实例。虽然类是一个蓝图,但对象是实际的存在,拥有许多独特的特征,同时因为共享的特征而属于该类别。
在图片和视频中,我们看到很多这样的对象。例如,当你正在拍摄交通视频时,很可能会看到很多行人、汽车、自行车等实例。而知道它们在图像中是非常有价值的!
传统上,物体检测是通过卷积神经网络(Convolutional Neural Networks, CNN)来执行的。通常,它们的架构是特别为物体检测量身定制的,因为它们将图像作为输入,并输出图像的边界框。
如果你熟悉神经网络,你就会知道,卷积网络在学习图像中的重要特征,以及实现空间不变性方面非常有用——换句话说,学习到的对象在图像中的位置或大小并不重要。如果网络能够看到对象的特点,并将其与特定的类别相关联,那么它就能识别它。例如,可以将许多不同的猫识别为猫类的实例。
Transformers通过将输入编码成一个高维状态,然后将其解码回所需的输出。通过巧妙地使用自注意力(self-attention)概念,Transformers不仅学会了检测特定的模式,还学会了将这些模式与其他模式关联起来。
如果Transformers可以被用于图像分类,那么将它们用于物体检测只是距离上的一小步。Carion等人(2020年)已经展示了实际上可以使用基于Transformer的架构来执行此操作。在他们的工作《用Transformers进行端到端物体检测》中,他们引入了检测Transformer(Detection Transformer,简称DETR),我们今天将使用它来创建我们的物体检测管道。
它的工作过程如下,并且甚至没有完全放弃CNN:
ObjectDetectionPipeline可以很容易地作为一个流水线实例初始化……换句话说,通过pipeline("object-detection")的方式,我们将在下面的示例中看到这一点。根据GitHub(没有日期)所述,当你不提供其他输入时,流水线就是这样初始化的:
"object-detection": {
"impl": ObjectDetectionPipeline,
"tf": (),
"pt": (AutoModelForObjectDetection,) if is_torch_available() else (),
"default": {"model": {"pt": "facebook/detr-resnet-50"}},
"type": "image",
},
不出意外,对象检测任务中会使用一个专门为目标检测量身定制的ObjectDetectionPipeline实例。在HuggingFace Transformers的PyTorch版本中,使用AutoModelForObjectDetection来达到这个目的。
正如你所了解的,默认情况下,facebook/detr-resnet-50模型用于提取图像特征。
COCO数据集(Common Objects in Context,即上下文中的常见对象)是用于训练目标检测模型的标准数据集之一,并被用于训练这个模型。
要使用ObjectDetectionPipeline,必须安装包含PyTorch图像模型的timm包。如果你还没安装它,确保运行以下命令:pip install timm。
使用Python实现简易的目标检测管道
现在让我们看看如何使用Python实现一个简单的目标检测解决方案。你在使用HuggingFace Transformers,必须安装在你的系统上 —— 如果你还没有安装,请运行pip install transformers。
我还假设已经安装了PyTorch,这是当今深度学习领域的领先库之一。
这是我们将在本文后面为之创建目标检测管道并运行的图像:
我们从导入开始:
from transformers import pipeline
from PIL import Image, ImageDraw, ImageFont
显然,我们正在使用变压器,特别是它的管道表示形式。然后,我们还使用了PIL,这是一个用于加载、可视化和操作图片的Python库。具体来说,我们正在使用第一个导入 - Image来加载图片,ImageDraw来绘制边界框和标签,后者还需要ImageFont。
说到这两个,接下来是加载字体(我们选择了Arial)并初始化我们上面提到的目标检测管道。
# Load font
font = ImageFont.truetype("arial.ttf", 40)
# Initialize the object detection pipeline
object_detector = pipeline("object-detection")
然后,我们创建一个名为draw_bounding_box的定义,顾名思义,它将用于绘制边界框。它需要输入图像(im)、类概率、边界框的坐标、该定义将用于的边界框列表中的边界框索引,以及该列表的长度。
在这个定义中,你将会:
# Draw bounding box definition
def draw_bounding_box(im, score, label, xmin, ymin, xmax, ymax, index, num_boxes):
""" Draw a bounding box. """
print(f"Drawing bounding box {index} of {num_boxes}...")
# Draw the actual bounding box
im_with_rectangle = ImageDraw.Draw(im)
im_with_rectangle.rounded_rectangle((xmin, ymin, xmax, ymax), outline = "red", width = 5, radius = 10)
# Draw the label
im_with_rectangle.text((xmin+35, ymin-25), label, fill="white", stroke_fill = "red", font = font)
# Return the intermediate result
return im
剩下的就是核心部分 —— 使用管道然后根据其结果绘制边界框。
首先,图片 —— 我们称呼其为street.jpg,它和Python脚本存放在同一个目录下 —— 将会被打开并存储在一个名为im的PIL对象中。我们只需将其提供给已初始化的object_detector —— 这就足够让模型返回边界框了!Transformers库会处理剩余的部分。
我们接着给一些变量分配数据,并迭代每一个结果,绘制边界框。
最后,我们将图片保存到street_bboxes.jpg。
# Open the image
with Image.open("street.jpg") as im:
# Perform object detection
bounding_boxes = object_detector(im)
# Iteration elements
num_boxes = len(bounding_boxes)
index = 0
# Draw bounding box for each result
for bounding_box in bounding_boxes:
# Get actual box
box = bounding_box["box"]
# Draw the bounding box
im = draw_bounding_box(im, bounding_box["score"], bounding_box["label"],\
box["xmin"], box["ymin"], box["xmax"], box["ymax"], index, num_boxes)
# Increase index by one
index += 1
# Save image
im.save("street_bboxes.jpg")
# Done
print("Done!")
如果你创建了自己的模型,或者想要使用一个不同的模型,将它代替基于ResNet-50的DETR Transformer使用起来非常简单。
这样做将需要你在导入部分添加以下内容:
from transformers import DetrFeatureExtractor, DetrForObjectDetection
然后,你可以初始化特征提取器和模型,并用它们来初始化object_detector,而不是使用默认的那个。例如,如果你想使用ResNet-101作为你的主干网络,你可以按照以下方式操作:
# Initialize another model and feature extractor
feature_extractor = DetrFeatureExtractor.from_pretrained('facebook/detr-resnet-101')
model = DetrForObjectDetection.from_pretrained('facebook/detr-resnet-101')
# Initialize the object detection pipeline
object_detector = pipeline("object-detection", model = model, feature_extractor = feature_extractor)
结果
以下是我们对输入图像运行对象检测流程后得到的结果:
或者,当放大查看时:
对象检测示例 —— 完整代码
以下是完整代码,供想要立即开始的人使用:
from transformers import pipeline
from PIL import Image, ImageDraw, ImageFont
# Load font
font = ImageFont.truetype("arial.ttf", 40)
# Initialize the object detection pipeline
object_detector = pipeline("object-detection")
# Draw bounding box definition
def draw_bounding_box(im, score, label, xmin, ymin, xmax, ymax, index, num_boxes):
""" Draw a bounding box. """
print(f"Drawing bounding box {index} of {num_boxes}...")
# Draw the actual bounding box
im_with_rectangle = ImageDraw.Draw(im)
im_with_rectangle.rounded_rectangle((xmin, ymin, xmax, ymax), outline = "red", width = 5, radius = 10)
# Draw the label
im_with_rectangle.text((xmin+35, ymin-25), label, fill="white", stroke_fill = "red", font = font)
# Return the intermediate result
return im
# Open the image
with Image.open("street.jpg") as im:
# Perform object detection
bounding_boxes = object_detector(im)
# Iteration elements
num_boxes = len(bounding_boxes)
index = 0
# Draw bounding box for each result
for bounding_box in bounding_boxes:
# Get actual box
box = bounding_box["box"]
# Draw the bounding box
im = draw_bounding_box(im, bounding_box["score"], bounding_box["label"],\
box["xmin"], box["ymin"], box["xmax"], box["ymax"], index, num_boxes)
# Increase index by one
index += 1
# Save image
im.save("street_bboxes.jpg")
# Done
print("Done!")