iOS 適用的圖片區隔指南

「圖片分割工具」工作可讓您根據預先定義的不同區域將圖片區分成多個區域 然後將背景模糊處理等視覺效果這些 指示將說明如何在 iOS 應用程式中使用圖片區隔工具。

如需上述指示中所述的程式碼範例,請前往 GitHub

如要查看這項工作的實際運作情形,請前往網頁版 示範。 進一步瞭解功能、模型和設定選項 請參閱 總覽

程式碼範例

MediaPipe 工作程式碼範例包含 iOS 版 Image Segmenter 應用程式。

這個範例會實作一個圖片分段器,輸出類別遮罩。使用 使用實體 iOS 裝置的相機,在即時影像上分割影像 攝影機畫面或裝置圖片庫中的圖片和影片。

你可以將這個應用程式做為起點,開始使用 iOS 應用程式,也可以參照這個應用程式 做出決定Image Segmenter 範例程式碼 GitHub

下載程式碼

以下說明如何建立範例的本機副本 git 指令列工具編寫程式碼。

如要下載範例程式碼,請按照下列步驟操作:

  1. 使用下列指令複製 git 存放區:

    git clone https://github.com/google-ai-edge/mediapipe-samples/
    
  2. 您也可以設定 Git 執行個體來使用稀疏結帳功能, 只有 Image Segmenter 範例應用程式的檔案:

    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/image_segmentation/ios/
    

建立範例程式碼的本機版本後,您可以安裝 MediaPipe 工作程式庫,使用 Xcode 開啟專案,然後執行應用程式。適用對象 操作說明,請參閱 iOS 設定指南

重要元件

下列檔案包含 Image Segmenter 範例的重要程式碼 應用程式:

設定

本節說明設定開發環境的重要步驟,以及 程式碼專案使用 Image Segmenter。如需設定 使用 MediaPipe 工作 (包括平台版本) 的開發環境 規定,請參閱 iOS 設定指南

依附元件

Image Segmenter 使用 MediaPipeTasksVision 程式庫 (必須安裝) 開發應用程式這個程式庫同時與 Swift 和 Objective-C 應用程式相容 且不需要額外設定任何特定語言

如需在 macOS 上安裝 CocoaPods 的操作說明,請參閱 CocoaPods 安裝指南。 有關如何建立包含所需 Pod 的 Podfile 的操作說明 請參閱使用 CocoaPods

使用下列程式碼在 Podfile 中新增 MediaPipeTasksVision Pod:

target 'MyImageSegmenterApp' do
  use_frameworks!
  pod 'MediaPipeTasksVision'
end

如果您的應用程式包含單元測試目標,請參閱設定指南: iOS,進一步瞭解設定方式 您的 Podfile

型號

MediaPipe Image Segmenter 工作需要訓練完成且彼此相容的模型 透過這項工作如要進一步瞭解 圖片區隔工具,請參閱工作總覽模型 專區

選取並下載模型,然後用 Xcode 將模型新增到專案目錄。 如需將檔案新增至 Xcode 專案的操作說明,請參閱管理 Xcode 中的檔案和資料夾 專案

使用 BaseOptions.modelAssetPath 屬性指定模型的路徑 。如需程式碼範例,請參閱下一節。

建立工作

您可以呼叫其中一個初始化器來建立 Image Segmenter 工作。 ImageSegmenter(options:) 初始化器接受設定值 只要設定成「自動重新啟動」 和「在主機維護期間」選項即可

如果您不需要用自訂設定初始化的影像分割器 您可以使用 ImageSegmenter(modelPath:) 初始化器建立 帶有預設選項的圖片分割工具。進一步瞭解設定 選項,請參閱設定總覽

「圖片區隔工具」工作支援 3 種輸入資料類型:靜態圖片、影片檔案 和即時影像串流根據預設,ImageSegmenter(modelPath:) 會初始化 靜態圖片如要初始化工作以處理影片 檔案或直播影片串流,請使用 ImageSegmenter(options:) 指定影片 或即時串流執行模式使用直播模式時, imageSegmenterLiveStreamDelegate 設定選項,以便啟用 Image Segmenter 將圖片區隔結果傳送給委派代表 以非同步方式載入物件

選擇與執行中模式對應的分頁,瞭解如何建立工作 然後執行推論

Swift

圖片

import MediaPipeTasksVision

let modelPath = Bundle.main.path(forResource: "model",
                                      ofType: "tflite")

