iOS应用中的实时对象检测

在许多场景中,仅仅给图片分配一个标签是不够的。例如,在安全系统中,不仅想知道图片中是否有行李箱,还想知道它的位置,以及它是否被人群包围或被遗弃。对象检测模型可以帮助实现这一点。在本系列文章中,将使用预训练的模型创建一个iOS应用程序,该程序可以在实时摄像头输入中检测多个人物和对象,而不仅仅是静态图片。

本系列文章假设熟悉Python、Conda和ONNX,并且具有在Xcode中开发iOS应用程序的经验。可以通过下载源代码来运行这些代码。将在macOS 10.15+、Xcode 11.7+和iOS 13+上运行代码。要创建coreml Conda环境,请使用下载的代码中的env-coreml.yml文件和以下命令:

$ conda env create -f env-coreml.yml $ conda activate coreml $ jupyter notebook

在本文中,将从将YOLO对象检测模型从Open Neural Network Exchange格式(ONNX)转换为Core ML格式开始。虽然可以从头开始训练自己的对象检测器,但这将是一项令人沮丧的练习。除非拥有庞大的训练数据集和大量用于GPU时间的预算,否则使用预训练的YOLO对象检测模型将获得更准确的结果。

对象检测模型接受图像作为输入,并返回检测到的对象列表,以及相应的标签和边界框。上面的图像来自Open Images数据集。在本系列中,将使用YOLO架构,首次在文章"YOLO9000: Better, Faster, Stronger"中介绍,或者更确切地说,是YOLO v2。YOLO代表"只需查看一次",这反映了它的基本思想:一次通过神经网络的图像数据产生检测结果。许多文章详细解释了YOLO的工作原理;例如,"YOLO: Real-Time Object Detection"或"Real-time object detection with YOLO"。在这里,将专注于基础知识。

YOLO将输入图像划分为网格。对于每个网格单元,它计算多个带有相应对象标签的框。为了使事情变得更简单(对于模型,而不是那些使用它的人),它不计算绝对框坐标,而是计算一组因子来缩放预定义的"锚"框。

将在本文系列中使用的YOLOv2使用了一个169个单元(13 x 13)的网格,每个单元有五个框。这些为模型处理的每个图像提供了845个边界框。在深入了解YOLO的输出之前,让专注于第一步:将YOLO模型转换为Core ML格式。

在ONNX模型动物园中有许多模型可用,尽管并非所有模型都同样易于使用或转换,主要是因为缺乏文档。选择了YOLO v2,这是在几次尝试转换YOLO v4或v3失败后的宝贵教训:即使Core ML支持源ONNX模型声明的相同操作集("opset")版本,转换也可能失败。选定的模型在COCO数据集上进行了训练,包括80种不同的对象类别,如人、狗、刀等。

一旦模型被下载到存储在onnx_model_path变量中的路径,就可以加载它:

import os import coremltools as ct import onnx import urllib onnx_model_path = './models/yolov2-coco-9.onnx' with open(onnx_model_path, 'rb') as f: model_onnx = onnx.load(f)

接下来,让检查模型的输入:

print(model_onnx.graph.input)

因此,输入节点的名称为"input.1",它接受一个维度为(1, 3, 416, 416)的数组,这对应于一批3通道图像(RGB),每个图像的像素为416 x 416。ONNX模型动物园页面上关于这个特定模型的文档非常模糊。然而,由于YOLO v3和v4的可用预处理代码将输入值缩放到[0 - 1]的范围,将假设v2也是如此。现在可以运行转换:

cml_model = ct.converters.onnx.convert( model=onnx_model_path, image_input_names=['input.1'], preprocessing_args={ 'image_scale': 1/255.0, 'is_bgr': False}, minimum_ios_deployment_target='13', )

确保将单个元素列表分配给image_input_names参数。如果错过了那里的方括号,一切似乎都会正常工作,但是预处理_args参数定义的输入缩放将在预测期间被静默忽略。这可能看起来不多 - 但它是。它会弄乱模型返回的结果,可能会花费很长时间试图弄清楚出了什么问题,并质疑世界的确定性本质。去过那里!

转换完成后,应该在日志中看到确认:

cml_model_path = './models/yolov2-coco-9.mlmodel' cml_model.save(cml_model_path) print(cml_model)

好的:输入是一个RGB图像,尺寸为416 x 416。现在让快速检查否可以用它进行预测。要做到这一点,需要将图像裁剪和缩放到416 x 416像素:

def load_and_scale_image(image_url): image = Image.open(urlopen(image_url)) w, h = image.size min_dim = min(w, h) x0 = int((w - min_dim)/2) y0 = int((h - min_dim)/2) box = (x0, y0, x0 + min_dim, y0 + min_dim) return image.crop(box=box).resize((416, 416)) image = load_and_scale_image('https://c2.staticflickr.com/4/3393/3436245648_c4f76c0a80_o.jpg') pred = cml_model.predict(data={INPUT_NODE: image})

如所见,模型的输出是一个形状为(1, 425, 13, 13)的数组。第一个维度代表一批,最后两个代表上面提到的13 x 13 YOLO网格。但是每个单元中的"425"值呢?继续阅读本系列的下一篇文章,以找出答案。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485