iOS开发之视觉框架中的人员与背景分割

入门

在本教程中,您将学习:

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

注意:本教程假设您具备SwiftUIUIKitAVFoundations的工作知识。SwiftUI 的更多信息,请参阅SwiftUI:入门。您还需要一个物理的 iOS 15 相关设备来跟随。

01.gif

您将看到一张照片和一个漂亮的问候视频家庭语*。**照片人员将在两个选项卡中显示和使用教程的背景。背景上。点击视频*标签并显示您将看到显示的相机源头。启动项目设置为权限和相机框架。您更新实时以生成问候!

在深入了解这些之前,您需要什么人员细分。准备好是好有趣的旅行社。

介绍图像分割

图像分割将图像划分为多个图像并进行细化而处理。它提供了更多对像素的理解。

分割有类型:图像分割实例分割

当您的一个分割实例**是一个相同的部分是其组合的过程。每个人的掩体。代码分割为图像中的每个人生成一个单独的掩体。

Apple 的 Vision 框架的人员分割 API 是单帧码。它的使用 API 是面向中流式分割为框架中的单独提供的。它用于分割处理和离线处理。

人物分割的过程有四个步骤:

  1. 创建用户细分请求。
  2. 为该请求创建请求处理程序。
  3. 处理请求。
  4. 处理结果。

,您将使用 API 和步骤来创建使用照片!

创建照片

你有一个家庭的形象和一个形象。你的目标家庭背景中的人在背景上的背景,以产生背景。

打开 RayGreetings并打开GreetingProcessor.swift

在下面添加以下内容import Combine

进口愿景
复制代码

下一个,将下面的内容添加到下面的GreetingProcessor内容中@Published var photoOutput = UIImage()

让请求=  VNGeneratePersonSegmentationRequest ()
复制代码

在这里,您创建个人细分用于请求的实例。一个有状态的请求,可以重复整个帧的离线处理视频。

将以下内容添加到GreetingProcessor

func  generatePhotoGreeting(问候:问候) {
   // 1
  警卫
    让 backgroundImage = greeting.backgroundImage.cgImage,
    让foregroundImage.foregroundImage.c else { print (
     "呼唤的图像" )
    返回
  }
 
  // 2 
  // 创建请求处理程序
  让 requestHandler =  VNImageRequestHandler (
    cgImage:自己的图像,
    选项: [:])
 
  // 执行
}
复制代码

这是上面的代码正在做的事情:

  1. cgImagebackgroundImage和访问foregroundImage。然后,它确保两个图像都是有效的。您将很容易使用它们来使用 Core Image 混合图像。
  2. 创建requestHandler为的实例VNImageRequestHandler。它接收图像以及指定如何处理图像的任选字典。

替换// TODO为以下内容:

做 {
   // 1
  试用 requestHandler.perform([request])
 
  // 2
  守卫让掩码= request.results ? .first else {
     print ( "生成分割掩码时出错" )
    返回
  }
 
  // 3
  让属性=  CIImage (cgImage:foregroundImage)
  让 maskImage =  CIImage (cvPixelBuffer: mask.pixelBuffer)
  让背景=  CIImage(cgImage:backgroundImage)
 
  // TODO:混合图像
} 抓住 {
  print ( "处理人员分区请求异常" )
}
复制代码

这是自己的代码细分:

  1. requestHandler使用处理人员分割perform(_:)。如果有多个请求,则在所有请求存在完成或失败后返回。perform(_:)在请求处理时可能会通过错误提示,因此您可以将其包含在do-catch
  2. 然后,您从结果中检索掩饰码。因为您只提交了一个请求,所以从结果中检索第一个对象。
  3. 返回结果的pixelBuffer属性掩码。然后创建 CIImage 有背景和蒙版的版本。CIImage 是 Core Image 过滤器将处理的图像的混合表示。您将需要它来图像。

混合所有图像

在下面的 GreetingProcessor.swift中添加以下内容import Vision

导入CoreImage 。CIFilterBuiltins
复制代码

Core Image 提供了提供类型安全实例的方法CIFilter。在这里,您可以导入CIFilterBuiltins访问类型的安全 API。

将以下内容添加到GreetingProcessor

游戏混合图像(
  背景:CIImage,对应
  :CIImage,
  掩饰:CIImage 
) -> CIImage? {
   // 1
  让 maskScaleX = foreground.extent.width / mask.extent.width
  让 maskScaleY = foreground.extent.height / mask.extent.height
  让 maskScaled = mask.transformed(
    作者:__CGAffineTransformMake(maskScaleX, 0 , 0 , maskScaleY, 0 , 0 ))
 
  // 2
  让 backgroundScaleX = (foreground.extent.width / background.extent.width)
  让 backgroundScaleY = (foreground.extent.height / background.extent.height)
  让 backgroundScaled = background.transformed(
    作者:__CGAffineTransformMake(backgroundScaleX,
    0 , 0 , backgroundScaleY, 0 , 0 ))
 
  // 3
  让 blendFilter =  CIFilter .blendWithMask()
  blendFilter.inputImage =其他
  blendFilter.backgroundImage = backgroundScaled
  blendFilter.maskImage = maskScaled
 
  // 4
  返回 blendFilter.outputImage
}
复制代码

