From bbe2914b2dbf11406038892ec79227b51dd7cd21 Mon Sep 17 00:00:00 2001 From: Duy Mai M Date: Thu, 4 Jan 2024 16:59:10 +0700 Subject: [PATCH 1/3] init project --- examples/holistic/android/.gitignore | 16 + examples/holistic/android/app/.gitignore | 1 + .../holistic/android/app/build.gradle.kts | 78 +++ .../holistic/android/app/proguard-rules.pro | 21 + .../holistic/ExampleInstrumentedTest.kt | 28 ++ .../android/app/src/main/AndroidManifest.xml | 30 ++ .../HolisticLandmarkerHelper.kt | 364 ++++++++++++++ .../holisticlandmarker/MainActivity.kt | 42 ++ .../holisticlandmarker/MainViewModel.kt | 135 +++++ .../holisticlandmarker/OverlayView.kt | 216 ++++++++ .../fragments/CameraFragment.kt | 368 ++++++++++++++ .../fragments/FaceBlendshapesResultAdapter.kt | 81 +++ .../fragments/GalleryFragment.kt | 467 ++++++++++++++++++ .../fragments/PermissionsFragment.kt | 92 ++++ .../app/src/main/res/color/bg_nav_item.xml | 20 + .../main/res/drawable/ic_baseline_add_24.xml | 20 + .../drawable/ic_baseline_photo_camera_24.xml | 21 + .../drawable/ic_baseline_photo_library_24.xml | 20 + .../app/src/main/res/drawable/ic_minus.xml | 24 + .../app/src/main/res/drawable/ic_plus.xml | 24 + .../src/main/res/drawable/icn_chevron_up.png | Bin 0 -> 2429 bytes .../main/res/drawable/media_pipe_banner.xml | 84 ++++ .../app/src/main/res/layout/activity_main.xml | 57 +++ .../res/layout/face_blendshapes_result.xml | 39 ++ .../src/main/res/layout/fragment_camera.xml | 41 ++ .../src/main/res/layout/fragment_gallery.xml | 111 +++++ .../src/main/res/layout/info_bottom_sheet.xml | 457 +++++++++++++++++ .../app/src/main/res/menu/menu_bottom_nav.xml | 27 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 2003 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 4064 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1325 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2386 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 2655 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 5382 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 4404 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 9275 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 5432 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 11590 bytes .../app/src/main/res/navigation/nav_graph.xml | 36 ++ .../app/src/main/res/values/colors.xml | 19 + .../app/src/main/res/values/dimens.xml | 18 + .../app/src/main/res/values/strings.xml | 35 ++ .../app/src/main/res/values/styles.xml | 41 ++ .../examples/holistic/ExampleUnitTest.kt | 17 + examples/holistic/android/build.gradle.kts | 6 + examples/holistic/android/gradle.properties | 23 + .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + examples/holistic/android/gradlew | 185 +++++++ examples/holistic/android/gradlew.bat | 89 ++++ examples/holistic/android/settings.gradle.kts | 17 + 51 files changed, 3376 insertions(+) create mode 100644 examples/holistic/android/.gitignore create mode 100644 examples/holistic/android/app/.gitignore create mode 100644 examples/holistic/android/app/build.gradle.kts create mode 100644 examples/holistic/android/app/proguard-rules.pro create mode 100644 examples/holistic/android/app/src/androidTest/java/com/google/mediapipe/examples/holistic/ExampleInstrumentedTest.kt create mode 100644 examples/holistic/android/app/src/main/AndroidManifest.xml create mode 100644 examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/HolisticLandmarkerHelper.kt create mode 100644 examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainActivity.kt create mode 100644 examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainViewModel.kt create mode 100644 examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/OverlayView.kt create mode 100644 examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/CameraFragment.kt create mode 100644 examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/FaceBlendshapesResultAdapter.kt create mode 100644 examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/GalleryFragment.kt create mode 100644 examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/PermissionsFragment.kt create mode 100644 examples/holistic/android/app/src/main/res/color/bg_nav_item.xml create mode 100644 examples/holistic/android/app/src/main/res/drawable/ic_baseline_add_24.xml create mode 100644 examples/holistic/android/app/src/main/res/drawable/ic_baseline_photo_camera_24.xml create mode 100644 examples/holistic/android/app/src/main/res/drawable/ic_baseline_photo_library_24.xml create mode 100644 examples/holistic/android/app/src/main/res/drawable/ic_minus.xml create mode 100644 examples/holistic/android/app/src/main/res/drawable/ic_plus.xml create mode 100644 examples/holistic/android/app/src/main/res/drawable/icn_chevron_up.png create mode 100644 examples/holistic/android/app/src/main/res/drawable/media_pipe_banner.xml create mode 100644 examples/holistic/android/app/src/main/res/layout/activity_main.xml create mode 100644 examples/holistic/android/app/src/main/res/layout/face_blendshapes_result.xml create mode 100644 examples/holistic/android/app/src/main/res/layout/fragment_camera.xml create mode 100644 examples/holistic/android/app/src/main/res/layout/fragment_gallery.xml create mode 100644 examples/holistic/android/app/src/main/res/layout/info_bottom_sheet.xml create mode 100644 examples/holistic/android/app/src/main/res/menu/menu_bottom_nav.xml create mode 100644 examples/holistic/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 examples/holistic/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 examples/holistic/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 examples/holistic/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 examples/holistic/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 examples/holistic/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 examples/holistic/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 examples/holistic/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 examples/holistic/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 examples/holistic/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 examples/holistic/android/app/src/main/res/navigation/nav_graph.xml create mode 100644 examples/holistic/android/app/src/main/res/values/colors.xml create mode 100644 examples/holistic/android/app/src/main/res/values/dimens.xml create mode 100644 examples/holistic/android/app/src/main/res/values/strings.xml create mode 100644 examples/holistic/android/app/src/main/res/values/styles.xml create mode 100644 examples/holistic/android/app/src/test/java/com/google/mediapipe/examples/holistic/ExampleUnitTest.kt create mode 100644 examples/holistic/android/build.gradle.kts create mode 100644 examples/holistic/android/gradle.properties create mode 100644 examples/holistic/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 examples/holistic/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 examples/holistic/android/gradlew create mode 100644 examples/holistic/android/gradlew.bat create mode 100644 examples/holistic/android/settings.gradle.kts diff --git a/examples/holistic/android/.gitignore b/examples/holistic/android/.gitignore new file mode 100644 index 00000000..7b7fa05d --- /dev/null +++ b/examples/holistic/android/.gitignore @@ -0,0 +1,16 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties +/app/src/main/assets/tasks/holistic_landmarker.task diff --git a/examples/holistic/android/app/.gitignore b/examples/holistic/android/app/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/examples/holistic/android/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/examples/holistic/android/app/build.gradle.kts b/examples/holistic/android/app/build.gradle.kts new file mode 100644 index 00000000..acf83fc2 --- /dev/null +++ b/examples/holistic/android/app/build.gradle.kts @@ -0,0 +1,78 @@ +import de.undercouch.gradle.tasks.download.Download + +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") + id("de.undercouch.download") +} + +android { + namespace = "com.google.mediapipe.examples.holisticlandmarker" + compileSdk = 34 + + defaultConfig { + applicationId = "com.google.mediapipe.examples.holisticlandmarker" + minSdk = 24 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } + buildFeatures { + viewBinding = true + } +} + +val downloadTaskFile = tasks.register("downloadTaskFile") { + src("https://storage.googleapis.com/mediapipe-models/holistic_landmarker/holistic_landmarker/float16/latest/holistic_landmarker.task") + dest("$projectDir/src/main/assets/tasks/holistic_landmarker.task") + overwrite(false) +} + +tasks.named("preBuild") { + dependsOn(downloadTaskFile) +} + +dependencies { + + implementation("androidx.core:core-ktx:1.9.0") + implementation("androidx.appcompat:appcompat:1.6.1") + implementation("com.google.android.material:material:1.10.0") + implementation("androidx.constraintlayout:constraintlayout:2.1.4") + implementation("androidx.navigation:navigation-fragment-ktx:2.7.5") + implementation("androidx.navigation:navigation-ui-ktx:2.7.5") + implementation("androidx.fragment:fragment-ktx:1.6.2") + + // CameraX core library + val cameraxVersion = "1.3.0" + implementation("androidx.camera:camera-core:$cameraxVersion") + // CameraX Camera2 extensions + implementation("androidx.camera:camera-camera2:$cameraxVersion") + // CameraX Lifecycle library + implementation("androidx.camera:camera-lifecycle:$cameraxVersion") + // CameraX View class + implementation("androidx.camera:camera-view:$cameraxVersion") + + implementation("com.google.mediapipe:tasks-vision:0.10.9") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.5") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") +} diff --git a/examples/holistic/android/app/proguard-rules.pro b/examples/holistic/android/app/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/examples/holistic/android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/examples/holistic/android/app/src/androidTest/java/com/google/mediapipe/examples/holistic/ExampleInstrumentedTest.kt b/examples/holistic/android/app/src/androidTest/java/com/google/mediapipe/examples/holistic/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..635a9751 --- /dev/null +++ b/examples/holistic/android/app/src/androidTest/java/com/google/mediapipe/examples/holistic/ExampleInstrumentedTest.kt @@ -0,0 +1,28 @@ +package com.google.mediapipe.examples.holistic + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = + InstrumentationRegistry.getInstrumentation().targetContext + assertEquals( + "com.google.mediapipe.examples.holistic", + appContext.packageName + ) + } +} \ No newline at end of file diff --git a/examples/holistic/android/app/src/main/AndroidManifest.xml b/examples/holistic/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..1fd92efb --- /dev/null +++ b/examples/holistic/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/HolisticLandmarkerHelper.kt b/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/HolisticLandmarkerHelper.kt new file mode 100644 index 00000000..adf7eba1 --- /dev/null +++ b/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/HolisticLandmarkerHelper.kt @@ -0,0 +1,364 @@ +package com.google.mediapipe.examples.holisticlandmarker + +/* + * Copyright 2023 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Matrix +import android.media.MediaMetadataRetriever +import android.net.Uri +import android.os.SystemClock +import android.util.Log +import androidx.camera.core.ImageProxy +import com.google.mediapipe.framework.MediaPipeException +import com.google.mediapipe.framework.image.BitmapImageBuilder +import com.google.mediapipe.framework.image.MPImage +import com.google.mediapipe.tasks.core.BaseOptions +import com.google.mediapipe.tasks.core.Delegate +import com.google.mediapipe.tasks.vision.core.RunningMode +import com.google.mediapipe.tasks.vision.holisticlandmarker.HolisticLandmarker +import com.google.mediapipe.tasks.vision.holisticlandmarker.HolisticLandmarkerResult + +class HolisticLandmarkerHelper( + var currentDelegate: Int = DELEGATE_CPU, + var runningMode: RunningMode = RunningMode.IMAGE, + val context: Context, + val minFaceLandmarksConfidence: Float, + val minHandLandmarksConfidence: Float, + val minPoseLandmarksConfidence: Float, + val minFaceDetectionConfidence: Float, + val minPoseDetectionConfidence: Float, + val minFaceSuppressionThreshold: Float, + val minPoseSuppressionThreshold: Float, + val isFaceBlendShapes: Boolean, + val isPoseSegmentationMark: Boolean, + // this listener is only used when running in RunningMode.LIVE_STREAM + val landmarkerHelperListener: LandmarkerListener? = null +) { + private var holisticLandmarker: HolisticLandmarker? = null + + init { + setUpHolisticLandmarker() + } + + fun setUpHolisticLandmarker() { + val baseOptionBuilder = BaseOptions.builder() + + // Use the specified hardware for running the model. Default to CPU + when (currentDelegate) { + DELEGATE_CPU -> { + baseOptionBuilder.setDelegate(Delegate.CPU) + } + + DELEGATE_GPU -> { + baseOptionBuilder.setDelegate(Delegate.GPU) + } + } + // Check if runningMode is consistent with landmarkerHelperListener + when (runningMode) { + RunningMode.LIVE_STREAM -> { + if (landmarkerHelperListener == null) { + throw IllegalStateException( + "holisticLandmarkerHelperListener must be set when runningMode is LIVE_STREAM." + ) + } + } + + else -> { + // no-op + } + } + try { + baseOptionBuilder.setModelAssetPath(MP_HOLISTIC_LANDMARKER_TASK) + val baseOptions = baseOptionBuilder.build() + val optionsBuilder = + HolisticLandmarker.HolisticLandmarkerOptions.builder() + .setBaseOptions(baseOptions) + .setRunningMode(runningMode) + .setMinFaceLandmarksConfidence(minFaceLandmarksConfidence) + .setMinHandLandmarksConfidence(minHandLandmarksConfidence) + .setMinPoseLandmarksConfidence(minPoseLandmarksConfidence) + .setMinFaceDetectionConfidence(minFaceDetectionConfidence) + .setMinPoseDetectionConfidence(minPoseDetectionConfidence) + .setMinFaceSuppressionThreshold(minFaceSuppressionThreshold) + .setMinPoseSuppressionThreshold(minPoseSuppressionThreshold) + .setOutputFaceBlendshapes(isFaceBlendShapes) + .setOutputPoseSegmentationMasks(isPoseSegmentationMark) + + // The ResultListener and ErrorListener only use for LIVE_STREAM mode. + if (runningMode == RunningMode.LIVE_STREAM) { + optionsBuilder + .setResultListener(this::returnLivestreamResult) + .setErrorListener(this::returnLivestreamError) + } + + val options = optionsBuilder.build() + holisticLandmarker = + HolisticLandmarker.createFromOptions(context, options) + } catch (e: IllegalStateException) { + landmarkerHelperListener?.onError( + "Holistic Landmarker failed to initialize. See error logs for " + + "details" + ) + Log.e( + TAG, "MediaPipe failed to load the task with error: " + e + .message + ) + } catch (e: RuntimeException) { + landmarkerHelperListener?.onError( + "Holistic Landmarker failed to initialize. See error logs for " + + "details" + ) + Log.e( + TAG, "MediaPipe failed to load the task with error: " + e + .message + ) + } + } + + fun isClose(): Boolean { + return holisticLandmarker == null + } + + fun detectVideoFile( + videoUri: Uri, + inferenceIntervalMs: Long + ): VideoResultBundle? { + if (runningMode != RunningMode.VIDEO) { + throw IllegalArgumentException( + "Attempting to call detectVideoFile" + + " while not using RunningMode.VIDEO" + ) + } + + // Inference time is the difference between the system time at the start and finish of the + // process + val startTime = SystemClock.uptimeMillis() + + // Load frames from the video and run the holistic landmarker. + val retriever = MediaMetadataRetriever() + retriever.setDataSource(context, videoUri) + val videoLengthMs = + retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION) + ?.toLong() + + // Note: We need to read width/height from frame instead of getting the width/height + // of the video directly because MediaRetriever returns frames that are smaller than the + // actual dimension of the video file. + val firstFrame = retriever.getFrameAtTime(0) + val width = firstFrame?.width + val height = firstFrame?.height + + // If the video is invalid, returns a null detection result + if ((videoLengthMs == null) || (width == null) || (height == null)) return null + + // Next, we'll get one frame every frameInterval ms, then run detection on these frames. + val resultList = mutableListOf() + val numberOfFrameToRead = videoLengthMs.div(inferenceIntervalMs) + + for (i in 0..numberOfFrameToRead) { + val timestampMs = i * inferenceIntervalMs // ms + + retriever + .getFrameAtTime( + timestampMs * 1000, // convert from ms to micro-s + MediaMetadataRetriever.OPTION_CLOSEST + ) + ?.let { frame -> + // Convert the video frame to ARGB_8888 which is required by the MediaPipe + val argb8888Frame = + if (frame.config == Bitmap.Config.ARGB_8888) frame + else frame.copy(Bitmap.Config.ARGB_8888, false) + + // Convert the input Bitmap object to an MPImage object to run inference + val mpImage = BitmapImageBuilder(argb8888Frame).build() + + // Run holistic landmarker using MediaPipe Holistic Landmarker API + try { + holisticLandmarker?.detectForVideo(mpImage, timestampMs) + ?.let { detectionResult -> + resultList.add(detectionResult) + } + } catch (e: MediaPipeException) { + resultList.add(null); + } + } + } + + retriever.release() + + val inferenceTimePerFrameMs = + (SystemClock.uptimeMillis() - startTime).div(numberOfFrameToRead) + + return VideoResultBundle( + resultList, + inferenceTimePerFrameMs, + height, + width + ) + } + + fun detectImage(image: Bitmap): ResultBundle? { + if (runningMode != RunningMode.IMAGE) { + throw IllegalArgumentException( + "Attempting to call detectImage" + + " while not using RunningMode.IMAGE" + ) + } + + // Inference time is the difference between the system time at the + // start and finish of the process + val startTime = SystemClock.uptimeMillis() + + // Convert the input Bitmap object to an MPImage object to run inference + val mpImage = BitmapImageBuilder(image).build() + + // Run holistic landmarker using MediaPipe Holistic Landmarker API + holisticLandmarker?.detect(mpImage)?.also { landmarkResult -> + val inferenceTimeMs = SystemClock.uptimeMillis() - startTime + return ResultBundle( + landmarkResult, + inferenceTimeMs, + image.height, + image.width + ) + } + + // If holisticLandmarker?.detect() returns null, this is likely an error. Returning null + // to indicate this. + landmarkerHelperListener?.onError( + "Holistic Landmarker failed to detect." + ) + return null + } + + fun detectLiveStreamCamera(imageProxy: ImageProxy, isFrontCamera: Boolean) { + if (runningMode != RunningMode.LIVE_STREAM) { + throw IllegalArgumentException( + "Attempting to call detectLiveStream" + + " while not using RunningMode.LIVE_STREAM" + ) + } + val frameTime = SystemClock.uptimeMillis() + + // Copy out RGB bits from the frame to a bitmap buffer + val bitmapBuffer = + Bitmap.createBitmap( + imageProxy.width, + imageProxy.height, + Bitmap.Config.ARGB_8888 + ) + imageProxy.use { bitmapBuffer.copyPixelsFromBuffer(imageProxy.planes[0].buffer) } + imageProxy.close() + + val matrix = Matrix().apply { + // Rotate the frame received from the camera to be in the same direction as it'll be shown + postRotate(imageProxy.imageInfo.rotationDegrees.toFloat()) + + // flip image if user use front camera + if (isFrontCamera) { + postScale( + -1f, + 1f, + imageProxy.width.toFloat(), + imageProxy.height.toFloat() + ) + } + } + val rotatedBitmap = Bitmap.createBitmap( + bitmapBuffer, 0, 0, bitmapBuffer.width, bitmapBuffer.height, + matrix, true + ) + + // Convert the input Bitmap object to an MPImage object to run inference + val mpImage = BitmapImageBuilder(rotatedBitmap).build() + + detectAsync(mpImage, frameTime) + } + + private fun detectAsync(mpImage: MPImage?, frameTime: Long) { + holisticLandmarker?.detectAsync(mpImage, frameTime) + } + + fun clearHolisticLandmarker() { + holisticLandmarker?.close() + holisticLandmarker = null + } + + private fun returnLivestreamResult( + result: HolisticLandmarkerResult, + input: MPImage + ) { + val finishTimeMs = SystemClock.uptimeMillis() + val inferenceTime = finishTimeMs - result.timestampMs() + + landmarkerHelperListener?.onResults( + ResultBundle( + result, + inferenceTime, + input.height, + input.width + ) + ) + } + + // Return errors thrown during detection to this HolisticLandmarkerHelper's + // caller + private fun returnLivestreamError(error: RuntimeException) { + landmarkerHelperListener?.onError( + error = error.message ?: "Unknown error" + ) + } + + data class ResultBundle( + val result: HolisticLandmarkerResult, + val inferenceTime: Long, + val inputImageHeight: Int, + val inputImageWidth: Int, + ) + + data class VideoResultBundle( + val results: List, + val inferenceTime: Long, + val inputImageHeight: Int, + val inputImageWidth: Int, + ) + + companion object { + private const val MP_HOLISTIC_LANDMARKER_TASK = + "tasks/holistic_landmarker.task" + const val TAG = "HolisticLandmarkerHelper" + const val OTHER_ERROR = 0 + const val GPU_ERROR = 1 + const val DELEGATE_CPU = 0 + const val DELEGATE_GPU = 1 + const val DEFAULT_MIN_FACE_LANDMARKS_CONFIDENCE = 0.5F + const val DEFAULT_MIN_HAND_LANDMARKS_CONFIDENCE = 0.5F + const val DEFAULT_MIN_POSE_LANDMARKS_CONFIDENCE = 0.5F + const val DEFAULT_MIN_FACE_DETECTION_CONFIDENCE = 0.5F + const val DEFAULT_MIN_POSE_DETECTION_CONFIDENCE = 0.5F + const val DEFAULT_MIN_FACE_SUPPRESSION_THRESHOLD = 0.5F + const val DEFAULT_MIN_POSE_SUPPRESSION_THRESHOLD = 0.5F + const val DEFAULT_FACE_BLEND_SHAPES = false + const val DEFAULT_POSE_SEGMENTATION_MARK = false + } + + interface LandmarkerListener { + fun onError(error: String, errorCode: Int = OTHER_ERROR) + fun onResults(resultBundle: ResultBundle) + } +} diff --git a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainActivity.kt b/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainActivity.kt new file mode 100644 index 00000000..2a7ddc80 --- /dev/null +++ b/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainActivity.kt @@ -0,0 +1,42 @@ +package com.google.mediapipe.examples.holisticlandmarker + +/* + * Copyright 2023 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import androidx.activity.viewModels +import androidx.navigation.fragment.NavHostFragment +import androidx.navigation.ui.setupWithNavController +import com.google.mediapipe.examples.holisticlandmarker.databinding.ActivityMainBinding + +class MainActivity : AppCompatActivity() { + private lateinit var activityMainBinding: ActivityMainBinding + private val viewModel: MainViewModel by viewModels() + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + activityMainBinding = ActivityMainBinding.inflate(layoutInflater) + setContentView(activityMainBinding.root) + + val navHostFragment = + supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment + val navController = navHostFragment.navController + activityMainBinding.navigation.setupWithNavController(navController) + activityMainBinding.navigation.setOnNavigationItemReselectedListener { + // ignore the reselection + } + } +} diff --git a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainViewModel.kt b/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainViewModel.kt new file mode 100644 index 00000000..5b6c128d --- /dev/null +++ b/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainViewModel.kt @@ -0,0 +1,135 @@ +package com.google.mediapipe.examples.holisticlandmarker + +/* + * Copyright 2023 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel + +class MainViewModel : ViewModel() { + val helperState: MutableLiveData = MutableLiveData( + HelperState() + ) + + fun setMinFaceLandmarkConfidence(confidence: Float) { + val currentLandmarkConfidence = + helperState.value?.minFaceLandmarkThreshold ?: 0f + helperState.value = helperState.value?.copy( + minFaceLandmarkThreshold = ((currentLandmarkConfidence.toBigDecimal() + confidence.toBigDecimal()).toFloat()).coerceIn( + 0f, + 1f + ) + ) + } + + fun setMinHandLandmarkConfidence(confidence: Float) { + val currentLandmarkConfidence = + helperState.value?.minHandLandmarkThreshold ?: 0f + helperState.value = + helperState.value?.copy( + minHandLandmarkThreshold = ((currentLandmarkConfidence.toBigDecimal() + confidence.toBigDecimal()).toFloat()).coerceIn( + 0f, + 1f + ) + ) + } + + fun setMinPoseLandmarkConfidence(confidence: Float) { + val currentLandmarkConfidence = + helperState.value?.minPoseLandmarkThreshold ?: 0f + helperState.value = + helperState.value?.copy( + minPoseLandmarkThreshold = ((currentLandmarkConfidence.toBigDecimal() + confidence.toBigDecimal()).toFloat()).coerceIn( + 0f, 1f + ) + ) + } + + fun setMinFaceDetectionConfidence(confidence: Float) { + val currentDetectionConfidence = + helperState.value?.minFaceDetectionThreshold ?: 0f + helperState.value = + helperState.value?.copy( + minFaceDetectionThreshold = ((currentDetectionConfidence.toBigDecimal() + confidence.toBigDecimal()).toFloat()).coerceIn( + 0f, + 1f + ) + ) + } + + fun setMinPoseDetectionConfidence(confidence: Float) { + val currentDetectionConfidence = + helperState.value?.minPoseDetectionThreshold ?: 0f + helperState.value = + helperState.value?.copy( + minPoseDetectionThreshold = ((currentDetectionConfidence.toBigDecimal() + confidence.toBigDecimal()).toFloat()).coerceIn( + 0f, + 1f + ) + ) + } + + fun setMinPoseSuppressionConfidence(confidence: Float) { + val currentSuppressionConfidence = + helperState.value?.minPoseSuppressionThreshold ?: 0f + helperState.value = + helperState.value?.copy( + minPoseSuppressionThreshold = ((currentSuppressionConfidence.toBigDecimal() + confidence.toBigDecimal()).toFloat()).coerceIn( + 0f, + 1f + ) + ) + } + + fun setMinFaceSuppressionConfidence(confidence: Float) { + val currentSuppressionConfidence = + helperState.value?.minFaceSuppressionThreshold ?: 0f + helperState.value = + helperState.value?.copy( + minFaceSuppressionThreshold = ((currentSuppressionConfidence.toBigDecimal() + confidence.toBigDecimal()).toFloat()).coerceIn( + 0f, + 1f + ) + ) + } + + fun setFaceBlendMode(faceBlendMode: Boolean) { + helperState.value = + helperState.value?.copy(isFaceBlendMode = faceBlendMode) + } + + fun setPoseSegmentationMarks(poseSegmentationMarks: Boolean) { + helperState.value = + helperState.value?.copy(isPoseSegmentationMarks = poseSegmentationMarks) + } + + fun setDelegate(delegate: Int) { + helperState.value = helperState.value?.copy(delegate = delegate) + } +} + +data class HelperState( + val delegate: Int = HolisticLandmarkerHelper.DELEGATE_CPU, + val minFaceLandmarkThreshold: Float = HolisticLandmarkerHelper.DEFAULT_MIN_FACE_LANDMARKS_CONFIDENCE, + val minHandLandmarkThreshold: Float = HolisticLandmarkerHelper.DEFAULT_MIN_HAND_LANDMARKS_CONFIDENCE, + val minPoseLandmarkThreshold: Float = HolisticLandmarkerHelper.DEFAULT_MIN_POSE_LANDMARKS_CONFIDENCE, + val minFaceDetectionThreshold: Float = HolisticLandmarkerHelper.DEFAULT_MIN_FACE_DETECTION_CONFIDENCE, + val minPoseDetectionThreshold: Float = HolisticLandmarkerHelper.DEFAULT_MIN_POSE_DETECTION_CONFIDENCE, + val minPoseSuppressionThreshold: Float = HolisticLandmarkerHelper.DEFAULT_MIN_POSE_SUPPRESSION_THRESHOLD, + val minFaceSuppressionThreshold: Float = HolisticLandmarkerHelper.DEFAULT_MIN_FACE_SUPPRESSION_THRESHOLD, + val isFaceBlendMode: Boolean = HolisticLandmarkerHelper.DEFAULT_FACE_BLEND_SHAPES, + val isPoseSegmentationMarks: Boolean = HolisticLandmarkerHelper.DEFAULT_POSE_SEGMENTATION_MARK, +) diff --git a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/OverlayView.kt b/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/OverlayView.kt new file mode 100644 index 00000000..923511b9 --- /dev/null +++ b/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/OverlayView.kt @@ -0,0 +1,216 @@ +package com.google.mediapipe.examples.holisticlandmarker + +/* + * Copyright 2023 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.util.AttributeSet +import android.view.View +import androidx.core.content.ContextCompat +import com.google.mediapipe.framework.image.ByteBufferExtractor +import com.google.mediapipe.tasks.vision.core.RunningMode +import com.google.mediapipe.tasks.vision.facelandmarker.FaceLandmarker +import com.google.mediapipe.tasks.vision.handlandmarker.HandLandmarker +import com.google.mediapipe.tasks.vision.holisticlandmarker.HolisticLandmarkerResult +import com.google.mediapipe.tasks.vision.poselandmarker.PoseLandmarker +import kotlin.math.max +import kotlin.math.min +import kotlin.math.roundToInt + +class OverlayView(context: Context?, attrs: AttributeSet?) : + View(context, attrs) { + private var results: HolisticLandmarkerResult? = null + private var facePaint = Paint() + private var handPaint = Paint() + private var posePaint = Paint() + private var pointPaint = Paint() + + private var scaleFactor: Float = 1f + private var imageWidth: Int = 1 + private var imageHeight: Int = 1 + + init { + initPaints() + } + + fun clear() { + results = null + facePaint.reset() + pointPaint.reset() + invalidate() + initPaints() + } + + private fun initPaints() { + facePaint.color = + ContextCompat.getColor(context!!, R.color.mp_color_primary) + facePaint.strokeWidth = LANDMARK_STROKE_WIDTH + facePaint.style = Paint.Style.STROKE + + handPaint.color = + ContextCompat.getColor(context!!, R.color.color_right_hand) + handPaint.strokeWidth = LANDMARK_STROKE_WIDTH + handPaint.style = Paint.Style.STROKE + + posePaint.color = + ContextCompat.getColor(context!!, R.color.color_pose) + posePaint.strokeWidth = LANDMARK_STROKE_WIDTH + posePaint.style = Paint.Style.STROKE + + + pointPaint.color = Color.YELLOW + pointPaint.strokeWidth = LANDMARK_STROKE_WIDTH + pointPaint.style = Paint.Style.FILL + } + + override fun draw(canvas: Canvas) { + super.draw(canvas) + if (results == null || results!!.faceLandmarks().isEmpty()) { + clear() + return + } + // draw segmentation mask if present + if (results?.segmentationMask()?.isPresent == true) { + val buffer = ByteBufferExtractor.extract( + results?.segmentationMask()!!.get() + ) + // convert bytebuffer to bitmap + val bitmap = Bitmap.createBitmap( + results?.segmentationMask()!!.get().width, + results?.segmentationMask()!!.get().height, + Bitmap.Config.ARGB_8888 + ) + bitmap.copyPixelsFromBuffer(buffer) + // scale + val scaledBitmap = Bitmap.createScaledBitmap( + bitmap, + (bitmap.width * scaleFactor).roundToInt(), + (bitmap.height * scaleFactor).roundToInt(), + false + ) + // draw bitmap on canvas + canvas.drawBitmap( + scaledBitmap, + 0f, + 0f, + null + ) + } + + // draw pose landmarks + results?.poseLandmarks()?.let { poseLandmarkerResult -> + PoseLandmarker.POSE_LANDMARKS.forEach { + canvas.drawLine( + poseLandmarkerResult[it!!.start()] + .x() * imageWidth * scaleFactor, + poseLandmarkerResult[it.start()] + .y() * imageHeight * scaleFactor, + poseLandmarkerResult[it.end()] + .x() * imageWidth * scaleFactor, + poseLandmarkerResult[it.end()] + .y() * imageHeight * scaleFactor, + posePaint + ) + } + } + + // draw hand landmarks + results?.leftHandLandmarks()?.let { leftHandLandmarkerResult -> + HandLandmarker.HAND_CONNECTIONS.forEach { + canvas.drawLine( + leftHandLandmarkerResult[it!!.start()] + .x() * imageWidth * scaleFactor, + leftHandLandmarkerResult[it.start()] + .y() * imageHeight * scaleFactor, + leftHandLandmarkerResult[it.end()] + .x() * imageWidth * scaleFactor, + leftHandLandmarkerResult[it.end()] + .y() * imageHeight * scaleFactor, + handPaint + ) + } + } + + results?.rightHandLandmarks()?.let { rightHandLandmarkerResult -> + HandLandmarker.HAND_CONNECTIONS.forEach { + canvas.drawLine( + rightHandLandmarkerResult[it!!.start()] + .x() * imageWidth * scaleFactor, + rightHandLandmarkerResult[it.start()] + .y() * imageHeight * scaleFactor, + rightHandLandmarkerResult[it.end()] + .x() * imageWidth * scaleFactor, + rightHandLandmarkerResult[it.end()] + .y() * imageHeight * scaleFactor, + handPaint + ) + } + } + + results?.faceLandmarks()?.let { faceLandmarkerResult -> + + FaceLandmarker.FACE_LANDMARKS_CONNECTORS.forEach { + canvas.drawLine( + faceLandmarkerResult[it!!.start()] + .x() * imageWidth * scaleFactor, + faceLandmarkerResult[it.start()] + .y() * imageHeight * scaleFactor, + faceLandmarkerResult[it.end()] + .x() * imageWidth * scaleFactor, + faceLandmarkerResult[it.end()] + .y() * imageHeight * scaleFactor, + facePaint + ) + } + } + } + + fun setResults( + holisticLandmarkerResults: HolisticLandmarkerResult?, + imageHeight: Int, + imageWidth: Int, + runningMode: RunningMode = RunningMode.IMAGE + ) { + results = holisticLandmarkerResults + + this.imageHeight = imageHeight + this.imageWidth = imageWidth + + scaleFactor = when (runningMode) { + RunningMode.IMAGE, + RunningMode.VIDEO -> { + min(width * 1f / imageWidth, height * 1f / imageHeight) + } + + RunningMode.LIVE_STREAM -> { + // PreviewView is in FILL_START mode. So we need to scale up the + // landmarks to match with the size that the captured images will be + // displayed. + max(width * 1f / imageWidth, height * 1f / imageHeight) + } + } + invalidate() + } + + companion object { + private const val LANDMARK_STROKE_WIDTH = 8F + private const val TAG = "Holistic Landmarker Overlay" + } +} diff --git a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/CameraFragment.kt b/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/CameraFragment.kt new file mode 100644 index 00000000..cdfe0652 --- /dev/null +++ b/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/CameraFragment.kt @@ -0,0 +1,368 @@ +package com.google.mediapipe.examples.holisticlandmarker.fragments + +/* + * Copyright 2023 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import android.annotation.SuppressLint +import android.content.res.Configuration +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import androidx.camera.core.AspectRatio +import androidx.camera.core.Camera +import androidx.camera.core.CameraSelector +import androidx.camera.core.ImageAnalysis +import androidx.camera.core.ImageProxy +import androidx.camera.core.Preview +import androidx.camera.lifecycle.ProcessCameraProvider +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.navigation.Navigation +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.viewpager2.widget.ViewPager2 +import com.google.mediapipe.examples.holisticlandmarker.HolisticLandmarkerHelper +import com.google.mediapipe.examples.holisticlandmarker.MainViewModel +import com.google.mediapipe.examples.holisticlandmarker.R +import com.google.mediapipe.examples.holisticlandmarker.HelperState +import com.google.mediapipe.examples.holisticlandmarker.databinding.FragmentCameraBinding +import com.google.mediapipe.tasks.vision.core.RunningMode +import java.util.* +import java.util.concurrent.* + +class CameraFragment() : Fragment(), + HolisticLandmarkerHelper.LandmarkerListener { + companion object { + private const val TAG = "Holistic Landmarker" + } + + private var _fragmentCameraBinding: FragmentCameraBinding? = null + + private val fragmentCameraBinding + get() = _fragmentCameraBinding!! + + private val viewModel: MainViewModel by activityViewModels() + + private var preview: Preview? = null + private var imageAnalyzer: ImageAnalysis? = null + private var camera: Camera? = null + private var cameraProvider: ProcessCameraProvider? = null + private var cameraFacing = CameraSelector.LENS_FACING_BACK + private lateinit var holisticLandmarkerHelper: HolisticLandmarkerHelper; + private val faceBlendshapesResultAdapter by lazy { + FaceBlendshapesResultAdapter() + } + + /** Blocking ML operations are performed using this executor */ + private lateinit var backgroundExecutor: ExecutorService + + override fun onResume() { + super.onResume() + // Make sure that all permissions are still present, since the + // user could have removed them while the app was in paused state. + if (!PermissionsFragment.hasPermissions(requireContext())) { + Navigation.findNavController( + requireActivity(), R.id.fragment_container + ).navigate(R.id.action_camera_to_permissions) + } + + // Start the HolisticLandmarkerHelper again when users come back + // to the foreground. + backgroundExecutor.execute { + if (holisticLandmarkerHelper.isClose()) { + holisticLandmarkerHelper.setUpHolisticLandmarker() + } + } + } + + override fun onPause() { + super.onPause() + if (this::holisticLandmarkerHelper.isInitialized) { + // Close the HolisticLandmarkerHelper and release resources + backgroundExecutor.execute { + holisticLandmarkerHelper.clearHolisticLandmarker() + } + } + } + + override fun onDestroyView() { + _fragmentCameraBinding = null + super.onDestroyView() + + // Shut down our background executor + backgroundExecutor.shutdown() + backgroundExecutor.awaitTermination( + Long.MAX_VALUE, TimeUnit.NANOSECONDS + ) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _fragmentCameraBinding = + FragmentCameraBinding.inflate(inflater, container, false) + + return fragmentCameraBinding.root + } + + @SuppressLint("MissingPermission") + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + with(fragmentCameraBinding.recyclerviewResults) { + layoutManager = LinearLayoutManager(requireContext()) + adapter = faceBlendshapesResultAdapter + } + + // Initialize our background executor + backgroundExecutor = Executors.newSingleThreadExecutor() + + // Wait for the views to be properly laid out + fragmentCameraBinding.viewFinder.post { + // Set up the camera and its use cases + setUpCamera() + } + setUpListener() + viewModel.helperState.observe(viewLifecycleOwner) { helperState -> + updateBottomSheetControlsUi(helperState) + } + } + + private fun setUpListener() { + with(fragmentCameraBinding.bottomSheetLayout) { + faceLandmarksThresholdMinus.setOnClickListener { + viewModel.setMinFaceLandmarkConfidence(-0.1f) + } + faceLandmarksThresholdPlus.setOnClickListener { + viewModel.setMinFaceLandmarkConfidence(0.1f) + } + poseLandmarksThresholdMinus.setOnClickListener { + viewModel.setMinPoseLandmarkConfidence(-0.1f) + } + poseLandmarksThresholdPlus.setOnClickListener { + viewModel.setMinPoseLandmarkConfidence(0.1f) + } + handLandmarksThresholdMinus.setOnClickListener { + viewModel.setMinHandLandmarkConfidence(-0.1f) + } + handLandmarksThresholdPlus.setOnClickListener { + viewModel.setMinHandLandmarkConfidence(0.1f) + } + faceDetectionThresholdMinus.setOnClickListener { + viewModel.setMinFaceDetectionConfidence(-0.1f) + } + faceDetectionThresholdPlus.setOnClickListener { + viewModel.setMinFaceDetectionConfidence(0.1f) + } + poseDetectionThresholdMinus.setOnClickListener { + viewModel.setMinPoseDetectionConfidence(-0.1f) + } + poseDetectionThresholdPlus.setOnClickListener { + viewModel.setMinPoseDetectionConfidence(0.1f) + } + faceSuppressionMinus.setOnClickListener { + viewModel.setMinFaceSuppressionConfidence(-0.1f) + } + faceSuppressionPlus.setOnClickListener { + viewModel.setMinFaceSuppressionConfidence(0.1f) + } + poseSuppressionMinus.setOnClickListener { + viewModel.setMinPoseSuppressionConfidence(-0.1f) + } + poseSuppressionPlus.setOnClickListener { + viewModel.setMinPoseSuppressionConfidence(0.1f) + } + switchFaceBlendShapes.setOnCheckedChangeListener { _, isChecked -> + viewModel.setFaceBlendMode(isChecked) + } + switchPoseSegmentationMarks.setOnCheckedChangeListener { _, isChecked -> + viewModel.setPoseSegmentationMarks(isChecked) + } + spinnerDelegate.onItemSelectedListener = + object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long + ) { + try { + viewModel.setDelegate(p2) + } catch (e: UninitializedPropertyAccessException) { + Log.e( + TAG, + "HolisticLandmarkerHelper has not been initialized yet." + ) + } + } + + override fun onNothingSelected(p0: AdapterView<*>?) { + /* no op */ + } + } + } + } + + private fun updateBottomSheetControlsUi(helperState: HelperState) { + // update bottom sheet settings + with(fragmentCameraBinding.bottomSheetLayout){ + faceLandmarksThresholdValue.text = helperState.minFaceLandmarkThreshold.toString() + poseLandmarksThresholdValue.text = helperState.minPoseLandmarkThreshold.toString() + handLandmarksThresholdValue.text = helperState.minHandLandmarkThreshold.toString() + faceDetectionThresholdValue.text = helperState.minFaceDetectionThreshold.toString() + poseDetectionThresholdValue.text = helperState.minPoseDetectionThreshold.toString() + faceSuppressionValue.text = helperState.minFaceSuppressionThreshold.toString() + poseSuppressionValue.text = helperState.minPoseSuppressionThreshold.toString() + switchFaceBlendShapes.isChecked = helperState.isFaceBlendMode + switchPoseSegmentationMarks.isChecked = helperState.isPoseSegmentationMarks + } + + // Create the HolisticLandmarkerHelper that will handle the inference + backgroundExecutor.execute { + // clear it and recreate with new settings + holisticLandmarkerHelper = HolisticLandmarkerHelper( + context = requireContext(), + runningMode = RunningMode.LIVE_STREAM, + currentDelegate = helperState.delegate, + minFaceLandmarksConfidence = helperState.minFaceLandmarkThreshold, + minHandLandmarksConfidence = helperState.minHandLandmarkThreshold, + minPoseLandmarksConfidence = helperState.minPoseLandmarkThreshold, + minFaceDetectionConfidence = helperState.minFaceDetectionThreshold, + minPoseDetectionConfidence = helperState.minPoseDetectionThreshold, + minFaceSuppressionThreshold = helperState.minFaceSuppressionThreshold, + minPoseSuppressionThreshold = helperState.minPoseSuppressionThreshold, + isFaceBlendShapes = helperState.isFaceBlendMode, + isPoseSegmentationMark = helperState.isPoseSegmentationMarks, + landmarkerHelperListener = this + ) + _fragmentCameraBinding?.overlay?.clear() + } + } + + // Initialize CameraX, and prepare to bind the camera use cases + private fun setUpCamera() { + val cameraProviderFuture = + ProcessCameraProvider.getInstance(requireContext()) + cameraProviderFuture.addListener( + { + // CameraProvider + cameraProvider = cameraProviderFuture.get() + + // Build and bind the camera use cases + bindCameraUseCases() + }, ContextCompat.getMainExecutor(requireContext()) + ) + } + + // Declare and bind preview, capture and analysis use cases + @SuppressLint("UnsafeOptInUsageError") + private fun bindCameraUseCases() { + + // CameraProvider + val cameraProvider = cameraProvider + ?: throw IllegalStateException("Camera initialization failed.") + + val cameraSelector = + CameraSelector.Builder().requireLensFacing(cameraFacing).build() + + // Preview. Only using the 4:3 ratio because this is the closest to our models + preview = Preview.Builder().setTargetAspectRatio(AspectRatio.RATIO_4_3) + .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation) + .build() + + // ImageAnalysis. Using RGBA 8888 to match how our models work + imageAnalyzer = + ImageAnalysis.Builder().setTargetAspectRatio(AspectRatio.RATIO_4_3) + .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation) + .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) + .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888) + .build() + // The analyzer can then be assigned to the instance + .also { + it.setAnalyzer(backgroundExecutor) { image -> + detectFace(image) + } + } + + // Must unbind the use-cases before rebinding them + cameraProvider.unbindAll() + + try { + // A variable number of use-cases can be passed here - + // camera provides access to CameraControl & CameraInfo + camera = cameraProvider.bindToLifecycle( + this, cameraSelector, preview, imageAnalyzer + ) + + // Attach the viewfinder's surface provider to preview use case + preview?.setSurfaceProvider(fragmentCameraBinding.viewFinder.surfaceProvider) + } catch (exc: Exception) { + Log.e(TAG, "Use case binding failed", exc) + } + } + + private fun detectFace(imageProxy: ImageProxy) { + holisticLandmarkerHelper.detectLiveStreamCamera( + imageProxy = imageProxy, + isFrontCamera = cameraFacing == CameraSelector.LENS_FACING_FRONT + ) + } + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + imageAnalyzer?.targetRotation = + fragmentCameraBinding.viewFinder.display.rotation + } + + override fun onError(error: String, errorCode: Int) { + Log.e(TAG, "An error $error, code $errorCode occurred") + activity?.runOnUiThread { + faceBlendshapesResultAdapter.updateResults(null) + faceBlendshapesResultAdapter.notifyDataSetChanged() + } + } + + // Update UI after face have been detected. Extracts original + // image height/width to scale and place the landmarks properly through + // OverlayView + override fun onResults( + resultBundle: HolisticLandmarkerHelper.ResultBundle + ) { + activity?.runOnUiThread { + if (_fragmentCameraBinding != null) { + if (fragmentCameraBinding.recyclerviewResults.scrollState != ViewPager2.SCROLL_STATE_DRAGGING) { + faceBlendshapesResultAdapter.updateResults(resultBundle.result) + faceBlendshapesResultAdapter.notifyDataSetChanged() + } + + fragmentCameraBinding.bottomSheetLayout.inferenceTimeVal.text = + String.format("%d ms", resultBundle.inferenceTime) + + // Pass necessary information to OverlayView for drawing on the canvas + fragmentCameraBinding.overlay.setResults( + resultBundle.result, + resultBundle.inputImageHeight, + resultBundle.inputImageWidth, + RunningMode.LIVE_STREAM + ) + // Force a redraw + fragmentCameraBinding.overlay.invalidate() + } + } + } +} \ No newline at end of file diff --git a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/FaceBlendshapesResultAdapter.kt b/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/FaceBlendshapesResultAdapter.kt new file mode 100644 index 00000000..55982172 --- /dev/null +++ b/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/FaceBlendshapesResultAdapter.kt @@ -0,0 +1,81 @@ +/* + * Copyright 2023 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.mediapipe.examples.holisticlandmarker.fragments + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.google.mediapipe.examples.holisticlandmarker.databinding.FaceBlendshapesResultBinding +import com.google.mediapipe.tasks.components.containers.Category +import com.google.mediapipe.tasks.vision.holisticlandmarker.HolisticLandmarkerResult + +class FaceBlendshapesResultAdapter : + RecyclerView.Adapter() { + companion object { + private const val NO_VALUE = "--" + } + + private var categories: MutableList = MutableList(52) { null } + + fun updateResults(faceLandmarkerResult: HolisticLandmarkerResult? = null) { + categories = MutableList(52) { null } + if (faceLandmarkerResult?.faceBlendshapes()?.isPresent == true) { + val sortedCategories = + faceLandmarkerResult.faceBlendshapes().get() + .sortedBy { -it.score() } + val min = + kotlin.math.min(sortedCategories.size, categories.size) + for (i in 0 until min) { + categories[i] = sortedCategories[i] + } + } + } + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): ViewHolder { + val binding = FaceBlendshapesResultBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + return ViewHolder(binding) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + categories[position].let { category -> + holder.bind(category?.categoryName(), category?.score()) + } + } + + override fun getItemCount(): Int = categories.size + + inner class ViewHolder(private val binding: FaceBlendshapesResultBinding) : + RecyclerView.ViewHolder(binding.root) { + + fun bind(label: String?, score: Float?) { + with(binding) { + tvLabel.text = label ?: NO_VALUE + tvScore.text = if (score != null) String.format( + "%.2f", + score + ) else NO_VALUE + } + } + } +} diff --git a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/GalleryFragment.kt b/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/GalleryFragment.kt new file mode 100644 index 00000000..1f42a904 --- /dev/null +++ b/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/GalleryFragment.kt @@ -0,0 +1,467 @@ +package com.google.mediapipe.examples.holisticlandmarker.fragments + +/* + * Copyright 2023 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import android.graphics.Bitmap +import android.graphics.ImageDecoder +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.os.SystemClock +import android.provider.MediaStore +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.viewpager2.widget.ViewPager2 +import com.google.mediapipe.examples.holisticlandmarker.HelperState +import com.google.mediapipe.examples.holisticlandmarker.HolisticLandmarkerHelper +import com.google.mediapipe.examples.holisticlandmarker.MainViewModel +import com.google.mediapipe.examples.holisticlandmarker.R +import com.google.mediapipe.examples.holisticlandmarker.databinding.FragmentGalleryBinding +import com.google.mediapipe.tasks.vision.core.RunningMode +import java.util.concurrent.* + +class GalleryFragment : Fragment(), + HolisticLandmarkerHelper.LandmarkerListener { + enum class MediaType { + IMAGE, VIDEO, UNKNOWN + } + + private var _fragmentGalleryBinding: FragmentGalleryBinding? = null + private val fragmentGalleryBinding + get() = _fragmentGalleryBinding!! + private lateinit var holisticLandmarkerHelper: HolisticLandmarkerHelper + private val viewModel: MainViewModel by activityViewModels() + private val faceBlendshapesResultAdapter by lazy { + FaceBlendshapesResultAdapter() + } + private lateinit var backgroundExecutor: ScheduledExecutorService + + private val getContent = + registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri: Uri? -> + // Handle the returned Uri + uri?.let { mediaUri -> + when (val mediaType = loadMediaType(mediaUri)) { + MediaType.IMAGE -> runDetectionOnImage(mediaUri) + MediaType.VIDEO -> runDetectionOnVideo(mediaUri) + MediaType.UNKNOWN -> { + updateDisplayView(mediaType) + Toast.makeText( + requireContext(), + "Unsupported data type.", + Toast.LENGTH_SHORT + ).show() + } + } + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _fragmentGalleryBinding = + FragmentGalleryBinding.inflate(inflater, container, false) + + return fragmentGalleryBinding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + with(fragmentGalleryBinding.recyclerviewResults) { + layoutManager = LinearLayoutManager(requireContext()) + adapter = faceBlendshapesResultAdapter + } + fragmentGalleryBinding.fabGetContent.setOnClickListener { + getContent.launch(arrayOf("image/*", "video/*")) + // reset the view + clearView() + } + setUpListener() + viewModel.helperState.observe(viewLifecycleOwner) { + updateBottomSheetControlsUi(it) + } + } + + override fun onPause() { + super.onPause() + clearView() + } + + private fun setUpListener() { + with(fragmentGalleryBinding.bottomSheetLayout) { + faceLandmarksThresholdMinus.setOnClickListener { + viewModel.setMinFaceLandmarkConfidence(-0.1f) + } + faceLandmarksThresholdPlus.setOnClickListener { + viewModel.setMinFaceLandmarkConfidence(0.1f) + } + poseLandmarksThresholdMinus.setOnClickListener { + viewModel.setMinPoseLandmarkConfidence(-0.1f) + } + poseLandmarksThresholdPlus.setOnClickListener { + viewModel.setMinPoseLandmarkConfidence(0.1f) + } + handLandmarksThresholdMinus.setOnClickListener { + viewModel.setMinHandLandmarkConfidence(-0.1f) + } + handLandmarksThresholdPlus.setOnClickListener { + viewModel.setMinHandLandmarkConfidence(0.1f) + } + faceDetectionThresholdMinus.setOnClickListener { + viewModel.setMinFaceDetectionConfidence(-0.1f) + } + faceDetectionThresholdPlus.setOnClickListener { + viewModel.setMinFaceDetectionConfidence(0.1f) + } + poseDetectionThresholdMinus.setOnClickListener { + viewModel.setMinPoseDetectionConfidence(-0.1f) + } + poseDetectionThresholdPlus.setOnClickListener { + viewModel.setMinPoseDetectionConfidence(0.1f) + } + faceSuppressionMinus.setOnClickListener { + viewModel.setMinFaceSuppressionConfidence(-0.1f) + } + faceSuppressionPlus.setOnClickListener { + viewModel.setMinFaceSuppressionConfidence(0.1f) + } + poseSuppressionMinus.setOnClickListener { + viewModel.setMinPoseSuppressionConfidence(-0.1f) + } + poseSuppressionPlus.setOnClickListener { + viewModel.setMinPoseSuppressionConfidence(0.1f) + } + switchFaceBlendShapes.setOnCheckedChangeListener { _, isChecked -> + viewModel.setFaceBlendMode(isChecked) + } + switchPoseSegmentationMarks.setOnCheckedChangeListener { _, isChecked -> + viewModel.setPoseSegmentationMarks(isChecked) + } + + spinnerDelegate.onItemSelectedListener = + object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long + ) { + try { + viewModel.setDelegate(p2) + } catch (e: UninitializedPropertyAccessException) { + Log.e( + TAG, + "HolisticLandmarkerHelper has not been initialized yet." + ) + } + } + + override fun onNothingSelected(p0: AdapterView<*>?) {/* no op */ + } + } + } + } + + private fun updateBottomSheetControlsUi(uiState: HelperState) { + // init bottom sheet settings + with(fragmentGalleryBinding.bottomSheetLayout) { + faceLandmarksThresholdValue.text = + uiState.minFaceLandmarkThreshold.toString() + poseLandmarksThresholdValue.text = + uiState.minPoseLandmarkThreshold.toString() + handLandmarksThresholdValue.text = + uiState.minHandLandmarkThreshold.toString() + faceDetectionThresholdValue.text = + uiState.minFaceDetectionThreshold.toString() + poseDetectionThresholdValue.text = + uiState.minPoseDetectionThreshold.toString() + faceSuppressionValue.text = + uiState.minFaceSuppressionThreshold.toString() + poseSuppressionValue.text = + uiState.minPoseSuppressionThreshold.toString() + switchFaceBlendShapes.isChecked = uiState.isFaceBlendMode + switchPoseSegmentationMarks.isChecked = + uiState.isPoseSegmentationMarks + } + clearView() + } + + // Load and display the image. + private fun runDetectionOnImage(uri: Uri) { + setUiEnabled(false) + updateDisplayView(MediaType.IMAGE) + backgroundExecutor = Executors.newSingleThreadScheduledExecutor() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + val source = ImageDecoder.createSource( + requireActivity().contentResolver, uri + ) + ImageDecoder.decodeBitmap(source) + } else { + MediaStore.Images.Media.getBitmap( + requireActivity().contentResolver, uri + ) + }.copy(Bitmap.Config.ARGB_8888, true)?.let { bitmap -> + fragmentGalleryBinding.imageResult.setImageBitmap(bitmap) + + // Run face landmarker on the input image + backgroundExecutor.execute { + viewModel.helperState.value?.let { + holisticLandmarkerHelper = HolisticLandmarkerHelper( + context = requireContext(), + runningMode = RunningMode.IMAGE, + currentDelegate = it.delegate, + minFaceLandmarksConfidence = it.minFaceLandmarkThreshold, + minHandLandmarksConfidence = it.minHandLandmarkThreshold, + minPoseLandmarksConfidence = it.minPoseLandmarkThreshold, + minFaceDetectionConfidence = it.minFaceDetectionThreshold, + minPoseDetectionConfidence = it.minPoseDetectionThreshold, + minFaceSuppressionThreshold = it.minFaceSuppressionThreshold, + minPoseSuppressionThreshold = it.minPoseSuppressionThreshold, + isFaceBlendShapes = it.isFaceBlendMode, + isPoseSegmentationMark = it.isPoseSegmentationMarks, + landmarkerHelperListener = this + ) + } + + holisticLandmarkerHelper.detectImage(bitmap) + ?.let { result -> + activity?.runOnUiThread { + if (fragmentGalleryBinding.recyclerviewResults.scrollState != ViewPager2.SCROLL_STATE_DRAGGING) { + faceBlendshapesResultAdapter.updateResults( + result.result + ) + faceBlendshapesResultAdapter.notifyDataSetChanged() + } + + fragmentGalleryBinding.overlay.setResults( + result.result, + bitmap.height, + bitmap.width, + RunningMode.IMAGE + ) + + setUiEnabled(true) + + fragmentGalleryBinding.bottomSheetLayout.inferenceTimeVal.text = + String.format("%d ms", result.inferenceTime) + } + } ?: run { + activity?.runOnUiThread { + setUiEnabled(true) + } + Log.e( + TAG, "Error running face landmarker." + ) + } + + holisticLandmarkerHelper.clearHolisticLandmarker() + } + } + } + + // clear view when switching between image and video + private fun clearView() { + with(fragmentGalleryBinding) { + tvPlaceholder.visibility = View.VISIBLE + bottomSheetLayout.inferenceTimeVal.text = + getString(R.string.tv_default_inference_time) + if (videoView.isPlaying) { + videoView.stopPlayback() + } + videoView.visibility = View.GONE + imageResult.visibility = View.GONE + overlay.clear() + } + faceBlendshapesResultAdapter.updateResults(null) + faceBlendshapesResultAdapter.notifyDataSetChanged() + } + + private fun runDetectionOnVideo(uri: Uri) { + setUiEnabled(false) + updateDisplayView(MediaType.VIDEO) + + with(fragmentGalleryBinding.videoView) { + setVideoURI(uri) + // mute the audio + setOnPreparedListener { it.setVolume(0f, 0f) } + requestFocus() + } + backgroundExecutor = Executors.newSingleThreadScheduledExecutor() + + backgroundExecutor.execute { + + viewModel.helperState.value?.let { + holisticLandmarkerHelper = HolisticLandmarkerHelper( + context = requireContext(), + runningMode = RunningMode.VIDEO, + currentDelegate = it.delegate, + minFaceLandmarksConfidence = it.minFaceLandmarkThreshold, + minHandLandmarksConfidence = it.minHandLandmarkThreshold, + minPoseLandmarksConfidence = it.minPoseLandmarkThreshold, + minFaceDetectionConfidence = it.minFaceDetectionThreshold, + minPoseDetectionConfidence = it.minPoseDetectionThreshold, + minFaceSuppressionThreshold = it.minFaceSuppressionThreshold, + minPoseSuppressionThreshold = it.minPoseSuppressionThreshold, + isFaceBlendShapes = it.isFaceBlendMode, + isPoseSegmentationMark = it.isPoseSegmentationMarks, + landmarkerHelperListener = this + ) + } + + activity?.runOnUiThread { + fragmentGalleryBinding.videoView.visibility = View.GONE + fragmentGalleryBinding.progress.visibility = View.VISIBLE + } + + holisticLandmarkerHelper.detectVideoFile(uri, VIDEO_INTERVAL_MS) + ?.let { resultBundle -> + activity?.runOnUiThread { displayVideoResult(resultBundle) } + } ?: run { + setUiEnabled(true) + Log.e(TAG, "Error running face landmarker.") + } + + holisticLandmarkerHelper.clearHolisticLandmarker() + } + } + + // Setup and display the video. + private fun displayVideoResult(result: HolisticLandmarkerHelper.VideoResultBundle) { + + fragmentGalleryBinding.videoView.visibility = View.VISIBLE + fragmentGalleryBinding.progress.visibility = View.GONE + + fragmentGalleryBinding.videoView.start() + val videoStartTimeMs = SystemClock.uptimeMillis() + + backgroundExecutor.scheduleAtFixedRate( + { + activity?.runOnUiThread { + val videoElapsedTimeMs = + SystemClock.uptimeMillis() - videoStartTimeMs + val resultIndex = + videoElapsedTimeMs.div(VIDEO_INTERVAL_MS).toInt() + + if (resultIndex >= result.results.size || fragmentGalleryBinding.videoView.visibility == View.GONE) { + // The video playback has finished so we stop drawing bounding boxes + backgroundExecutor.shutdown() + } else { + fragmentGalleryBinding.overlay.setResults( + result.results[resultIndex], + result.inputImageHeight, + result.inputImageWidth, + RunningMode.VIDEO + ) + + if (fragmentGalleryBinding.recyclerviewResults.scrollState != ViewPager2.SCROLL_STATE_DRAGGING) { + faceBlendshapesResultAdapter.updateResults(result.results[resultIndex]) + faceBlendshapesResultAdapter.notifyDataSetChanged() + } + + setUiEnabled(true) + + fragmentGalleryBinding.bottomSheetLayout.inferenceTimeVal.text = + String.format("%d ms", result.inferenceTime) + } + } + }, 0, VIDEO_INTERVAL_MS, TimeUnit.MILLISECONDS + ) + } + + private fun updateDisplayView(mediaType: MediaType) { + fragmentGalleryBinding.imageResult.visibility = + if (mediaType == MediaType.IMAGE) View.VISIBLE else View.GONE + fragmentGalleryBinding.videoView.visibility = + if (mediaType == MediaType.VIDEO) View.VISIBLE else View.GONE + fragmentGalleryBinding.tvPlaceholder.visibility = + if (mediaType == MediaType.UNKNOWN) View.VISIBLE else View.GONE + } + + // Check the type of media that user selected. + private fun loadMediaType(uri: Uri): MediaType { + val mimeType = context?.contentResolver?.getType(uri) + mimeType?.let { + if (mimeType.startsWith("image")) return MediaType.IMAGE + if (mimeType.startsWith("video")) return MediaType.VIDEO + } + + return MediaType.UNKNOWN + } + + private fun setUiEnabled(enabled: Boolean) { + fragmentGalleryBinding.progress.visibility = + if (enabled) View.GONE else View.VISIBLE + fragmentGalleryBinding.fabGetContent.isEnabled = enabled + with(fragmentGalleryBinding.bottomSheetLayout) { + faceLandmarksThresholdMinus.isEnabled = enabled + faceLandmarksThresholdPlus.isEnabled = enabled + poseLandmarksThresholdMinus.isEnabled = enabled + poseLandmarksThresholdPlus.isEnabled = enabled + handLandmarksThresholdMinus.isEnabled = enabled + handLandmarksThresholdPlus.isEnabled = enabled + faceDetectionThresholdMinus.isEnabled = enabled + faceDetectionThresholdPlus.isEnabled = enabled + poseDetectionThresholdMinus.isEnabled = enabled + poseDetectionThresholdPlus.isEnabled = enabled + faceSuppressionMinus.isEnabled = enabled + faceSuppressionPlus.isEnabled = enabled + poseSuppressionMinus.isEnabled = enabled + poseSuppressionPlus.isEnabled = enabled + switchFaceBlendShapes.isEnabled = enabled + switchPoseSegmentationMarks.isEnabled = enabled + spinnerDelegate.isEnabled = enabled + } + } + + private fun classifyingError() { + activity?.runOnUiThread { + fragmentGalleryBinding.progress.visibility = View.GONE + setUiEnabled(true) + updateDisplayView(MediaType.UNKNOWN) + } + } + + override fun onError(error: String, errorCode: Int) { + classifyingError() + activity?.runOnUiThread { + Toast.makeText(requireContext(), error, Toast.LENGTH_SHORT).show() + if (errorCode == HolisticLandmarkerHelper.GPU_ERROR) { + fragmentGalleryBinding.bottomSheetLayout.spinnerDelegate.setSelection( + HolisticLandmarkerHelper.DELEGATE_CPU, false + ) + } + } + } + + override fun onResults(resultBundle: HolisticLandmarkerHelper.ResultBundle) { + // no-op + } + + companion object { + private const val TAG = "GalleryFragment" + + // Value used to get frames at specific intervals for inference (e.g. every 300ms) + private const val VIDEO_INTERVAL_MS = 300L + } + +} diff --git a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/PermissionsFragment.kt b/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/PermissionsFragment.kt new file mode 100644 index 00000000..845dad05 --- /dev/null +++ b/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/PermissionsFragment.kt @@ -0,0 +1,92 @@ +package com.google.mediapipe.examples.holisticlandmarker.fragments + +/* + * Copyright 2023 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import android.Manifest +import android.content.Context +import android.content.pm.PackageManager +import android.os.Bundle +import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import androidx.navigation.Navigation +import com.google.mediapipe.examples.holisticlandmarker.R + +private val PERMISSIONS_REQUIRED = arrayOf(Manifest.permission.CAMERA) + +class PermissionsFragment: Fragment() { + private val requestPermissionLauncher = + registerForActivityResult( + ActivityResultContracts.RequestPermission() + ) { isGranted: Boolean -> + if (isGranted) { + Toast.makeText( + context, + "Permission request granted", + Toast.LENGTH_LONG + ).show() + navigateToCamera() + } else { + Toast.makeText( + context, + "Permission request denied", + Toast.LENGTH_LONG + ).show() + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + when (PackageManager.PERMISSION_GRANTED) { + ContextCompat.checkSelfPermission( + requireContext(), + Manifest.permission.CAMERA + ) -> { + navigateToCamera() + } + else -> { + requestPermissionLauncher.launch( + Manifest.permission.CAMERA + ) + } + } + } + + private fun navigateToCamera() { + lifecycleScope.launchWhenStarted { + Navigation.findNavController( + requireActivity(), + R.id.fragment_container + ).navigate( + R.id.action_permissions_to_camera + ) + } + } + + companion object { + + /** Convenience method used to check if all permissions required by this app are granted */ + fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all { + ContextCompat.checkSelfPermission( + context, + it + ) == PackageManager.PERMISSION_GRANTED + } + } +} diff --git a/examples/holistic/android/app/src/main/res/color/bg_nav_item.xml b/examples/holistic/android/app/src/main/res/color/bg_nav_item.xml new file mode 100644 index 00000000..6dc6b015 --- /dev/null +++ b/examples/holistic/android/app/src/main/res/color/bg_nav_item.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/examples/holistic/android/app/src/main/res/drawable/ic_baseline_add_24.xml b/examples/holistic/android/app/src/main/res/drawable/ic_baseline_add_24.xml new file mode 100644 index 00000000..4b1f08d2 --- /dev/null +++ b/examples/holistic/android/app/src/main/res/drawable/ic_baseline_add_24.xml @@ -0,0 +1,20 @@ + + + + diff --git a/examples/holistic/android/app/src/main/res/drawable/ic_baseline_photo_camera_24.xml b/examples/holistic/android/app/src/main/res/drawable/ic_baseline_photo_camera_24.xml new file mode 100644 index 00000000..3a623419 --- /dev/null +++ b/examples/holistic/android/app/src/main/res/drawable/ic_baseline_photo_camera_24.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/examples/holistic/android/app/src/main/res/drawable/ic_baseline_photo_library_24.xml b/examples/holistic/android/app/src/main/res/drawable/ic_baseline_photo_library_24.xml new file mode 100644 index 00000000..d9d3ef5f --- /dev/null +++ b/examples/holistic/android/app/src/main/res/drawable/ic_baseline_photo_library_24.xml @@ -0,0 +1,20 @@ + + + + diff --git a/examples/holistic/android/app/src/main/res/drawable/ic_minus.xml b/examples/holistic/android/app/src/main/res/drawable/ic_minus.xml new file mode 100644 index 00000000..ed3e021f --- /dev/null +++ b/examples/holistic/android/app/src/main/res/drawable/ic_minus.xml @@ -0,0 +1,24 @@ + + + + diff --git a/examples/holistic/android/app/src/main/res/drawable/ic_plus.xml b/examples/holistic/android/app/src/main/res/drawable/ic_plus.xml new file mode 100644 index 00000000..ca42bc27 --- /dev/null +++ b/examples/holistic/android/app/src/main/res/drawable/ic_plus.xml @@ -0,0 +1,24 @@ + + + + diff --git a/examples/holistic/android/app/src/main/res/drawable/icn_chevron_up.png b/examples/holistic/android/app/src/main/res/drawable/icn_chevron_up.png new file mode 100644 index 0000000000000000000000000000000000000000..67e1975a7fd1bdc6d50bcf775ae55c920cf26c6a GIT binary patch literal 2429 zcmai0Yg7|w8jf2j6chz(ROHY&SiC?oxe+81DJ0wqK?nhoE_+HyCS-tQhGZxSS}(My ziG1GljM7!=X<~R zedd{W^4F-ykmdF+_7n1sgHP+>_3 z!>Gjp4!HhCT%k-w2wD=7jOql?&#fI04OI!CcyBRNj0+JB8k(g?VzMHo%B)nSuL|TK1KnR#bU>1W3FQnrdjKB;U44<#)nLYFH z3mXYk{gQJ?} zVoU{+a-M_2gu*C2rbg2MW{`vg(!zp-JeDtyM`tsv1&})O!?cQIBm&VT6B)?e6}~A!=p2Ke5O?u3m;RVYSaH?la8kO0lrww4@C_GrZ-taR-4Bl>DHBX znTA^YO*)+9E0kn+0tnY*sthHfvNUM%KZzPJHIb>%BLT_a1`D77wHgIFm}ub9L6dce zp2lLZm<;b{H)U!NkkHy3lg{EwSxi2M&1bUbnu8ZD$6~sdmSaUaAZJR{c>15f_@jG2~QD zokZZlVq`@G=VsK+?wwA#VB6mHW2FBLLD~IXJ~yz=ox|LYyoiV9wJFEHXUAOWcRPRe z;15rl2Qw&rW%!=hL8ujPvia%b(n?R8FN5#8ecX9x3zz=ZgXLVt>RoZS&G(j^U;m%j zp~kZ-1p0HPCu@we$>U!|f9L>SUa#Syas!3pu!_8vP)f_!QYe3l4-*AQWjXCbVKsTt zYi%b+LexR*lQLS4S6XqP%5J;!C%pU1+BX~?Xw9!HG+kU7Bq_?@Or>57_A06>%3*JG zUdK+LnsfDk8TtCr^mxa)zDGM|%}-`W496dqpRE))jmqjTyx!J>jKx!T-VP5uF}^x* zY~R1X+xJVKXm^ikOhP^TfL^`lfN(4?M|MyV+dQ6Vdu7XEY%)J~SLX)D^IN@y6>(}) z?)%$ny@m(hb~(d!9bH@BlR%t|9$c|4ul>?!I^DUY4?nazqWDN;ea>flKXWQ+jFI2f zgjWu(b8K2q4gJj*D*WxW>Z@xw*I0#nSG3~6^4)#+)Gm(c`?zt(!m|w%%WTs_?e}FdA(j!Zaq~o zXS8RlSB8Xln?FzWm_B#ITwUB%;{V1B&o#-vUNbe>?=>MRE$b=jYK_ocwre`Y9f(hi z@|b?pG1T1cS!37a?oxF*aJ;PT&Gv5{E>*G$%MPyGuE_hxk4nz1iW4@s5}cA=;i>aG zM(b}>WO5vaUfqyac%=WB&tOaZhmT!fsk7Vjk^A+bzSFylGu}G%L6b}PwO0G?!zs)B zFSwOUsTH?RHapri*;2!lTBAb&yfyId0db#So5!R%7Ww7x_n`;-xr1MX-FaWaaa!Kt z*wApj+BG4+<);sRjh1h!x_o_WZ_1Q;Y0J#gfZdE=kpG&(f+9sH0~53V2T?hW A>Hq)$ literal 0 HcmV?d00001 diff --git a/examples/holistic/android/app/src/main/res/drawable/media_pipe_banner.xml b/examples/holistic/android/app/src/main/res/drawable/media_pipe_banner.xml new file mode 100644 index 00000000..aa1b1fa8 --- /dev/null +++ b/examples/holistic/android/app/src/main/res/drawable/media_pipe_banner.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + diff --git a/examples/holistic/android/app/src/main/res/layout/activity_main.xml b/examples/holistic/android/app/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..dd8342f0 --- /dev/null +++ b/examples/holistic/android/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + diff --git a/examples/holistic/android/app/src/main/res/layout/face_blendshapes_result.xml b/examples/holistic/android/app/src/main/res/layout/face_blendshapes_result.xml new file mode 100644 index 00000000..b2316561 --- /dev/null +++ b/examples/holistic/android/app/src/main/res/layout/face_blendshapes_result.xml @@ -0,0 +1,39 @@ + + + + + + + + diff --git a/examples/holistic/android/app/src/main/res/layout/fragment_camera.xml b/examples/holistic/android/app/src/main/res/layout/fragment_camera.xml new file mode 100644 index 00000000..0436c4c4 --- /dev/null +++ b/examples/holistic/android/app/src/main/res/layout/fragment_camera.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + diff --git a/examples/holistic/android/app/src/main/res/layout/fragment_gallery.xml b/examples/holistic/android/app/src/main/res/layout/fragment_gallery.xml new file mode 100644 index 00000000..6bc5b9d1 --- /dev/null +++ b/examples/holistic/android/app/src/main/res/layout/fragment_gallery.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/holistic/android/app/src/main/res/layout/info_bottom_sheet.xml b/examples/holistic/android/app/src/main/res/layout/info_bottom_sheet.xml new file mode 100644 index 00000000..0b9f0fc4 --- /dev/null +++ b/examples/holistic/android/app/src/main/res/layout/info_bottom_sheet.xml @@ -0,0 +1,457 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/holistic/android/app/src/main/res/menu/menu_bottom_nav.xml b/examples/holistic/android/app/src/main/res/menu/menu_bottom_nav.xml new file mode 100644 index 00000000..c6309a01 --- /dev/null +++ b/examples/holistic/android/app/src/main/res/menu/menu_bottom_nav.xml @@ -0,0 +1,27 @@ + + + + + + + diff --git a/examples/holistic/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/examples/holistic/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..2d2fd07d2a9656e34810ded0997f54e05ba813df GIT binary patch literal 2003 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 literal 0 HcmV?d00001 diff --git a/examples/holistic/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/examples/holistic/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..7cdb3cd73b860df0c942d42555cf370b021484f1 GIT binary patch literal 2386 zcmV-Y39a^tP)NSf^3dxN4_YiLA*OlNeMQ5&uPVarU*w6%8lLmXeE6Rsx$W0bSaP=9+hqLe^>6P zFpI?+X{te%C)hx%2btk*2|pZc56^CVSMQi{c6|$ZDA+;rDBoHeG|2G|vWJ>G*jT+B z4BPMlU+^haHF{|Xd@Wc8p;N_+gvsUgr$Wo z*LYej7Q;Bc1`G~g{SQ(2gp~}FQhKI?5o*q?>XS~ynfsP`FE!fn` z;WKwzE296sS&UD##ydueDq zM$;Wzo*vP?VEj_Zs@;d(mYUJ$Yiv|ZvcJE=%XKrsLe>DN%4INPf#A3YL+e&`?UN`x9 zXE(Xm|DO8w!LX&*hfny{#v+w9^hv4<0}!qnwNsQg{m83#M){?h$cP zh_f7%W7C!oY|2_-vCA8TeN)YJnrCuX8JU4(0GLt}Bu{sBllJ2$$@?cxk(GNtRx6NY z0Eo#G#J(_`v3%7m27tvbmnE8C`SReXHX#`Re%kpVId^3!N)8Z-0if&&jv|f3;*wu8 z04#QS1{~$yDmI9>yKw=$xYIg-(!&Q0^1slTxV?_2lVt#`9S;!udRMItyR)Xfm>^bc z-)RN_{msz}gHgs&4pC7cw^D`htd~Ovx{$}OLK*<{SJ>d$Lt~DV(wm*m46$;1$P56Q zus_m1M%xOYrlTxEo*NTDqzd6#FNY3vIRHylNR7==Qg>XKKVMV4=1M#@(qXft2UCFLfFVwzgar6GMB!w6fsA&66yJc z_fg?xKG=FQ2S#HC5NOoUusze&!y34x0&w$YPAuDcCGCl)9^~2MOwqQ}8IDc0U2Jdy z?X;HBDx+oAzfTGPIVNFkX)ldcU=gda%j>{iw6+jACTY?r-hsvn<8Y(b2LK#~Adi2b z39$4LT{L|Pi;zAk(Z@>Vc$SlM2i>OoWhF~TN-&Ox_xj%@Pjqy%CJ!Z6*(bnbuR=T) z$6M}@Ejv3qDrS{9qDjKCHANEcVz6K=9O}V1pyN^s2X>TLwof!fF0RIyLh9GI7rgp9 zVl*D=K@mi^(ROtob8g{U1-EL!(11a{;%|`|8WR8|_lwI8(Ad7oBAhob#x)~HmYV)# z{^O1c-|*PA`C@BYLo?|;ZM@jRAw~h1vCvWEumzj!a+msmFZjdQrrBRx-^yp=9r8%9 zePnjv8rSR1W4vCcw&4T5Snw-~-=Q&lo5d0{?_&|)p;Ia6*k4`arDtYTCV5c>zJfn+ z1o$5~u-nqKu`hcAU;Zu@1KV-0hqxj@WD7J89VHE4FlQ*SNduw=&TI*9*L{)stC!co zC-Z&X!7GGxnlw(JX+uBNu{VLsr? z@`6o-cnwBM-D*pB+$St3EE1d>dc~5uOKeuKgDYQI2~ra*V>$-ADs%+r#$MQfEoReF zogXXV%Wx-Yl&4Uhk$V64=4SljrQh3sSL~xH*5~+>J0xzZ{g?|=jKNsw zP+i(^b07*qoM6N<$ Ef-i7`H~;_u literal 0 HcmV?d00001 diff --git a/examples/holistic/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/examples/holistic/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..c8c00e9216d258cc1ec84dce87391a8b2a043331 GIT binary patch literal 2655 zcmV-l3ZV6gP)IMWSI8*o;fSoPehLO^@t|xWW%XN#q{_Su zH9>8}*8^$^rAhto{*jzKz@C~NXHClZA5Y?I zX4-T$A8};aS(5WeWZRJQAA4%f7wq@{Dj2L5T1_CrzqVbl{OR3TzILWe?{Mvo?02+J zzGFuxOZ26dOu_Rdt5K&tC5vqDee_qpcK(}wc$Jtq(B{ZF?n#sl)y3or4Nq;N*KVkc zNJy}F)zB~NQqcO&KgO~R-_nvecKlCHtk6JhSSt~|exuv+^TY{!4gE7u@I#grt0%Fq zSNq*0Vq%4dE&*)oGdc$U>{m~tc7YJ!;i%<*nXClp1-bx#-_#!Y${Ww1c7YI}UsU4C zsY-xepbPM~jUADTmdrrylmHJBB|sk&tO7jwaXvj?u$P|BE|UJ|i!qyXX?EUjn&B*> z;px3U`yR2;Nzdl)rtjwOrAIgKl>YCFX*&uiA%71&zAaDsUylTv0N?tgkdmt^>B7xh z)X>^OSKQ51c=;mD$t$Lz2L3v6o1BzXQ9&mguTx!13teq)CRcquEiNgeVd=8J&Tv-v zB_%F8%zSHE+%=Z7vZ;yQX4@LGDOd8>!F%~06RY5IHPyJ?RL|nh-n_xy`ze}|RUr9y zOE3w5;vcB1rQ02Ms9m7OeTxBtzIC6}d z+FGT!+iY9uXU@>*3|Ryee?iec?L8UAmDFCLsl62;m;^{UU1?Z+9anv=Ve|0Xr)AXI z-Y)s;)HL6s*|~e9I5atzo86MX4vM+BskUbl09AZK$JhnQ}X> z1mNP#2ml_<%%ycFPO{uBcYRyf5moM`YMQt`U-AbbDZq<``+6n7uMfGr;^LVApEcD> zej?kd&qz6SaK=1!dl9`_T1u}SJU}yZcGD<><9{8z2XXr%*ca@R7%LGh0?aSmPmR40 zVA-L=UU3lM^QLQ(pUC#=b5c%!!Fv$5FM@r+K8djs!6HCkH9`Iml1-2jAj}0AnwCRf zXUF@Gu}&FBN8{E5HwS z@289Io05NX`)yiU=Au{w5tIO2oEZTyZeMw$QS#Msx3|%PUGkx%5`c>{Bfv7((QeOz zbu!MJlO}YO09>3I0UT#fNq!<_4Odvl>^&M9k^*3I1XGFPj)=TkTH+OFMgY7f`iWrM z$ic^jqyU%{$Goh#Bi=jJLQ}REdc}?3l26B*8YEwltc&uQFkTb=M6hk-;NzAb4#6{F zu%y)0?eH+#Y7Cl*S2|!Q;@`a(3)*Th7KGPCKM`yjIrzB5P#g;axIzJwlv4@7;jaM0Sf7x2)^}5a{v+|=5hV+_9nr;4mWq&K;Jz4r$kPivHE^8#KSjxDZMJ4l@D( zoTx3>RjdtLnH&@0wut`pakGh z0`#!}{$8GL23)c(ZeIkKzKcT-#n=&!gp5W}fog&{%m^?pbEkGgo#+d27L1{6qxl3S z0EZa?a4V_k$AckmOzMbnN&pUj1;9qG^k%^(qgAuwGIA;Xtnrcq=zuZNZ*;#G zUi;1ABh+Xx4~=gdifFW86PR1Dmo6EI=UK4nDor*R{x&3-1ON~r#c0X0{9>PjYv_!z zWEqwlL;H*AP0TUlifdeLA^PiROO`PoSACtf%voJaGo5I>PH$I~Q(O;Amf<}aEm=10 z6YqIzp9Gr#I{q%&hb9VZxuMAs+iqXrj2R34VZlFfd77RGsYUz34kbVzqXg(91XkWS1 zFh{F__DHWz_BUBqy@M~f{{jrB)6DHz`K%*@${b-SO_4<0-2U|R< zv-VEm)M|m6Jd(8LTGYIkeu`Sf%z9!7YA4jvZ|gtg@li4CXZ)balb^RN{o_{as&yBm z-%Dw=z3;eVTf3n{`x#Dj?Q`^+ls44D^5*Zi4gBV$d8m;kegq!m7MLgLbs3Z#ICxCV zDE2d+F{QuFHY@U}@BBFOxj8RK&YH6*EO{>BWz>Mx;#t%LwdtzSz^aZ)5fd|#F)BTmrcxB3zDO09QnX=HZ{{uANd$x!{DtG_@ N002ovPDHLkV1kv)3x@yz literal 0 HcmV?d00001 diff --git a/examples/holistic/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/examples/holistic/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..f5bcd8c9c2a2985f14d5c6cabacbe399c150ff70 GIT binary patch literal 5382 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 literal 0 HcmV?d00001 diff --git a/examples/holistic/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/examples/holistic/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..fab12be5d6ebc09a86bcf44ac2a16aac919b3fc4 GIT binary patch literal 4404 zcmZ`-XH-*5v?d|c2%$zmsvuGViXhSgkt&FQh!FZUbm^glPUsN2AckI)qJYwi5a|Z# z2#VCJ6zL!x1i}kS4xlg&g zXsOBfWzG2}3JRuEZT0&`epZ{gf!;>n2t=(VX5HSj82XAeDqtD*dqZQLa#LgZoXSR2 zv1=>6B+kgmWV;7yY##WEt=8p!;dAqH;IC8;{~KXKkq)%BOtAv`ktvZ%6m;7BVUeIJ zONlA#qoVw>-{KiHz}abBW2x zMyYdJ6c!m78HdwrEMXiG8-!X!_%Nh*=W~4pV#jA z1HDob?|`)5vQgbUC@S(!^J&(t@{pE1IoNSVmpE6iyECuObEc*$o>~`uX-RF8J<^>2 z=xQB{yNIBpR);aM{&Ufl6zaafEfIZw<9aYRPez{fY$5SPQH%(Zp1xi2xbqzhXI}l+ zlZd1q+Sd&^SPLs6%J`h} z7>T)Ouu<+#h(s1ir!351?6a4fB|JA9;91zZ9HRD(On%L?(b6S@nQf8_do}kz+S+sm zlD6w>Zk)32a|Vx7Ya4hUJjCZ3y`Zylk%|wsSvtXM@p2~NsqHyxkCdz9O%krM9}idw z*q-W%eE~^bwDX0YWjlq&g=`U@FpstGtG+cON%Q|+y0_)k8;nE<5M{Hmb7p}yz0!LZ zJW}-o`FvQVUg3hUQE;fV{Ejuo3RaNPC0TH9Vus_z9S1HH7YAUHl-e<>a=t_v2P^4dNRmZ~MOWQNH=Vot0%l2R8W7)mZJm-!*)0%6_hO zKNT8uax{oVkAV+I#kIsmUN?l*ME!fOT+g77gyNDQ!7oSGyL{#YJZGCi>biwLm=suj!xZr`gz!4o|~E3Q_%qZ z`ikYqOl)+#=Hxh1pD^)3oMD+3NcvnYR_XjISZ?7Yf<~iD@#-7Fm}xUd^mo1DR%zbe zIgR4YWp=(hn{fDkXNQ`OzeB=6@9U*}qvsPhPd&SZj{_$=yzN{r6#Mi=_BcuIJN~+3|nm_J5vkmS)%gd%2#A$*VG=a!>L=JReVy z+f_p5l_@7r|ICT5=EU19BLq__$re|WbZf`r#YbB)xe|{KN+=m5EjNoDaExLh-2U&6 z1viftYqk)>VyB*x4xr=CtnR6#p4)a+#FzXXVvrlO3$wI&%7)7HZGoM6Mf^Q z9Zn@T^AxvI69cCbac)6iY~_YOXojSSe{whJ;|Swj4~)_vPZ?jkQ=GtQ%=_W!8BS0a zqdy`SIE60K3{CdzyTd;7tVu$Ftv?IF%GwNYeJw9nAt>FZE#CP=)R9 z+$Mc^fyTF!#Xl11ZUS--^$?DJlle)Fz;~+~1$5>)VA{sv;>x)Lg#Lz4?t?&a{<>>Z z`iBmTP=j_V&{zFjBl3Q@SMn0yQw{=g`~bKqQkRDS*OA9mD44!J_Kc)ryASLH{r>w= zp*Tl;?Uv8TM8lTxnOx`PVh=%|sZR z`BsFJJmxth#NeusW%jRSx_gZiO^nUF_J|gZR_qoVL)G4ZX|d-(im`e5@tFT;K=)D_ z8Z++e`!!gY@7-UpMT`YRx?pxBq(;RnG8OBaqx@PXiPQLFb4#WJe8UbhuANq6q2jv& z_TWLo2e7{Jet@XwjEnkmG$_Cc2pmhb$oh${H>ieWheW{mx58Ad5%}fIjJE8&IqM3E zsn8%yVFT8h1jkfiuNxpRxI!Df9RV?C_t#)JeB5VK%m=Ix82I+r?hSGj*#-3QUt$u; zOAS7~Wl4CKKGE@UX+A@Lb=~|(WQn{2vk-r4%CU}V^INJv$YjFBj44;!=y@v(SR@>@ zkGwCCyDEEBLW9t+dsCshvq1abeY#9LOEOt;jU(p3C61OciV;u0AbKTA<&81$07|EeO6DSEAs&1S7g7>v)TbOEIS{33Enp4mKGOl%4T3ga!ENT;N*~;5X-Qa9$ zV!bdLxbY#w=N35?4KO>$OhAK$R7TX3m8w9s~gVSCnAeSD%@&=k-V=4T@Oe_k@2L{jZcJ}ND1 z-3<4g4yvoSoMwO(_r8O7r}xW6P}!>E>3dUKlzYd1TeX_X^Z@T%2Z9#CFn&QK!Yz&) z;6JB0{STn|1{%~2H^^@X-U+8#gpz$T^YecKdqKdxbk--g^Fu_quI(Y%5B&>jzWe^I zDok1q6G9?bp44ItJ2LIp(A7vD=~2!De4H<-qL2A^o1-66MC~A2YW+KGJEDWrvjB_&}Q}VFf z|B-h$nAWnYF;J-JmtCkrZG7d_G!q%@+pf2L1;J(81H(X7$^rp0!x4&rV9b%#W-yFQ zUW~b!CGS6-z}=XrSgfYq?m13UMkG?q9O~&jVY^5FWC(+IGnHJdhKm z-{t;$`;JdWl%5BDFYyXo?|(OjOcu2f)EMCwMSG zZ>Pga84XXsVm$&yA@k375uM6imNcKOv8lJ{}zZ9Y(>{-ZF1Ky>(fj0~OS2RQqw<;4@ zuPQp8mHej|An&3sML^+YP?PiG=PUk$gJ!2+NCV`rsgrvkD=XkKk(xa%IUYO7dj)5b z-RI6M{7d;m0(333&n}?}wkBm`ojR|-T>lPzsaE-An3nLbaI)b2`I^d7?3u7a2+7S8 z^UnRFH!!6qVYP_3#6o%MQP`9;p^=Oy>_!{+$mH6fw=eZhb2gC(iqE`>nfm4yRsKQBc1c@IfTRR%hD0 zK6iI%cL<+DNK2}#7g4sI3V1(aaqlL*eE8kTewDZ0EysL61M66)9v=SUBnLHi@!-Jb zrT3^t`|y#k(*nr@!b;4HqFGs?S_Tl)b_br+m&jJ4ZHqXRW$!gZgO++^vzm{vhC@6} z@AF@#8<>CCo2j&OsfU}D4peG)uusTrsG;_pQMld&=pUf7`@1Hy(Yw2;m?m=T-hRzW?UuMHl;N7gYsHIKl5(0rwBIn5Dk`GvAJOs z9XDm`U$pH}#nOck*qfw1W*gRz+$~+zu~_`4Nb>QRtdu)+P^JY|82{&+NJN7Eek1nx z#cp}r`mWm#@opf!yf>hdz@#r2@m!18DTmV1~dY@e#*?k(y zx!Vo-6oohA4WP|c<1^RkbA|3BAj*Cw^I4S=hkZeX#wm1em$k%Z@@51vi6;g>r5K~! zXfE+u3&NbQO}Fh{GbO&)Y<6F`%ryVhzjMt67ktvVbUmYMLyGVe=ac`VGK0vM49(Z} zjDOXZ)8t!Tb_Fc*2HEn6HRVBpXu#G5Aqi==mCgIQN0!1j-fpI;V_v$0MKP%LL8tOJ z^}0iQ)j^On6T~g#D_Z1mqJIh|rJYuXoD0UJL#HV?NU33JY1>@F%ETGv)1Lh93*|v$ zsn`eQ1!*|x^JGchR3&2Zr$=h`$^<5{t@mHa+MDs(* z8x?(DKWZ{aXtQQ2vt9}El$LawWII|jXM?Y6wP|g@;^J(W9tP@CUgzidtmEyk9kDR~ z88>`dl%G?7`|!frJ>I$+$71Z(Aj;d%gfF0=^E$(QV>^wXn0oDS_R}Obvj^HD6bPey zaj{auU<1uYZ*$0auc+_H`?rsDJ4w}AA3vGt>>;A8P3M@8rd7`GZY93VCrSivQ;$Jf zNu*`ZhZkoPKYFf1Rjh+xzbE~-V>zA;In?w(YZW)7uKf73;xli)dK9L4XUzflY#MiF? z0{LbOkgkTKsPbORjE)GRDGSS1;}tF$jk6237Cb(Lx_Sh~B1pCoFmAvLkS*HD7Tg+a z;vA9)RB&?H3h42wb_O1Qni&F;;!u(^jgOO%jb%oC+nyhNbd{O6d)BS`DP!CuZM^jHU#%-l;BHX$ zXcVi=yr&ES^`MVJtbbo6Kq=fx$!EXyCNglCuLY1DvErJWe7~>Nv zM#MTEw#7R-+2b5h_F0Zu^mCm(-m#D348;w5e9Eu%^LL6rDToOC{k!fYNsCUV5>U8%k`%ZWN|YDv@yX+8C+-q0hBKo4X^&0*ogpsy4*^Mk3P}FT zJ@S4h)XS5@{?7@q*gfnL?kVL(=KiFGyE(o1p*XWPv;!jIlb^B8N|{2R`V$Jm#EYGr zM-oN^r6utVM1o1LnOwj^;U00Xlt@n(cmQ;(RK!K5Tp`#^=}$Z5U;)y^J7_05c;^)* zS=0jeAP|b(Te9?C526idOI|zqL!uWS6&Z1Cvy&dT#X06u<=-h_X&7l_ZlpvaNpPS1 z-$EPipiO97Ub}T7d0AwxDk47RIjZP21*}cJrE$_xr2^d5Nv+Zv`>f>WUFktDg%y^+ z$&U@Fa$8fSra2)_NpMfT!e35^MBAM5Se{;>6FojEq)3Qko#m({x+<;h)ZQ z8L^hkTbdGjWG$%#28>&P#t0p9br+MYSk-eiF&^n-fLo z@Cr%4G2O-_9<_{`QCF(_(p$s^&?ln_9eQT#Z%YdC+p-XatI`%G0`>ai0RFweNlP}cU!SZHEClmxFS!rxhbSo(kk*w+D7up=1lUz zh79t2>ITw0A&pd?&FXlKx(k+($CJ~^8>=^wZd)=*r*&IN8^>DGWbq0T8L#}kRzOB$ zD(gwd8uG6-n@RVr+ewF&8%gWr)ucAPw*2>=0J?xqpd082x`NJHC9YyRl>a`@5=~!{ zZ5ceNl({pWAe_3uMjLV18F?Zo9(AOVc}IUDN3UNdxp(f6+jo9pxOw|F*>m{<8Ml8g zsk_jba@3*XtYq@~+I1xD^ht6q=X#!e;7V=|$-Iz7zRJub_jp0nO7t3EWNsrF7tWLH zTQ>?^vL)8 ztnG4Ht^l2O*_@$}We1636NzoY>}9lX1oD)~X%E3X-erqR9x9SYkzIZ;X*tQdrJg(- z8?(-kDs$8+|GkZyRFj7z=k^`uCY(IM3c%$>AKI^)JRER8A8gpFkmn3&>y`^=6fz1! z7IoFl6PGLGu{_bt1Y#K%HwJxF@?4o1 zxbhn=T28iKJgca$258jkXGi=K~$E=rJrX1Zfw-$&CwIJs3A#(2= z#e={WsM)41BDUetZ%O;C_>0J9AyMeKE)Yn$0IWsgN^%&PATeX!S}m&DCA1fDmjfDxOzcRPc5M1mzHl($Q3~QCWgjC^|YWc z0s%{ZKCY;z1$}n9u?`eyBibsjrv-%&h;UbC9UHfs_Ms3=`_+|k*A=t$i^5zNN1zbs zs~y`E_0-~8?k)19!@V2!4}p>eS2lGau?(5iAARXRS0-dK@3KvtTf;7#l|$4lfDaS` zJ+w59oVamWQCAI8>FBEFJoobbMj)}ztP^J6H23(nKKj%@S!yBnDQAyKidEyfI0A)0 znAhsLZ3nrYt1Vq{KIaB`Db4-;1O6irK&{a9CB!mx$^!Ih(GTl+;5==?!*+gxmz%&@ zia;R{fPB;}I+{|O@y^P*NqW;kgqlfJ{}M|KY|hr1C7+`6Vg*Odygs%`rTBl>xb-B6DM+^|3}T zcv=jcRaqX6KqC-Pk&>cK>S_{m=rGxO;Ve0N{SrA#?-LpKwP((ds9ig$yi^jQ!t!wV zpFqj1FKH}9O#KGGf<7)9E5xl&T;g(pKuL<}3U}}}4}?ir*k{rl z#6E>44V|IS_8%1u)q;z+Zju+4yPro&Jn^%lo)-L&olWjvvO*!(pFkVYR{6bY0dy^w zTL^?$X#6Z1Rsyc#Jo?$ zkZD!5KH z9g2-Wu}KI`{;*A%Pt4yAk3ydtI(xfjNSGf1g6IW=b9+2IlTas+Iusj$0CpsDBC!sS zK0y0Cg!R2wb7W%dW1Bel36VU2rr?=FCy+W6D}i`0Jz*9xe;M@>`rf->S}yRVMjj8w3=ze{`Y%n_&rsy=radBTxSzSy2Y zqL1t)@kbAmaR+yi_cv}L4=r6uDyhssa}+y)*g4io#4;@Un4$HHcE$mZ+?eRTtP|se z%(LP`+7M_2!q%I|QdW^g$BvQ9w{jG+fa5o=kl}lFllvB`PYo15fs$FC4>=*z=l%W; z4tVCqgi1lSsf&hrn){I=&?!g{(kg`vXDAs2O8x_Dh0Og& zOaTY96DWW5Rly#UnBmRniz8471j~adi$hl~DeAidNR&=VRAa7t*+5Al5LPT&hD`dA zasWG8HM%iz-pf3AVtqT#YZe51H2UHQ6apdUS#d`DwwqhG?~p-1?jjWnnOH3m1mdHy zF*9$6zt^ofxS%bmD2~2t6KDNPv_2jO&m4h5px4%TzW40NwM(Sg5~q8%@=ziOBqSC8 zv`w5tOkaN28C)=QjH)1~iR&Wxyz=&Gi}2z$D%8992M81bO*^z-QBN%pFTJ~AvqGj6 zC4xWz;vfv?zZn?=E~t-#;Ohi)6>OiGuwExnKJJ~9OxB+EZ0j_~*aN#2a-}Fa1WICY zkagg=Z3e_a;Do9;NQ{3>Z*`8OrtF*`(DVEc#}^si5eN&lu+~N{SBj!0(0z-svCzAH zVY7}NRLGU0#1M#2t`47?9on{CHE=@RIw8SS-ED~)|ByZZcowe$XaFaJ$s4K#$8KC= zqaV3kDT&Ye|crG)@G$TBA8O(FlI zZRx5jJmpwRE_ty~3a5ZIUgG{_3t#L^ZRmzT_|97b;U493rFfPS=)CMiHLdUofrLQ% zF9gzIpMCuyIH4|(76??qHhFGeMxfY4uiY2WXweF?`TRLWW7T5%!NY|PffJ4&Q`FOf zA1-8*CT>3Z*@8|>;A3v(7s|7nQTySgU@Ev|5ucrmLN4Z=n zo?pI>T#%i&s1?043qPht6~tAF=6=KGf)nZp)V3{RpitYiCChwCAOO1cm9Fp(RD6yZlO%e$e=RloKYA8*Ya3!Z0n!V7Hs?F^NCr36Tr- z5{(uwCow-AAlKY%%EPd|_x2ryo=PT#P9On3B@q3z44HJoU^L>WU`(vK8OjR@#GvrM zb-afX=)54v`y7xn;2ghaSF9l;_w6DpPoALx_Ia}L+^ci2_(RW1QL>oqh{U-d%0r^ zgIWTGf7a&#D&SX9kWL^GWtKo=V*VHU+FPx`4fg~RQq{rX-Fi2-$0z@vC`c!eh%!u| z-$P%2=W%d@8W}gaAdx^}U3xyK6G(|NOQ5kazo{dTV4i}*Kl;2WCD4CGK{|m%lvM&* z#?1UR^p%cqVZnwLE!<3~iUcyg^GRbWAXgMpfkfh_bLU9%@sp(APCbz*ucrmRCXjV#barslmJPrS_oFb8Kok-7nF&Xj zkRq7MXt;PenQ{04xti;GD&tJfRr2-r9i+NGm7y6vBM_g;7&QI_<;1;=8-?u7C}*fu z3#T&JZPONSmkRL}9@03OI^(cDnW`1OBM`PZCld33(R(Q;DvI22GZ%)LR^dlB+m!h! zzFy9OlS-GJ{#g}yIIx`KJAFBaCisj%;@OGjzC*B_!>*1%E!v0CPpfTw{8V2O$eRUa zm-Pi@n&6WKWo${Su(ikh<##yt&MI=l&2kQW*_yqa)nXYorN2*?v@Tvjwq)r`T1(xM z*2(ioc-LN|!3iw{GMoDje@n1HzDXpqMfD!ODiYY5b)KywlFOAsUsNx^CoW7}RBs(0 zPr^EN?g~z*BT&0`7=@Wa-|zMqbzeT4Q0=sSi=v@gfJ3}-oQhno6nYFKz$XOaS?H0| z{uli6t1p5RbODRp7qd=74x9XK2#aNIxU3 zBWH~F)kw@A1bTPF#zGH#a);=H`xP>!C^H1&Bd{s+iRsfm@!*2U3AaOdUdmBZ1-9AE zy$8MPt3cWx1ZtkJlEq5$x@v$UTiUN)r;sT{*&+}PpvT4%kKzg)%{){QS=!A+Nmo4me{ILJ`r0UQ0SY92gsoFKe(5y;mFcmhe>S9s^1ap-$PwI(>NzK}if&I^x& zDuuoCaXVXVqF1{#bOH$|(FEe|Yee*Kp)b7pPxQSf@eeQXdBFygDZ&6ZHet3`3(Rx^ z2`JeFN@k-kQ?EfdjiavE_i$gD;vZgMvdW7ltg5NkzNppMj!i&NMwI>yI0tqOw1QPqy{N1Q4 z!`U!3ErZ6#F)QT3i&y;d(%qs4UdW3V^UCi{3-DrK zxm+oV<)yoP?rYK%&1ya=N)AuC-a_;0VhWSzF?|2&x=^T%ty ziyC+(I9?4dzc($wtI6eZr6``)eha~~dC-L4Dm?Yv>*#BP&1PYJ?}hu~1tu)iP^nU7 z`dP<3Z1OTstWa@UpgIdu$fJ&xY&{Vcr8QcTN+Jtc9_tJNX@Mj&@5b(Zv3S0Y*wXrNAMGU5^A(*Zf*5R!Vw&+E_i#MR!3i&$*!VXs~K^?MZ7g>Au3^{!5B3qP(+4`*) z&XSlzM@U;rC>;DLuj>i{8UqPK_ra6?2zmAuWS*-Ts#f(nc$O9j0TaV$P=(E9^{n5I zOUCJ)TC9*i2~>HugM7GYGdXqB`7{Ox&S)G;308Byx{P$d5r|u#$@7S@Yp>PlTd|M% z;xV5P2nB&u1%ufPD^w%+nRe}Lljr;*-iRtIzXwq;BI;g^DH)WzXZus)Hjk9}-?coBy5 zV5q>$h_ib=BgF%xwypQUnZvc-DEuG!piY_lBY` zi`x3|BE&+N;tvh2PCpx(2aeyuTv@DxkIbCH9|Y?AV}_!h7A!q>Tp?46fFKZ?ad-TV z2jBd8|DCiC8;O01r8_>4edHy7L{_olJ@m6q@DtCzVjCOxU*X1FPjUEzK+})xQ`FOf zjc3oZL$2goXi_aJ!p{ z)-Ibf6%Ew_49VN1x<6oH_EGOP7H&PCMd~kfJ*2NozuR)&yEhib9zCFtD+Stk`sP(d zJuUcTQ>H>LgSg(uGJGmAzWwn8^qts;m}x+t7OC~&g$PW$5HAtS50s zq85oO$)Rf(6%AI4loKZk&DH#E)jE=M$Fo;*1A}(E?_6N;o}G$%YJt6HudH%EpI3ce z3Tvypu38+rdXee6fLuQAB1Xx~*~eOSbSv#Y`bYD=Cgd+n|Ftz%-? zD}v%;NkrY-DAm&tP^HCb!A<*Y$fMzswdID%NNPB z%h#|I&g8P30Z-qw!~0d|gFv2jXU~uZ3+0M;jrt2y$!ZOo7q92$l1T^lsUj$Vwmz4Z z&i1UyWx2w+n>R?;%^3w{@oe+xnZ)!_k5u%H*hg6FQ*5qFUbrhWi9QuuK_0O59=s8& z`_TYtqoBkd8QWC3fdf}AlE0_Bj~Sey-hxyz`Osd4JZDHhbCNuqRQRq`IdBXyNFSqf=L#Lxtfv9>C522mpv&%CGnsYFtIx?py@9!F?+n@}IOn;(Av6?I zQCMkMY_3aQz+hpT1Ieh6keUYgSc9Rtx!;K0EK?ol_+1=AGT;e&Y*`vvaO@~Kdi`<% ze}98ojy;zzka7F>vSG7ascM0Ft(Vi&iR0wYieYi~tsCsPz3v$qn$Mz^0%u+K`C$jy zo}I;B3@p9InVZ+hiqj{_>ucA0b&a|UQ^>@F`^era7Yev$SU)UHnfKFA9A@e>9J)y`RAI=^u4kPoSjI&a!W zUS6@0=}s{+<>Z+>4~Ju(460b=3G{{7Cm6C9d)(uy@OTIr%}6m&YZTI;$F zL{1c>!$**i$I^4a*^oMooJrm~;^B3Qp5Xr+wT-X$WKJnQq!=fyFs)KC@64~E4s8W^3&AkWi#`+VHM5-2| z=}-d<)oH*yHkQUbefI}dyccKsfIQGWMqKC%zfD#6qHq!++OKRdMczk0o0KnK{vqR+ z-)yjsiQ#!7G3h8*hz?cY30s^dHflOCb?v!@>bNCzE$SSL+qK>)cDYfL` zMM@(0;fibq*l27*|85%gV*8KG{f3qwdn&A>U>FiCEIqD=I7OnRZX`xSZ;f`4=mF#@jNSXL#~Ky3RY)J|JBK(#$ERiJ6k_Vq zb5l^{J#C;vgUxn7)2X0aKH4hPmPcN=B|^iHi@-!)bxNYf^z(kiMd9ys9%YRhbB8TV zLx9Z|ee2eU=|FgQ>0d~LSo#mY6aJ6?jDyagJMlrpl2qsvx(zt;NDEIkA{&a+&#F+L zeV-vb`~jHZu;!1vYU>VScO8C#r#=LIq}xf+|*g z()7{i^Q;3#=UT_l5*3X2C_r5sw>W5Hyn|q1Vf}VguJOID3xXme+c2v`bwkmR=}OQU z50=Xmd3ce=MKHgZB}VQ-YK@wQHE+=&yz`f-mVsmc$FkT^wvam#h*X-%#P6TQIJ8Za zIEVo)-;e#D>Eka}gf@HVH78aFI)bjCvw)6w)D~`y*n7$h!bk$vh{i^?ru6ToP+~~y zr``_l^3_V~_hWy7Sr-m>aBx#!y4;)8O(A_j-L!RF9HG|s7t<$St_peN$xb41nlc>- zu@0;bbORkhS7p-bxGHjMgtfZx?=_go4!0sn)(lET5NUem{qb(Uks~+4ij7giz!&ADLb^N!3o1h zQk*n15}38IM2-6e_ilXs?V+?SPvl#iaKt%{Seu2mLkG}BnT>U{7H*9&B!#I)pooP~ z*gjNCut<5XtvN-D(3Y*=GIr`b!}8^T{pP4~zhd;3XPKmgVo$$>RNlr=1!RcUg~u}@ zVemUqXJh;GUY}9>se_79Mj~Xam}UHt80p2#b_RhzF3iM-mMq6PSF=1=Y6&gLz!T3V^?%{9#d!*@u&*p@8>6vEg(SN(C_n^a;zC%x&z8RHe88G&W zdEmI~=0Ov0ng>%19Dkk0ELTk5j?Oao8+OXnXV78O=ih7(@6s!c$$0bK5s(SlK|w*S z#cMIbNGuH}O|)3T;4JbmI8AK-qGuBqb9vwHW?lt_V*&2vwhra5$-~*5+)uELsVXTvE#= zMc#M(_pC0HA(92MaSeemxF)WRd%(R&Nh7y5I&q4*xFQo=0}^k@+#)!|S{VeY*jxvT zfWQ@wIB4rIConB7}eAJAoi@L#TtgkO5hc3E8*?u7zvj+PDXuG^MCW$pqJc zPJ^33aEe46aw@O{h@UvIU?JdI(D?{xVKqPoDe!;z4!^-~QHK)+vLF+(aSdDx*Tl7@ z?nx(2DJ)V#NlC?t1>I;p%+<_jF5PRvW3@Pb{S+HJBf%o~Sto^PzUIg|X>b@$0JYdY%zFXi0kh|{6O zU1F0ahKbZhdsyOcrpjsmB7R{>NV2OzhpkGNO$&Q@2sn6d!AL`5Kx*jgaH^|ayZ+$o zzI*`JMHbFiVu{!&1t1G`C9stVc>)dM{V;Oo6<^J(dV#ou`PuA-N&`UQX*XoP)f{CsZgy}9sa+$yu@zBz zb+}J_FJs4W6{|Qr1;C$-UepdmvR|yH%$bpr#-p<2vN}_fb1n}V1iT-~mS6y;qt*<* zJ*i0C86NpNqS8+2T9`;TO)&Urtr8>YKxk?qUP-wm2J?PKj>C9M+BcK{OW?C^)7UWU zNNt8x%QzSkMi%x_9|X#U9zMMn@R%Kmxr^jum`!Zq#7TXOk=AAdx2akV+W~M!9z>%8 zL!HdJ^!w85=13Mfv$1$mcIXzSW-#~4JsUwD74DG%4lv4`g1}yX`r76QY28Gb7;Umh z;CtM!I+nTVyB~E-I5pSsBRlb8s!cO+;ai&pL*37=!PB@5e>tQs?AMHgD>s%?|6A;0 z0;OqL(CAIg4WaYx4=Mc-0D_P7x+f8im)kT(b9GWN18021f}eKGE({E(w6i+Z``0>e z)G)@HaMGaIn;?|F1J5b`5H~0lR$0k}gr*a4Lw#>a^zSuL*oI40-$9P7yn|tN@*mWvGd6yKvqg;5^+$3d5AXZ#4fgYXEmm-Q$t&U4b%$+ED|#{(7(WtS6# zBN<#g?Rhigf8jEE{r1_4Ms4)u>T!k%U!Zk|1>jj9r5{TD!Q&Km-E*tc{sr4yx$nOJ zw0-r|_v3b<3!A$|Dei@b)wEUY!PrO<@hn^=sF=-$@`5*a%kbV|wx5Q#J3Etp_rQ#F zlNT{#_)4Ye{0h{Azbav*uxpV0@{tSw8r*uiayWacEj|0I)2V!<3>4afK5ePmk08_M zYLb%a7O{8d_Dc?1C${~UtTf2}aox-=KK`wr7#Wy&BYw2W8)<<}=w#*pZjOa9fx+e8 zF$>J0q@D-KnbPQS!>q3(+|mz|oPAOz6ZRFbLu$LrNfkWmvDOb)73>Lmo`cQ|T$!;$ z{Oi5JdyYfm(EB?yYov1B@=ssUlPUAsI6z5$)ZIJt#7;NlMqX5m;!)6pGi4hKFJBc8?wUCDR=WBgdff=(=t7|BzFfdy!X8O|*vF!amKx&kp7fOa0!R zKTXUGe%1)%_=p9i1$(Cs%?>n{4l2VyY$4|^XC^r5AsYh3@1Ix5`T9w7ZnBvQfzQ6U z>*zT!W7;g2fw^~3;Eg3rXK6HO<#&1VaH&j&#g53>VDD}(<#W z2!K|Z3pE+-?k^cGeTF{=3Dt}9&DJo+0}LTj_(*rZaG&O zT4*Mj6AV`@Oc1I3=}q%7;xsIuBQ({Jj0Bry^V+QXU_{N$4sp3 z+?PYjoT7Tlf#G0Nr*>brZ!jK(^kM^%D*-j#q1^j;!Ua&laj?D)Gh5Km-hd zM{#=}AUZXMLply<%@N1wM*IEvICOD}W@v#?dh`R44UT%k5AE?Bf>`(>aE_K`{Z;8%wPo9~dq2Fe z8hyWiDID*+Pu!hyl4`zVL1KP6+C% z{W_hO#d$dD3^k^$XDJq)$sQbQcK@8=4pM#5H-!TEp+Lg{v(x(2TVVcE_F7vXw7=%Z z9}g8CVt|eiZqOv7N-&*+4w?!6%|?4&^&;P0yzzhn9$1~M`k;U8mA_c^fpgHL?_~u6 zt7QAfG&6z|mBDr?M-o=McBa%YfV=VS<+`?^Kc2kOx2;$^E)U&|7#O${v=pw$TRpqN z?aF4Ivy}(2xOY#_-lr0V=E@`dvA6aeTe^&uM#damy4&#fk-onYlWuqEPWT-my%;d0 z2L$nA&Tj>Q7XWZ|+(c#b>z1lD<4|6OpwUT>#>G8@jLOt|X5&2%C7bh?V*6tF526)jfnseG$7}hP>)7 zood>FYnHhnl>CoXA_RWvOhZ`SIEmqbgb1~qqlIxWIoEK|ihBGt{7U>om&ytn$q66i zu&{23tVcgR_SRf^wH->>hOj{1q8#>vMw_zf4 zH%bBvshRxe1vuXKVjTF+74o>s?cHRuX|16k6e0Pj4n)cA!--$2lsQ#fG$;D__{MaZ z!Pvim;{dc<<#858wE*ctnpujHxckO$S;04E6MNztYzsV-w9}lQA?`QR&D!cd(3{&C zDl4ygk4B4+se?_aMG@N+OJ$s(NgiQ_-!7@iahf3CDMV_qCBnKgzzu)y3zQrBM>`!?~oiQSP zzuldb^sT-6gRfu2zgC3r7-ipH8DvhD5dn`$Z48$v7B;=TI(9}Am6m9lznN-CL7 zj?>v^dGaLn1tNrty`!v-El5aQyyQ~2!->6Akp4$%nF}VG#Xktc(11m&|2o4rZ|aGp z4Rok>!$6%*bE(*3C_aJd)`z*4jSIt>!3k7##%|Gtelv0JW7j7_ENpMRf5akeq;u7D zzKY6fUN$B8UV|Jy4b#XuoG}$CQBSwV{kh&(bHY^R0JoSv5FIeP7{YTD+rQ^dE#^Bm z8M>_lKYtaD@}}M~po)esR3SB+tJg2zaju{sZ@uVSFI6~59$djxPF$2So}MxAzT-OM zzbm&%bE_E-xYw189DV3;VtOFcMrNZ$f7=Mmu20^Q>V8AjSPxrTEvCzae;4Ttq}|Qi9G?~94Vz@wkOhM?rmRJ< zz@RSQQPgHN;p=;Wnv5ky!{74#CQ^>r@tXI}9b>@>cXuUU3qFwT%@t4AoLTl+8DXcw zI&}UcOK~qWoiC&F>=DByPuI--zRs3%7c`ht_;JXQey%Ut@~W}JTlzzQN`78|O#(~Do#$~IrU*8Xeqa5E(EcUeadW3O-AVykrv2~EgPIGMc(>WeQ3%%09 zqb4Bv_EX3YmUqG0OUa)Rt9)F5T5Rj`-ZsD3dY!&jcZUQgtk#4y%p_+ejO+$C#x6*} z5`~n0UF^2}{JCNh4wUE@g2IbMkCYuD=JrJv%VPyM234i-_({;_kfh6SS%T4ouE^fy zlKY2&x;KFoRg%G$j;GOzq_FZ=Si(NDZj$Ug3)o8;Q{^}NW|S6tt4DcbzbW2$Lf3v5 zjeGzH<_ybyugrcD7v215?r_U;upPEMD0!2Vu2~GKW=UqS%U%9-97j3u7@e8WpEc&N zsU}+hW?2=>30TpmG-da)zrWK>Ck(%aK&y>NX<736++-iIv$tQ_+2h2I4i8(D6>$Q4 zp&YM8$#rft>8DYKO;em>@9-;fqLWp}sf4uG6|FG)qQExb{3z%2t4yKA=heuTd6h;o zCEw8Nh$GMYW^g2>&gy&z&|kjzcmWTggpo<%U-EJMDbn$;C#(wmqS>6pMW<`=gC&p|mS%Y3L9zo|z|Xtd z0W-VYw^XZ2lPqLg2U9Roq%@H$sAeLG>9r6a6L#ryOAAlyyo8jLR8_08%x3(QK;9GH z&ate-z4cd$MlD_QL;S%-H=;fGSPbEYWgha|tKYP{*sJzIc zHRmx(M^!YXxv(lDqh)7POONB8@Z^tjdq{(9syEsULZc%;3u&!!Xl-t)=2qk3VrRLo zP*^rDHajr2d=VexB|Xbxp*FRiRj`VM=*J%TvAq%|8*@xlOqW_VXc3yt22Vq7KX0xv zQ|bgAH=T6f%y=;WdNjkZ&Mo|LC8w)#Bu%5WQE&dZ7&}wnFjJl9?KOzs{{uryI)!{H z+hk$edD2yTLlG3*a>KW_bu-<5UbfdyXtq!+6@zjzKT>*ylSiT|@M#G55)CtP&M`w= zHkn4Co8F3x*}?l&@FV+bXR>k=P8k^{+c+GhIAyal760P4VECjRD5_sAo;AdYH{{XuSPgVc` literal 0 HcmV?d00001 diff --git a/examples/holistic/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/examples/holistic/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..6bffe6adb6554535bb57c37f56ffe549d76ce5c6 GIT binary patch literal 11590 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 AcK`qY literal 0 HcmV?d00001 diff --git a/examples/holistic/android/app/src/main/res/navigation/nav_graph.xml b/examples/holistic/android/app/src/main/res/navigation/nav_graph.xml new file mode 100644 index 00000000..27598774 --- /dev/null +++ b/examples/holistic/android/app/src/main/res/navigation/nav_graph.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/holistic/android/app/src/main/res/values/colors.xml b/examples/holistic/android/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..e12b673f --- /dev/null +++ b/examples/holistic/android/app/src/main/res/values/colors.xml @@ -0,0 +1,19 @@ + + + #007F8B + #12B5CB + #00676D + #FBBC04 + #F9AB00 + #B00020 + #FF0000 + #FF9800 + #03A9F4 + #E0E0E0 + #FFFFFF + #EEEEEE + @android:color/black + #FFFFFFFF + #DDFFFFFF + #AAFFFFFF + diff --git a/examples/holistic/android/app/src/main/res/values/dimens.xml b/examples/holistic/android/app/src/main/res/values/dimens.xml new file mode 100644 index 00000000..9d6b4e90 --- /dev/null +++ b/examples/holistic/android/app/src/main/res/values/dimens.xml @@ -0,0 +1,18 @@ + + + 4dp + 64dp + + + 14sp + 16dp + 50dp + 0dp + 48dp + 10dp + 160dp + 240dp + + 3 + 16dp + diff --git a/examples/holistic/android/app/src/main/res/values/strings.xml b/examples/holistic/android/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..26118428 --- /dev/null +++ b/examples/holistic/android/app/src/main/res/values/strings.xml @@ -0,0 +1,35 @@ + + Holistic Landmarker + MediaPipe + + Camera + Gallery + Click + to add an image or a video + to begin running the face landmarker. + + Inference Time + Frames per Second + Detection + Threshold + Tracking + Threshold + Presence + Threshold + Number of Faces + Delegate + Face landmarks threshold + Pose landmarks threshold + Hand landmarks threshold + Face detection threshold + Pose detection threshold + Face suppression threshold + Pose suppression threshold + Face Blend Shapes + Pose Segmentation marks + 0 ms + + + CPU + GPU + + diff --git a/examples/holistic/android/app/src/main/res/values/styles.xml b/examples/holistic/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..511449af --- /dev/null +++ b/examples/holistic/android/app/src/main/res/values/styles.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + diff --git a/examples/holistic/android/app/src/test/java/com/google/mediapipe/examples/holistic/ExampleUnitTest.kt b/examples/holistic/android/app/src/test/java/com/google/mediapipe/examples/holistic/ExampleUnitTest.kt new file mode 100644 index 00000000..c3ad93be --- /dev/null +++ b/examples/holistic/android/app/src/test/java/com/google/mediapipe/examples/holistic/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.google.mediapipe.examples.holistic + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/examples/holistic/android/build.gradle.kts b/examples/holistic/android/build.gradle.kts new file mode 100644 index 00000000..d2c9dcc9 --- /dev/null +++ b/examples/holistic/android/build.gradle.kts @@ -0,0 +1,6 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id("com.android.application") version "8.1.2" apply false + id("org.jetbrains.kotlin.android") version "1.9.0" apply false + id("de.undercouch.download") version "4.1.2" apply false +} diff --git a/examples/holistic/android/gradle.properties b/examples/holistic/android/gradle.properties new file mode 100644 index 00000000..3c5031eb --- /dev/null +++ b/examples/holistic/android/gradle.properties @@ -0,0 +1,23 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/examples/holistic/android/gradle/wrapper/gradle-wrapper.jar b/examples/holistic/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f GIT binary patch literal 59203 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

