ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
アプリ内の画像からの被写体の切り抜き
アプリで、画像の背景から被写体を簡単に切り抜く方法を紹介します。VisionKitを使用して、メインの被写体を切り抜いたり、指定したポイントで被写体にアクセスする方法を学びます。また、Visionを使用して被写体を切り抜き、Core Imageのような下位レベルのフレームワークと組み合わせて、楽しい画像エフェクトやより複雑な合成パイプラインを作成する方法も紹介します。 VisionKitの最新アップデートについては、「What's new in VisionKit」を、画像内の人物セグメンテーションについてはWWDC23の 「Explore 3D body pose and person segmentation in Vision」をご覧ください。
リソース
関連ビデオ
WWDC23
-
ダウンロード
♪ ♪
こんにちは Lizzyです AppleでVisionKitに取り組む エンジニアです アプリに 「被写体切り抜き」を 取り入れる方法をお話しします iOS 16から導入された 被写体切り抜きでは 画像の被写体を選択し 切り抜き 共有できるようになりました まず 被写体切り抜きとは何か説明します 続いて 新しいVisionKit APIを使った 被写体切り抜きの追加方法を話します 最後に 同僚のSaumitroが 新しいVision APIについて 詳しく説明します
被写体とは何でしょう? 被写体とは 写真の前景となる物のことです 人やペットとは限りません 建物や料理の皿 靴など何でもいいのです
画像はこの3杯のコーヒーのように 複数の被写体を持つことができます 重要なのは被写体は必ずしも 個別の物体ではないということです この例では 男性と犬が一緒に写ることで 一つのまとまった被写体となっています ではどうやって アプリに落とし込むのか アプリに被写体切り抜きを追加できる 2つのAPIがあります VisionKit そしてVisionです VisionKitを使えば システム内蔵のような 被写体切り抜きの動作を 今すぐ簡単に採用できます 被写体切り抜きのUIを 数行のコードで簡単に再現できます VisionKitでは被写体に関する 基本情報も公開しているので 画像の被写体との新しい インタラクションを提供できます
すべてアウトオブプロセスで行われるため パフォーマンス上の利点はありますが 画像サイズは制限されることになります Visionは下位のフレームワークであり 即時利用できるUIはありません ビューに縛られないので より柔軟な対応が可能です
画像解析はインプロセスで行われ VisionKitのような 画像解像度の制限はありません 最後にこのAPIは CoreImageを使用するような より高度な画像編集パイプラインの 一部になり得ます VisionKitの被写体切り抜きAPIから 見ていきましょう VisionKitで 被写体切り抜きを追加するには ImageAnalysisInteractionを 初期化し 画像を含むビューに 追加するだけです UIImageViewでも そうでなくても構いません 非常にシンプルです これであなたの画像でシステム被写体の 切り抜きインタラクションができます 同様にmacOSでは ImageAnalysisOverlayViewを作成し 画像を含むNSViewの サブビューとして追加します ImageAnalysisInteractionまたは ImageAnalysisOverlayViewの 優先インタラクションタイプを設定して サポートするVisionKitインタラクションの 種類が選択できます デフォルトのインタラクションタイプは .automaticで システムの動作を反映したものです 被写体切り抜き ライブテキスト データディテクタが必要な場合に使用します テキストをインタラクティブに 表示したくない場合のために 新しいimageSubjectタイプは 被写体切り抜きのみ含みます これらのUIインタラクションに加え VisionKitでは ImageAnalysisを使用して プログラム的に画像の被写体に アクセスができます 画像解析を行うには ImageAnalyzerを作成し analyze関数を呼び出すだけです 希望する画像とアナライザーの コンフィギュレーションを入力します ImageAnalysisの subjectsプロパティを使用すると 画像すべての被写体のリストに 非同期でアクセスできます これは画像とその境界を含む 新しいSubject構造体を使用します highlighted subjectsプロパティは ハイライトされた被写体のセットを返します この例では下の2つの被写体が ハイライトされています ユーザーは被写体を長押しして ハイライト表示できますが コードで設定した highlightedSubjectsを更新して 選択状態を変更することも可能です asyncのsubject(at:)メソッドで ポイントごとに調べられます この例ではここをタップすると 真ん中の被写体を返します 被写体が存在しない場合 このメソッドはnilを返します 最後に 2つの方法で 被写体画像を生成できます 被写体が1つの場合は 被写体の イメージプロパティにアクセスするだけです 複数の被写体からなる 画像が必要な場合は async image(for:)メソッドを使用し 含みたい被写体を渡します この例では 下の2つの 被写体のみの画像が欲しい場合 このメソッドを使用して この画像を生成します 実演でご覧ください パズルアプリに取り組みます パズルのピースをドラッグしたいのですが まだどれも持ち上げることができません 解決しましょう まずこの画像で被写体切り抜きの インタラクションを有効にして ピースのインタラクションを可能にします ImageAnalysisInteractionを作成し― ビューに追加するだけで可能になります
ここではimageSubject インタラクションタイプを使用します ライブテキストを含める必要がないためです
いいですね! パズルのピースを選択して 操作できるようになりました この画像は 一切前処理をしていません 被写体を切り抜いただけです パズルのピースをドロップして 処理するコードを追加し 所定の位置に調整するようにしました
かなり良くなりましたが もっと魅力的な アプリにしましょう パズルのピースにカーソルを合わせると その下にドロップシャドウが表示されて 立体感が出るようにしていきます すでにホバー ジェスチャハンドラがあるので あとは影をつけるだけです 画像編集が簡単にできないため 代わりに画像を重ね合わせていきます imageAnalysis.subject(at point:)を 呼び出して 被写体にカーソルが 合っていることを確認します addShadow(for subject:)という メソッドがあり 被写体画像のコピーを挿入してグレーにし 元の被写体の位置から少しずらします 影の上に被写体像のコピーを乗せて 立体的に見えるようにします ホバーポイントが被写体と 交差していない場合は 影を消去します
試してみましょう
いいですね ピースにカーソルを合わせると 影の効果が出ます
VisionKitによって 自分のアプリで 被写体切り抜きを設定し 数行のコードで 被写体の効果を追加できました
次は同僚のSaumitroが 新しいVision APIと アプリへの統合方法についてお話します ありがとう Lizzy! 私 Saumitroは Vision Teamのエンジニアです VisionKitのAPIは 被写体切り抜きを始める最も簡単な方法です より高度な機能を必要とする アプリには Visionが対応します 被写体切り抜きはサリエンシーや 人物セグメンテーションなどのVisionの 既存のセグメンテーションAPIの コレクションに加わります それぞれの強みを確認し 被写体切り抜きが どのようにフィットするか見てみましょう 注目度や物体感のような サリエンシーのリクエストは 粗い 領域ベースの分析に最適です 生成されたサリエンシーマップは かなり低い解像度であるため セグメンテーションには適さないことに 注意してください 代わりに 顕著な領域を画像の 自動切り抜きなどのタスクに使用できます 人物セグメンテーションAPIは シーン内の人物の詳細な セグメンテーションマスクの作成に優れます 特に人のセグメンテーションに 重点を置きたい場合に使用します 新しい人物インスタンスセグメンテーション APIは シーン内の各人物に 個別のマスクを提供することで さらに進化しています 詳しくは人物セグメンテーションに関する セッションをご覧ください 人物セグメンテーションとは対照的に 新しく導入された被写体切り抜きAPIは 「クラスにとらわれない」ものです 前景のオブジェクトは その意味的なクラスに関係なく 潜在的に区分される可能性があります 例えばこの画像では 人物だけでなく 車も切り取られていることに 注目してください キーコンセプトの一部をご紹介します 入力画像から始めます 被写体切り抜き要求では この画像を処理し 同じ解像度でソフトセグメンテーション マスクを作成します このマスクを元画像に適用すると マスクされた画像になります 分割された各オブジェクトは インスタンスと呼ばれます Visionはこれらの インスタンスに関する ピクセル単位の情報を提供します インスタンスマスクはソース画像内の ピクセルをインスタンスインデックスに マッピングします ゼロインデックスは背景用に 確保されたのち 各前景インスタンスが 1から順次ラベル付けされます 連続的にラベル付けされているだけであり IDの順序は保証されていません これらのインデックスを使用して ソース画像内の前景オブジェクトの サブセットをセグメント化できます インタラクティブなアプリを 設計している場合 インスタンスマスクは ヒットテストにも有効です この2つの作業を どのように行うか実演します APIを見ていきましょう 被写体切り抜きは Visionでおなじみの 画像系リクエストのパターンを 踏襲しています まず 前景インスタンスの マスクリクエストをインスタンス化し 入力画像の画像リクエストハンドラを インスタンス化します それからリクエストを実行します 内部では Visionが画像を解析して 被写体を特定します Appleハードウェアの効率的な 活用のために最適化されていますが リソースを消費するタスクであるため 最善は UIをブロックしないように バックグラウンドスレッドに 延期することです 一般的には このステップを 別のDispatchQueueで 非同期的に実行します 入力画像から1つ以上の被写体が 検出された場合は 結果として配列には 1つの観測結果が入力されます ここからは マスクや分割された画像に対し 観測をクエリできます どのインスタンスを分割し どのように結果を切り取るかを制御する― 2つのパラメータを詳しく見ていきます instancesパラメータは 最終的なセグメント化された― 画像やマスクから抽出されるオブジェクトを 制御するIndexSetです 例として この画像には 背景インスタンスを除いた― 前景インスタンスが2つ含まれています 検出されたすべての前景インスタンスの セグメント化は 一般的な操作なので Visionはすべての 前景インスタンスインデックスを含む― IndexSetを返す便利な allInstancesプロパティを提供します この画像の場合 インデックス1と2が含まれます 背景インスタンス0は含まれません インデックスのサブセットだけを 提供することもできます これはインスタンス1のみ こちらはインスタンス2のみです 最終的にマスクされた画像の 切り取り方もコントロールできます このパラメータをfalseに設定すると 出力画像の解像度が 入力画像に一致します これは例えば 下流の合成操作のために 分割されたオブジェクトの 相対的な位置を保持する際に便利です trueに設定すると 選択されたインスタンスに対し タイトなクロップを行います ここまでの例では 完全にマスクされた 画像出力を扱ってきました しかし マスク効果の適用など 一部の操作では 分割マスクだけを使用した作業の方が 便利な場合があります これらマスクは createScaledMaskメソッドを 呼び出すことで生成できます パラメータの動作は従来通りです 出力は ソフトセグメンテーションマスクを含む 単一チャネルの 浮動小数点ピクセルバッファです 今生成したマスクは CoreImageでの使用に適します Visionは VisionKitと同様に SDR出力を生成します ただし CoreImageで マスキングを実行すると 入力の高ダイナミックレンジが保持されます 詳しくは アプリへの HDRの追加に関する― セッションをご覧ください マスキングを行う方法の 一例として CIBlendWithMask フィルタを使用します まず マスクが必要な元画像から始めます これは通常 Visionに渡したものと 同じ画像になります VisionのcreateScaledMaskの 呼び出しから得られたマスクです 最後に 被写体が重ねられる― 新しい背景画像です 空の画像を使うと 背景が透明になります 結果を新しい背景の上に 合成する予定の場合は ここで直接渡すことができます 以上でほぼカバーできました 出力はHDR保存されたマスクと 合成された画像になります すべて組み合わせてクールな被写体切り抜き 視覚効果アプリを作りましょう 背景を削除して その背後の景色を見せたり 別のものに置き換えたりすることができます そのうえで プリセットされた いずれかのエフェクトが適用できます そして 選択した背景と エフェクトが合成されます 前景のインスタンスをタップして 選択的に切り抜くこともできます アプリ制作の概要を見てみましょう アプリの中核はUIからの入力を 受け取り 最終的な出力の生成に必要な すべての作業を実行する エフェクトパイプラインに依存しています まず 元画像に被写体切り抜きを行います オプションのタップで 個々のインスタンスが選択できます 出来上がったマスクは ソース画像に適用されます 最後に選択した背景と視覚効果を 適用して合成し 最終的な出力画像を作成します この最後の2つのステップは CoreImageで実現します トップレベルの機能は 入力画像や 選択された背景画像と効果 そして インスタンスの1つを選択するユーザーの タップ位置の可能性を受け取ります ここでのEffectタイプは プリセットのための単なる列挙型です 出力は UIで表示する準備が整った 最終的な合成画像になります この作業は2つのステージに分けられます まず 選択したインスタンスの 被写体マスクを生成します 次に そのマスクを使って 選択したエフェクトを適用します 第一段階から始めましょう このステージへの入力は ソース画像とオプションのタップ位置です ここでは Visionリクエストを実行し マスクを返すだけのコードの ほとんどに すでに遭遇しています 面白いのはこの行で ラベルマスクを用いて タップ位置をインデックスの集合に マッピングしています 詳しく見ていきましょう タップがない場合は すべての インスタンスの使用をデフォルトとします タップ位置とインスタンスマスクの ピクセルを対応させたいとします ここで関係する情報は2つあります まず UIはタップ位置を 0, 1に正規化してから渡します ディスプレイの解像度や スケーリングファクターなど 細かい点を気にする必要がなくて便利です 次に 左上を原点とするUIKitの デフォルトの座標系を使用します これは ピクセルバッファの 画像空間座標とアラインされます よって この既存の Visionヘルパー関数を使って 変換できます これで タップしたインスタンスラベルの 調査に必要な情報はすべて揃いました これにはピクセルバッファのデータに 直接アクセスすることが必要ですが その方法を次に紹介します ラベルを手に入れたら ゼロかどうか確認します ゼロラベルは ユーザーが背景画素を タップしたことを示します この場合 すべてのインスタンスを 選択することに戻ります そうでなければ 選択されたラベルだけを 含むシングルトンセットを返します このビットコードではインスタンスラベルの ルックアップの実行方法を記載しています 他のピクセルバッファと同様 そのデータに アクセスする前にロックする必要があります 読み取り専用のアクセスで十分です ピクセルバッファの行は アライメントのために パディングされることがあるので ピクセルの バイトオフセットの最も堅牢な計算方法は bytesPerRow値の使用です instanceMaskは 単一チャンネルのUInt8バッファなので それ以上のスケーリングを 気にする必要はありません インスタンスマスクからの読み込みが 終了したのでバッファのロックを解除します 包括的に終了し 選択したインスタンスが 分離されたマスクができました これでエフェクトの適用に 移ることができます ここではまず 選択したエフェクトを 背景に適用します 終わったら CoreImageを使って マスクされた被写体を 変形した背景の上に合成します 最初の数種類のエフェクトは 既存の CoreImageフィルタをそのまま応用した 非常にわかりやすいものです 例えば 被写体を際立たせるために 露出調整フィルターで背景を暗くしました ぼかし効果はもう少し複雑です 背景をぼかすだけでなく 選んだ被写体に ハレーションを起こしていきます ぼかす前の被写体を 白く切り抜くと効果的です これを素早く実現するには 現在の関数を再利用し 被写体には白無地の画像を渡すことです これで コンポジット用の ベースレイヤーが完成しました 最後に 先ほどのCoreImageの ブレンディングスニペットを落とし込みます これにより 切り抜かれた被写体が 新たに変換された背景上に合成されます エフェクトパイプラインの 最後のピースが揃ったことで アプリが完成しました 新しい被写体切り抜きAPIの可能性を 感じていただけましたか まとめると VisionKitは 被写体切り抜きをアプリに 取り入れる最速の方法です より高度なアプリに対しては VisionのAPIにドロップダウンできます 最後に CoreImageは 被写体切り抜きでHDR対応の 画像処理を行う際に最適です Lizzyも私も皆さんに 動画をお楽しみいただけたら幸いです 皆さんが今後何を創作されるか 非常に楽しみです ♪ ♪
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。