iOS 適用的圖片區隔指南

您可以使用圖片區隔器工作,根據預先定義的類別將圖片分割成區域,並套用背景模糊處理等視覺效果。本說明文件將說明如何在 iOS 應用程式中使用圖像分割器。

您可以在 GitHub 上找到這些操作說明中所述的程式碼範例。

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

程式碼範例

MediaPipe Tasks 程式碼範例包含適用於 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 設定指南

重要元件

以下檔案包含圖片區隔器範例應用程式的重要程式碼:

設定

本節將說明設定開發環境和程式碼專案以使用圖片分割器的關鍵步驟。如要進一步瞭解如何設定開發環境,以便使用 MediaPipe 工作,包括平台版本需求,請參閱 iOS 專用設定指南

依附元件

圖片分割器會使用 MediaPipeTasksVision 程式庫,必須使用 CocoaPods 安裝。這個程式庫與 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 屬性指定應用程式套件中的模型路徑。如需程式碼範例,請參閱下一節。

建立工作

您可以呼叫其中一個初始化器,建立 ImageSegmenter 工作。ImageSegmenter(options:) 初始化器可接受設定選項的值。

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

Image Segmenter 工作支援 3 種輸入資料類型:靜態圖片、影片檔案和直播影片串流。根據預設,ImageSegmenter(modelPath:) 會初始化靜態圖片的工作。如要初始化工作以處理影片檔案或直播影片串流,請使用 ImageSegmenter(options:) 指定影片或即時串流執行模式。直播模式也需要額外的 imageSegmenterLiveStreamDelegate 設定選項,才能讓圖片區隔器以非同步方式將圖片區隔結果傳送至委派函。

選擇執行模式對應的分頁,瞭解如何建立工作並執行推論。

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];
    

圖片區塊判斷器範例程式碼實作可讓使用者切換處理模式。這個方法會使建立工作程式的程式碼更加複雜,可能不適合您的用途。

設定選項

這項工作有下列 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 設定選項,才能讓圖片區隔器以非同步方式提供圖片區隔結果。委派必須實作 imageSegmenter(_:didFinishSegmentation:timestampInMilliseconds:error:) 方法,Image Segmenter 會在處理每個影格執行區隔的結果後呼叫該方法。

選項名稱 說明 值範圍 預設值
imageSegmenterLiveStreamDelegate 讓圖片區隔器在直播模式下,以非同步方式接收圖片區隔結果。將例項設為此屬性的類別必須實作 imageSegmenter(_:didFinishSegmentation:timestampInMilliseconds:error:) 方法。 不適用 未設定

準備資料

您必須先將輸入圖片或影格轉換為 MPImage 物件,才能將其傳遞至圖片區隔器。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];
    

這個範例會使用預設的 UIImage.Orientation.Up 方向初始化 MPImage。您可以使用任何支援的 UIImage.Orientation 值初始化 MPImage。圖片區隔器不支援鏡像方向,例如 .upMirrored.downMirrored.leftMirrored.rightMirrored

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

CVPixelBuffer

CVPixelBuffer 格式非常適合用於產生影格,並使用 iOS CoreImage 架構進行處理的應用程式。

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

  • 圖片:如果應用程式在使用 iOS 的 CoreImage 架構進行一些處理後產生 CVPixelBuffer 圖片,則可在圖片執行模式下傳送至圖片區塊分割器。

  • 影片:可將影片影格轉換為 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 AVCaptureVideoDataOutput 會以 CMSampleBuffer 格式,以非同步方式傳送 iOS 攝影機的即時影格。

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 開發人員說明文件

執行工作

如要執行 Image Segmenter,請使用已指派的執行模式專用的 segment() 方法:

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

以下程式碼範例顯示瞭如何在不同的執行模式中執行 Image Segmenter:

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:)

注意事項:

  • 在影片模式或直播模式下執行時,您也必須向圖片區隔器工作提供輸入影格時間戳記。

  • 在圖片或影片模式下執行時,圖片分割器工作會阻斷目前的執行緒,直到處理完輸入圖片或影格為止。為避免阻斷目前執行緒,請使用 iOS DispatchNSOperation 架構,在背景執行緒中執行處理作業。

  • 在直播模式下執行時,圖片分割器工作會立即傳回,且不會封鎖目前的執行緒。處理每個輸入影格後,它就會透過圖片分段器叫用 imageSegmenter(_:didFinishSegmentation:timestampInMilliseconds:error:) 方法。ImageSegmenter 會在專屬的逐一調度佇列上以非同步方式叫用此方法。如要在使用者介面上顯示結果,請在處理結果後將結果調度至主佇列。如果在圖片區塊分割器工作忙於處理其他影格時呼叫 segmentAsync 函式,圖片區塊分割器會忽略新的輸入影格。

處理及顯示結果

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

下圖顯示類別值遮罩的工作輸出結果視覺化資訊。類別遮罩範圍是 [0, 255],每個像素值都代表模型輸出內容的勝出類別索引。勝出的類別索引是模型可辨識的類別中分數最高的類別。

原始圖片和類別遮罩輸出結果。Pascal VOC 2012 資料集的來源映像檔。

圖片區塊分割器範例程式碼示範如何顯示圖片區塊分割器結果,請參閱程式碼範例瞭解詳情。