K7UpUK$>l@ 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 E2O(~oYXATM literal 0 HcmV?d00001 diff --git a/examples/holistic/android/gradle/wrapper/gradle-wrapper.properties b/examples/holistic/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..c7639a2a --- /dev/null +++ b/examples/holistic/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Dec 04 10:39:01 ICT 2023 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/examples/holistic/android/gradlew b/examples/holistic/android/gradlew new file mode 100755 index 00000000..4f906e0c --- /dev/null +++ b/examples/holistic/android/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/examples/holistic/android/gradlew.bat b/examples/holistic/android/gradlew.bat new file mode 100644 index 00000000..ac1b06f9 --- /dev/null +++ b/examples/holistic/android/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/examples/holistic/android/settings.gradle.kts b/examples/holistic/android/settings.gradle.kts new file mode 100644 index 00000000..042bb7cf --- /dev/null +++ b/examples/holistic/android/settings.gradle.kts @@ -0,0 +1,17 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "Holistic Landmarker" +include(":app") From a3531bf2aebaa1cd115520b34b2ee6391d0da81a Mon Sep 17 00:00:00 2001 From: Duy Mai M Date: Fri, 5 Jan 2024 16:25:36 +0700 Subject: [PATCH 2/3] add readme --- .../holistic/ExampleInstrumentedTest.kt | 28 -------- .../examples/holistic/ExampleUnitTest.kt | 17 ----- .../android/.gitignore | 2 +- .../holistic_landmarker/android/README.md | 48 ++++++++++++++ .../android/app/.gitignore | 0 .../android/app/build.gradle.kts | 0 .../android/app/proguard-rules.pro | 0 .../android/app/src/main/AndroidManifest.xml | 0 .../HolisticLandmarkerHelper.kt | 6 +- .../holisticlandmarker/MainActivity.kt | 2 +- .../holisticlandmarker/MainViewModel.kt | 2 +- .../holisticlandmarker/OverlayView.kt | 6 +- .../fragments/CameraFragment.kt | 49 +++++++++----- .../fragments/FaceBlendshapesResultAdapter.kt | 2 +- .../fragments/GalleryFragment.kt | 62 +++++++++++------- .../fragments/PermissionsFragment.kt | 2 +- .../app/src/main/res/color/bg_nav_item.xml | 2 +- .../main/res/drawable/ic_baseline_add_24.xml | 2 +- .../drawable/ic_baseline_photo_camera_24.xml | 2 +- .../drawable/ic_baseline_photo_library_24.xml | 2 +- .../app/src/main/res/drawable/ic_minus.xml | 2 +- .../app/src/main/res/drawable/ic_plus.xml | 2 +- .../src/main/res/drawable/icn_chevron_up.png | Bin .../main/res/drawable/media_pipe_banner.xml | 2 +- .../app/src/main/res/layout/activity_main.xml | 0 .../res/layout/face_blendshapes_result.xml | 2 +- .../src/main/res/layout/fragment_camera.xml | 0 .../src/main/res/layout/fragment_gallery.xml | 3 +- .../src/main/res/layout/info_bottom_sheet.xml | 2 +- .../app/src/main/res/menu/menu_bottom_nav.xml | 2 +- .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../res/mipmap-hdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../res/mipmap-mdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../res/mipmap-xhdpi/ic_launcher_round.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin .../app/src/main/res/navigation/nav_graph.xml | 0 .../app/src/main/res/values/colors.xml | 0 .../app/src/main/res/values/dimens.xml | 0 .../app/src/main/res/values/strings.xml | 2 +- .../app/src/main/res/values/styles.xml | 2 +- .../android/build.gradle.kts | 0 .../android/gradle.properties | 0 .../android/gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 0 .../android/gradlew | 0 .../android/gradlew.bat | 0 .../android/screenshot.jpg | Bin 0 -> 110650 bytes .../android/settings.gradle.kts | 0 53 files changed, 147 insertions(+), 106 deletions(-) delete mode 100644 examples/holistic/android/app/src/androidTest/java/com/google/mediapipe/examples/holistic/ExampleInstrumentedTest.kt delete mode 100644 examples/holistic/android/app/src/test/java/com/google/mediapipe/examples/holistic/ExampleUnitTest.kt rename examples/{holistic => holistic_landmarker}/android/.gitignore (81%) create mode 100644 examples/holistic_landmarker/android/README.md rename examples/{holistic => holistic_landmarker}/android/app/.gitignore (100%) rename examples/{holistic => holistic_landmarker}/android/app/build.gradle.kts (100%) rename examples/{holistic => holistic_landmarker}/android/app/proguard-rules.pro (100%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/AndroidManifest.xml (100%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/HolisticLandmarkerHelper.kt (98%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainActivity.kt (96%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainViewModel.kt (98%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/OverlayView.kt (97%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/CameraFragment.kt (88%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/FaceBlendshapesResultAdapter.kt (97%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/GalleryFragment.kt (88%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/PermissionsFragment.kt (97%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/color/bg_nav_item.xml (93%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/drawable/ic_baseline_add_24.xml (93%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/drawable/ic_baseline_photo_camera_24.xml (94%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/drawable/ic_baseline_photo_library_24.xml (94%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/drawable/ic_minus.xml (93%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/drawable/ic_plus.xml (93%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/drawable/icn_chevron_up.png (100%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/drawable/media_pipe_banner.xml (99%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/layout/activity_main.xml (100%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/layout/face_blendshapes_result.xml (96%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/layout/fragment_camera.xml (100%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/layout/fragment_gallery.xml (98%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/layout/info_bottom_sheet.xml (99%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/menu/menu_bottom_nav.xml (94%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png (100%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png (100%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png (100%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png (100%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png (100%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/navigation/nav_graph.xml (100%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/values/colors.xml (100%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/values/dimens.xml (100%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/values/strings.xml (96%) rename examples/{holistic => holistic_landmarker}/android/app/src/main/res/values/styles.xml (96%) rename examples/{holistic => holistic_landmarker}/android/build.gradle.kts (100%) rename examples/{holistic => holistic_landmarker}/android/gradle.properties (100%) rename examples/{holistic => holistic_landmarker}/android/gradle/wrapper/gradle-wrapper.jar (100%) rename examples/{holistic => holistic_landmarker}/android/gradle/wrapper/gradle-wrapper.properties (100%) rename examples/{holistic => holistic_landmarker}/android/gradlew (100%) rename examples/{holistic => holistic_landmarker}/android/gradlew.bat (100%) create mode 100644 examples/holistic_landmarker/android/screenshot.jpg rename examples/{holistic => holistic_landmarker}/android/settings.gradle.kts (100%) diff --git a/examples/holistic/android/app/src/androidTest/java/com/google/mediapipe/examples/holistic/ExampleInstrumentedTest.kt b/examples/holistic/android/app/src/androidTest/java/com/google/mediapipe/examples/holistic/ExampleInstrumentedTest.kt deleted file mode 100644 index 635a9751..00000000 --- a/examples/holistic/android/app/src/androidTest/java/com/google/mediapipe/examples/holistic/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.google.mediapipe.examples.holistic - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = - InstrumentationRegistry.getInstrumentation().targetContext - assertEquals( - "com.google.mediapipe.examples.holistic", - appContext.packageName - ) - } -} \ No newline at end of file diff --git a/examples/holistic/android/app/src/test/java/com/google/mediapipe/examples/holistic/ExampleUnitTest.kt b/examples/holistic/android/app/src/test/java/com/google/mediapipe/examples/holistic/ExampleUnitTest.kt deleted file mode 100644 index c3ad93be..00000000 --- a/examples/holistic/android/app/src/test/java/com/google/mediapipe/examples/holistic/ExampleUnitTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.google.mediapipe.examples.holistic - -import org.junit.Test - -import org.junit.Assert.* - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/examples/holistic/android/.gitignore b/examples/holistic_landmarker/android/.gitignore similarity index 81% rename from examples/holistic/android/.gitignore rename to examples/holistic_landmarker/android/.gitignore index 7b7fa05d..ab234627 100644 --- a/examples/holistic/android/.gitignore +++ b/examples/holistic_landmarker/android/.gitignore @@ -13,4 +13,4 @@ .externalNativeBuild .cxx local.properties -/app/src/main/assets/tasks/holistic_landmarker.task +*.task diff --git a/examples/holistic_landmarker/android/README.md b/examples/holistic_landmarker/android/README.md new file mode 100644 index 00000000..33b4c496 --- /dev/null +++ b/examples/holistic_landmarker/android/README.md @@ -0,0 +1,48 @@ +# MediaPipe Tasks Holistic Landmark Detection Android Demo + +### Overview + +This is a camera app that continuously detects the body, hand, and face +landmarks in the frames seen by your device's back camera, using a +custom **task** file. + +The task file is downloaded by a Gradle script when you build and run the app. +You don't need to do any additional steps to download task files into the +project explicitly unless you wish to use your own landmark detection task. If +you do use your own task file, place it into the app's assets/tasks directory. + +This application should be run on physical Android devices with a back camera. + +![Holistic Landmarker Demo](screenshot.jpg?raw=true "Holistic Landmarker Demo") + +## Build the demo using Android Studio + +### Prerequisites + +* The **[Android Studio](https://developer.android.com/studio/index.html)** IDE. + This sample has been tested on Android Studio Giraffe. + +* A physical Android device with a minimum OS version of SDK 24 (Android 7.0 - + Nougat) with developer mode enabled. The process of enabling developer mode + may vary by device. + +### Building + +* Open Android Studio. From the Welcome screen, select Open an existing + Android Studio project. + +* From the Open File or Project window that appears, navigate to and select + the mediapipe/examples/holistic_landmarker/android directory. Click OK. You + may + be asked if you trust the project. Select Trust. + +* If it asks you to do a Gradle Sync, click OK. + +* With your Android device connected to your computer and developer mode + enabled, click on the green Run arrow in Android Studio. + +### Models used + +Downloading, extraction, and placing the models into the *assets/tasks* folder +is +managed automatically by the **app/build.gradle.kts** file. \ No newline at end of file diff --git a/examples/holistic/android/app/.gitignore b/examples/holistic_landmarker/android/app/.gitignore similarity index 100% rename from examples/holistic/android/app/.gitignore rename to examples/holistic_landmarker/android/app/.gitignore diff --git a/examples/holistic/android/app/build.gradle.kts b/examples/holistic_landmarker/android/app/build.gradle.kts similarity index 100% rename from examples/holistic/android/app/build.gradle.kts rename to examples/holistic_landmarker/android/app/build.gradle.kts diff --git a/examples/holistic/android/app/proguard-rules.pro b/examples/holistic_landmarker/android/app/proguard-rules.pro similarity index 100% rename from examples/holistic/android/app/proguard-rules.pro rename to examples/holistic_landmarker/android/app/proguard-rules.pro diff --git a/examples/holistic/android/app/src/main/AndroidManifest.xml b/examples/holistic_landmarker/android/app/src/main/AndroidManifest.xml similarity index 100% rename from examples/holistic/android/app/src/main/AndroidManifest.xml rename to examples/holistic_landmarker/android/app/src/main/AndroidManifest.xml diff --git a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/HolisticLandmarkerHelper.kt b/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/HolisticLandmarkerHelper.kt similarity index 98% rename from examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/HolisticLandmarkerHelper.kt rename to examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/HolisticLandmarkerHelper.kt index adf7eba1..0fd78da1 100644 --- a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/HolisticLandmarkerHelper.kt +++ b/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/HolisticLandmarkerHelper.kt @@ -1,7 +1,7 @@ package com.google.mediapipe.examples.holisticlandmarker /* - * Copyright 2023 The TensorFlow Authors. All Rights Reserved. + * Copyright 2024 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -196,7 +196,9 @@ class HolisticLandmarkerHelper( } catch (e: MediaPipeException) { resultList.add(null); } - } + } ?: kotlin.run { + resultList.add(null); + } } retriever.release() diff --git a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainActivity.kt b/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainActivity.kt similarity index 96% rename from examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainActivity.kt rename to examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainActivity.kt index 2a7ddc80..5cd2185a 100644 --- a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainActivity.kt +++ b/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainActivity.kt @@ -1,7 +1,7 @@ package com.google.mediapipe.examples.holisticlandmarker /* - * Copyright 2023 The TensorFlow Authors. All Rights Reserved. + * Copyright 2024 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainViewModel.kt b/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainViewModel.kt similarity index 98% rename from examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainViewModel.kt rename to examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainViewModel.kt index 5b6c128d..f181b69d 100644 --- a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainViewModel.kt +++ b/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/MainViewModel.kt @@ -1,7 +1,7 @@ package com.google.mediapipe.examples.holisticlandmarker /* - * Copyright 2023 The TensorFlow Authors. All Rights Reserved. + * Copyright 2024 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/OverlayView.kt b/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/OverlayView.kt similarity index 97% rename from examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/OverlayView.kt rename to examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/OverlayView.kt index 923511b9..7f2cb962 100644 --- a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/OverlayView.kt +++ b/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/OverlayView.kt @@ -1,7 +1,7 @@ package com.google.mediapipe.examples.holisticlandmarker /* - * Copyright 2023 The TensorFlow Authors. All Rights Reserved. + * Copyright 2024 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -93,8 +93,8 @@ class OverlayView(context: Context?, attrs: AttributeSet?) : ) // convert bytebuffer to bitmap val bitmap = Bitmap.createBitmap( - results?.segmentationMask()!!.get().width, - results?.segmentationMask()!!.get().height, + imageWidth, + imageHeight, Bitmap.Config.ARGB_8888 ) bitmap.copyPixelsFromBuffer(buffer) diff --git a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/CameraFragment.kt b/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/CameraFragment.kt similarity index 88% rename from examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/CameraFragment.kt rename to examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/CameraFragment.kt index cdfe0652..059bf4b3 100644 --- a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/CameraFragment.kt +++ b/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/CameraFragment.kt @@ -1,7 +1,7 @@ package com.google.mediapipe.examples.holisticlandmarker.fragments /* - * Copyright 2023 The TensorFlow Authors. All Rights Reserved. + * Copyright 2024 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,8 +43,9 @@ import com.google.mediapipe.examples.holisticlandmarker.R import com.google.mediapipe.examples.holisticlandmarker.HelperState import com.google.mediapipe.examples.holisticlandmarker.databinding.FragmentCameraBinding import com.google.mediapipe.tasks.vision.core.RunningMode -import java.util.* -import java.util.concurrent.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit class CameraFragment() : Fragment(), HolisticLandmarkerHelper.LandmarkerListener { @@ -219,19 +220,35 @@ class CameraFragment() : Fragment(), } private fun updateBottomSheetControlsUi(helperState: HelperState) { + val isFaceBlendShapes = + if (helperState.delegate == HolisticLandmarkerHelper.DELEGATE_CPU) helperState.isFaceBlendMode else false + val isPoseSegmentationMarks = + if (helperState.delegate == HolisticLandmarkerHelper.DELEGATE_CPU) helperState.isPoseSegmentationMarks else false + // update bottom sheet settings - with(fragmentCameraBinding.bottomSheetLayout){ - faceLandmarksThresholdValue.text = helperState.minFaceLandmarkThreshold.toString() - poseLandmarksThresholdValue.text = helperState.minPoseLandmarkThreshold.toString() - handLandmarksThresholdValue.text = helperState.minHandLandmarkThreshold.toString() - faceDetectionThresholdValue.text = helperState.minFaceDetectionThreshold.toString() - poseDetectionThresholdValue.text = helperState.minPoseDetectionThreshold.toString() - faceSuppressionValue.text = helperState.minFaceSuppressionThreshold.toString() - poseSuppressionValue.text = helperState.minPoseSuppressionThreshold.toString() - switchFaceBlendShapes.isChecked = helperState.isFaceBlendMode - switchPoseSegmentationMarks.isChecked = helperState.isPoseSegmentationMarks + with(fragmentCameraBinding.bottomSheetLayout) { + faceLandmarksThresholdValue.text = + helperState.minFaceLandmarkThreshold.toString() + poseLandmarksThresholdValue.text = + helperState.minPoseLandmarkThreshold.toString() + handLandmarksThresholdValue.text = + helperState.minHandLandmarkThreshold.toString() + faceDetectionThresholdValue.text = + helperState.minFaceDetectionThreshold.toString() + poseDetectionThresholdValue.text = + helperState.minPoseDetectionThreshold.toString() + faceSuppressionValue.text = + helperState.minFaceSuppressionThreshold.toString() + poseSuppressionValue.text = + helperState.minPoseSuppressionThreshold.toString() + // enable with CPU delegate + switchFaceBlendShapes.isChecked = isFaceBlendShapes + switchPoseSegmentationMarks.isChecked = isPoseSegmentationMarks + switchFaceBlendShapes.isEnabled = + helperState.delegate == HolisticLandmarkerHelper.DELEGATE_CPU + switchPoseSegmentationMarks.isEnabled = + helperState.delegate == HolisticLandmarkerHelper.DELEGATE_CPU } - // Create the HolisticLandmarkerHelper that will handle the inference backgroundExecutor.execute { // clear it and recreate with new settings @@ -246,8 +263,8 @@ class CameraFragment() : Fragment(), minPoseDetectionConfidence = helperState.minPoseDetectionThreshold, minFaceSuppressionThreshold = helperState.minFaceSuppressionThreshold, minPoseSuppressionThreshold = helperState.minPoseSuppressionThreshold, - isFaceBlendShapes = helperState.isFaceBlendMode, - isPoseSegmentationMark = helperState.isPoseSegmentationMarks, + isFaceBlendShapes = isFaceBlendShapes, + isPoseSegmentationMark = isPoseSegmentationMarks, landmarkerHelperListener = this ) _fragmentCameraBinding?.overlay?.clear() diff --git a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/FaceBlendshapesResultAdapter.kt b/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/FaceBlendshapesResultAdapter.kt similarity index 97% rename from examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/FaceBlendshapesResultAdapter.kt rename to examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/FaceBlendshapesResultAdapter.kt index 55982172..b774f521 100644 --- a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/FaceBlendshapesResultAdapter.kt +++ b/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/FaceBlendshapesResultAdapter.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The TensorFlow Authors. All Rights Reserved. + * Copyright 2024 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/GalleryFragment.kt b/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/GalleryFragment.kt similarity index 88% rename from examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/GalleryFragment.kt rename to examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/GalleryFragment.kt index 1f42a904..68d183cd 100644 --- a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/GalleryFragment.kt +++ b/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/GalleryFragment.kt @@ -1,7 +1,7 @@ package com.google.mediapipe.examples.holisticlandmarker.fragments /* - * Copyright 2023 The TensorFlow Authors. All Rights Reserved. + * Copyright 2024 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,7 +40,9 @@ import com.google.mediapipe.examples.holisticlandmarker.MainViewModel import com.google.mediapipe.examples.holisticlandmarker.R import com.google.mediapipe.examples.holisticlandmarker.databinding.FragmentGalleryBinding import com.google.mediapipe.tasks.vision.core.RunningMode -import java.util.concurrent.* +import java.util.concurrent.Executors +import java.util.concurrent.ScheduledExecutorService +import java.util.concurrent.TimeUnit class GalleryFragment : Fragment(), HolisticLandmarkerHelper.LandmarkerListener { @@ -182,26 +184,39 @@ class GalleryFragment : Fragment(), } } - private fun updateBottomSheetControlsUi(uiState: HelperState) { + private var isFaceBlendShapes = false + private var isPoseSegmentationMarks = false + + private fun updateBottomSheetControlsUi(helperState: HelperState) { + isFaceBlendShapes = + if (helperState.delegate == HolisticLandmarkerHelper.DELEGATE_CPU) helperState.isFaceBlendMode else false + isPoseSegmentationMarks = + if (helperState.delegate == HolisticLandmarkerHelper.DELEGATE_CPU) helperState.isPoseSegmentationMarks else false + + // init bottom sheet settings with(fragmentGalleryBinding.bottomSheetLayout) { faceLandmarksThresholdValue.text = - uiState.minFaceLandmarkThreshold.toString() + helperState.minFaceLandmarkThreshold.toString() poseLandmarksThresholdValue.text = - uiState.minPoseLandmarkThreshold.toString() + helperState.minPoseLandmarkThreshold.toString() handLandmarksThresholdValue.text = - uiState.minHandLandmarkThreshold.toString() + helperState.minHandLandmarkThreshold.toString() faceDetectionThresholdValue.text = - uiState.minFaceDetectionThreshold.toString() + helperState.minFaceDetectionThreshold.toString() poseDetectionThresholdValue.text = - uiState.minPoseDetectionThreshold.toString() + helperState.minPoseDetectionThreshold.toString() faceSuppressionValue.text = - uiState.minFaceSuppressionThreshold.toString() + helperState.minFaceSuppressionThreshold.toString() poseSuppressionValue.text = - uiState.minPoseSuppressionThreshold.toString() - switchFaceBlendShapes.isChecked = uiState.isFaceBlendMode - switchPoseSegmentationMarks.isChecked = - uiState.isPoseSegmentationMarks + helperState.minPoseSuppressionThreshold.toString() + // enable with CPU delegate + switchFaceBlendShapes.isChecked = isFaceBlendShapes + switchPoseSegmentationMarks.isChecked = isPoseSegmentationMarks + switchFaceBlendShapes.isEnabled = + helperState.delegate == HolisticLandmarkerHelper.DELEGATE_CPU + switchPoseSegmentationMarks.isEnabled = + helperState.delegate == HolisticLandmarkerHelper.DELEGATE_CPU } clearView() } @@ -223,7 +238,7 @@ class GalleryFragment : Fragment(), }.copy(Bitmap.Config.ARGB_8888, true)?.let { bitmap -> fragmentGalleryBinding.imageResult.setImageBitmap(bitmap) - // Run face landmarker on the input image + // Run holistic landmarker on the input image backgroundExecutor.execute { viewModel.helperState.value?.let { holisticLandmarkerHelper = HolisticLandmarkerHelper( @@ -237,8 +252,8 @@ class GalleryFragment : Fragment(), minPoseDetectionConfidence = it.minPoseDetectionThreshold, minFaceSuppressionThreshold = it.minFaceSuppressionThreshold, minPoseSuppressionThreshold = it.minPoseSuppressionThreshold, - isFaceBlendShapes = it.isFaceBlendMode, - isPoseSegmentationMark = it.isPoseSegmentationMarks, + isFaceBlendShapes = isFaceBlendShapes, + isPoseSegmentationMark = isPoseSegmentationMarks, landmarkerHelperListener = this ) } @@ -270,7 +285,7 @@ class GalleryFragment : Fragment(), setUiEnabled(true) } Log.e( - TAG, "Error running face landmarker." + TAG, "Error running holistic landmarker." ) } @@ -322,8 +337,8 @@ class GalleryFragment : Fragment(), minPoseDetectionConfidence = it.minPoseDetectionThreshold, minFaceSuppressionThreshold = it.minFaceSuppressionThreshold, minPoseSuppressionThreshold = it.minPoseSuppressionThreshold, - isFaceBlendShapes = it.isFaceBlendMode, - isPoseSegmentationMark = it.isPoseSegmentationMarks, + isFaceBlendShapes = isFaceBlendShapes, + isPoseSegmentationMark = isPoseSegmentationMarks, landmarkerHelperListener = this ) } @@ -338,7 +353,7 @@ class GalleryFragment : Fragment(), activity?.runOnUiThread { displayVideoResult(resultBundle) } } ?: run { setUiEnabled(true) - Log.e(TAG, "Error running face landmarker.") + Log.e(TAG, "Error running holistic landmarker.") } holisticLandmarkerHelper.clearHolisticLandmarker() @@ -428,8 +443,11 @@ class GalleryFragment : Fragment(), poseSuppressionMinus.isEnabled = enabled poseSuppressionPlus.isEnabled = enabled switchFaceBlendShapes.isEnabled = enabled - switchPoseSegmentationMarks.isEnabled = enabled - spinnerDelegate.isEnabled = enabled + // only enable with CPU delegate + switchPoseSegmentationMarks.isEnabled = + if (viewModel.helperState.value?.delegate == HolisticLandmarkerHelper.DELEGATE_CPU) enabled else false + spinnerDelegate.isEnabled = + if (viewModel.helperState.value?.delegate == HolisticLandmarkerHelper.DELEGATE_CPU) enabled else false } } diff --git a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/PermissionsFragment.kt b/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/PermissionsFragment.kt similarity index 97% rename from examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/PermissionsFragment.kt rename to examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/PermissionsFragment.kt index 845dad05..48528c8a 100644 --- a/examples/holistic/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/PermissionsFragment.kt +++ b/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/fragments/PermissionsFragment.kt @@ -1,7 +1,7 @@ package com.google.mediapipe.examples.holisticlandmarker.fragments /* - * Copyright 2023 The TensorFlow Authors. All Rights Reserved. + * Copyright 2024 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/examples/holistic/android/app/src/main/res/color/bg_nav_item.xml b/examples/holistic_landmarker/android/app/src/main/res/color/bg_nav_item.xml similarity index 93% rename from examples/holistic/android/app/src/main/res/color/bg_nav_item.xml rename to examples/holistic_landmarker/android/app/src/main/res/color/bg_nav_item.xml index 6dc6b015..d6336b54 100644 --- a/examples/holistic/android/app/src/main/res/color/bg_nav_item.xml +++ b/examples/holistic_landmarker/android/app/src/main/res/color/bg_nav_item.xml @@ -1,6 +1,6 @@ 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 Date: Fri, 5 Jan 2024 16:40:34 +0700 Subject: [PATCH 3/3] remove unused import --- .../holisticlandmarker/HolisticLandmarkerHelper.kt | 4 ++-- .../mediapipe/examples/holisticlandmarker/OverlayView.kt | 9 --------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/HolisticLandmarkerHelper.kt b/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/HolisticLandmarkerHelper.kt index 0fd78da1..2f84c66d 100644 --- a/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/HolisticLandmarkerHelper.kt +++ b/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/HolisticLandmarkerHelper.kt @@ -194,10 +194,10 @@ class HolisticLandmarkerHelper( resultList.add(detectionResult) } } catch (e: MediaPipeException) { - resultList.add(null); + resultList.add(null) } } ?: kotlin.run { - resultList.add(null); + resultList.add(null) } } diff --git a/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/OverlayView.kt b/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/OverlayView.kt index 7f2cb962..d487c4e5 100644 --- a/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/OverlayView.kt +++ b/examples/holistic_landmarker/android/app/src/main/java/com/google/mediapipe/examples/holisticlandmarker/OverlayView.kt @@ -19,7 +19,6 @@ package com.google.mediapipe.examples.holisticlandmarker import android.content.Context import android.graphics.Bitmap import android.graphics.Canvas -import android.graphics.Color import android.graphics.Paint import android.util.AttributeSet import android.view.View @@ -40,7 +39,6 @@ class OverlayView(context: Context?, attrs: AttributeSet?) : private var facePaint = Paint() private var handPaint = Paint() private var posePaint = Paint() - private var pointPaint = Paint() private var scaleFactor: Float = 1f private var imageWidth: Int = 1 @@ -53,7 +51,6 @@ class OverlayView(context: Context?, attrs: AttributeSet?) : fun clear() { results = null facePaint.reset() - pointPaint.reset() invalidate() initPaints() } @@ -73,11 +70,6 @@ class OverlayView(context: Context?, attrs: AttributeSet?) : ContextCompat.getColor(context!!, R.color.color_pose) posePaint.strokeWidth = LANDMARK_STROKE_WIDTH posePaint.style = Paint.Style.STROKE - - - pointPaint.color = Color.YELLOW - pointPaint.strokeWidth = LANDMARK_STROKE_WIDTH - pointPaint.style = Paint.Style.FILL } override fun draw(canvas: Canvas) { @@ -211,6 +203,5 @@ class OverlayView(context: Context?, attrs: AttributeSet?) : companion object { private const val LANDMARK_STROKE_WIDTH = 8F - private const val TAG = "Holistic Landmarker Overlay" } }