소개
Hello World! 튜토리얼은 MediaPipe 프레임워크를 사용하여 Android에서 MediaPipe 그래프를 실행합니다.
빌드 대상
라이브 동영상에 적용되는 실시간 Sobel 에지 감지를 위한 간단한 카메라 앱 Android 기기에서 스트리밍할 수 있습니다.
설정
- 시스템에 MediaPipe 프레임워크를 설치합니다. 프레임워크 설치 참조 가이드를 참조하세요.
- Android 개발 SDK 및 Android NDK 설치 자세한 방법은 [프레임워크 설치 가이드]
- Android 기기에서 개발자 옵션을 사용 설정합니다.
- 시스템에 Bazel을 설정하여 Android 앱을 빌드하고 배포합니다.
에지 감지 그래프
edge_detection_mobile_gpu.pbtxt
그래프를 사용합니다.
# 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"
}
다음은 그래프를 시각화한 것입니다.
이 그래프에는 모든 수신 프레임에 input_video
라는 단일 입력 스트림이 있습니다.
기기의 카메라에서 제공합니다.
그래프의 첫 번째 노드인 LuminanceCalculator
는 단일 패킷을 가져옵니다.
OpenGL 셰이더를 사용하여 휘도 변경을 적용합니다. 결과
luma_video
출력 스트림으로 전송됩니다.
두 번째 노드 SobelEdgesCalculator
는 수신에 에지 감지를 적용합니다.
luma_video
스트림의 패킷 및 출력 결과 output_video
출력
있습니다.
Android 애플리케이션은
output_video
스트림.
초기 최소 애플리케이션 설정
먼저 'Hello World!'를 표시하는 간단한 Android 애플리케이션부터 시작해 보겠습니다.
화면에 나타납니다. Android 빌드에 익숙하다면 이 단계를 건너뛰어도 됩니다.
bazel
를 사용하는 애플리케이션.
Android 애플리케이션을 만들 새 디렉터리를 만듭니다. 대상
이 튜토리얼의 전체 코드는 다음 위치에서 확인할 수 있습니다.
mediapipe/examples/android/src/java/com/google/mediapipe/apps/basic
YouTube에서는
Codelab 전체에서 이 경로를 $APPLICATION_PATH
로 참조합니다.
애플리케이션 경로에서 다음 사항에 유의하세요.
- 애플리케이션의 이름은
helloworld
입니다. - 애플리케이션의
$PACKAGE_PATH
가com.google.mediapipe.apps.basic
입니다. 이 튜토리얼의 코드 스니펫에 사용되므로 코드 스니펫을 복사/사용할 때 자신의$PACKAGE_PATH
를 사용해야 합니다.
activity_main.xml
파일을 $APPLICATION_PATH/res/layout
에 추가합니다. 이렇게 하면
애플리케이션 전체 화면에서 Hello
World!
문자열이 있는 TextView
<?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>
콘텐츠를 로드하는 간단한 MainActivity.java
를 $APPLICATION_PATH
에 추가합니다.
activity_main.xml
레이아웃의 예입니다.
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);
}
}
매니페스트 파일 AndroidManifest.xml
를 $APPLICATION_PATH
에 추가합니다.
애플리케이션 시작 시 MainActivity
시작:
<?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>
이 애플리케이션에서는 앱에 Theme.AppCompat
테마를 사용하므로
적절한 테마 참조를 제공합니다. colors.xml
추가 대상
$APPLICATION_PATH/res/values/
:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>
</resources>
styles.xml
를 $APPLICATION_PATH/res/values/
에 추가합니다.
<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>
애플리케이션을 빌드하려면 BUILD
파일을 $APPLICATION_PATH
에 추가합니다.
매니페스트의 ${appName}
및 ${mainActivity}
가 문자열로 대체됩니다.
BUILD
에 지정됩니다.
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",
],
)
android_library
규칙은 MainActivity
, 리소스 파일의 종속 항목을 추가합니다.
및 AndroidManifest.xml
android_binary
규칙은 다음을 위해 생성된 basic_lib
Android 라이브러리를 사용합니다.
Android 기기에 설치할 바이너리 APK를 빌드합니다.
앱을 빌드하려면 다음 명령어를 사용합니다.
bazel build -c opt --config=android_arm64 $APPLICATION_PATH:helloworld
생성된 APK 파일을 adb install
를 사용하여 설치합니다. 예를 들면 다음과 같습니다.
adb install bazel-bin/$APPLICATION_PATH/helloworld.apk
기기에서 애플리케이션을 엽니다. 화면에 텍스트가
Hello World!
CameraX
을(를) 통해 카메라 사용 중
카메라 권한
애플리케이션에서 카메라를 사용하려면 사용자에게
카메라에 액세스할 수 없습니다. 카메라 권한을 요청하려면 다음을 추가하세요.
AndroidManifest.xml
:
<!-- For using the camera -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
다음에서 최소 SDK 버전을 21
으로, 타겟 SDK 버전을 27
로 변경합니다.
동일한 파일:
<uses-sdk
android:minSdkVersion="21"
android:targetSdkVersion="27" />
이렇게 하면 사용자에게 카메라 권한을 요청하라는 메시지가 표시되고 카메라 액세스에 CameraX 라이브러리를 사용할 수 있습니다.
카메라 권한을 요청하려면 MediaPipe 프레임워크에서 제공하는 유틸리티를 사용하면 됩니다.
구성요소로, 즉 PermissionHelper
입니다. 사용하려면 종속 항목을 추가합니다.
"//mediapipe/java/com/google/mediapipe/components:android_components"
BUILD
에 규칙이 mediapipe_lib
개 있습니다.
MainActivity
에서 PermissionHelper
를 사용하려면 다음 줄을
onCreate
함수:
PermissionHelper.checkAndRequestCameraPermissions(this);
그러면 사용자에게 다음에 대한 권한을 요청하라는 대화상자가 화면에 표시됩니다. 이 애플리케이션에서 카메라를 사용할 것입니다.
다음 코드를 추가하여 사용자 응답을 처리합니다.
@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() {}
지금은 startCamera()
메서드를 비워 둡니다. 사용자가 응답할 때
MainActivity
가 다시 시작되고 onResume()
가 호출됩니다.
이 코드는 카메라 사용 권한이 부여되었는지 확인합니다.
카메라를 시작합니다.
애플리케이션을 다시 빌드하고 설치합니다. 이제 모바일 인앱 인벤토리에서 애플리케이션 카메라에 액세스할 수 있습니다.
카메라 액세스
카메라 권한을 사용할 수 있게 되면 있습니다.
카메라의 프레임을 보기 위해 SurfaceView
를 사용합니다. 각 프레임
SurfaceTexture
객체에 저장됩니다. 이러한 기능을 사용하기 위해
먼저 애플리케이션의 레이아웃을 변경해야 합니다
다음에서 전체 TextView
코드 블록을 삭제합니다.
$APPLICATION_PATH/res/layout/activity_main.xml
하고 다음 코드를 추가합니다.
다음 코드를 사용하세요.
<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>
이 코드 블록에는 preview_display_layout
라는 새 FrameLayout
및
no_camera_access_preview
로 이름이 지정된 TextView
가 내부에 중첩되어 있습니다. 카메라에서
권한이 부여되지 않으면 애플리케이션은
문자열 메시지가 포함된 TextView
. no_camera_access
변수에 저장됨
$APPLICATION_PATH/res/values/strings.xml
파일에 다음 줄을 추가합니다.
<string name="no_camera_access" translatable="false">Please grant camera permissions.</string>
사용자가 카메라 권한을 부여하지 않으면 이제 화면이 다음과 같이 표시됩니다. 다음과 같습니다.
이제 SurfaceTexture
및 SurfaceView
객체를
MainActivity
:
private SurfaceTexture previewFrameTexture;
private SurfaceView previewDisplayView;
onCreate(Bundle)
함수에서 앞에 다음 두 줄을 추가합니다.
카메라 권한 요청:
previewDisplayView = new SurfaceView(this);
setupPreviewDisplayView();
이제 setupPreviewDisplayView()
를 정의하는 코드를 추가합니다.
private void setupPreviewDisplayView() {
previewDisplayView.setVisibility(View.GONE);
ViewGroup viewGroup = findViewById(R.id.preview_display_layout);
viewGroup.addView(previewDisplayView);
}
새 SurfaceView
객체를 정의하여
preview_display_layout
FrameLayout
객체를 사용하여
previewFrameTexture
이라는 SurfaceTexture
객체를 사용하여 카메라 프레임을 조정합니다.
카메라 프레임을 가져오는 데 previewFrameTexture
를 사용하려면 CameraX를 사용합니다.
프레임워크는 CameraX를 사용할 수 있도록 CameraXPreviewHelper
라는 유틸리티를 제공합니다.
이 클래스는 다음을 통해 카메라가 시작될 때 리스너를 업데이트합니다.
onCameraStarted(@Nullable SurfaceTexture)
이 유틸리티를 사용하려면 BUILD
파일을 수정하여 유틸리티에 종속 항목을 추가합니다.
"//mediapipe/java/com/google/mediapipe/components:android_camerax_helper"
입니다.
이제 CameraXPreviewHelper
를 가져오고 다음 줄을 추가합니다.
MainActivity
:
private CameraXPreviewHelper cameraHelper;
이제 구현을 startCamera()
에 추가할 수 있습니다.
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);
});
}
이렇게 하면 새 CameraXPreviewHelper
객체가 생성되고 익명처리됩니다.
리스너입니다. cameraHelper
에서 카메라가 시작되었음을 알리는 경우
프레임을 가져올 surfaceTexture
가 있으면 저장합니다.
surfaceTexture
로 previewFrameTexture
로 설정하고 previewDisplayView
를
previewFrameTexture
에서 프레임을 볼 수 있습니다.
그러나 카메라를 시작하기 전에 어느 카메라를 사용할지 결정해야 합니다.
사용합니다 CameraXPreviewHelper
는 다음 두 가지를 제공하는 CameraHelper
에서 상속됩니다.
옵션, FRONT
, BACK
입니다. BUILD
파일에서 결정을 전달할 수 있습니다.
다른 버전의 API를 빌드하기 위해 코드를 변경할 필요가 없도록
앱이 다른 카메라를 사용 중입니다.
BACK
카메라를 사용하여 실시간 장면에서 가장자리 감지를 실행한다고 가정
다음과 같이 메타데이터를 AndroidManifest.xml
에 추가합니다.
...
<meta-data android:name="cameraFacingFront" android:value="${cameraFacingFront}"/>
</application>
</manifest>
helloworld
Android 바이너리 규칙의 BUILD
에서 선택을 지정합니다.
manifest_values
에 새 항목이 추가된 경우:
manifest_values = {
"applicationId": "com.google.mediapipe.apps.basic",
"appName": "Hello World",
"mainActivity": ".MainActivity",
"cameraFacingFront": "False",
},
이제 MainActivity
에서 manifest_values
에 지정된 메타데이터를 검색합니다.
ApplicationInfo
객체를 추가합니다.
private ApplicationInfo applicationInfo;
onCreate()
함수에 다음을 추가합니다.
try {
applicationInfo =
getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
} catch (NameNotFoundException e) {
Log.e(TAG, "Cannot find application info: " + e);
}
이제 startCamera()
함수 끝에 다음 줄을 추가합니다.
CameraHelper.CameraFacing cameraFacing =
applicationInfo.metaData.getBoolean("cameraFacingFront", false)
? CameraHelper.CameraFacing.FRONT
: CameraHelper.CameraFacing.BACK;
cameraHelper.startCamera(this, cameraFacing, /*unusedSurfaceTexture=*/ null);
이제 애플리케이션이 성공적으로 빌드됩니다. 그러나
앱이 실행되는 경우 검은색 화면이 표시됩니다.
권한이 부여됨). 왜냐하면 우리가 이 객체를
CameraXPreviewHelper
에서 제공하는 surfaceTexture
변수
previewSurfaceView
는 아직 출력을 사용하지 않으며 화면에 표시하지 않습니다.
MediaPipe 그래프에서 프레임을 사용하려고 하므로 이 튜토리얼에서 직접 카메라 출력을 볼 수 있습니다. 대신 Kubernetes에서 처리할 카메라 프레임을 MediaPipe 그래프로 보내고 출력됩니다.
ExternalTextureConverter
설정
SurfaceTexture
는 스트림에서 이미지 프레임을 OpenGL ES로 캡처합니다.
만들 수도 있습니다 MediaPipe 그래프를 사용하려면 카메라에서 캡처한 프레임이
일반 Open GL 텍스처 객체에 저장됩니다. Framework는
ExternalTextureConverter
: SurfaceTexture
에 저장된 이미지를 변환합니다.
객체를 일반 OpenGL 텍스처 객체로 변환합니다.
ExternalTextureConverter
를 사용하려면 EGLContext
도 필요합니다.
EglManager
객체로 생성 및 관리됩니다. BUILD
에 종속 항목 추가
파일(EglManager
, "//mediapipe/java/com/google/mediapipe/glutil"
사용)
MainActivity
에서 다음 선언을 추가합니다.
private EglManager eglManager;
private ExternalTextureConverter converter;
onCreate(Bundle)
함수에서
eglManager
객체를 사용하여 카메라 권한을 요청합니다.
eglManager = new EglManager(null);
MainActivity
에서 onResume()
함수를 정의해 확인했음을 떠올려 보세요.
카메라 권한이 부여되었으며 startCamera()
을(를) 호출합니다. 변경 전
onResume()
에 다음 줄을 추가하여 converter
를 초기화합니다.
객체:
converter = new ExternalTextureConverter(eglManager.getContext());
이제 이 converter
는 eglManager
에서 관리하는 GLContext
를 사용합니다.
또한 MainActivity
에서 onPause()
함수를 재정의해야 합니다.
애플리케이션이 일시중지 상태가 되면 converter
를 올바르게 닫습니다.
@Override
protected void onPause() {
super.onPause();
converter.close();
}
previewFrameTexture
의 출력을 converter
로 파이핑하려면 다음을 추가합니다.
setupPreviewDisplayView()
에 다음 코드 블록을 추가합니다.
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) {}
});
이 코드 블록에서는 맞춤 SurfaceHolder.Callback
를
previewDisplayView
하고 surfaceChanged(SurfaceHolder holder, int
format, int width, int height)
함수를 구현하여 적절한 디스플레이 크기 계산
기기 화면의 카메라 프레임 중 하나에서 previewFrameTexture
객체를 사용하고 계산된 displaySize
의 프레임을 converter
에 전송합니다.
이제 MediaPipe 그래프에서 카메라 프레임을 사용할 준비가 되었습니다.
Android에서 MediaPipe 그래프 사용
관련 종속 항목 추가
MediaPipe 그래프를 사용하려면 MediaPipe 프레임워크에 종속 항목을 추가해야 합니다.
. 먼저 JNI 코드를 사용하여 cc_binary
를 빌드하는 빌드 규칙을 추가합니다.
그런 다음 cc_library
규칙을 빌드하여 이 바이너리를 사용합니다.
살펴보겠습니다 다음 코드 블록을 BUILD
파일에 추가합니다.
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,
)
mediapipe_lib
빌드 규칙에 종속 항목 ":mediapipe_jni_lib"
를 추가합니다.
BUILD
파일
다음으로 사용하려는 MediaPipe 그래프 관련 종속 항목을 추가해야 합니다. 사용할 수 없습니다.
먼저 libmediapipe_jni.so
의 모든 계산기 코드에 종속 항목을 추가합니다.
빌드 규칙:
"//mediapipe/graphs/edge_detection:mobile_calculators",
MediaPipe 그래프는 .pbtxt
파일이지만 애플리케이션에서 사용하려면
mediapipe_binary_graph
빌드 규칙을 사용하여 .binarypb
파일을 생성합니다.
helloworld
Android 바이너리 빌드 규칙에 mediapipe_binary_graph
를 추가합니다.
그래프와 관련된 타겟으로 정합니다.
assets = [
"//mediapipe/graphs/edge_detection:mobile_gpu_binary_graph",
],
assets_dir = "",
assets
빌드 규칙에서 TensorFlowLite와 같은 다른 애셋도 추가할 수 있습니다.
모델을 학습시키는 작업도
반복해야 합니다
또한manifest_values
나중에 MainActivity
에서 가져올 수 있습니다.
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",
},
binaryGraphName
는 바이너리 그래프의 파일 이름을 나타냅니다.
mediapipe_binary_graph
타겟의 output_name
필드에 의해 결정됩니다.
inputVideoStreamName
와 outputVideoStreamName
는 입력과 출력임
각각 그래프에 지정된 동영상 스트림 이름입니다.
이제 MainActivity
가 MediaPipe 프레임워크를 로드해야 합니다. 또한
프레임워크는 OpenCV를 사용하므로 MainActvity
도 OpenCV
를 로드해야 합니다. 사용
MainActivity
의 다음 코드 (클래스 내부, 함수 내부 아님)
를 사용하여 두 종속 항목을 모두 로드합니다.
static {
// Load all native libraries needed by the app.
System.loadLibrary("mediapipe_jni");
System.loadLibrary("opencv_java3");
}
MainActivity
그래프 사용
먼저 다음에서 컴파일된 .binarypb
가 포함된 애셋을 로드해야 합니다.
그래프의 .pbtxt
파일입니다. 이를 위해 MediaPipe 유틸리티를 사용할 수 있습니다.
AndroidAssetUtil
초기화하기 전에 onCreate(Bundle)
에서 애셋 관리자를 초기화합니다.
eglManager
:
// Initialize asset manager so that MediaPipe native libraries can access the app assets, e.g.,
// binary graphs.
AndroidAssetUtil.initializeNativeAssetManager(this);
이제 카메라 프레임을 전송하는 FrameProcessor
객체를 설정해야 합니다.
converter
에 의해 MediaPipe 그래프에 준비되고, 그래프를 실행하고,
그런 다음 previewDisplayView
를 업데이트하여 출력을 표시합니다. 추가
다음 코드를 사용하여 FrameProcessor
를 선언합니다.
private FrameProcessor processor;
eglManager
를 초기화한 후 onCreate(Bundle)
에서 초기화합니다.
processor =
new FrameProcessor(
this,
eglManager.getNativeContext(),
applicationInfo.metaData.getString("binaryGraphName"),
applicationInfo.metaData.getString("inputVideoStreamName"),
applicationInfo.metaData.getString("outputVideoStreamName"));
processor
는 converter
에서 변환된 프레임을
가장 적합합니다 다음을 초기화한 후 onResume()
에 다음 줄을 추가합니다.
converter
:
converter.setConsumer(processor);
processor
는 출력을 previewDisplayView
에 전송해야 합니다. 이렇게 하려면
맞춤 SurfaceHolder.Callback
에 다음 함수 정의를 추가합니다.
@Override
public void surfaceCreated(SurfaceHolder holder) {
processor.getVideoSurfaceOutput().setSurface(holder.getSurface());
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
processor.getVideoSurfaceOutput().setSurface(null);
}
SurfaceHolder
이 생성되면 Surface
processor
의 VideoSurfaceOutput
입니다. 정보가 파기되면
processor
의 VideoSurfaceOutput
입니다.
이상입니다 이제 Google Cloud 콘솔에서 라이브 카메라에서 실행되는 Sobel 에지 감지를 확인합니다. 피드에! 축하합니다.
문제가 발생한 경우 튜토리얼의 전체 코드를 확인하세요. 여기에서 확인할 수 있습니다.