mediapipe 中文教程 android开发
<p>今天开始给大家分享mediapipe学习,踩坑过程.</p><p> </p>
<p>首先还是要学习官方教程的 https://google.github.io/mediapipe/getting_started/hello_world_android.html</p>
<p> </p>
<p>环境搭建后期在分享给大家</p>
<p><img src="https://img2022.cnblogs.com/blog/881683/202202/881683-20220222195728659-924633040.png" alt="" loading="lazy"></p>
<p> </p>
<p> </p>
<p>我现在使用的是windown系统, 也跑过centos上,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,</p>
<h1 id="hello-world-on-android" class="no_toc"><span><span>helloworld!</span><span>android</span></span></h1>
<ol id="markdown-toc">
<li><span><span>介绍</span></span><ol>
<li><span><span>你会学到什么</span></span></li>
<li><span><span>你将建造什么</span></span></li>
</ol></li>
<li><span><span>设置</span></span></li>
<li><span><span>边缘检测图</span></span></li>
<li><span><span>初始最小应用程序设置</span></span></li>
<li><span><span>通过使用相机</span></span><code class="language-plaintext highlighter-rouge">CameraX</code><ol>
<li><span><span>相机权限</span></span></li>
<li><span><span>相机访问</span></span></li>
</ol></li>
<li><code class="language-plaintext highlighter-rouge">ExternalTextureConverter</code><span><span>设置</span></span></li>
<li><span><span>在 Android 中使用 MediaPipe 图</span></span><ol>
<li><span><span>添加相关依赖</span></span></li>
<li><span><span>使用图表</span></span><code class="language-plaintext highlighter-rouge">MainActivity</code></li>
</ol></li>
</ol><hr>
<h2 id="introduction"><span><span>介绍</span></span></h2>
<p><span><span>此 Codelab 在 Android 设备上使用 MediaPipe。</span></span></p>
<h3 id="what-you-will-learn"><span><span>你会学到什么</span></span></h3>
<p><span><span>如何开发使用 MediaPipe 的 Android 应用程序并在 Android 上运行 MediaPipe 图。</span></span></p>
<h3 id="what-you-will-build"><span><span>你将建造什么</span></span></h3>
<p><span><span>用于实时 Sobel 边缘检测的简单相机应用程序,适用于 Android 设备上的实时视频流。</span></span></p>
<p><img src="https://google.github.io/mediapipe/images/mobile/edge_detection_android_gpu.gif" alt="edge_detection_android_gpu_gif"></p>
<h2 id="setup"><span><span>设置</span></span></h2>
<ol>
<li><span><span>在您的系统上安装 MediaPipe,有关详细信息,请参阅</span></span><span><span>MediaPipe 安装指南</span></span><span><span>。</span></span></li>
<li><span><span>安装 Android 开发 SDK 和 Android NDK。</span><span>另请参阅 中的操作方法。</span></span></li>
<li><span><span>在您的 Android 设备上</span><span>启用</span></span><span><span>开发者选项。</span></span></li>
<li><span><span>在您的系统上设置</span></span><span><span>Bazel</span></span><span><span>以构建和部署 Android 应用程序。</span></span></li>
</ol>
<h2 id="graph-for-edge-detection"><span><span>边缘检测图</span></span></h2>
<p><span><span>我们将使用下图</span></span><code class="language-plaintext highlighter-rouge">edge_detection_mobile_gpu.pbtxt</code><span><span>:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code># MediaPipe graph that performs GPU Sobel edge detection on a live video stream.
# Used in the examples in
# mediapipe/examples/android/src/java/com/mediapipe/apps/basic and
# mediapipe/examples/ios/edgedetectiongpu.
# Images coming into and out of the graph.
input_stream: "input_video"
output_stream: "output_video"
# Converts RGB images into luminance images, still stored in RGB format.
node: {
calculator: "LuminanceCalculator"
input_stream: "input_video"
output_stream: "luma_video"
}
# Applies the Sobel filter to luminance images stored in RGB format.
node: {
calculator: "SobelEdgesCalculator"
input_stream: "luma_video"
output_stream: "output_video"
}
</code></pre>
</div>
</div>
<p><span><span>图表的可视化如下所示:</span></span></p>
<p><img src="https://google.github.io/mediapipe/images/mobile/edge_detection_mobile_gpu.png" alt="edge_detection_mobile_gpu"></p>
<p><span><span>该图有一个输入流,命名</span></span><code class="language-plaintext highlighter-rouge">input_video</code><span><span>为所有传入的帧,这些帧将由您的设备的相机提供。</span></span></p>
<p><span><span>图中的第一个节点</span></span><code class="language-plaintext highlighter-rouge">LuminanceCalculator</code><span><span>,采用单个数据包(图像帧)并使用 OpenGL 着色器应用亮度变化。</span><span>生成的图像帧被发送到</span></span><code class="language-plaintext highlighter-rouge">luma_video</code><span><span>输出流。</span></span></p>
<p><span><span>第二个节点对流</span></span><code class="language-plaintext highlighter-rouge">SobelEdgesCalculator</code><span><span>中的传入数据包应用边缘检测,</span></span><code class="language-plaintext highlighter-rouge">luma_video</code><span><span>并在输出流中输出结果</span></span><code class="language-plaintext highlighter-rouge">output_video</code><span><span>。</span></span></p>
<p><span><span>我们的 Android 应用程序将显示</span></span><code class="language-plaintext highlighter-rouge">output_video</code><span><span>流的输出图像帧。</span></span></p>
<h2 id="initial-minimal-application-setup"><span><span>初始最小应用程序设置</span></span></h2>
<p><span><span>我们首先从一个显示“Hello World!”的简单 Android 应用程序开始。</span><span>屏幕上。</span><span>如果您熟悉使用</span></span><code class="language-plaintext highlighter-rouge">bazel</code><span><span>.</span></span></p>
<p><span><span>创建一个新目录,您将在其中创建 Android 应用程序。</span><span>例如,本教程的完整代码可以在</span></span><code class="language-plaintext highlighter-rouge">mediapipe/examples/android/src/java/com/google/mediapipe/apps/basic</code><span><span>. </span></span><code class="language-plaintext highlighter-rouge">$APPLICATION_PATH</code><span><span>我们将在整个 Codelab 中</span><span>引用此路径。</span></span></p>
<p><span><span>请注意,在应用程序的路径中:</span></span></p>
<ul>
<li><span><span>该应用程序名为</span></span><code class="language-plaintext highlighter-rouge">helloworld</code><span><span>.</span></span></li>
<li><span><span>该</span></span><code class="language-plaintext highlighter-rouge">$PACKAGE_PATH</code><span><span>应用程序是</span></span><code class="language-plaintext highlighter-rouge">com.google.mediapipe.apps.basic</code><span><span>. </span><span>这在本教程的代码片段中使用,因此请记住</span></span><code class="language-plaintext highlighter-rouge">$PACKAGE_PATH</code><span><span>在复制/使用代码片段时使用您自己的。</span></span></li>
</ul>
<p><span><span>将文件添加</span></span><code class="language-plaintext highlighter-rouge">activity_main.xml</code><span><span>到</span></span><code class="language-plaintext highlighter-rouge">$APPLICATION_PATH/res/layout</code><span><span>. </span><span>这将</span></span><code class="language-plaintext highlighter-rouge">TextView</code><span><span>在应用程序的全屏上显示一个字符串</span></span><code class="language-plaintext highlighter-rouge">Hello World!</code><span><span>:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code><?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
</code></pre>
</div>
</div>
<p><span><span>添加一个</span><span>加载布局内容的简单对象,</span></span><code class="language-plaintext highlighter-rouge">MainActivity.java</code><span><span>如下</span><span>所示:</span></span><code class="language-plaintext highlighter-rouge">$APPLICATION_PATH</code><code class="language-plaintext highlighter-rouge">activity_main.xml</code></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>package com.google.mediapipe.apps.basic;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
/** Bare-bones main activity. */
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
</code></pre>
</div>
</div>
<p><span><span>添加一个清单文件</span></span><code class="language-plaintext highlighter-rouge">AndroidManifest.xml</code><span><span>to </span></span><code class="language-plaintext highlighter-rouge">$APPLICATION_PATH</code><span><span>,它</span></span><code class="language-plaintext highlighter-rouge">MainActivity</code><span><span>在应用程序启动时启动:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code><?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.mediapipe.apps.basic">
<uses-sdk
android:minSdkVersion="19"
android:targetSdkVersion="19" />
<application
android:allowBackup="true"
android:label="${appName}"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name="${mainActivity}"
android:exported="true"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
</code></pre>
</div>
</div>
<p><span><span>在我们的应用程序中,我们在应用程序中使用了一个</span></span><code class="language-plaintext highlighter-rouge">Theme.AppCompat</code><span><span>主题,因此我们需要适当的主题引用。</span><span>添加</span></span><code class="language-plaintext highlighter-rouge">colors.xml</code><span><span>到</span></span><code class="language-plaintext highlighter-rouge">$APPLICATION_PATH/res/values/</code><span><span>:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code><?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>
</resources>
</code></pre>
</div>
</div>
<p><span><span>添加</span></span><code class="language-plaintext highlighter-rouge">styles.xml</code><span><span>到</span></span><code class="language-plaintext highlighter-rouge">$APPLICATION_PATH/res/values/</code><span><span>:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code><resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
</code></pre>
</div>
</div>
<p><span><span>要构建应用程序,请将</span></span><code class="language-plaintext highlighter-rouge">BUILD</code><span><span>文件添加到清单中</span></span><code class="language-plaintext highlighter-rouge">$APPLICATION_PATH</code><span><span>,并且</span></span><code class="language-plaintext highlighter-rouge">${appName}</code><span><span>清单</span></span><code class="language-plaintext highlighter-rouge">${mainActivity}</code><span><span>中的和将替换为中指定的字符串,</span></span><code class="language-plaintext highlighter-rouge">BUILD</code><span><span>如下所示。</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>android_library(
name = "basic_lib",
srcs = glob(["*.java"]),
manifest = "AndroidManifest.xml",
resource_files = glob(["res/**"]),
deps = [
"//third_party:android_constraint_layout",
"//third_party:androidx_appcompat",
],
)
android_binary(
name = "helloworld",
manifest = "AndroidManifest.xml",
manifest_values = {
"applicationId": "com.google.mediapipe.apps.basic",
"appName": "Hello World",
"mainActivity": ".MainActivity",
},
multidex = "native",
deps = [
":basic_lib",
],
)
</code></pre>
</div>
</div>
<p><span><span>该</span></span><code class="language-plaintext highlighter-rouge">android_library</code><span><span>规则为</span></span><code class="language-plaintext highlighter-rouge">MainActivity</code><span><span>、 资源文件和</span></span><code class="language-plaintext highlighter-rouge">AndroidManifest.xml</code><span><span>.</span></span></p>
<p><span><span>该</span></span><code class="language-plaintext highlighter-rouge">android_binary</code><span><span>规则使用</span></span><code class="language-plaintext highlighter-rouge">basic_lib</code><span><span>生成的 Android 库构建二进制 APK 以安装在您的 Android 设备上。</span></span></p>
<p><span><span>要构建应用程序,请使用以下命令:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>bazel build -c opt --config=android_arm64 $APPLICATION_PATH:helloworld
</code></pre>
</div>
</div>
<p><span><span>使用 . 安装生成的 APK 文件</span></span><code class="language-plaintext highlighter-rouge">adb install</code><span><span>。</span><span>例如:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>adb install bazel-bin/$APPLICATION_PATH/helloworld.apk
</code></pre>
</div>
</div>
<p><span><span>在您的设备上打开应用程序。</span><span>它应该显示一个带有文本的屏幕</span></span><code class="language-plaintext highlighter-rouge">Hello World!</code><span><span>。</span></span></p>
<p><img src="https://google.github.io/mediapipe/images/mobile/bazel_hello_world_android.png" alt="bazel_hello_world_android"></p>
<h2 id="using-the-camera-via-camerax"><span><span>通过使用相机</span></span><code class="language-plaintext highlighter-rouge">CameraX</code></h2>
<h3 id="camera-permissions"><span><span>相机权限</span></span></h3>
<p><span><span>要在我们的应用程序中使用摄像头,我们需要请求用户提供对摄像头的访问权限。</span><span>要请求相机权限,请将以下内容添加到</span></span><code class="language-plaintext highlighter-rouge">AndroidManifest.xml</code><span><span>:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code><!-- For using the camera -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
</code></pre>
</div>
</div>
<p><span><span>在同一文件</span><span>中将最低 SDK 版本更改为</span></span><code class="language-plaintext highlighter-rouge">21</code><span><span>和目标 SDK 版本:</span></span><code class="language-plaintext highlighter-rouge">27</code></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code><uses-sdk
android:minSdkVersion="21"
android:targetSdkVersion="27" />
</code></pre>
</div>
</div>
<p><span><span>这可确保提示用户请求相机权限,并使我们能够使用</span></span><span><span>CameraX</span></span><span><span>库进行相机访问。</span></span></p>
<p><span><span>要请求摄像头权限,我们可以使用 MediaPipe 组件提供的实用程序,即</span></span><code class="language-plaintext highlighter-rouge">PermissionHelper</code><span><span>. </span><span>要使用它,</span><span>请</span></span><code class="language-plaintext highlighter-rouge">"//mediapipe/java/com/google/mediapipe/components:android_components"</code><span><span>在</span><span>.</span></span><code class="language-plaintext highlighter-rouge">mediapipe_lib</code><code class="language-plaintext highlighter-rouge">BUILD</code></p>
<p><span><span>要使用</span></span><code class="language-plaintext highlighter-rouge">PermissionHelper</code><span><span>in </span></span><code class="language-plaintext highlighter-rouge">MainActivity</code><span><span>,请将以下行添加到</span></span><code class="language-plaintext highlighter-rouge">onCreate</code><span><span>函数中:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>PermissionHelper.checkAndRequestCameraPermissions(this);
</code></pre>
</div>
</div>
<p><span><span>这会通过屏幕上的对话框提示用户请求在此应用程序中使用相机的权限。</span></span></p>
<p><span><span>添加以下代码来处理用户响应:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>@Override
public void onRequestPermissionsResult(
int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
PermissionHelper.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
protected void onResume() {
super.onResume();
if (PermissionHelper.cameraPermissionsGranted(this)) {
startCamera();
}
}
public void startCamera() {}
</code></pre>
</div>
</div>
<p><span><span>我们暂时将</span></span><code class="language-plaintext highlighter-rouge">startCamera()</code><span><span>方法留空。</span><span>当用户响应提示时,</span></span><code class="language-plaintext highlighter-rouge">MainActivity</code><span><span>将恢复并</span></span><code class="language-plaintext highlighter-rouge">onResume()</code><span><span>调用。</span><span>该代码将确认已授予使用相机的权限,然后将启动相机。</span></span></p>
<p><span><span>重建并安装应用程序。</span><span>您现在应该会看到请求访问应用程序的摄像头的提示。</span></span></p>
<p><span><span>注意:如果没有对话框提示,请卸载并重新安装应用程序。</span><span>如果您没有更改文件</span><span>中的</span></span><code class="language-plaintext highlighter-rouge">minSdkVersion</code><span><span>and也可能发生这种情况</span><span>。</span></span><code class="language-plaintext highlighter-rouge">targetSdkVersion</code><code class="language-plaintext highlighter-rouge">AndroidManifest.xml</code></p>
<h3 id="camera-access"><span><span>相机访问</span></span></h3>
<p><span><span>有了可用的相机权限,我们就可以开始并从相机中获取帧。</span></span></p>
<p><span><span>要查看来自相机的帧,我们将使用</span></span><code class="language-plaintext highlighter-rouge">SurfaceView</code><span><span>. </span><span>来自相机的每一帧都将存储在一个</span></span><code class="language-plaintext highlighter-rouge">SurfaceTexture</code><span><span>对象中。</span><span>要使用这些,我们首先需要更改应用程序的布局。</span></span></p>
<p><span><span>从中删除整个</span></span><code class="language-plaintext highlighter-rouge">TextView</code><span><span>代码块</span></span><code class="language-plaintext highlighter-rouge">$APPLICATION_PATH/res/layout/activity_main.xml</code><span><span>并添加以下代码:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code><FrameLayout
android:id="@+id/preview_display_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1">
<TextView
android:id="@+id/no_camera_access_view"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:gravity="center"
android:text="@string/no_camera_access" />
</FrameLayout>
</code></pre>
</div>
</div>
<p><span><span>这个代码块有一个新的</span></span><code class="language-plaintext highlighter-rouge">FrameLayout</code><span><span>命名</span></span><code class="language-plaintext highlighter-rouge">preview_display_layout</code><span><span>和</span></span><code class="language-plaintext highlighter-rouge">TextView</code><span><span>嵌套在它里面,命名</span></span><code class="language-plaintext highlighter-rouge">no_camera_access_preview</code><span><span>。</span><span>当未授予相机访问权限时,我们的应用程序将显示</span></span><code class="language-plaintext highlighter-rouge">TextView</code><span><span>存储在变量中的字符串消息</span></span><code class="language-plaintext highlighter-rouge">no_camera_access</code><span><span>。</span><span>在文件中添加以下行</span></span><code class="language-plaintext highlighter-rouge">$APPLICATION_PATH/res/values/strings.xml</code><span><span>:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code><string name="no_camera_access" translatable="false">Please grant camera permissions.</string>
</code></pre>
</div>
</div>
<p><span><span>当用户不授予相机权限时,屏幕现在将如下所示:</span></span></p>
<p><img src="https://google.github.io/mediapipe/images/mobile/missing_camera_permission_android.png" alt="missing_camera_permission_android"></p>
<p><span><span>现在,我们将</span></span><code class="language-plaintext highlighter-rouge">SurfaceTexture</code><span><span>和</span></span><code class="language-plaintext highlighter-rouge">SurfaceView</code><span><span>对象添加到</span></span><code class="language-plaintext highlighter-rouge">MainActivity</code><span><span>:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>private SurfaceTexture previewFrameTexture;
private SurfaceView previewDisplayView;
</code></pre>
</div>
</div>
<p><span><span>在函数中,在</span><span>请求相机权限</span><em><span>之前</span></em></span><code class="language-plaintext highlighter-rouge">onCreate(Bundle)</code><span><span>添加以下两行:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>previewDisplayView = new SurfaceView(this);
setupPreviewDisplayView();
</code></pre>
</div>
</div>
<p><span><span>现在添加代码定义</span></span><code class="language-plaintext highlighter-rouge">setupPreviewDisplayView()</code><span><span>:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>private void setupPreviewDisplayView() {
previewDisplayView.setVisibility(View.GONE);
ViewGroup viewGroup = findViewById(R.id.preview_display_layout);
viewGroup.addView(previewDisplayView);
}
</code></pre>
</div>
</div>
<p><span><span>我们定义了一个新</span></span><code class="language-plaintext highlighter-rouge">SurfaceView</code><span><span>对象并将其添加到</span></span><code class="language-plaintext highlighter-rouge">preview_display_layout</code> <code class="language-plaintext highlighter-rouge">FrameLayout</code><span><span>对象中,以便我们可以使用它来使用</span></span><code class="language-plaintext highlighter-rouge">SurfaceTexture</code><span><span>名为 的对象显示相机帧</span></span><code class="language-plaintext highlighter-rouge">previewFrameTexture</code><span><span>。</span></span></p>
<p><span><span>要</span></span><code class="language-plaintext highlighter-rouge">previewFrameTexture</code><span><span>用于获取相机帧,我们将使用</span></span><span><span>CameraX</span></span><span><span>。</span><span>MediaPipe 提供了一个名为</span></span><code class="language-plaintext highlighter-rouge">CameraXPreviewHelper</code><span><span>使用</span></span><span><span>CameraX</span></span><span><span>的实用程序。</span><span>当通过 启动相机时,此类会更新侦听器</span></span><code class="language-plaintext highlighter-rouge">onCameraStarted(@Nullable SurfaceTexture)</code><span><span>。</span></span></p>
<p><span><span>要使用此实用程序,请修改</span></span><code class="language-plaintext highlighter-rouge">BUILD</code><span><span>文件以添加对</span></span><code class="language-plaintext highlighter-rouge">"//mediapipe/java/com/google/mediapipe/components:android_camerax_helper"</code><span><span>.</span></span></p>
<p><span><span>现在导入</span></span><code class="language-plaintext highlighter-rouge">CameraXPreviewHelper</code><span><span>并将以下行添加到</span></span><code class="language-plaintext highlighter-rouge">MainActivity</code><span><span>:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>private CameraXPreviewHelper cameraHelper;
</code></pre>
</div>
</div>
<p><span><span>现在,我们可以将我们的实现添加到</span></span><code class="language-plaintext highlighter-rouge">startCamera()</code><span><span>:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>public void startCamera() {
cameraHelper = new CameraXPreviewHelper();
cameraHelper.setOnCameraStartedListener(
surfaceTexture -> {
previewFrameTexture = surfaceTexture;
// Make the display view visible to start showing the preview.
previewDisplayView.setVisibility(View.VISIBLE);
});
}
</code></pre>
</div>
</div>
<p><span><span>这将创建一个新</span></span><code class="language-plaintext highlighter-rouge">CameraXPreviewHelper</code><span><span>对象并在该对象上添加一个匿名侦听器。</span><span>当</span></span><code class="language-plaintext highlighter-rouge">cameraHelper</code><span><span>有信号表明相机已经启动并且</span></span><code class="language-plaintext highlighter-rouge">surfaceTexture</code><span><span>可以获取帧时,我们将其另存</span></span><code class="language-plaintext highlighter-rouge">surfaceTexture</code><span><span>为</span></span><code class="language-plaintext highlighter-rouge">previewFrameTexture</code><span><span>,并使其</span></span><code class="language-plaintext highlighter-rouge">previewDisplayView</code><span><span>可见,以便我们可以开始查看</span></span><code class="language-plaintext highlighter-rouge">previewFrameTexture</code><span><span>.</span></span></p>
<p><span><span>但是,在启动相机之前,我们需要决定要使用哪个相机。</span></span><code class="language-plaintext highlighter-rouge">CameraXPreviewHelper</code><span><span>继承 from </span></span><code class="language-plaintext highlighter-rouge">CameraHelper</code><span><span>which 提供两个选项,</span></span><code class="language-plaintext highlighter-rouge">FRONT</code><span><span>和</span></span><code class="language-plaintext highlighter-rouge">BACK</code><span><span>. </span><span>我们可以将</span></span><code class="language-plaintext highlighter-rouge">BUILD</code><span><span>文件中的决定作为元数据传递,这样无需更改代码即可使用不同的相机构建另一个版本的应用程序。</span></span></p>
<p><span><span>假设我们想使用</span></span><code class="language-plaintext highlighter-rouge">BACK</code><span><span>相机对我们从相机查看的实时场景执行边缘检测,请将元数据添加到</span></span><code class="language-plaintext highlighter-rouge">AndroidManifest.xml</code><span><span>:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code> ...
<meta-data android:name="cameraFacingFront" android:value="${cameraFacingFront}"/>
</application>
</manifest>
</code></pre>
</div>
</div>
<p><code class="language-plaintext highlighter-rouge">BUILD</code><span><span>并在</span></span><code class="language-plaintext highlighter-rouge">helloworld</code><span><span>android 二进制规则中使用新条目</span><span>指定选择</span></span><code class="language-plaintext highlighter-rouge">manifest_values</code><span><span>:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>manifest_values = {
"applicationId": "com.google.mediapipe.apps.basic",
"appName": "Hello World",
"mainActivity": ".MainActivity",
"cameraFacingFront": "False",
},
</code></pre>
</div>
</div>
<p><span><span>现在,</span></span><code class="language-plaintext highlighter-rouge">MainActivity</code><span><span>要检索 中指定的元数据</span></span><code class="language-plaintext highlighter-rouge">manifest_values</code><span><span>,添加一个</span></span><code class="language-plaintext highlighter-rouge">ApplicationInfo</code><span><span>对象:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>private ApplicationInfo applicationInfo;
</code></pre>
</div>
</div>
<p><span><span>在</span></span><code class="language-plaintext highlighter-rouge">onCreate()</code><span><span>函数中,添加:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>try {
applicationInfo =
getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
} catch (NameNotFoundException e) {
Log.e(TAG, "Cannot find application info: " + e);
}
</code></pre>
</div>
</div>
<p><code class="language-plaintext highlighter-rouge">startCamera()</code><span><span>现在在函数</span><span>末尾添加以下行:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>CameraHelper.CameraFacing cameraFacing =
applicationInfo.metaData.getBoolean("cameraFacingFront", false)
? CameraHelper.CameraFacing.FRONT
: CameraHelper.CameraFacing.BACK;
cameraHelper.startCamera(this, cameraFacing, /*unusedSurfaceTexture=*/ null);
</code></pre>
</div>
</div>
<p><span><span>此时,应用程序应该构建成功。</span><span>但是,当您在设备上运行应用程序时,您会看到黑屏(即使已授予相机权限)。</span><span>这是因为即使我们保存了</span></span><code class="language-plaintext highlighter-rouge">surfaceTexture</code><span><span>提供的变量</span></span><code class="language-plaintext highlighter-rouge">CameraXPreviewHelper</code><span><span>,</span></span><code class="language-plaintext highlighter-rouge">previewSurfaceView</code><span><span>也不会使用它的输出并将其显示在屏幕上。</span></span></p>
<p><span><span>由于我们想在 MediaPipe 图中使用帧,因此我们不会在本教程中添加代码来直接查看相机输出。</span><span>相反,我们直接跳到如何将相机帧发送到 MediaPipe 图形并在屏幕上显示图形的输出进行处理。</span></span></p>
<h2 id="externaltextureconverter-setup"><code class="language-plaintext highlighter-rouge">ExternalTextureConverter</code><span><span>设置</span></span></h2>
<p><span><span>A</span></span><code class="language-plaintext highlighter-rouge">SurfaceTexture</code><span><span>从流中捕获图像帧作为 OpenGL ES 纹理。</span><span>要使用 MediaPipe 图,从相机捕获的帧应存储在常规 Open GL 纹理对象中。</span><span>MediaPipe 提供了一个类,</span></span><code class="language-plaintext highlighter-rouge">ExternalTextureConverter</code><span><span>用于将存储在</span></span><code class="language-plaintext highlighter-rouge">SurfaceTexture</code><span><span>对象中的图像转换为常规的 OpenGL 纹理对象。</span></span></p>
<p><span><span>要使用</span></span><code class="language-plaintext highlighter-rouge">ExternalTextureConverter</code><span><span>,我们还需要一个</span></span><code class="language-plaintext highlighter-rouge">EGLContext</code><span><span>,它由一个</span></span><code class="language-plaintext highlighter-rouge">EglManager</code><span><span>对象创建和管理。</span><span>将依赖项添加到</span></span><code class="language-plaintext highlighter-rouge">BUILD</code><span><span>要使用的文件中</span></span><code class="language-plaintext highlighter-rouge">EglManager</code><span><span>,</span></span><code class="language-plaintext highlighter-rouge">"//mediapipe/java/com/google/mediapipe/glutil"</code><span><span>.</span></span></p>
<p><span><span>在</span></span><code class="language-plaintext highlighter-rouge">MainActivity</code><span><span>中,添加以下声明:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>private EglManager eglManager;
private ExternalTextureConverter converter;
</code></pre>
</div>
</div>
<p><span><span>在函数中,</span><span>在请求相机权限之前</span></span><code class="language-plaintext highlighter-rouge">onCreate(Bundle)</code><span><span>添加一条语句来初始化对象:</span></span><code class="language-plaintext highlighter-rouge">eglManager</code></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>eglManager = new EglManager(null);
</code></pre>
</div>
</div>
<p><span><span>回想一下,我们在 中定义了</span><span>确认相机权限已被授予并调用的</span></span><code class="language-plaintext highlighter-rouge">onResume()</code><span><span>函数</span><span>。</span><span>在此检查之前,添加以下行</span><span>以初始化</span><span>对象:</span></span><code class="language-plaintext highlighter-rouge">MainActivity</code><code class="language-plaintext highlighter-rouge">startCamera()</code><code class="language-plaintext highlighter-rouge">onResume()</code><code class="language-plaintext highlighter-rouge">converter</code></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>converter = new ExternalTextureConverter(eglManager.getContext());
</code></pre>
</div>
</div>
<p><span><span>这</span></span><code class="language-plaintext highlighter-rouge">converter</code><span><span>现在使用由</span></span><code class="language-plaintext highlighter-rouge">GLContext</code><span><span>管理</span></span><code class="language-plaintext highlighter-rouge">eglManager</code><span><span>。</span></span></p>
<p><span><span>我们还需要重写中的</span></span><code class="language-plaintext highlighter-rouge">onPause()</code><span><span>函数,</span></span><code class="language-plaintext highlighter-rouge">MainActivity</code><span><span>以便如果应用程序进入暂停状态,我们会</span></span><code class="language-plaintext highlighter-rouge">converter</code><span><span>正确关闭:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>@Override
protected void onPause() {
super.onPause();
converter.close();
}
</code></pre>
</div>
</div>
<p><span><span>要将输出通过管道</span></span><code class="language-plaintext highlighter-rouge">previewFrameTexture</code><span><span>传输到</span></span><code class="language-plaintext highlighter-rouge">converter</code><span><span>,请将以下代码块添加到</span></span><code class="language-plaintext highlighter-rouge">setupPreviewDisplayView()</code><span><span>:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>previewDisplayView
.getHolder()
.addCallback(
new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// (Re-)Compute the ideal size of the camera-preview display (the area that the
// camera-preview frames get rendered onto, potentially with scaling and rotation)
// based on the size of the SurfaceView that contains the display.
Size viewSize = new Size(width, height);
Size displaySize = cameraHelper.computeDisplaySizeFromViewSize(viewSize);
// Connect the converter to the camera-preview frames as its input (via
// previewFrameTexture), and configure the output width and height as the computed
// display size.
converter.setSurfaceTextureAndAttachToGLContext(
previewFrameTexture, displaySize.getWidth(), displaySize.getHeight());
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {}
});
</code></pre>
</div>
</div>
<p><span><span>在此代码块中,我们添加了一个自定义</span></span><code class="language-plaintext highlighter-rouge">SurfaceHolder.Callback</code><span><span>并</span></span><code class="language-plaintext highlighter-rouge">previewDisplayView</code><span><span>实现该</span></span><code class="language-plaintext highlighter-rouge">surfaceChanged(SurfaceHolder holder, int format, int width, int height)</code><span><span>函数,以计算设备屏幕上相机帧的适当显示大小,并将</span></span><code class="language-plaintext highlighter-rouge">previewFrameTexture</code><span><span>对象绑定并将计算的帧发送</span></span><code class="language-plaintext highlighter-rouge">displaySize</code><span><span>到</span></span><code class="language-plaintext highlighter-rouge">converter</code><span><span>.</span></span></p>
<p><span><span>我们现在准备好在 MediaPipe 图中使用相机帧。</span></span></p>
<h2 id="using-a-mediapipe-graph-in-android"><span><span>在 Android 中使用 MediaPipe 图</span></span></h2>
<h3 id="add-relevant-dependencies"><span><span>添加相关依赖</span></span></h3>
<p><span><span>要使用 MediaPipe 图,我们需要将依赖项添加到 Android 上的 MediaPipe 框架。</span><span>我们将首先添加一个构建规则来构建一个</span></span><code class="language-plaintext highlighter-rouge">cc_binary</code><span><span>使用 MediaPipe 框架的 JNI 代码,然后构建一个</span></span><code class="language-plaintext highlighter-rouge">cc_library</code><span><span>规则以在我们的应用程序中使用这个二进制文件。</span><span>将以下代码块添加到您的</span></span><code class="language-plaintext highlighter-rouge">BUILD</code><span><span>文件中:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>cc_binary(
name = "libmediapipe_jni.so",
linkshared = 1,
linkstatic = 1,
deps = [
"//mediapipe/java/com/google/mediapipe/framework/jni:mediapipe_framework_jni",
],
)
cc_library(
name = "mediapipe_jni_lib",
srcs = [":libmediapipe_jni.so"],
alwayslink = 1,
)
</code></pre>
</div>
</div>
<p><span><span>将依赖项添加</span></span><code class="language-plaintext highlighter-rouge">":mediapipe_jni_lib"</code><span><span>到文件中的</span></span><code class="language-plaintext highlighter-rouge">mediapipe_lib</code><span><span>构建规则</span></span><code class="language-plaintext highlighter-rouge">BUILD</code><span><span>。</span></span></p>
<p><span><span>接下来,我们需要添加特定于我们要在应用程序中使用的 MediaPipe 图的依赖项。</span></span></p>
<p><code class="language-plaintext highlighter-rouge">libmediapipe_jni.so</code><span><span>首先,在构建规则</span><span>中为所有计算器代码添加依赖项:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>"//mediapipe/graphs/edge_detection:mobile_calculators",
</code></pre>
</div>
</div>
<p><span><span>MediaPipe 图表是</span></span><code class="language-plaintext highlighter-rouge">.pbtxt</code><span><span>文件,但要在应用程序中使用它们,我们需要使用</span></span><code class="language-plaintext highlighter-rouge">mediapipe_binary_graph</code><span><span>构建规则生成</span></span><code class="language-plaintext highlighter-rouge">.binarypb</code><span><span>文件。</span></span></p>
<p><span><span>在</span></span><code class="language-plaintext highlighter-rouge">helloworld</code><span><span>android 二进制构建规则中,将</span></span><code class="language-plaintext highlighter-rouge">mediapipe_binary_graph</code><span><span>特定于图形的目标添加为资产:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>assets = [
"//mediapipe/graphs/edge_detection:mobile_gpu_binary_graph",
],
assets_dir = "",
</code></pre>
</div>
</div>
<p><span><span>在</span></span><code class="language-plaintext highlighter-rouge">assets</code><span><span>构建规则中,您还可以添加其他资产,例如图形中使用的 TensorFlowLite 模型。</span></span></p>
<p><span><span>此外,</span></span><code class="language-plaintext highlighter-rouge">manifest_values</code><span><span>为特定于图形的属性添加其他属性,以便稍后在以下位置检索</span></span><code class="language-plaintext highlighter-rouge">MainActivity</code><span><span>:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>manifest_values = {
"applicationId": "com.google.mediapipe.apps.basic",
"appName": "Hello World",
"mainActivity": ".MainActivity",
"cameraFacingFront": "False",
"binaryGraphName": "mobile_gpu.binarypb",
"inputVideoStreamName": "input_video",
"outputVideoStreamName": "output_video",
},
</code></pre>
</div>
</div>
<p><span><span>请注意,</span></span><code class="language-plaintext highlighter-rouge">binaryGraphName</code><span><span>表示二进制图的文件名,由目标</span></span><code class="language-plaintext highlighter-rouge">output_name</code><span><span>中的字段确定</span></span><code class="language-plaintext highlighter-rouge">mediapipe_binary_graph</code><span><span>。</span></span><code class="language-plaintext highlighter-rouge">inputVideoStreamName</code><span><span>和</span></span><code class="language-plaintext highlighter-rouge">outputVideoStreamName</code><span><span>分别是图中指定的输入和输出视频流名称。</span></span></p>
<p><span><span>现在,</span></span><code class="language-plaintext highlighter-rouge">MainActivity</code><span><span>需要加载 MediaPipe 框架。</span><span>此外,该框架使用 OpenCV,因此</span></span><code class="language-plaintext highlighter-rouge">MainActvity</code><span><span>也应该加载</span></span><code class="language-plaintext highlighter-rouge">OpenCV</code><span><span>. </span></span><code class="language-plaintext highlighter-rouge">MainActivity</code><span><span>在(在类内部,但不在任何函数内部)</span><span>使用以下代码来加载两个依赖项:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>static {
// Load all native libraries needed by the app.
System.loadLibrary("mediapipe_jni");
System.loadLibrary("opencv_java3");
}
</code></pre>
</div>
</div>
<h3 id="use-the-graph-in-mainactivity"><span><span>使用图表</span></span><code class="language-plaintext highlighter-rouge">MainActivity</code></h3>
<p><span><span>首先,我们需要加载包含从</span><span>图形文件</span></span><code class="language-plaintext highlighter-rouge">.binarypb</code><span><span>编译的资产。</span></span><code class="language-plaintext highlighter-rouge">.pbtxt</code><span><span>为此,我们可以使用 MediaPipe 实用程序,</span></span><code class="language-plaintext highlighter-rouge">AndroidAssetUtil</code><span><span>.</span></span></p>
<p><code class="language-plaintext highlighter-rouge">onCreate(Bundle)</code><span><span>在初始化之前初始化</span><span>资产管理器</span></span><code class="language-plaintext highlighter-rouge">eglManager</code><span><span>:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>// Initialize asset manager so that MediaPipe native libraries can access the app assets, e.g.,
// binary graphs.
AndroidAssetUtil.initializeNativeAssetManager(this);
</code></pre>
</div>
</div>
<p><span><span>现在,我们需要设置一个</span></span><code class="language-plaintext highlighter-rouge">FrameProcessor</code><span><span>对象,将 准备好的相机帧发送</span></span><code class="language-plaintext highlighter-rouge">converter</code><span><span>到 MediaPipe 图并运行该图,准备输出,然后更新</span></span><code class="language-plaintext highlighter-rouge">previewDisplayView</code><span><span>以显示输出。</span><span>添加以下代码以声明</span></span><code class="language-plaintext highlighter-rouge">FrameProcessor</code><span><span>:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>private FrameProcessor processor;
</code></pre>
</div>
</div>
<p><span><span>并在初始化</span></span><code class="language-plaintext highlighter-rouge">onCreate(Bundle)</code><span><span>后对其进行初始化</span></span><code class="language-plaintext highlighter-rouge">eglManager</code><span><span>:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>processor =
new FrameProcessor(
this,
eglManager.getNativeContext(),
applicationInfo.metaData.getString("binaryGraphName"),
applicationInfo.metaData.getString("inputVideoStreamName"),
applicationInfo.metaData.getString("outputVideoStreamName"));
</code></pre>
</div>
</div>
<p><code class="language-plaintext highlighter-rouge">processor</code><span><span>需要消耗来自</span></span><code class="language-plaintext highlighter-rouge">converter</code><span><span>处理</span><span>的转换帧。</span></span><code class="language-plaintext highlighter-rouge">onResume()</code><span><span>在初始化之后</span><span>添加以下行</span></span><code class="language-plaintext highlighter-rouge">converter</code><span><span>:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>converter.setConsumer(processor);
</code></pre>
</div>
</div>
<p><span><span>应该将</span></span><code class="language-plaintext highlighter-rouge">processor</code><span><span>其输出发送到</span></span><code class="language-plaintext highlighter-rouge">previewDisplayView</code><span><span>为此,将以下函数定义添加到我们的自定义</span></span><code class="language-plaintext highlighter-rouge">SurfaceHolder.Callback</code><span><span>:</span></span></p>
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<pre class="highlight"><code>@Override
public void surfaceCreated(SurfaceHolder holder) {
processor.getVideoSurfaceOutput().setSurface(holder.getSurface());
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
processor.getVideoSurfaceOutput().setSurface(null);
}
</code></pre>
</div>
</div>
<p><span><span>创建时</span><span>,</span></span><code class="language-plaintext highlighter-rouge">SurfaceHolder</code><span><span>我们</span></span><code class="language-plaintext highlighter-rouge">Surface</code><span><span>有</span></span><code class="language-plaintext highlighter-rouge">VideoSurfaceOutput</code><span><span>. </span></span><code class="language-plaintext highlighter-rouge">processor</code><span><span>当它被销毁时,我们将其</span></span><code class="language-plaintext highlighter-rouge">VideoSurfaceOutput</code><span><span>从</span></span><code class="language-plaintext highlighter-rouge">processor</code><span><span>.</span></span></p>
<p><span><span>就是这样!</span><span>您现在应该能够在设备上成功构建和运行应用程序,并在实时摄像机源上看到 Sobel 边缘检测运行!</span><span>恭喜!</span></span></p>
<p><img src="https://google.github.io/mediapipe/images/mobile/edge_detection_android_gpu.gif" alt="edge_detection_android_gpu_gif"></p>
<p><span><span>如果您遇到任何问题,请在</span></span><span><span>此处查看</span></span><span><span>教程的完整代码</span></span></p><br><br>
来源:https://www.cnblogs.com/Amos-Turing/p/15924705.html
頁:
[1]