WWDC Quick Look 💓 By SwiftGGTeam
Lift subjects from images in your app

Lift subjects from images in your app

元の動画を見る

ハイライト

iOS 17 および macOS Sonoma はシステムレベルの画像主題抽出機能をオープンし、VisionKit はすぐに使用できるインタラクティブなカットアウト UI を提供し、Vision フレームワークは基礎となるセグメンテーション マスクを提供します。どちらも Core Image と組み合わせて、複雑な画像特殊効果パイプラインを実装できます。

主要内容

iOS 16では、フォトアルバム内の写真を長押しすると被写体を引き出すことができます。この機能は開発者に公開されました。 2 つのパスを選択できます。VisionKit がシステムレベルの対話に素早くアクセスするか、Vision フレームワークがカスタマイズされた処理のために基礎となるセグメンテーション データを取得します。

VisionKit: 数行のコードでシステムレベルのカットアウトインタラクションを取得します

(02:28) VisionKit の処理はプロセス外で実行され、メインスレッドにほとんど影響を与えませんが、画像の解像度には制限があります。

iOSで作成ImageAnalysisInteractionそして画像ビューに追加します:

let interaction = ImageAnalysisInteraction()
interaction.preferredInteractionTypes = .imageSubject
imageView.addInteraction(interaction)

macOSでの対応ImageAnalysisOverlayView

let overlayView = ImageAnalysisOverlayView()
overlayView.preferredInteractionTypes = .imageSubject
imageContainerView.addSubview(overlayView)

キーポイント:

  • .automaticカットアウト、ライブ テキストおよびデータ検出器、システム動作の完全なミラーリングが含まれます -.imageSubjectカットアウトのみを有効にし、テキストの対話を必要としないシナリオに適しています。
  • サポートUIImageView以外のビュー

対象データへのプログラムによるアクセス

(03:27) UI インタラクションに加えて、VisionKit は主題情報を取得するコードもサポートしています。

let analyzer = ImageAnalyzer()
let analysis = try await analyzer.analyze(image, configuration: .init([.imageSubject]))

// 获取所有主体
let allSubjects = analysis.subjects

// 按坐标查询主体
if let subject = try await analysis.subject(at: tapPoint) {
    // 用户点击的位置有主体
}

// 获取高亮(选中)的主体
let highlighted = analysis.highlightedSubjects

// 生成包含指定主体的图像
let compositeImage = try await analysis.image(for: highlighted)

キーポイント:

  • Subject構造には以下が含まれますimage(被写体画像)とbounds(境界ボックス) -subject(at:)座標に本体がないことを示すには nil を返します -highlightedSubjects読み取りおよび書き込み可能、選択されたステータスのコード制御をサポート -image(for:)複数の被写体を背景が透明な 1 つの画像に結合します

ビジョン: 低レベルのセグメンテーション API とコア イメージの組み合わせ

(07:32) ビジョン フレームワークVNGenerateForegroundInstanceMaskRequest下位レベルの制御機能を提供します。処理はインプロセスで実行され、解像度の制限はありません。

VisionKitとの比較:

特長ビジョンキットビジョン
UI組み込みのシステムレベルの対話なし。自分で構築する必要があります。
処理場所アウトプロセス進行中
解像度の制限はいいいえ
該当するシナリオクイックアクセス高度な画像処理パイプライン

(08:30) Vision のセグメンテーションはクラスに依存しません。人物だけを扱うポートレートのセグメンテーションとは異なり、被写体の抽出では、建物、食べ物、乗り物など、あらゆる前景オブジェクトをセグメント化できます。

詳細

視覚対象抽出完了プロセス

(09:50) Vision のリクエストは標準的なパターンに従います。

import Vision
import CoreImage

// 1. 创建请求
let request = VNGenerateForegroundInstanceMaskRequest()

// 2. 创建请求处理器
let handler = VNImageRequestHandler(cgImage: sourceCGImage)

// 3. 在后台队列执行
DispatchQueue.global(qos: .userInitiated).async {
    try? handler.perform([request])
    
    guard let observation = request.results?.first else { return }
    
    // 4. 获取所有前景实例的掩码
    let mask = try? observation.generateScaledMask(
        for: observation.allInstances,
        from: handler,
        croppedToInstancesExtent: false
    )
}

キーポイント:

  • 被写体の検出は計算負荷の高いタスクであるため、バックグラウンド スレッドで実行する必要があります。 -results配列には観測値が 1 つだけあり、すべてのインスタンス情報が含まれています。 -allInstancesすべての前景インスタンスのインデックスを含むリストを返します。IndexSet、背景インデックス 0 は含まれません

インスタンスマスクとクリックテスト

(09:14) Vision はピクセルレベルのインスタンス マスクを提供します。各ピクセル値はインスタンス インデックスに対応します。0 は背景、1、2、3… は前景インスタンスです。インデックスの順序は保証されていませんが、連続した番号が付けられます。

クリックテストの実装:

