- Vision框架详细解析(十四) —— 基于Vision的人员分
- Vision框架详细解析(十五) —— 基于Vision的人员分
- Vision框架详细解析(十一) —— 基于Vision的Bod
- Vision框架详细解析(十) —— 基于Vision的Body
- Vision框架详细解析(十二) —— 基于Vision的Fac
- Vision框架详细解析(十三) —— 基于Vision的Fac
- Vision框架详细解析(九) —— 基于Vision的QR扫描
- Vision框架详细解析(八) —— 基于Vision的QR扫描
- Vision框架详细解析(十六) —— 基于Vision的轮廓检
- Vision框架详细解析(十七) —— 基于Vision的轮廓检
版本记录
版本号 | 时间 |
---|---|
V1.0 | 2022.03.20 星期日 |
前言
iOS 11+
和macOS 10.13+
新出了Vision
框架,提供了人脸识别、物体检测、物体跟踪等技术,它是基于Core ML的。可以说是人工智能的一部分,接下来几篇我们就详细的解析一下Vision框架。感兴趣的看下面几篇文章。
1. Vision框架详细解析(一) —— 基本概览(一)
2. Vision框架详细解析(二) —— 基于Vision的人脸识别(一)
3. Vision框架详细解析(三) —— 基于Vision的人脸识别(二)
4. Vision框架详细解析(四) —— 在iOS中使用Vision和Metal进行照片堆叠(一)
5. Vision框架详细解析(五) —— 在iOS中使用Vision和Metal进行照片堆叠(二)
6. Vision框架详细解析(六) —— 基于Vision的显著性分析(一)
7. Vision框架详细解析(七) —— 基于Vision的显著性分析(二)
8. Vision框架详细解析(八) —— 基于Vision的QR扫描(一)
9. Vision框架详细解析(九) —— 基于Vision的QR扫描(二)
10. Vision框架详细解析(十) —— 基于Vision的Body Detect和Hand Pose(一)
11. Vision框架详细解析(十一) —— 基于Vision的Body Detect和Hand Pose(二)
12. Vision框架详细解析(十二) —— 基于Vision的Face Detection新特性(一)
13. Vision框架详细解析(十三) —— 基于Vision的Face Detection新特性(二)
开始
首先看下主要内容:
了解如何通过 Vision 框架使用人员分割。内容来自翻译。
接着看下写作环境:
Swift 5, iOS 15, Xcode 13
下面就是正文了。
计算机视觉(Computer Vision)
比以往任何时候都更加突出。它的应用包括癌症检测、细胞分类、交通流分析、实时运动分析等等。 Apple 在 iOS 11
中引入了 Vision
框架。它允许您执行各种任务,例如面部跟踪、条形码检测和图像注册。在 iOS 15
中,Apple
在 Vision
框架中引入了一个 API 来执行人物分割,这也为肖像模式(Portrait mode)
提供了支持。
在本教程中,您将学习:
- 什么是图像分割以及不同类型的分割。
- 为照片创建了人物分割。
- 了解不同的质量水平和性能权衡。
- 为实时视频捕获创建了人物分割。
- 提供人员分割的其他框架。
- 人员分割的最佳实践。
注意:本教程假设您具备
SwiftUI、UIKit
和AVFoundation
的工作知识。有关 SwiftUI 的更多信息,请参阅 SwiftUI: Getting Started。您还需要一个物理的iOS 15
设备。
打开起始项目。 在starter
中打开 RayGreetings
。 在物理设备上构建和运行。
您将看到两个tab
:Photo Greeting
和Video Greeting
。Photo Greeting tab
会显示一个漂亮的背景图片和一张家庭照片。在本教程中,您将使用人员分割将家庭成员叠加在问候语背景上。点击Video Greeting tab
并授予摄像头权限。您将看到显示的相机流。启动项目设置为捕获和显示相机帧。您将更新实时帧以生成视频问候!
在深入实施这些之前,您需要了解什么是人员分割。准备好有趣的旅程。
Introducing Image Segmentation
图像分割将图像划分为多个片段并对其进行处理。它提供了对图像的更细粒度的理解。对象检测提供图像中所需对象的边界框,而图像分割提供对象的像素掩码。
图像分割有两种类型:语义分割和实例分割(semantic segmentation and instance segmentation)
。
Semantic segmentation
是检测图像中属于同一类的相似部分并将其组合在一起的过程。Instance segmentation
是检测对象的特定实例的过程。当您将语义分割应用于包含人的图像时,它会生成一个包含所有人的掩码。实例分割为图像中的每个人生成一个单独的掩码。
Apple
的 Vision
框架中提供的人员分割 API 是单帧 API。它使用语义分割为框架中的所有人提供单个掩码。它用于流式处理和离线处理。
人物分割的过程有四个步骤:
- 1) 创建人员细分请求。
- 2) 为该请求创建
request handler
。 - 3) 处理请求。
- 4) 处理结果。
接下来,您将使用 API 和这些步骤来创建照片问候语!
Creating Photo Greeting
你有一个家庭的图片和一个节日背景的图片。 您的目标是将家庭照片中的人叠加在节日背景上,以产生有趣的问候。
打开 RayGreetings
并打开 GreetingProcessor.swift
。
在import Combine
下面添加以下内容:
import Vision
这将导入 Vision
框架。 接下来,将以下内容添加到 @Published var photoOutput = UIImage()
下面的 GreetingProcessor
:
let request = VNGeneratePersonSegmentationRequest()
在这里,您创建人员分割请求的实例。 这是一个有状态的请求,可以重复用于整个帧序列。 这在离线处理视频和实时摄像机捕捉时特别有用。
接下来,将以下内容添加到 GreetingProcessor
:
func generatePhotoGreeting(greeting: Greeting) {
// 1
guard
let backgroundImage = greeting.backgroundImage.cgImage,
let foregroundImage = greeting.foregroundImage.cgImage else {
print("Missing required images")
return
}
// 2
// Create request handler
let requestHandler = VNImageRequestHandler(
cgImage: foregroundImage,
options: [:])
// TODO
}
这是上面的代码正在做的事情:
- 1) 从
backgroundImage
和foregroundImage
访问cgImage
。 然后,它确保两个图像都是有效的。 您将很快使用它们来使用Core Image
混合图像。 - 2) 创建
requestHandler
作为VNImageRequestHandler
的一个实例。 它接收图像以及指定如何处理图像的可选字典。
接下来,将 // TODO
替换为以下内容:
do {
// 1
try requestHandler.perform([request])
// 2
guard let mask = request.results?.first else {
print("Error generating person segmentation mask")
return
}
// 3
let foreground = CIImage(cgImage: foregroundImage)
let maskImage = CIImage(cvPixelBuffer: mask.pixelBuffer)
let background = CIImage(cgImage: backgroundImage)
// TODO: Blend images
} catch {
print("Error processing person segmentation request")
}
这是上面代码的细分:
- 1)
requestHandler
使用perform(_:)
处理人员分割请求。 如果存在多个请求,则在所有请求完成或失败后返回。perform(_:)
在处理请求时可能会引发错误,因此您可以通过将其包含在do-catch
中来处理它。 - 2) 然后,您从结果中检索掩码。 因为您只提交了一个请求,所以您从结果中检索第一个对象。
- 3) 返回结果的
pixelBuffer
属性有掩码。 然后创建CIImage
版本的前景、背景和掩码。CIImage
是Core Image
过滤器将处理的图像的表示。 你需要这个来混合图像。
1. Blending All the Images
在import Vision
下面的 GreetingProcessor.swift
中添加以下内容:
import CoreImage.CIFilterBuiltins
Core Image
提供了提供 CIFilter
类型安全实例的方法。 在这里,您导入 CIFilterBuiltins
以访问其类型安全的 API。
接下来,将以下内容添加到 GreetingProcessor
:
func blendImages(
background: CIImage,
foreground: CIImage,
mask: CIImage
) -> CIImage? {
// 1
let maskScaleX = foreground.extent.width / mask.extent.width
let maskScaleY = foreground.extent.height / mask.extent.height
let maskScaled = mask.transformed(
by: __CGAffineTransformMake(maskScaleX, 0, 0, maskScaleY, 0, 0))
// 2
let backgroundScaleX = (foreground.extent.width / background.extent.width)
let backgroundScaleY = (foreground.extent.height / background.extent.height)
let backgroundScaled = background.transformed(
by: __CGAffineTransformMake(backgroundScaleX,
0, 0, backgroundScaleY, 0, 0))
// 3
let blendFilter = CIFilter.blendWithMask()
blendFilter.inputImage = foreground
blendFilter.backgroundImage = backgroundScaled
blendFilter.maskImage = maskScaled
// 4
return blendFilter.outputImage
}
上面的代码:
- 1) 计算蒙版相对于前景图像的 X 和 Y 比例。 然后它使用
CGAffineTransformMake
将mask
大小缩放到前景图像。 - 2) 与
mask
的缩放一样,它计算背景的 X 和 Y 比例,然后将背景background
缩放到前景foreground
的大小。 - 3) 创建
blendFilter
,它是一个Core Image
过滤器。 然后它将过滤器的inputImage
设置为前景foreground
。 过滤器的backgroundImage
和maskImage
设置为图像的缩放版本。 - 4)
outputImage
包含混合的结果。
返回的结果是 CIImage
类型的。 您需要将其转换为 UIImage
以在 UI
中显示。
在 GreetingProcessor
中,在顶部添加以下内容,在 let request = VNGeneratePersonSegmentationRequest()
下方:
let context = CIContext()
在这里,您创建 CIContext
的一个实例。 它用于从 CIImage
对象创建 Quartz 2D
图像。
将以下内容添加到 GreetingProcessor
:
private func renderAsUIImage(_ image: CIImage) -> UIImage? {
guard let cgImage = context.createCGImage(image, from: image.extent) else {
return nil
}
return UIImage(cgImage: cgImage)
}
在这里,您使用context
从 CIImage
创建 CGImage
的实例。
然后使用 cgImage
创建一个 UIImage
。 用户将看到该图像。
2. Displaying the Photo Greeting
替换generatePhotoGreeting(greeting:)
中的 // TODO: Blend images
并添加以下内容:
// 1
guard let output = blendImages(
background: background,
foreground: foreground,
mask: maskImage) else {
print("Error blending images")
return
}
// 2
if let photoResult = renderAsUIImage(output) {
self.photoOutput = photoResult
}
这是正在发生的事情:
- 1)
blendImages(background:foreground:mask:)
混合图像并确保输出不为nil
。 - 2) 然后,将输出转换为
UIImage
的实例并将其设置为photoOutput
。photoOutput
是一个已发布的属性。 访问它以在PhotoGreetingView.swift
中显示输出。
最后一步,打开 PhotoGreetingView.swift
。 将 Button
的动作闭包中的 // TODO: Generate Photo Greeting
替换为以下内容:
GreetingProcessor.shared.generatePhotoGreeting(greeting: greeting)
在这里,您调用 generatePhotoGreeting(greeting:)
以在点击 Button
时生成问候语。
在物理设备上构建和运行。 点击Generate Photo Greeting
。
瞧! 您现在已经为您的家庭照片添加了自定义背景。 是时候将它发送给您的朋友和家人了。
默认情况下,您将获得质量最好的人物分割。 它确实具有很高的处理成本,并且可能不适合所有实时场景。 了解可用的不同质量和性能选项至关重要。 接下来你会学到这一点。
Quality and Performance Options
您之前创建的人员分段请求的默认质量级别为 VNGeneratePersonSegmentationRequest.QualityLevel.accurate
。
您可以从三个质量级别中进行选择:
- 准确
accurate
:非常适合您想要获得最高质量且不受时间限制的场景。 - 平衡
balanced
:非常适合处理视频帧。 - 快速
fast
:最适合处理流媒体内容。
生成mask
的质量取决于质量级别集。
请注意,随着质量级别的提高,mask
的质量看起来要好得多。 准确的质量在mask
中显示更精细的细节。 帧大小、内存和处理时间因质量级别而异。
与fast
质量级别相比,accurate
级别的帧大小高达 64
倍。 与fast and balanced
级别相比,处理accurate
级别所需的内存和时间要高得多。 这代表了对mask
质量和生成mask
所需资源的权衡。
现在您知道了权衡,是时候生成一个有趣的视频问候了!
Creating Video Greeting
打开 CameraViewController.swift
。 它设置了所有功能来捕获相机帧并使用 Metal
渲染它们。 要了解有关使用 AVFoundation
和 SwiftUI
设置相机的更多信息,请查看本教程和本视频系列。
查看CameraViewController
中的逻辑,符合AVCaptureVideoDataOutputSampleBufferDelegate
。
extension CameraViewController: AVCaptureVideoDataOutputSampleBufferDelegate {
func captureOutput(_ output: AVCaptureOutput,
didOutput sampleBuffer: CMSampleBuffer,
from connection: AVCaptureConnection) {
// Grab the pixelbuffer frame from the camera output
guard let pixelBuffer = sampleBuffer.imageBuffer else {
return
}
self.currentCIImage = CIImage(cvPixelBuffer: pixelBuffer)
}
}
在这里,请注意 pixelBuffer
是从 sampleBuffer
中检索的。 然后通过更新 currentCIImage
来渲染它。 您的目标是使用此 pixelBuffer
作为foreground
图像并创建视频问候语。
打开 GreetingProcessor.swift
并将以下内容添加到 GreetingProcessor
:
func processVideoFrame(
foreground: CVPixelBuffer,
background: CGImage
) -> CIImage? {
let ciForeground = CIImage(cvPixelBuffer: foreground)
// TODO: person segmentation request
return nil
}
在这里,您从foreground CVPixelBuffer
创建一个 CIImage
实例,以便您可以使用 Core Image
过滤器混合图像。
到目前为止,您已经使用 Vision
框架来创建、处理和处理人员分割请求。尽管它易于使用,但其他框架提供由相同技术支持的类似功能。接下来你会学到这个。
1. Alternatives for Generating Person Segmentation
您可以使用这些框架作为 Vision
的替代方案来生成人物分割mask
:
-
AVFoundation
:拍摄照片时可以在某些较新的设备上生成人物分割mask
。您可以通过AVCapturePhoto
的PortraitEffectsMatte
属性获取mask
。 -
ARKit
:在处理相机流时生成分割mask
。您可以使用ARFrame
的segmentationBuffer
属性获取mask
。它在具有A12 Bionic
及更高版本的设备上受支持。 -
Core Image
:Core Image
为Vision
框架提供了一个精简的包装器。它公开了您为VNGeneratePersonSegmentationRequest
设置的qualityLevel
属性。
接下来,您将使用Core Image
为视频问候生成人物分割mask
。
2. Using Core Image to Generate Person Segmentation Mask
将 processVideoFrame(foreground:background:)
中的 // TODO: person segmentation request
替换为以下内容:
// 1
let personSegmentFilter = CIFilter.personSegmentation()
personSegmentFilter.inputImage = ciForeground
personSegmentFilter.qualityLevel = 1
// 2
if let mask = personSegmentFilter.outputImage {
guard let output = blendImages(
background: CIImage(cgImage: background),
foreground: ciForeground,
mask: mask) else {
print("Error blending images")
return nil
}
return output
}
这是这样做的:
- 1) 使用
Core Image
的CIFilter
创建personSegmentFilter
并将inputImage
设置为前景图像。qualityLevel
接受一个数字。 不同的质量级别选项包括:-
0
:准确 -
1
平衡 -
2
:快
在这里,您将qualityLevel
设置为 1。
-
- 2) 从
personSegmentationFilter
的outputImage
中获取mask
并确保它不为nil
。 然后,它使用blendImages(background:foreground:mask:)
混合图像并返回结果。
打开 CameraViewController.swift
。 将 CameraViewController
扩展中 captureOutput(_:didOutput:from:)
的内容替换为以下内容:
// 1
guard
let pixelBuffer = sampleBuffer.imageBuffer,
let backgroundImage = self.background?.cgImage else {
return
}
// 2
DispatchQueue.global().async {
if let output = GreetingProcessor.shared.processVideoFrame(
foreground: pixelBuffer,
background: backgroundImage) {
DispatchQueue.main.async {
self.currentCIImage = output
}
}
}
这是上面代码的细分。 它:
- 1) 检查
pixelBuffer
和backgroundImage
是否有效。 - 2) 通过调用
GreetingProcessor
中定义的processVideoFrame(foreground:background:)
异步处理视频帧。 然后,它用output
更新currentCIImage
。
在物理设备上构建和运行。 点击Video Greeting
标签。
不好了! 没有可见的摄像机流。 发生了什么?
打开 GreetingProcessor.swift
并在processVideoFrame(foreground:background:)
中的 guard let output = blendImages
处设置断点。 请注意在调试器中使用 Quick Look
生成的mask
。
mask
是红色的! 您需要使用红色mask
而不是常规的白色mask
创建一个混合滤镜。
更新 blendImages(background:foreground:mask:)
以采用新的布尔参数,如下所示:
func blendImages(
background: CIImage,
foreground: CIImage,
mask: CIImage,
isRedMask: Bool = false
) -> CIImage? {
这使用 isRedMask
来确定要生成的混合过滤器的类型。 默认情况下,它的值为 false
。
在 blendImages(background:foreground:mask:isRedMask:)
中替换 let blendFilter = CIFilter.blendWithMask()
如下:
let blendFilter = isRedMask ?
CIFilter.blendWithRedMask() :
CIFilter.blendWithMask()
在这里,如果 isRedMask
为真,则生成带有红色mask
的 blendFilter
。 否则,您将使用白色蒙版进行创建。
接下来,替换:
guard let output = blendImages(
background: CIImage(cgImage: background),
foreground: ciForeground,
mask: mask) else {
在 processVideoFrame(foreground:background:)
中具有以下内容:
guard let output = blendImages(
background: CIImage(cgImage: background),
foreground: ciForeground,
mask: mask,
isRedMask: true) else {
在这里,您指定生成带有red mask
的混合滤镜。
在物理设备上构建和运行。 点击Video Greeting
并将前置摄像头对准您。
您现在看到您的图像覆盖在友好的问候语上。 制作视频问候语很棒!
您现在可以创建缩放模糊背景过滤器。
Understanding Best Practices
虽然人物分割适用于照片和视频问候语,但请记住以下一些最佳做法:
- 尝试在一个场景中最多分割四个人,并确保所有人都可见。
- 一个人的身高应该至少是图像高度的一半。
- 避免在框架中出现以下歧义:
Statues
Long distance
要了解更多信息,请观看此 WWDC
视频: Detect people, faces, and poses using Vision。
后记
本篇主要讲述了基于
Vision
的人员分割,感兴趣的给个赞或者关注~~~
网友评论