美文网首页
Vision框架详细解析(十四) —— 基于Vision的人员分

Vision框架详细解析(十四) —— 基于Vision的人员分

作者: 刀客传奇 | 来源:发表于2022-03-20 16:14 被阅读0次

版本记录

版本号 时间
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 中,AppleVision 框架中引入了一个 API 来执行人物分割,这也为肖像模式(Portrait mode)提供了支持。

在本教程中,您将学习:

  • 什么是图像分割以及不同类型的分割。
  • 为照片创建了人物分割。
  • 了解不同的质量水平和性能权衡。
  • 为实时视频捕获创建了人物分割。
  • 提供人员分割的其他框架。
  • 人员分割的最佳实践。

注意:本教程假设您具备 SwiftUI、UIKitAVFoundation 的工作知识。有关 SwiftUI 的更多信息,请参阅 SwiftUI: Getting Started。您还需要一个物理的 iOS 15 设备。

打开起始项目。 在starter中打开 RayGreetings。 在物理设备上构建和运行。

您将看到两个tabPhoto GreetingVideo GreetingPhoto Greeting tab会显示一个漂亮的背景图片和一张家庭照片。在本教程中,您将使用人员分割将家庭成员叠加在问候语背景上。点击Video Greeting tab并授予摄像头权限。您将看到显示的相机流。启动项目设置为捕获和显示相机帧。您将更新实时帧以生成视频问候!

在深入实施这些之前,您需要了解什么是人员分割。准备好有趣的旅程。


Introducing Image Segmentation

图像分割将图像划分为多个片段并对其进行处理。它提供了对图像的更细粒度的理解。对象检测提供图像中所需对象的边界框,而图像分割提供对象的像素掩码。

图像分割有两种类型:语义分割和实例分割(semantic segmentation and instance segmentation)

Semantic segmentation是检测图像中属于同一类的相似部分并将其组合在一起的过程。Instance segmentation是检测对象的特定实例的过程。当您将语义分割应用于包含人的图像时,它会生成一个包含所有人的掩码。实例分割为图像中的每个人生成一个单独的掩码。

AppleVision 框架中提供的人员分割 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) 从 backgroundImageforegroundImage 访问 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 版本的前景、背景和掩码。 CIImageCore 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 比例。 然后它使用 CGAffineTransformMakemask大小缩放到前景图像。
  • 2) 与mask的缩放一样,它计算背景的 X 和 Y 比例,然后将背景background缩放到前景foreground的大小。
  • 3) 创建 blendFilter,它是一个Core Image过滤器。 然后它将过滤器的 inputImage 设置为前景foreground。 过滤器的 backgroundImagemaskImage 设置为图像的缩放版本。
  • 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)
}

在这里,您使用contextCIImage 创建 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 的实例并将其设置为 photoOutputphotoOutput 是一个已发布的属性。 访问它以在 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 渲染它们。 要了解有关使用 AVFoundationSwiftUI 设置相机的更多信息,请查看本教程和本视频系列。

查看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。您可以通过 AVCapturePhotoPortraitEffectsMatte 属性获取mask
  • ARKit:在处理相机流时生成分割mask。您可以使用 ARFramesegmentationBuffer 属性获取mask。它在具有 A12 Bionic 及更高版本的设备上受支持。
  • Core ImageCore ImageVision 框架提供了一个精简的包装器。它公开了您为 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 ImageCIFilter 创建 personSegmentFilter 并将 inputImage 设置为前景图像。 qualityLevel 接受一个数字。 不同的质量级别选项包括:
    • 0:准确
    • 1 平衡
    • 2:快
      在这里,您将 qualityLevel 设置为 1。
  • 2) 从 personSegmentationFilteroutputImage 中获取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) 检查 pixelBufferbackgroundImage 是否有效。
  • 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 为真,则生成带有红色maskblendFilter。 否则,您将使用白色蒙版进行创建。

接下来,替换:

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的人员分割,感兴趣的给个赞或者关注~~~

相关文章

网友评论

      本文标题:Vision框架详细解析(十四) —— 基于Vision的人员分

      本文链接:https://www.haomeiwen.com/subject/jkaodrtx.html