func instanceAtPoint(_ point: CGPoint, instanceMask: VNPixelBufferObservation) -> IndexSet {
    // point 已归一化到 [0, 1],原点在左上角(UIKit 坐标系)
    let pixelX = Int(point.x * CGFloat(instanceMask.width))
    let pixelY = Int(point.y * CGFloat(instanceMask.height))
    
    instanceMask.lockPixelBuffer()
    defer { instanceMask.unlockPixelBuffer() }
    
    let bytesPerRow = CVPixelBufferGetBytesPerRow(instanceMask.pixelBuffer)
    let baseAddress = CVPixelBufferGetBaseAddress(instanceMask.pixelBuffer)!
    
    // 单通道 UInt8,计算像素偏移
    let offset = pixelY * bytesPerRow + pixelX
    let label = baseAddress.load(fromByteOffset: offset, as: UInt8.self)
    
    // 0 是背景,返回所有实例;否则返回选中的单个实例
    return label == 0 ? observation.allInstances : IndexSet(integer: Int(label))
}

キーポイント:

  • ピクセル バッファーの行にはパディングが含まれる場合があり、これを使用する必要があります。bytesPerRowオフセットの計算
  • ピクセルバッファにアクセスする前に必要ですlockPixelBuffer()、終了後unlockPixelBuffer()- 座標系は UIKit と一致しており、左上隅が原点であり、追加の変換は必要ありません

Core Image と組み合わせて特殊効果を実現

(12:23) Vision によって出力されたマスクは、Core Image で直接使用できます。CIBlendWithMaskフィルター:

func applyEffect(
    sourceImage: CIImage,
    mask: CIImage,
    backgroundImage: CIImage,
    effect: Effect
) -> CIImage {
    // 对背景应用特效(如暗化、模糊)
    let processedBackground = applyBackgroundEffect(backgroundImage, effect: effect)
    
    // 使用 CIBlendWithMask 将主体合成到新背景
    let blendFilter = CIFilter.blendWithMask()
    blendFilter.inputImage = sourceImage
    blendFilter.backgroundImage = processedBackground
    blendFilter.maskImage = mask
    
    return blendFilter.outputImage!
}

キーポイント:

  • Vision は SDR 画像を出力し、Core Image 処理は HDR 情報を保存します -croppedToInstancesExtent: false合成を容易にするために出力を入力と同じ解像度に保つ -croppedToInstancesExtent: true被写体を包み込むようにトリミングされた画像を出力します

スピーカーの特殊効果パイプライン

(13:48) 講演者は、完全な特殊効果アプリをデモンストレーションしました。プロセスは次のとおりです。

  1. ソース画像に対して Vision 主題抽出リクエストを実行します。
  2. オプション: ユーザーがクリックして特定のインスタンスを選択します
  3. 選択したインスタンスのマスクを生成します
  4. 選択した特殊効果を背景に適用します (暗くする、ぼかし、ハローなど)。
  5. Core Image を使用して、処理された背景に被写体を合成します。

ハロー効果を実現するためのテクニック: 真っ白な画像を本体として使用し、黒を背景として使用し、最初にそれをぼかしてから元のマスクと組み合わせて、本体の周囲に光る効果を生成します。

重要ポイント

1.パズル・シールアプリ本体をドラッグ&ドロップ

  • 機能: ユーザーは写真から任意のオブジェクトを切り取り、ドラッグしてパズルのピースやステッカーとして使用できます。
  • 実行する価値がある理由: VisionKitImageAnalysisInteractionわずか数行のコードでシステムレベルのカットアウトインタラクションを実現し、カスタムモデルをトレーニングする必要はありません
  • 開始方法: 与えるUIImageViewに追加ImageAnalysisInteraction、設定.imageSubject、ハンドルドラッグジェスチャ配信Subject.image

2.写真編集アプリのワンクリックで背景を置換

  • 内容: ユーザーが写真を選択すると、自動的に被写体が抽出され、背景 (単色、グラデーション、カスタム画像) が置き換えられます。
  • 実行する価値がある理由: Vision のVNGenerateForegroundInstanceMaskRequest出力は Core Image とシームレスに行われ、HDR 情報が保持されます
  • 開始方法: ビジョンリクエストでマスクを入手 →CIBlendWithMask新しい背景を合成 → 特定のインスタンスのクリック選択をサポート

3. e コマース アプリ用に製品の白い背景画像を生成します

  • 対処方法: ユーザーが製品写真をアップロードすると、背景が自動的に削除され、白い背景の製品画像が生成されます。
  • 実行する価値がある理由: 件名の抽出はカテゴリに依存しません。衣服、靴、家具はすべてセグメント化できるため、手動で切り抜く必要がなくなります。
  • 開始方法: VisionKitImageAnalyzer非同期解析、設定.imageSubject被写体画像を取得して白背景を合成する設定

4.ソーシャルアプリ向けARステッカー制作

  • 内容: 写真から被写体を抽出し、メッセージ ステッカーや AR シーン用の透過 PNG を生成します
  • 実行する価値がある理由:image(for:)このメソッドは背景が透明な合成画像を直接出力するため、すぐに使用できる形式です。
  • 開始方法: ユーザーが写真を選択 →ImageAnalyzer.analyze()→ 件名を選択 →analysis.image(for: subjects)→ PNGとして保存

関連セッション

コメント

GitHub Issues · utterances