的代码:

  1. 计算蒙版相对于用于相同图像的 X 和 Y 比例。然后它将缩放CGAffineTransformMake缩放到图像。mask``foreground
  2. 与的缩放一样mask,它计算出 X 和 Y 的大小,background然后缩放background到的大小foreground
  3. 创建一个核心blendFilter图像过滤器,inputImage设置为后续过滤器的和缩放版本foregroundbackgroundImage``maskImage
  4. outputImage包含混合的结果。

返回的结果是类型CIImage。您需要将其转换为一个UIImage以在 UI 中显示的。

GreetingProcessor以下中,在顶部、添加以下内容let request = VNGeneratePersonSegmentationRequest()

让时间=  CIContext ()
复制代码

在这里,您创建一个CIContext。它用于从CIImage对象创建 Quartz 2D 图像。

将以下内容添加到GreetingProcessor

函数 renderAsUIImage(_image : CIImage ) -> UIImage ? {
  守卫让 cgImage = context.createCGImage(image, from: image.extent) else {
    返回零
  }
  返回UIImage (cgImage: cgImage)
}
复制代码

在这里,您用于从实例context创建。CGImage``CIImage

使用cgImage,然后创建一个UIImage。用户将看到该图像。

显示照片问候语

替换// TODO: Blend imagesgeneratePhotoGreeting(greeting:)添加以下内容:

// 1守卫让输出= 
blendImages (
  背景:背景,
  作案:特殊,
  掩码:掩码图像)否则{
    打印(“错误混合图像”)
    返回
  }
 
// 2
如果让 photoResult = renderAsUIImage(output) {
   self .photoOutput =照片结果
}
复制代码

这是正在发生的事情:

  1. blendImages(background:foreground:mask:)混合图像并确保输出不是nil
  2. 然后,将输出转换为一个实例并将其设置为。是已发布的属性。访问它以在UIImagePhotoGreetingView.swift显示。photoOutput``photoOutput

最后,打开PhotoGreetingView.swift// TODO: Generate Photo Greeting在行动中闭包中替换Button为以下内容:

GreetingProcessor .shared.generatePhotoGreeting(问候语:问候语)
复制代码

在这里,您调用以在被点击generatePhotoGreeting(greeting:)时生成问候语。Button

在照片设备上制造和运行。点击问候

02.gif

默认情况下,您将获得质量最好的节目,它确实不适合这种情况,并且实时所有场景。了解可能的不同质量和性能的处理选项。一点。

质量和性能选项

您之前创建的个人细分请求的默认质量等级为VNGeneratePersonSegmentationRequest.QualityLevel.accurate

您可以从三个质量等级中进行选择:

  • accurate:非常适合您想要获得最高质量且不受时间限制的情况。
  • balanced:非常适合处理视频帧。
  • fast:最适合处理流媒体内容。

03.png

生成掩码的质量等级集。

04.png

请注意,随着质量级别的提高,蒙版的质量看起来要好得多。准确的质量在蒙版中显示更精细的细节。帧大小、内存和处理时间因质量级别而异。

05.png

与快速质量级别相比,精确级别的帧大小高达 64 倍。与快速和平衡的关卡相比,处理准确关卡所需的内存和时间要高得多。这代表了对掩码质量和生成掩码所需资源的权衡。

现在您知道了权衡,是时候生成一个有趣的视频问候了!:]

创建视频问候

打开CameraViewController.swift。它设置了所有功能来捕获相机帧并使用 Metal 渲染它们。要了解有关使用 AVFoundation 和 SwiftUI 设置相机的更多信息,请查看本教程和本视频系列

查看 中的逻辑CameraViewController,符合AVCaptureVideoDataOutputSampleBufferDelegate.

extension  CameraViewController : AVCaptureVideoDataOutputSampleBufferDelegate {
   func  captureOutput ( _  output : AVCaptureOutput ,
                      didOutput  sampleBuffer : CMSampleBuffer ,
                      from  connection : AVCaptureConnection ) {
     // 从相机输出
    守卫中获取像素缓冲帧 let pixelBuffer = sampleBuffer.imageBuffer else {
       return
    }
    自.currentCIImage =  CIImage (cvPixelBuffer: pixelBuffer)
  }
}
复制代码

在这里,注意pixelBuffer从 中检索sampleBuffer。然后通过更新渲染它currentCIImage。您的目标是将其pixelBuffer用作前景图像并创建视频问候语。

打开GreetingProcessor.swift并将以下内容添加到GreetingProcessor

func  processVideoFrame(
  前景:CVPixelBuffer,
  背景:CGImage 
)-> CIImage?{
  让ciForeground =  CIImage (cvPixelBuffer: 前景)

  // TODO:人物分割请求

  返回 零
}
复制代码

CIImage在这里,您从前景创建一个实例,CVPixelBuffer以便您可以使用 Core Image 过滤器混合图像。

