iOS開發之視覺框架中的人員與背景分割

語言: CN / TW / HK

theme: scrolls-light

入門

在本教程中,您將學習:

  • 什麼是影象分割以及不同型別的分割。
  • 為照片建立了人物分割。
  • 瞭解不同的質量水平和效能。
  • 人物為影片錄製建立了分割槽。
  • 提供人員分割槽的其他框架。
  • 細分人群的最佳實踐。

注意:本教程假設您具備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

瞭解最佳實踐

雖然人物分割適用於照片和影片問候語,但請記住以下一些最佳做法:

  • 嘗試在一個場景中最多分割四個人,並確保所有人都可見。
  • 一個人的身高應該至少是影象高度的一半。
  • 避免在框架中出現以下歧義:
    • 雕像
    • 遠距離

結束

您可以使用教程中的所有程式碼部分下載最終專案。

這裡也推薦一些面試相關的內容! * ① BAT等各個大廠iOS面試真題+答案大全