Skip to content

Commit 44f7215

Browse files
Copilotkirklandsign
andcommitted
Add MV3 demo, inline test script, and remove separate .sh file
- Added MV3 (MobileNetV3) Android demo from main branch - Inlined run-ci-tests.sh script content directly into workflow YAML - Removed dl3/android/DeepLabV3Demo/scripts/run-ci-tests.sh (no longer needed) - Updated workflow to use matrix strategy to test both DL3 and MV3 demos - Added mv3/android/** to workflow trigger paths - Script now runs inline with same functionality as before Co-authored-by: kirklandsign <107070759+kirklandsign@users.noreply.github.com>
1 parent 6cdad07 commit 44f7215

15 files changed

Lines changed: 1660 additions & 53 deletions

File tree

.github/workflows/android-emulator.yml

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ on:
1111
branches: [main]
1212
paths:
1313
- 'dl3/android/**'
14+
- 'mv3/android/**'
1415
- '.github/workflows/android-emulator.yml'
1516
workflow_dispatch:
1617
inputs:
@@ -26,12 +27,19 @@ permissions:
2627
jobs:
2728
instrumentation-test:
2829
runs-on: 8-core-ubuntu
30+
strategy:
31+
matrix:
32+
demo:
33+
- path: 'dl3/android/DeepLabV3Demo'
34+
name: 'DeepLabV3Demo'
35+
- path: 'mv3/android/MV3Demo'
36+
name: 'MV3Demo'
2937
env:
3038
API_LEVEL: 34
3139
ARCH: x86_64
32-
DEMO_PATH: ${{ inputs.demo_path || 'dl3/android/DeepLabV3Demo' }}
40+
DEMO_PATH: ${{ inputs.demo_path || matrix.demo.path }}
3341

34-
name: Instrumentation Test Android Emulator
42+
name: Instrumentation Test ${{ matrix.demo.name }}
3543
steps:
3644
- name: Checkout repository
3745
uses: actions/checkout@v4
@@ -42,6 +50,9 @@ jobs:
4250
DEMO_NAME=$(basename "${{ env.DEMO_PATH }}")
4351
echo "demo_name=$DEMO_NAME" >> $GITHUB_OUTPUT
4452
echo "Testing: $DEMO_NAME"
53+
echo "=== Test Configuration ==="
54+
echo "Demo: $DEMO_NAME"
55+
echo "Model will be downloaded by the test if not present"
4556
4657
- name: Enable KVM group perms
4758
run: |
@@ -92,7 +103,43 @@ jobs:
92103
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -no-snapshot-save -memory 8192
93104
disable-animations: true
94105
working-directory: ${{ env.DEMO_PATH }}
95-
script: bash ./scripts/run-ci-tests.sh
106+
script: |
107+
set -ex
108+
109+
DEMO_NAME=$(basename "$PWD")
110+
LOGCAT_FILE="/tmp/logcat-${DEMO_NAME}.txt"
111+
112+
echo "=== Emulator Memory Info ==="
113+
adb shell cat /proc/meminfo | head -5
114+
115+
echo "=== Emulator Disk Space ==="
116+
adb shell df -h /data
117+
118+
# Start logcat capture
119+
adb logcat -c
120+
adb logcat > "$LOGCAT_FILE" &
121+
LOGCAT_PID=$!
122+
123+
echo "=== Starting Gradle ==="
124+
./gradlew connectedCheck
125+
TEST_EXIT_CODE=$?
126+
127+
# Stop logcat
128+
if [ -n "$LOGCAT_PID" ]; then
129+
kill $LOGCAT_PID 2>/dev/null || true
130+
fi
131+
132+
echo "=== Test completion status ==="
133+
if [ $TEST_EXIT_CODE -eq 0 ]; then
134+
echo "✅ Tests passed successfully"
135+
else
136+
echo "❌ Tests failed with exit code $TEST_EXIT_CODE"
137+
fi
138+
139+
echo "=== Checking for test results in logcat ==="
140+
grep -E "SanityCheck|UIWorkflowTest" "$LOGCAT_FILE" || echo "No test logs found"
141+
142+
exit $TEST_EXIT_CODE
96143
97144
- name: Upload logcat
98145
if: always()

dl3/android/DeepLabV3Demo/scripts/run-ci-tests.sh

Lines changed: 0 additions & 50 deletions
This file was deleted.

mv3/android/MV3Demo/README.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# MV3 Android Demo
2+
3+
This is a sample Android application demonstrating MobileNet v3 (MV3) image classification using PyTorch ExecuTorch.
4+
5+
## Features
6+
7+
- **MobileNet v3 Inference**: Runs the MV3 model on Android.
8+
- **Live Camera Feed**: Real-time classification on camera frames.
9+
- **Image Selection**: Pick images from the gallery for classification.
10+
- **Material Design 3**: Modern UI with a bottom app bar and intuitive controls.
11+
12+
## Prerequisites
13+
14+
- Android SDK (API 34+)
15+
- JDK 17
16+
- ExecuTorch libraries (configured via Gradle)
17+
18+
## Setup
19+
20+
1. **Clone the repository**:
21+
```bash
22+
git clone https://github.com/meta-pytorch/executorch-examples.git
23+
cd executorch-examples/mv3/android/MV3Demo
24+
```
25+
26+
2. **Build the project**:
27+
```bash
28+
./gradlew :app:assembleDebug
29+
```
30+
31+
3. **Install on device**:
32+
```bash
33+
./gradlew :app:installDebug
34+
```
35+
36+
## Usage
37+
38+
- **Local Model**: The app attempts to load `mv3.pte` from the app's internal storage. If missing, it offers a download button (currently configured with a placeholder URL).
39+
- **Live Camera**: Grant camera permissions to use the real-time classification feature.
40+
- **Pick Image**: Select an image from your device to classify it.
41+
42+
## Architecture
43+
44+
- **UI**: Jetpack Compose
45+
- **Camera**: CameraX (Preview + ImageAnalysis)
46+
- **Inference**: ExecuTorch Android API
47+
- **Image Processing**: `TensorImageUtils` for bitmap-to-tensor conversion
48+
49+
## Testing
50+
51+
The app includes an instrumentation test that validates the complete image classification workflow.
52+
53+
### What the test does
54+
55+
1. Launches the app
56+
2. Downloads the MV3 model if not already present
57+
3. Downloads a cat image from HuggingFace
58+
4. Runs inference on the image
59+
5. Validates that the model correctly classifies it as a cat
60+
61+
### Running the test
62+
63+
1. **Connect a device or start an emulator**
64+
65+
2. **Build and install the test APKs**:
66+
```bash
67+
./gradlew installDebug installDebugAndroidTest
68+
```
69+
70+
3. **Run the test**:
71+
```bash
72+
adb shell am instrument -w -r \
73+
-e class 'org.pytorch.executorchexamples.mv3.UIWorkflowTest#testCatImageClassification' \
74+
org.pytorch.executorchexamples.mv3.test/androidx.test.runner.AndroidJUnitRunner
75+
```
76+
77+
Or run all tests via Gradle:
78+
```bash
79+
./gradlew connectedDebugAndroidTest
80+
```
81+
82+
### Test output
83+
84+
The test logs classification results to logcat with the tag `MV3_RESULT`:
85+
```bash
86+
adb logcat -s MV3_RESULT
87+
```
88+
89+
## License
90+
91+
BSD-3-Clause
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
plugins {
10+
id("com.android.application")
11+
id("org.jetbrains.kotlin.android")
12+
}
13+
14+
android {
15+
namespace = "org.pytorch.executorchexamples.mv3"
16+
compileSdk = 34
17+
18+
defaultConfig {
19+
applicationId = "org.pytorch.executorchexamples.mv3"
20+
minSdk = 24
21+
targetSdk = 34
22+
versionCode = 1
23+
versionName = "1.0"
24+
25+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
26+
vectorDrawables { useSupportLibrary = true }
27+
}
28+
29+
buildTypes {
30+
release {
31+
isMinifyEnabled = false
32+
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
33+
}
34+
}
35+
compileOptions {
36+
sourceCompatibility = JavaVersion.VERSION_1_8
37+
targetCompatibility = JavaVersion.VERSION_1_8
38+
}
39+
kotlinOptions { jvmTarget = "1.8" }
40+
buildFeatures { compose = true }
41+
composeOptions { kotlinCompilerExtensionVersion = "1.4.3" }
42+
}
43+
44+
dependencies {
45+
implementation("androidx.core:core-ktx:1.12.0")
46+
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
47+
implementation("androidx.activity:activity-compose:1.8.0")
48+
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
49+
implementation("androidx.compose.ui:ui")
50+
implementation("androidx.compose.ui:ui-graphics")
51+
implementation("androidx.compose.ui:ui-tooling-preview")
52+
implementation("androidx.compose.material3:material3")
53+
implementation("androidx.compose.material:material-icons-extended")
54+
implementation("com.google.android.material:material:1.12.0")
55+
implementation("androidx.appcompat:appcompat:1.7.0")
56+
implementation("org.pytorch:executorch-android:1.0.0")
57+
testImplementation("junit:junit:4.13.2")
58+
androidTestImplementation("androidx.test.ext:junit:1.1.5")
59+
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
60+
androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
61+
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
62+
debugImplementation("androidx.compose.ui:ui-tooling")
63+
debugImplementation("androidx.compose.ui:ui-test-manifest")
64+
65+
val cameraVersion = "1.3.0"
66+
implementation("androidx.camera:camera-core:$cameraVersion")
67+
implementation("androidx.camera:camera-camera2:$cameraVersion")
68+
implementation("androidx.camera:camera-lifecycle:$cameraVersion")
69+
implementation("androidx.camera:camera-view:$cameraVersion")
70+
}

0 commit comments

Comments
 (0)