到目前为止,您已经使用Vision框架来创建、处理和处理人员细分请求。尽管它易于使用,但其他框架提供由相同技术支持的类似功能。接下来你会学到这个。

生成人员分割的替代方案

您可以使用这些框架作为 Vision 的替代方案来生成人物分割掩码:

  • AVFoundation:可以在某些较新的设备上拍摄照片时生成人物分割蒙版。您可以通过portraitEffectsMatte.AVCapturePhoto
  • ARKit:在处理相机馈送时生成分割掩码。segmentationBuffer您可以使用 的属性获取掩码ARFrame。它在具有 A12 Bionic 及更高版本的设备上受支持。
  • Core Image:Core Image 在 Vision 框架上提供了一个瘦包装器。它公开了qualityLevel您设置的属性VNGeneratePersonSegmentationRequest

接下来,您将使用 Core Image 为视频问候生成人物分割掩码。

使用核心图像生成人物分割掩码

替换// TODO: person segmentation requestprocessVideoFrame(foreground:background:)以下内容:

// 1
让personSegmentFilter =  CIFilter .personSegmentation()
personSegmentFilter.inputImage = ciForeground
personSegmentFilter.qualityLevel =  1
 
// 2 
if  let mask = personSegmentFilter.outputImage {
   guard  let output = blendImages(
    背景:CIImage(cgImage:背景),
    前景:ciForeground,
    mask: mask) else {
       print ( "Error blending images" )
       return  nil
    }
  返回输出
}
复制代码

这就是这样做的:

  1. personSegmentFilter使用核心图像创建CIFilter并设置inputImage前景图像。接受一个qualityLevel数字。不同的质量级别选项包括:

    • 0:准确
    • 1平衡
    • 2:快

    在这里,您设置qualityLevel为 1。

  2. outputImage从of 中获取掩码personSegmentationFilter并确保它不是nil. 然后,它用于blendImages(background:foreground:mask:)混合图像并返回结果。

打开CameraViewController.swift。将扩展名中的内容替换为以下captureOutput(_:didOutput:from:)内容:CameraViewController

// 1
保护 
  let pixelBuffer = sampleBuffer.imageBuffer,
   let backgroundImage =  self .background ? .cgImage其他{
  返回
}
 
// 2 
DispatchQueue .global().async {
   if  let output =  GreetingProcessor .shared.processVideoFrame(
    前景:像素缓冲区,
    背景:背景图像){
    DispatchQueue .main.async {
       self .currentCIImage =输出
    }
  }
}
复制代码

这是上面代码的细分。它:

  1. 检查pixelBufferbackgroundImage有效。
  2. 通过调用processVideoFrame(foreground:background:)中定义的异步处理视频帧GreetingProcessor。然后,它更新currentCIImageoutput.

在物理设备上构建和运行。点击视频问候标签。

06.gif

不好了!没有可见的摄像机流。发生了什么?

打开GreetingProcessor.swiftguard let output = blendImages并在in处放置一个断点processVideoFrame(foreground:background:)。请注意在调试器中使用 Quick Look 生成的掩码。

07.gif

面具是红色的!您需要使用红色蒙版而不是常规的白色蒙版创建一个混合滤镜。

更新blendImages(background:foreground:mask:)以采用新的布尔参数,如下所示:

func  blendImages(
  背景:CIImage,
  前景:CIImage,
  掩码:CIImage,
   isRedMask:Bool  =  false 
)-> CIImage?{
复制代码

这用于isRedMask确定要生成的混合过滤器的类型。默认情况下,它的值为false

let blendFilter = CIFilter.blendWithMask()如下图所示替换blendImages(background:foreground:mask:isRedMask:)

让blendFilter = isRedMask ?
CIFilter .blendWithRedMask() :
 CIFilter .blendWithMask()
复制代码

在这里,您blendFilter使用红色蒙版生成 if isRedMaskis true。否则,您将使用白色蒙版进行创建。

接下来,替换:

守卫 让输出= blendImages(
  背景:CIImage(cgImage:背景),
  前景:ciForeground,
  掩码:掩码)否则{ 
复制代码

processVideoFrame(foreground:background:) with the following:

守卫 让输出= blendImages(
  背景:CIImage(cgImage:背景),
  前景:ciForeground,
  面具:面具,
  isRedMask: true ) else {
复制代码

在这里,您指定生成带有红色蒙版的混合滤镜。

在物理设备上构建和运行。点击视频问候并将前置摄像头对准您。

08.gif

了解最佳实践

虽然人物分割适用于照片和视频问候语,但请记住以下一些最佳做法:

  • 尝试在一个场景中最多分割四个人,并确保所有人都可见。
  • 一个人的身高应该至少是图像高度的一半。
  • 避免在框架中出现以下歧义:
    • 雕像
    • 远距离

结束

您可以使用教程中的所有代码部分下载最终项目。

这里也推荐一些面试相关的内容!

猜你喜欢

转载自juejin.im/post/7101219136507740168