Lesson 3A.5 Image Recognition in ARKit
ARKitフレームワークは、水平面と垂直面の検出を超えています。最新バージョンでは、アプリで指定された画像を見つけることができるようになりました。フレームワークに特定の画像セットを検索するように指示した後、アプリは平面検出で学んだのと同じアプローチを使用して検出に応答できます。
あなたが学ぶこと
画像検出を有効にする方法
物理的な世界でどの画像を見つけるかを指定する方法
画像の発見に反応する方法
SceneKitオブジェクトで簡単なアニメーションを実行する方法
Vocabulary
AR Resource Group
ARImageAnchor
ARReferenceImage
SCNAction
p.496
プロジェクトのセットアップ
拡張現実アプリのテンプレートを使用して、新しいプロジェクトを作成し、「ARImageFinder」という名前を付けます。船をシーンに追加するコードの一部と、ship.scnファイルを削除します。
画像の追加
ARKitが検出する画像はどれですか?Xcodeプロジェクトのアセットカタログにそれらをすべて追加する必要があります。Assets.xcassetsフォルダを開き、アセットリストの下部にあるプラス(+)ボタンをクリックしてオプションのメニューを表示します。新しいARリソースグループを選択すると、ARリソースというフォルダが作成されます。
p.497
友達の写真、本の表紙、有名な絵画など、検出したい画像をいくつか選択してください。作成したばかりのアセットフォルダに画像をドラッグします。画像を選択する際には、次の点に留意してください。
画像には透過的なデータを含めるべきではありません。これにより、アプリをビルドして実行するとエラーが返されます。
高品質の画像は、物理的な世界で発見する可能性を高めます(アプリのサイズが大きくなりますが)。
コントラストの高い画像(明暗の違いが大きい)も、ARKitの検出を容易にします。
高品質でコントラストの高い画像が付属すると、ARKitは各画像の実際のサイズが必要になります。この情報により、ARKitは画像とカメラの間の距離を推定し、シーンに追加された3Dオブジェクトを正しく配置することができます。
「AR Resources」フォルダで画像を選択し、「Attributes」インスペクタを開きます。画像の幅と高さを入力し、含めた他のアセットを繰り返します。(1つの値を入力するだけでよいことに注意してください。幅を変更すると、高さは自動的に調整され、正しいアスペクト比が保証されます。)指定した幅と高さの値に合わせて、正しい測定単位(インチ、メートルなど)を選択してください。
p.498
画像検出の有効化
ARWorldTrackingConfigurationには、アプリが発見したい画像のコレクションである検出画像と呼ばれるプロパティがあります。通常、Assetsts.xcassetsフォルダから画像を見つけると、UIImage(named:)イニシャライザを使用してUIImageを作成します。しかし、検出画像にはARReferenceImageオブジェクトのコレクションが必要なので、別の方法を使用する必要があります。
何をすべきか?いつものように、解決策を探すのに最適な場所はXcodeのドキュメントにあります。ARReferenceImageドキュメントには、クラスメソッドreferenceImages(inGroupNamed:, bundle:)をリストするリファレンス画像の読み込みに関するセクションが含まれています。
最初の引数では、Assetsts.xcassetsでフォルダの文字列名を指定します。このプロジェクトでは「ARリソース」です。(画像のコレクションが別のライブラリまたはフレームワークにある場合は、2番目の引数で場所を指定します。この場合、パラメータはnilです。)このメソッドはオプションのコレクションを返しますが、検出画像にはオプション以外のコレクションが必要です。オプションを強制的にラップから外すには、感嘆符を追加できます(!)をメソッドの最後にします。フォルダが見つからないとアプリがクラッシュするので、最初のパラメータの文字列がフォルダ名と正確に一致していることを確認してください。
p.499
画像検出への対応
検出された平面に応答するレンダラー(_:, didAdd:, for:)メソッドをすでに学習しました。画像が検出された場合は、同じ方法を使用します。ただし、予想通り、アンカーはARPlaneAnchorではなくARImageAnchor型になります。型検査を使用してアンカーのタイプを確認できます。
この方法が扱いにくくならないでください。アプリが平面と画像の両方を検出する必要がある場合は、メソッドを小さなチャンクに分割するのが賢明です。アンカーのタイプが確認されたら、ノードとアンカーを他のメソッドに渡すだけです。(planeDetectionプロパティを使用して設定で有効にしない限り、平面検出は発生しないことに注意してください。)
p.500
If-elseステートメントのチェーンを使用するのではなく、switchステートメントを使用することを考えるかもしれません。これは、コードの読みやすさを向上させるよりエレガントなアプローチです。必要なパターンマッチングの形式は、指定された型に関心のある値をキャストしようとするcase letです。キャストが成功すると、提供された変数は以下の節の範囲内で使用できます。(追加の利点として、スイッチを使用するとエラーの処理が容易になります。タイプのリストが長ければ長いほど、このアプローチの利点がわかります。
p.501
画像のカバー
平面検出と同様に、画像検出はアプリにいくつかの興味深い機能を与えることができます。たとえば、検出された画像からある程度離れた場所に配置された3Dモデルを読み込むことができます。または、画像に関連する新しい情報(作成された日付など)をUIViewControllerに表示することもできます。
レッスン3で水平面をカバーした方法と同様に、画像の上部にSCNPレーンを配置したいとしましょう。まず、物理ワールドの画像のサイズを取得する必要があります(画像をAssetsts.xcassetsフォルダに移動したときに属性インスペクタですでに指定しました)。
すべてのARImageAnchorには、検出画像プロパティを割り当てたときに使用されるのと同じタイプのARReferenceImageタイプのreferenceImageプロパティがあります。このオブジェクトには、SCNPレーンのサイズに使用できる幅と高さを含む物理サイズがあります。そこから、平面を使用してSCNNodeを構築し、平面の下に画像が表示されるように不透明度をダイヤルダウンし、ノードをシーンに追加します。
p.502 アプリをビルドして実行します。結果はあなたが期待していたものですか?そうでない場合は、レンダラー(_:, didAdd:, for:)メソッドが最初に呼び出されていることを確認してください。画像を検出できない場合は、以下を確認してください。
Xcodeコンソールデバッガが、提供した画像に関連する警告を出していないことを確認してください。
さまざまな種類の用紙に画像を印刷してみてください。光沢紙に印刷すると特定の画像がどのように見えるか、および紙の反射品質が画像転送にどのように干渉するかに注目してください。画像がコンピュータまたはデバイスの画面に表示される場合も同じ干渉が発生する可能性があります。
画像が検出された場合、平面が画像に垂直に配置されていることがわかります。レッスン2から、ARKitの向きに合わせてSCNPレーンをx軸に沿って90度回転させる必要があることを思い出してください。これは簡単な修正です。
p.503
画像の再発見
ARアプリを開発するにつれて、おそらく画像に関する情報を表示し、しばらく時間が経過した後に画像を閉じて、ユーザーがシーン内の画像の場所に戻ったときに画像を再発見できるようにする状況を見つけるでしょう。例えば、博物館の絵画に関する情報を表示するARアプリを想像してみてください。フローティングシェイプや3Dテキストが多すぎるシーンの乱雑さを軽減するには、指定された期間後にこれらのSceneKitオブジェクトを削除できます。
セッションの寿命を通して維持および追跡されるARPlaneAnchorとは異なり、ARImageAnchorは短期間だけ追跡されます。画像が閉じられ、その後再発見されると、レンダラー(_:, didAdd:, for:)メソッドが再び呼び出され、ノードが重複します。この問題を解決するには、作成したノードのクリーンアップを行う必要があります。
UIKitでビューを削除するには、削除するビューのremoveFromSuperview()メソッドを呼び出します。SceneKitノードには、同等のメソッドremoveFromParentNode()があります。メソッドを呼び出すのは簡単ですが、いつ呼び出すべきかを実装するのは少し難しいです。これをどうやって達成しますか?
UIKitの対応物(値を返さない)とは異なり、removeFromParentNode()はSceneKitのアニメーションバージョンであるSCNActionを返します。シーケンスの使用(_:)
p503
メソッドを使用すると、コレクションに配置することで、複数のアニメーションを連鎖させることができます。ノードを削除する前に5秒待つには、seequence(_:)の呼び出しは次のようになります。
SCNAction.sequence([.wait(duration: 5.0),
.removeFromParentNode()])
アニメーションシーケンスの完全なリストについては、SCNActionのドキュメントを参照してください。たとえば、次のシーケンスを使用してSceneKitオブジェクトを5秒間表示し、2秒間ゆっくりとフェードアウトします。
アニメーションの実行を開始するには、アニメーション化するノードのrunAction(_:)メソッドを呼び出します。次のスニペットでは、必要に応じて複数のメソッドでアクションを再利用できるように、SCNActionが計算されたプロパティに配置されています。
p.504
画像の区別
シーンに画像固有のコンテンツを追加できるように、どの画像が発見されたかを知る必要がある場合はどうなりますか?各ARReferenceImageには、プロジェクトに追加した画像を区別するのに役立つnameプロパティが含まれています。画像アンカーが作成されたときにプロパティを検査します。
演習
簡単な宝探しを作ろう。プロジェクトに3つの画像を含めてください。2番目の画像は、最初の画像が検出されるまで検出できません。同様に、3番目の画像は、2番目の画像が検出されるまで検出できません。
あらかじめ構築されたSCNActionシーケンスをもっと試してみてください。アニメーションを変更してオブジェクトをフェードインし、(選択した)期間を待ってからフェードアウトします。
この記事が気に入ったらサポートをしてみませんか?