ARSCNViewビデオフィードのビジョンフレームワークによって検出されたオブジェクトの3D座標の抽出

<p data-line="1" class="code-line">この記事は <a href="https://qiita.com/advent-calendar/2024/kinto-technologies" target="_blank" rel="nofollow noopener noreferrer">KINTOテクノロジーズアドベントカレンダー2024 の21日目の記事です</a>🎅🎄</p>
<h1 id="%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB" data-line="3" class="code-line"><a class="header-anchor-link" href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB" aria-hidden="true"></a> はじめに</h1>
<p data-line="5" class="code-line">こんにちは!iOSエンジニアのViacheslavです。</p>
<p data-line="7" class="code-line">今年、私は当社の<a href="https://apps.apple.com/jp/app/kinto-unlimited/id1663164737" target="_blank" rel="nofollow noopener noreferrer">Unlimitedアプリ</a>の新機能である<a href="https://www.youtube.com/watch?v=E8zfNzuHr7g" target="blank" rel="nofollow noopener noreferrer">「これなにガイド」</a>に取り組む機会を得ました。これなにガイドは、車のダッシュボードをスマホでスキャンすることで、車のボタンやスイッチの上に仮想マーカーを表示できる拡張現実(AR)マニュアルです。特定のボタンに対応するマーカーを選択すると、機能を確認できるマニュアルページにアクセスできます。</p>
<p data-line="9" class="code-line">今日は、この機能の開発中に遭遇した課題の1つである、「画面上でVisionフレームワークによって認識された物理オブジェクトの座標を正確にキャプチャし、それらの座標をARシーン内の3D座標に変換する」という課題に対する、短くてシンプルな解決策を共有したいと思います。</p>
<p data-line="11" class="code-line">最初は些細な作業に思えたものが、予想以上に複雑であることが判明しました。いくつかのアプローチを模索し、多くの手動計算を実行した後、ようやく、単純かつ驚くほど簡潔な解決策にたどり着きました。ARKit と CoreML の統合に関する情報は比較的少ないので、始めたときに知っていればよかったと思います。では、知識ベースに追加していきましょう!</p>
<h1 id="%E3%81%84%E3%81%8F%E3%81%A4%E3%81%8B%E3%81%AE%E5%89%8D%E6%8F%90%E6%9D%A1%E4%BB%B6" data-line="13" class="code-line"><a class="header-anchor-link" href="#%E3%81%84%E3%81%8F%E3%81%A4%E3%81%8B%E3%81%AE%E5%89%8D%E6%8F%90%E6%9D%A1%E4%BB%B6" aria-hidden="true"></a> いくつかの前提条件</h1>
<p data-line="15" class="code-line">実際のコードに進む前に、作業する環境を明確に定義してみましょう。</p>
<ul data-line="17" class="code-line">
<li data-line="17" class="code-line">
<p data-line="17" class="code-line"><strong><a href="https://developer.apple.com/documentation/arkit/arscnview" target="blank" rel="nofollow noopener noreferrer">ARSCNView</a></strong>
これは、デバイスのカメラからのビデオフィードを表示し、現実世界をキャプチャして3Dオブジェクトを「ブレンド」させてARエクスペリエンスを実現するビューです。<code>ARSCNView</code>はAppleの<a href="https://developer.apple.com/documentation/arkit" target="_blank" rel="nofollow noopener noreferrer">ARKit</a>の一部で、ARシーンでの3Dオブジェクトのレンダリングを処理する<a href="https://developer.apple.com/documentation/scenekit/" target="_blank" rel="nofollow noopener noreferrer">SceneKit</a>上に構築されます。</p>
</li>
<li data-line="20" class="code-line">
<p data-line="20" class="code-line"><strong><a href="https://developer.apple.com/documentation/coreml/" target="_blank" rel="nofollow noopener noreferrer">Core ML</a> オブジェクト検出モデル</strong>
オブジェクトの座標を決定する前に、まずデバイスのカメラによって提供されるビデオ フィード フレーム内でオブジェクトを認識する必要があります。Visionフレームワークは、この目的のためにCore MLオブジェクト検出モデルを利用します。本記事では、読者がすでに使用できるモデルを持っているものと想定します。そうでない場合は、<strong>YOLOv3-Tiny</strong> など、ダウンロード可能な事前訓練済みモデルが多数あります。<a href="https://developer.apple.com/machine-learning/models/" target="blank" rel="nofollow noopener noreferrer">こちら</a>から入手できます。</p>
</li>
</ul>
<p data-line="23" class="code-line">最低限のソリューションに必要なのはこれだけです。<code>ARSCNView</code> からビデオフレームをキャプチャし、Core MLモデルを使用して<code>ARSCNView</code>ビューポート内のオブジェクトの位置を検出し、「ヒットテスト」と呼ばれる手法を適用して3D AR空間におけるオブジェクトの座標を決定します。</p>
<h1 id="arscnview%E3%81%AB%E3%81%8A%E3%81%84%E3%81%A6%E8%AA%8D%E8%AD%98%E3%81%95%E3%82%8C%E3%81%9F%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E5%BA%A7%E6%A8%99%E3%82%92%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A3%E3%81%99%E3%82%8B" data-line="25" class="code-line"><a class="header-anchor-link" href="#arscnview%E3%81%AB%E3%81%8A%E3%81%84%E3%81%A6%E8%AA%8D%E8%AD%98%E3%81%95%E3%82%8C%E3%81%9F%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E5%BA%A7%E6%A8%99%E3%82%92%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A3%E3%81%99%E3%82%8B" aria-hidden="true"></a> ARSCNViewにおいて認識されたオブジェクトの座標をキャプチャする</h1>
<p data-line="27" class="code-line">Vision を使用して認識要求を実行する場合の一般的な設定は以下の通りです。</p>
<p data-line="29" class="code-line">Core MLモデルと <code>VNCoreMLRequest</code> を初期化して、そのモデルを使用して認識を処理します。</p>
<div class="code-block-container"><pre class="language-swift"><code class="language-swift code-line" data-line="31"><span class="token keyword">let</span> vnModel <span class="token operator">=</span> <span class="token keyword">try</span><span class="token operator">!</span><span class="token class-name">VNCoreMLModel</span><span class="token punctuation">(</span><span class="token keyword">for</span><span class="token punctuation">:</span> myCoreMLModel<span class="token punctuation">)</span>
<span class="token keyword">let</span> vnRequest <span class="token operator">=</span> <span class="token class-name">VNCoreMLRequest</span><span class="token punctuation">(</span>model<span class="token punctuation">:</span> vnModel<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">[</span><span class="token keyword">weak</span> <span class="token keyword">self</span><span class="token punctuation">]</span> request<span class="token punctuation">,</span> <span class="token omit keyword"></span> <span class="token keyword">in</span>
<span class="token keyword">guard</span> <span class="token keyword">let</span> observations <span class="token operator">=</span> request<span class="token punctuation">.</span>results <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">}</span>
<span class="token comment">// Observations handling</span>
<span class="token punctuation">}</span>
request<span class="token punctuation">.</span>imageCropAndScaleOption <span class="token operator">=</span> <span class="token punctuation">.</span>centerCrop
</code></pre></div><p data-line="40" class="code-line">次に、<code>vnRequest</code> への参照を適切な場所に保存し、次の引数セットで実行できるように準備します。引数のタイプは、ビデオフィードフレームをどこからキャプチャするかによって異なります。</p>
<p data-line="42" class="code-line">私たちのシナリオでは、<code>ARSCNView</code> からフレームを渡し、<code>ARSessionDelegate</code> の <code>session(:didUpdate:)</code> メソッドにおいてフレームをキャプチャする必要があります。このデリゲートメソッドは、<code>ARSCNView</code> で表示できる新しいフレームが利用可能になるたびに呼び出されます。</p>
<div class="code-block-container"><pre class="language-swift"><code class="language-swift code-line" data-line="44"><span class="token keyword">func</span> <span class="token function-definition function">session</span><span class="token punctuation">(</span><span class="token omit keyword"></span> session<span class="token punctuation">:</span><span class="token class-name">ARSession</span><span class="token punctuation">,</span> didUpdate frame<span class="token punctuation">:</span><span class="token class-name">ARFrame</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">guard</span> <span class="token keyword">let</span> vnRequest <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> <span class="token comment">// 1</span>
<span class="token keyword">let</span> options<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token class-name">VNImageOption</span><span class="token punctuation">:</span><span class="token keyword">Any</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">.</span>cameraIntrinsics<span class="token punctuation">:</span> frame<span class="token punctuation">.</span>camera<span class="token punctuation">.</span>intrinsics<span class="token punctuation">]</span> <span class="token comment">// 2</span>
<span class="token keyword">let</span> requestHandler <span class="token operator">=</span> <span class="token class-name">VNImageRequestHandler</span><span class="token punctuation">(</span>
cvPixelBuffer<span class="token punctuation">:</span> frame<span class="token punctuation">.</span>capturedImage<span class="token punctuation">,</span> <span class="token comment">// 3</span>
orientation<span class="token punctuation">:</span> <span class="token punctuation">.</span>downMirrored<span class="token punctuation">,</span> <span class="token comment">// 4</span>
options<span class="token punctuation">:</span> options
<span class="token punctuation">)</span>
<span class="token keyword">try</span><span class="token operator">?</span> requestHandler<span class="token punctuation">.</span><span class="token function">perform</span><span class="token punctuation">(</span><span class="token punctuation">[</span>vnRequest<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token comment">// 5</span>
<span class="token punctuation">}</span>
</code></pre></div><h3 id="%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E5%88%86%E8%A7%A3%E3%81%99%E3%82%8B%EF%BC%9A" data-line="57" class="code-line"><a class="header-anchor-link" href="#%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E5%88%86%E8%A7%A3%E3%81%99%E3%82%8B%EF%BC%9A" aria-hidden="true"></a> コードを分解する:</h3>
<ol data-line="58" class="code-line">
<li data-line="58" class="code-line"><strong><code>VNCoreMLRequest</code>の参照</strong>:新しいフレームを受信すると、先ほど初期化したリクエストを実行する準備が整います。</li>
<li data-line="59" class="code-line"><strong>カメラ内部パラメータ</strong>:<code>frame.camera.intrinsics</code> は、Visionがシーンの幾何学的特性を解釈するのに役立つカメラ較正データを提供します。</li>
<li data-line="60" class="code-line"><strong>画像入力</strong>:<code>VNImageRequestHandler</code> は、ARフレームから取得された生画像データを <code>CVPixelBuffer</code> として受け入れます。</li>
<li data-line="61" class="code-line"><strong>画像の向き</strong>:<code>.downMirrored</code> 方向は、Visionのデフォルト方向と比較して、カメラフィードの座標系の反転を考慮します。</li>
<li data-line="62" class="code-line"><strong>リクエストの実行</strong>:準備されたリクエストは、リクエストハンドラーを使用して実行されます。</li>
</ol>
<p data-line="64" class="code-line">Vision にフレームを渡し始めると、オブジェクト検出の結果が <code>VNCoreMLRequest</code> 完了ハンドラー内の<code>VNRecognizedObjectObservation</code> オブジェクトの配列として返されます。これらの結果を信頼度レベルでフィルタリングしたり、その他の処理を実行したりすることもできますが、今日は認識された特定のオブジェクトの座標を抽出することに焦点を当てます。</p>
<h3 id="%E5%A2%83%E7%95%8C%E3%83%9C%E3%83%83%E3%82%AF%E3%82%B9%E5%BA%A7%E6%A8%99%E3%81%AE%E6%8A%BD%E5%87%BA" data-line="66" class="code-line"><a class="header-anchor-link" href="#%E5%A2%83%E7%95%8C%E3%83%9C%E3%83%83%E3%82%AF%E3%82%B9%E5%BA%A7%E6%A8%99%E3%81%AE%E6%8A%BD%E5%87%BA" aria-hidden="true"></a> 境界ボックス座標の抽出</h3>
<p data-line="68" class="code-line">最初は、<code>VNRecognizedObjectObservation</code> に <code>boundingBox</code> プロパティ(認識されたオブジェクトを囲む <code>CGRect</code>)があるため、これは簡単に思えるかもしれません。ただし、いくつかの複雑な問題があります。</p>
<ul data-line="69" class="code-line">
<li data-line="69" class="code-line"><code>boundingBox</code>は、物体認識モデルの入力画像に対する正規化された座標系(座標値は0から1の間)であり、それもY軸が反転しています。</li>
<li data-line="70" class="code-line">カメラフィード、Core MLモデル入力、そして<code>ARSCNView</code>ビューポートのサイズとアスペクト比はそれぞれ異なります。</li>
</ul>
<p data-line="72" class="code-line">つまり、<code>boundingBox</code> を <code>ARSCNView</code> ビューポートの座標系に変換するには、一連の座標変換と再スケーリングの手順が必要になります。これらの変換を手動で行うのは面倒で、間違いが起こりやすくなります。幸いなことに、<code>CGAffineTransform</code> を使用すると、これを処理できる非常に簡単な方法があります。方法は次のとおりです。</p>
<div class="code-block-container"><pre class="language-swift"><code class="language-swift code-line" data-line="74"><span class="token keyword">let</span> sceneView<span class="token punctuation">:</span><span class="token class-name">ARSCNView</span>
<span class="token keyword">func</span> <span class="token function-definition function">getDetectionCenter</span><span class="token punctuation">(</span>from observation<span class="token punctuation">:</span><span class="token class-name">VNRecognizedObjectObservation</span><span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">CGRect</span><span class="token operator">?</span> <span class="token punctuation">{</span>
<span class="token keyword">guard</span> <span class="token keyword">let</span> currentFrame <span class="token operator">=</span> sceneView<span class="token punctuation">.</span>session<span class="token punctuation">.</span>currentFrame <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token nil constant">nil</span> <span class="token punctuation">}</span>
<span class="token keyword">let</span> viewportSize <span class="token operator">=</span> sceneView<span class="token punctuation">.</span>frame<span class="token punctuation">.</span>size
<span class="token comment">// 1</span>
<span class="token keyword">let</span> fromCameraImageToViewTransform <span class="token operator">=</span> currentFrame<span class="token punctuation">.</span><span class="token function">displayTransform</span><span class="token punctuation">(</span><span class="token keyword">for</span><span class="token punctuation">:</span> <span class="token punctuation">.</span>portrait<span class="token punctuation">,</span> viewportSize<span class="token punctuation">:</span> viewportSize<span class="token punctuation">)</span>
<span class="token keyword">let</span> viewNormalizedBoundingBox <span class="token operator">=</span> observation<span class="token punctuation">.</span>boundingBox<span class="token punctuation">.</span><span class="token function">applying</span><span class="token punctuation">(</span>fromCameraImageToViewTransform<span class="token punctuation">)</span>
<span class="token comment">// 2</span>
<span class="token keyword">let</span> scaleTransform <span class="token operator">=</span> <span class="token class-name">CGAffineTransform</span><span class="token punctuation">(</span>scaleX<span class="token punctuation">:</span> viewportSize<span class="token punctuation">.</span>width<span class="token punctuation">,</span> y<span class="token punctuation">:</span> viewportSize<span class="token punctuation">.</span>height<span class="token punctuation">)</span>
<span class="token keyword">let</span> viewBoundingBox <span class="token operator">=</span> viewNormalizedBoundingBox<span class="token punctuation">.</span><span class="token function">applying</span><span class="token punctuation">(</span>scaleTransform<span class="token punctuation">)</span>
<span class="token keyword">return</span> viewBoundingBox
<span class="token punctuation">}</span>
</code></pre></div><h3 id="%E8%AA%AC%E6%98%8E%EF%BC%9A" data-line="93" class="code-line"><a class="header-anchor-link" href="#%E8%AA%AC%E6%98%8E%EF%BC%9A" aria-hidden="true"></a> 説明:</h3>
<ol data-line="94" class="code-line">
<li data-line="94" class="code-line"><strong>ビュー座標への変換</strong>:<code>displayTransform(for:viewportSize:)</code> を使用して、検出された境界ボックスは、入力画像の正規化座標系から <code>ARSCNView</code> の正規化座標系に変換されます。</li>
<li data-line="95" class="code-line"><strong>ピクセル寸法へのスケーリング</strong>:正規化された境界ボックスは、<code>ARSCNView</code> ビューポートのサイズに合わせてスケーリングされ、画面のピクセル寸法での境界ボックスが生成されます。</li>
</ol>
<p data-line="97" class="code-line">以上です!これで、<code>ARSCNView</code> ビューポートの座標系に境界ボックスが作成されました。</p>
<h1 id="3%E7%95%AA%E7%9B%AE%E3%81%AE%E5%BA%A7%E6%A8%99%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B" data-line="99" class="code-line"><a class="header-anchor-link" href="#3%E7%95%AA%E7%9B%AE%E3%81%AE%E5%BA%A7%E6%A8%99%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B" aria-hidden="true"></a> 3番目の座標を取得する</h1>
<p data-line="101" class="code-line">私は、ARシーンの3D座標空間内で認識されたオブジェクトの座標を取得すると約束しました。
そのためには、「ヒットテスト」と呼ばれる手法を利用します。これにより、ビューポート内の任意のポイントで最も近い物理オブジェクトまでの距離を測定できます。この手法は、デバイスからビューポート内の選択したポイントにある物理オブジェクトとの最初の交点まで直線の光線を投影し、その光線の長さを測定するものと考えることができます。この機能は ARKit の一部であり、非常に使いやすいです。
先ほど検出したオブジェクトの知覚可能な中心の3D座標を見つける方法は以下の通りです。</p>
<div class="code-block-container"><pre class="language-swift"><code class="language-swift code-line" data-line="105"><span class="token keyword">func</span> <span class="token function-definition function">performHitTestInCenter</span><span class="token punctuation">(</span>of boundingBox<span class="token punctuation">:</span><span class="token class-name">CGRect</span><span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">SCNVector3</span><span class="token operator">?</span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> center <span class="token operator">=</span> <span class="token class-name">CGPoint</span><span class="token punctuation">(</span>x<span class="token punctuation">:</span> boundingBox<span class="token punctuation">.</span>midX<span class="token punctuation">,</span> y<span class="token punctuation">:</span> boundingBox<span class="token punctuation">.</span>midY<span class="token punctuation">)</span> <span class="token comment">// 1</span>
<span class="token keyword">return</span> <span class="token function">performHitTest</span><span class="token punctuation">(</span>at<span class="token punctuation">:</span> center<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token keyword">func</span> <span class="token function-definition function">performHitTest</span><span class="token punctuation">(</span>at location<span class="token punctuation">:</span><span class="token class-name">CGPoint</span><span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">SCNVector3</span><span class="token operator">?</span> <span class="token punctuation">{</span>
<span class="token keyword">guard</span> <span class="token keyword">let</span> query <span class="token operator">=</span> sceneView<span class="token punctuation">.</span><span class="token function">raycastQuery</span><span class="token punctuation">(</span> <span class="token comment">// 2</span>
from<span class="token punctuation">:</span> location<span class="token punctuation">,</span>
allowing<span class="token punctuation">:</span> <span class="token punctuation">.</span>estimatedPlane<span class="token punctuation">,</span> <span class="token comment">// 3</span>
alignment<span class="token punctuation">:</span> <span class="token punctuation">.</span>any <span class="token comment">// 4</span>
<span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token nil constant">nil</span>
<span class="token punctuation">}</span>
<span class="token keyword">guard</span> <span class="token keyword">let</span> result <span class="token operator">=</span> sceneView<span class="token punctuation">.</span>session<span class="token punctuation">.</span><span class="token function">raycast</span><span class="token punctuation">(</span>query<span class="token punctuation">)</span><span class="token punctuation">.</span>first <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token nil constant">nil</span> <span class="token punctuation">}</span> <span class="token comment">// 5</span>
<span class="token keyword">let</span> translation <span class="token operator">=</span> result<span class="token punctuation">.</span>worldTransform<span class="token punctuation">.</span>columns<span class="token punctuation">.</span><span class="token number">3</span> <span class="token comment">// 6</span>
<span class="token keyword">return</span> <span class="token punctuation">.</span><span class="token keyword">init</span><span class="token punctuation">(</span>x<span class="token punctuation">:</span> translation<span class="token punctuation">.</span>x<span class="token punctuation">,</span> y<span class="token punctuation">:</span> translation<span class="token punctuation">.</span>y<span class="token punctuation">,</span> z<span class="token punctuation">:</span> translation<span class="token punctuation">.</span>z<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre></div><h3 id="%E8%AA%AC%E6%98%8E%EF%BC%9A-1" data-line="125" class="code-line"><a class="header-anchor-link" href="#%E8%AA%AC%E6%98%8E%EF%BC%9A-1" aria-hidden="true"></a> 説明:</h3>
<ol data-line="126" class="code-line">
<li data-line="126" class="code-line">ここでは、ヒットテストを実行するために1つのポイントが必要なため、前の手順から境界ボックスの中心を計算します。</li>
<li data-line="127" class="code-line">指定された2Dポイントから始まるレイキャストクエリを作成します。</li>
<li data-line="128" class="code-line">ヒットテストで、ARKitが推定することしかできない非平面の表面や平面を考慮できるようにします。</li>
<li data-line="129" class="code-line">水平面と垂直面の両方のヒットテストを有効にします(既定値は水平面のみです)。</li>
<li data-line="130" class="code-line">ARセッションを使用してレイキャストクエリを実行します。交差がない場合は <code>nil</code> を返します。</li>
<li data-line="131" class="code-line">各 <code>ARRaycastResult</code> には、ワールド空間で検出されたポイントの3D変換を表す4x4行列である <code>worldTransform</code> が含まれます。<code>columns.3</code> には、交点の3D位置を指定する並進ベクトルが含まれています。この並進ベクトルは <code>SCNVector3</code> として返され、ARKit/SceneKitはこれを使用して3D位置を表します。</li>
</ol>
<p data-line="133" class="code-line">完了しました!これで、Visionによって検出されたオブジェクトの 3D座標を取得できました。目的に合わせてご利用ください。:)</p>
<h1 id="%E6%9C%80%E5%BE%8C%E3%81%AB" data-line="135" class="code-line"><a class="header-anchor-link" href="#%E6%9C%80%E5%BE%8C%E3%81%AB" aria-hidden="true"></a> 最後に</h1>
<p data-line="137" class="code-line">Unlimitedアプリでは、これらの3D座標を使用して、車内にARマニュアルマーカーを表示します。もちろん、ユーザーエクスペリエンスをよりスムーズにし、マーカーの位置をより安定させるために私たちが採用している追加テクニックは数多くありますが、これはコアテクニックの1つです。
とはいえ、この方法は、考えられる他のあらゆる目的にも使用できます。お役に立てたら幸いです。</p>
<p data-line="140" class="code-line">最後に、私たちのテストプロセスと、オブジェクト検出後にARマニュアルマーカーがどのように表示されるかについて少し紹介します。
<img src="/assets/blog/authors/vorona/ar_coordinates/demo.gif" alt="demo" /></p>
<p data-line="143" class="code-line">今日はここまでです。読んでいただきありがとうございました!
楽しいクリスマスをお過ごしください。そして、幸せな新年をお迎えください!</p>
関連記事 | Related Posts
We are hiring!
生成AIエンジニア/AIファーストG/東京・名古屋・大阪・福岡
AIファーストGについて生成AIの活用を通じて、KINTO及びKINTOテクノロジーズへ事業貢献することをミッションに2024年1月に新設されたプロジェクトチームです。生成AI技術は生まれて日が浅く、その技術を業務活用する仕事には定説がありません。
リードエンジニア/プロジェクト推進G/東京・名古屋
業務内容バックエンド開発を中心に、サービスの企画から設計・開発・運用までプロダクトに関わっていただきます。サービスや会社の成長を考えて、自分やチームが何をすべきか自律的に動き、スピード感を持って開発に取り組むことを期待しています。