let options = ImageSegmenterOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .image
options.shouldOutputCategoryMask = true
options.shouldOutputConfidenceMasks = false

let imageSegmenter = try ImageSegmenter(options: options)
    

影片

import MediaPipeTasksVision

let modelPath = Bundle.main.path(forResource: "model",
                                      ofType: "tflite")

let options = ImageSegmenterOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .video
options.shouldOutputCategoryMask = true
options.shouldOutputConfidenceMasks = false

let imageSegmenter = try ImageSegmenter(options: options)
    

直播

import MediaPipeTasksVision

// Class that conforms to the `imageSegmenterLiveStreamDelegate` protocol and
// implements the method that the image segmenter calls once it finishes
// performing segmentation of each input frame.
class ImageSegmenterResultProcessor: NSObject, ImageSegmenterLiveStreamDelegate {

  func imageSegmenter(
    _ imageSegmenter: ImageSegmenter,
    didFinishSegmentation result: ImageSegmenterResult?,
    timestampInMilliseconds: Int,
    error: Error?) {

    // Process the image segmentation result or errors here.

  }
}

let modelPath = Bundle.main.path(forResource: "model",
                                      ofType: "tflite")

let options = ImageSegmenterOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .liveStream
options.shouldOutputCategoryMask = true
options.shouldOutputConfidenceMasks = false

// Set `imageSegmenterLiveStreamDelegate` to the object of the class that
// confirms to the `ImageSegmenterLiveStreamDelegate` protocol.
let processor = ImageSegmenterResultProcessor()
options.imageSegmenterLiveStreamDelegate = processor

let imageSegmenter = try ImageSegmenter(options: options)
    

Objective-C

圖片

@import MediaPipeTasksVision;

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
                                                      ofType:@"tflite"];

