D3eec704e28ca7e97c82013b3242e9f5
理解 Vision 框架中的图片技术

WWDC 2019 session 222
Understanding Images in Vision Framework

Vision 是 Apple 2017 年和 Core ML 一起推出的、基于 Core ML 封装的图像识别库。根据官方文档看,Vision 包含有人脸识别、机器学习图片分析、条形码识别、文字识别等基础功能。

本文主要介绍了 Vision 框架在图像技术方面的一些酷炫功能,并一定程度上阐述了其原理。

一、图片重点区域 Saliency

Saliency 是什么?

Saliency 直译成中文就是:显著、凸起、特点。那么在 Vision 框架之中,Saliency 究竟指的是什么呢?先直接上图:

当我们第一次看到左侧这张图片时,视线一定是最先被这三张鸟脸 (海雀脸) 吸引的。如果把注意力集中的地方,用高光在图片中标注出来,那么大概就是第二张图这样:高光集中于鸟的头部。

这些高光部分,也就是人类注意力更容易集中地部分,实际上就是一种 Saliency (即基于注意力的 Saliency),而高光过后的效果图实际上就是 Saliency 的热力图 (The Heat map)。

两种 Saliency

这两种 Saliency 在算法上就有着比较明显的不同。基于注意力的 Saliency 实际上就是直观的由人类注意力的集中程度来判定,而基于物体的 Saliency 目的是识别出焦点物体,然后将焦点物体分割出来。

所以效果图如上,中间的图是基于注意力的结果,我们通常关注的是人物、动物的面部,所以只有面部附近会高亮。而右边的图将整个鸟都高亮了出来,是基于物体的结果。比如下面这张图,也是同样的道理:

实际上,基于注意力的 Saliency 会更为复杂。其实我们也能直观地感受到,因为注意力本身就会受到太多人为的不确定因素的影响,比如画面对比度、人物面部、画面主题、视野范围、光线强度等。有意思的是,它甚至会被持续的运动状态所影响,比如下图中,基于注意力的 Saliency 将人物行进路线前方的部分区域也进行了高亮:

具体的 Demo 可以参见:高亮图片中令人感兴趣的部分

The Heat Map: Saliency 热力图


热力图的概念很容易理解,那么如何获取 Saliency 热力图呢?Vision API 设计的基本使用逻辑就是 handler 搭配 request。通过创建 handler (VNImageRequestHandler, Vision 图片处理中最主要的 Handler) 之后调用 perform(_:) 方法,执行相应的 request (VNGenerateAttentionBasedSaliencyImageRequest,从名字就可以看出,关键词 AttentionBasedSaliency),具体代码如下:

let handler = VNImageRequestHandler(url: imageURL)
let request: VNImageBasedRequest = VNGenerateAttentionBasedSaliencyImageRequest()
request.revision = VNGenerateAttentionBasedSaliencyImageRequestRevision1

try? handler.perform([request])
guard let result = request.results?.first
let observation = result as? VNSaliencyImageObservation
else { fatalError("missing result") }

let pixelBuffer = observation.pixelBuffer

The Bounding Boxes: Saliency 位置

var boundingBox: CGRect { get }

Bounding boxes 就是探测出来的 Saliency 的位置信息,不过需要注意的是,坐标系的原点在图片左下角。对于基于注意力的 Saliency 来说,只有唯一的 bounding box,而基于物体的 Saliency 则最多有三个 bounding box。

获取 bounding box 代码如下:

func addSalientObjects(in observation: VNSaliencyImageObservation,
                        to path: CGMutablePath, 
                        transform: CGAffineTransform)
{
    guard let objects = observation.salientObjects else { return }
    for object in objects {
        // 得到 bounding box
        path.addRect(object.boundingBox, transform:transform)
    }
}

一些使用案例


得到图片 Saliency 之后其实有很多作用,具体举几个例子:

  • 用于滤镜:增加不同类型的滤镜、图片效果。
  • 用于相册:增加图片浏览的体验,将照片自动缩放到最佳位置。
  • 用于识别:与其他图像算法一起使用,先通过 bounding box 将物体切割出来,之后进行再识别提高准确率。

二、图片分类 Classification

图片识别、分类是 Vision 的最基础功能,Vision 框架提供了用于图片分类的 API,使用起来非常方便,iPhone 相册中就有大量使用。虽然 coreML 框架也能使用自己训练图片分类器,需要大量的数据以及计算资源等,对于普通开发者来说具有比较大的成本。而且 Vision 框架使用了多标签分类网络(Multi-label network)可以在一张图里面识别出多个物体。

哪些物体可以被识别? -- Taxonomy

到底哪些 object 可以被识别?这就要引出 Taxonomy 的概念了。Taxonomy 实际上指的是生物学上的分类系统,不同物体根据语义上的含义被分类。在这个树状结构中,有超过 1000 个分类,父类更加宽泛,子类更加具体。

也可以通过下面的语句,查看整个 Taxonomy:

// List full taxonomy with
VNClassifiyImageRequest.knownClassifications(forRevision: VNClassifyImageRequestRevision1 )

在构造这个 Taxonomy 树状结构时,每个分类都必须是可以通过视觉定义的,必须要避免形容词、抽象名词、太宽泛的名词,以及避免职业名称。具体使用,也符合 Vision API 统一的使用方法,handler (依然是 VNImageRequestHandler, Vision 图片处理中最主要的 Handler) 搭配 request (VNClassifyImageRequest,关键词 Classify,分类识别专用):

let handler = VNImageRequestHandler(url: imageUrl)
let request = VNClassifyImageRequest()
try? handler.perform([request])
let observations = request.results as? [VNClassificationObservation]

最终得到一个 Observation 观测值数组,包含一系列物体识别结果,以及分别对应的 confidence 信心度(一个 [0, 1.0] 区间内的浮点数,直接反映识别结果的准确性)。注意到信心度总和不为 1,这就是因为刚才提到的 Multi-label network 所产生的结果。

// 上图识别之后的 observations 示例结果
// 从图中识别出可能有:动物、猫、哺乳动物、衣服、小圆帽、帽子、人、成年人、雪...等一系列结果
[(animal, 0.848), (cat, 0.848), (mammal, 0.848), (clothing, 0.676), (beanie, 0.675), (hat, 0.675), (people, 0.616), (adult, 0.616), (snow, 0.445), (jacket, 0.214), (outdoor, 0.063), (leash, 0.057), (cord, 0.057)......]

结果的进一步筛选: Precision and Recall

得到识别结果之后,如何 Observation 数组进行进一步分析,来判定究竟识别结果中哪些是足够可信的呢?非常符合常识的一个关键公式是:Confidence > Threshold。很容易理解,当信心度大于某个阈值时,就可以判断图片中有相应物体。但是最大的问题在于,阈值应该如何确定,并且阈值并不固定,不同的图片中阈值肯定是不同的。

top Created with Sketch.