MPPImageSegmenterOptions *options = [[MPPImageSegmenterOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeImage;
options.shouldOutputCategoryMask = YES;
options.shouldOutputConfidenceMasks = NO;

MPPImageSegmenter *imageSegmenter =
  [[MPPImageSegmenter alloc] initWithOptions:options error:nil];
    

影片

@import MediaPipeTasksVision;

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
                                                      ofType:@"tflite"];

MPPImageSegmenterOptions *options = [[MPPImageSegmenterOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeVideo;
options.shouldOutputCategoryMask = YES;
options.shouldOutputConfidenceMasks = NO;

MPPImageSegmenter *imageSegmenter =
  [[MPPImageSegmenter alloc] initWithOptions:options error:nil];
    

直播

@import MediaPipeTasksVision;

// Class that conforms to the `MPPImageSegmenterLiveStreamDelegate` protocol
// and implements the method that the image segmenter calls once it finishes
// performing segmentation of each input frame.

@interface APPImageSegmenterResultProcessor : NSObject 

@end

@implementation APPImageSegmenterResultProcessor

-   (void)imageSegmenter:(MPPImageSegmenter *)imageSegmenter
    didFinishSegmentationWithResult:(MPPImageSegmenterResult *)imageSegmenterResult
         timestampInMilliseconds:(NSInteger)timestampInMilliseconds
                           error:(NSError *)error {

    // Process the image segmentation result or errors here.

}

@end

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
                                                      ofType:@"tflite"];

MPPImageSegmenterOptions *options = [[MPPImageSegmenterOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeLiveStream;
options.shouldOutputCategoryMask = YES;
options.shouldOutputConfidenceMasks = NO;

// Set `imageSegmenterLiveStreamDelegate` to the object of the class that
// confirms to the `MPPImageSegmenterLiveStreamDelegate` protocol.
APPImageSegmenterResultProcessor *processor =
  [APPImageSegmenterResultProcessor new];
options.imageSegmenterLiveStreamDelegate = processor;

MPPImageSegmenter *imageSegmenter =
  [[MPPImageSegmenter alloc] initWithOptions:options error:nil];
    

導入 Image Segmenter 程式碼範例後,使用者可在 處理模式這種方法使得工作建立程式碼變得更加複雜, 可能會不適合您的用途

設定選項

這項工作有下列 iOS 應用程式設定選項:

選項名稱 說明 值範圍 預設值
runningMode 設定任務的執行模式。在架構中 模式:

圖片:單一圖片輸入模式。

VIDEO:影片已解碼的影格模式。

LIVE_STREAM:輸入串流模式 擷取的資訊等。 在這個模式下,ImageSegmenterLiveStreamDelegate 必須設為實作 ImageSegmenterLiveStreamDelegate 以接收區隔 並以非同步的方式傳回結果
{RunningMode.image, RunningMode.video, RunningMode.liveStream} RunningMode.image
shouldOutputCategoryMask 如果設為 True,則輸出結果會包含區隔遮罩 視為 uint8 圖片,其中每個像素值都代表勝出的類別 值。 {True, False} False
shouldOutputConfidenceMasks 如果設為 True,則輸出結果會包含區隔遮罩 視為浮點值圖片,其中各浮點值代表信心值 分數圖。 {True, False} True
displayNamesLocale 設定標籤語言,供 工作模型的中繼資料 (如有)。以下項目的預設值為 en: 英語。您可以在自訂模型的中繼資料中加入經本地化的標籤 使用 TensorFlow Lite Metadata Writer API 語言代碼 en
result_callback 設定結果監聽器以接收區隔結果 圖片區隔工具處於 LIVE_STREAM 模式時,以非同步方式顯示。 只有在執行模式設為「LIVE_STREAM」時才能使用 不適用 不適用

當跑步模式設為 LIVE_STREAM 時,圖片區隔工具需要 額外的 imageSegmenterLiveStreamDelegate 設定選項 讓 Image Segmenter 以非同步方式提供圖片區隔結果。 委派項目必須執行 imageSegmenter(_:didFinishSegmentation:timestampInMilliseconds:error:) 方法, 圖片區隔工具在處理完執行結果後 每個影格的區隔

選項名稱 說明 值範圍 預設值
imageSegmenterLiveStreamDelegate 啟用圖片區隔工具以取得執行圖片的結果 以非同步的方式在直播模式下進行其例項所屬的類別 必須導入 imageSegmenter(_:didFinishSegmentation:timestampInMilliseconds:error:) 方法。 不適用 未設定

準備資料

您必須先將輸入圖片或影格轉換為 MPImage 物件。 並傳到 Image Segmenter (圖片區隔工具) 中MPImage 支援不同類型的 iOS 圖片 並且可在任何執行模式中使用,以便進行推論。如要 關於「MPImage」的資訊,請參閱 MPImage API

根據您的用途和執行模式,選擇 iOS 圖片格式 應用程式需要。MPImage 接受 UIImageCVPixelBufferCMSampleBuffer iOS 圖片格式。

UIImage

UIImage 格式非常適合下列執行模式:

  • 圖片:應用程式套件、使用者圖片庫或檔案系統的圖片,格式為 UIImage 張圖片可轉換成 MPImage 物件。

  • 影片:使用 AVAssetImageGenerator 將影片影格擷取到 CGImage 格式,然後轉換為 UIImage 張圖片。

Swift

// Load an image on the user's device as an iOS `UIImage` object.

// Convert the `UIImage` object to a MediaPipe's Image object having the default
// orientation `UIImage.Orientation.up`.
let image = try MPImage(uiImage: image)
    

Objective-C

// Load an image on the user's device as an iOS `UIImage` object.

// Convert the `UIImage` object to a MediaPipe's Image object having the default
// orientation `UIImageOrientationUp`.
MPImage *image = [[MPPImage alloc] initWithUIImage:image error:nil];
    

本範例使用預設值來初始化 MPImage UIImage.Orientation.Up 方向。您可以使用任一支援的任一種初始化 MPImage UIImage.Orientation 輕鬆分配獎金圖片區隔工具不支援鏡像螢幕方向,例如 .upMirrored.downMirrored.leftMirrored.rightMirrored

如要進一步瞭解 UIImage,請參閱 UIImage Apple 開發人員 說明文件

CVPixelBuffer

CVPixelBuffer 格式非常適合會產生影格的應用程式 並使用 iOS CoreImage 處理流程

CVPixelBuffer 格式非常適合下列執行模式:

  • 圖片:應用程式在經過處理後產生 CVPixelBuffer 張圖片 您可將 iOS 的 CoreImage 架構傳送給圖片區隔工具, 執行映像檔執行模式

  • 影片:您可以將影片影格轉換為 CVPixelBuffer 格式 然後傳送到影片模式的「圖片區隔工具」

  • 直播:系統可能會轉換使用 iOS 相機產生影格的應用程式。 轉換成 CVPixelBuffer 格式進行處理,才能傳送到 直播模式下的圖片區隔工具。

Swift

// Obtain a CVPixelBuffer.

// Convert the `CVPixelBuffer` object to a MediaPipe's Image object having the default
// orientation `UIImage.Orientation.up`.
let image = try MPImage(pixelBuffer: pixelBuffer)
    

Objective-C

// Obtain a CVPixelBuffer.

// Convert the `CVPixelBuffer` object to a MediaPipe's Image object having the
// default orientation `UIImageOrientationUp`.
MPImage *image = [[MPPImage alloc] initWithUIImage:image error:nil];
    

如要進一步瞭解 CVPixelBuffer,請參閱 CVPixelBuffer Apple 顯像組件 說明文件

CMSampleBuffer

CMSampleBuffer 格式會儲存統一媒體類型的媒體樣本, 也很適合使用直播模式iOS 攝影機的即時影格 由 iOS 以 CMSampleBuffer 格式非同步提供 AVCaptureVideoDataOutput

Swift

// Obtain a CMSampleBuffer.

// Convert the `CMSampleBuffer` object to a MediaPipe's Image object having the default
// orientation `UIImage.Orientation.up`.
let image = try MPImage(sampleBuffer: sampleBuffer)
    

Objective-C

// Obtain a `CMSampleBuffer`.

// Convert the `CMSampleBuffer` object to a MediaPipe's Image object having the
// default orientation `UIImageOrientationUp`.
MPImage *image = [[MPPImage alloc] initWithSampleBuffer:sampleBuffer error:nil];
    

如要進一步瞭解 CMSampleBuffer,請參閱 CMSampleBuffer Apple 顯像組件 說明文件

執行工作

如要執行圖片區隔工具,請使用指派的 segment() 方法 執行模式:

  • 靜態圖片:segment(image:)
  • 影片:segment(videoFrame:timestampInMilliseconds:)
  • 直播:segmentAsync(image:timestampInMilliseconds:)

以下程式碼範例顯示如何在 不同的跑步模式

Swift

圖片

let result = try imageSegmenter.segment(image: image)
    

影片

let result = try imageSegmenter.segment(
  videoFrame: image,
  timestampInMilliseconds: timestamp)
    

直播

try imageSegmenter.segmentAsync(
  image: image,
  timestampInMilliseconds: timestamp)
    

Objective-C

圖片

MPPImageSegmenterResult *result =
  [imageSegmenter segmentImage:image error:nil];
    

影片

MPPImageSegmenterResult *result =
  [imageSegmenter segmentVideoFrame:image
            timestampInMilliseconds:timestamp
                              error:nil];
    

直播

BOOL success =
  [imageSegmenter segmentAsyncImage:image
            timestampInMilliseconds:timestamp
                              error:nil];
    

圖片區隔程式碼示例顯示每種模式的實作方式 詳細瞭解 segment(image:) segment(videoFrame:timestampInMilliseconds:)segmentAsync(image:timestampInMilliseconds:)

注意事項:

  • 以影片模式或直播模式執行時,你必須一併提供 Image Segmenter 工作輸入框的時間戳記。

  • 在圖片或影片模式中執行時,圖片區隔工具工作會封鎖 直到完成處理輸入圖片或影格為止。目的地: 為避免封鎖目前的執行緒,請在背景執行處理作業 使用 iOS 的執行緒 調度NSOperation 架構。

  • 以直播模式執行時,Image Segmenter 工作會立即傳回 但不會封鎖目前的執行緒該函式會叫用 imageSegmenter(_:didFinishSegmentation:timestampInMilliseconds:error:) 方法與圖片分段器處理每個輸入影格 Image Segmenter 可在專屬序列以非同步方式叫用這個方法 調度佇列。為了在使用者介面顯示結果,請調度 結果傳送到主要佇列。如果 圖片區隔工具工作忙碌時,系統會呼叫 segmentAsync 函式 處理另一個影格時,Image Segmenter 會忽略新的輸入框。

處理及顯示結果

在執行推論時,Image Segmenter 工作會傳回 ImageSegmenterResult 物件,其中包含區隔工作的結果。當內容 輸出內容取決於您在 已設定 執行工作

下圖顯示特定類別的工作輸出圖表 值遮罩。類別遮罩範圍是 [0, 255],每個像素值 代表模型輸出的獲勝類別索引。優勝獎項 索引是模型能辨識的類別分數最高。

原始圖片和類別遮罩輸出內容。來源映像檔 2012 年 Pascal VOC 資料集。

「圖片區隔」程式碼範例示範如何顯示圖片區隔 結果,請參閱程式碼 示例