Skip to content

Commit 7b8a25c

Browse files
committed
feat: Add GitHub Actions workflow for model export and enhance DeepLabV3 Android demo with improved segmentation visualization and inference time display.
1 parent d0d845a commit 7b8a25c

5 files changed

Lines changed: 93 additions & 22 deletions

File tree

.github/workflows/export-models.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ jobs:
4141
4242
name: Export ${{ matrix.name }}
4343

44+
export-dl3:
45+
runs-on: ubuntu-latest
46+
name: Export DeepLabV3 Model
4447
steps:
4548
- name: Checkout repository
4649
uses: actions/checkout@v4
@@ -65,3 +68,14 @@ jobs:
6568
name: ${{ matrix.artifact }}
6669
path: ${{ matrix.output }}
6770
if-no-files-found: error
71+
run: pip install executorch torchvision
72+
73+
- name: Export DL3 model
74+
working-directory: dl3/python
75+
run: python export.py
76+
77+
- name: Upload PTE model
78+
uses: actions/upload-artifact@v4
79+
with:
80+
name: dl3_xnnpack_fp32.pte
81+
path: dl3/python/dl3_xnnpack_fp32.pte

dl3/android/DeepLabV3Demo/app/src/main/java/org/pytorch/executorchexamples/dl3/MainActivity.java

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,11 @@ public class MainActivity extends Activity implements Runnable {
4343
private ImageView mImageView;
4444
private Button mButtonXnnpack;
4545
private ProgressBar mProgressBar;
46+
private android.widget.TextView mInferenceTimeText;
4647
private Bitmap mBitmap = null;
4748
private Module mModule = null;
4849
private String mImagename = "corgi.jpeg";
50+
private long mInferenceTime = 0;
4951

5052
private final ArrayList<String> mImageFiles = new ArrayList<>();
5153

@@ -57,9 +59,31 @@ public class MainActivity extends Activity implements Runnable {
5759
// see http://host.robots.ox.ac.uk:8080/pascal/VOC/voc2007/segexamples/index.html for the list of
5860
// classes with indexes
5961
private static final int CLASSNUM = 21;
60-
private static final int DOG = 12;
61-
private static final int PERSON = 15;
62-
private static final int SHEEP = 17;
62+
63+
// Colors for all 21 PASCAL VOC classes
64+
private static final int[] CLASS_COLORS = {
65+
0x00000000, // 0: Background (transparent)
66+
0xFFE6194B, // 1: Aeroplane (red)
67+
0xFF3CB44B, // 2: Bicycle (green)
68+
0xFFFFE119, // 3: Bird (yellow)
69+
0xFF4363D8, // 4: Boat (blue)
70+
0xFFF58231, // 5: Bottle (orange)
71+
0xFF911EB4, // 6: Bus (purple)
72+
0xFF46F0F0, // 7: Car (cyan)
73+
0xFFF032E6, // 8: Cat (magenta)
74+
0xFFBCF60C, // 9: Chair (lime)
75+
0xFFFABEBE, // 10: Cow (pink)
76+
0xFF008080, // 11: Dining Table (teal)
77+
0xFF00FF00, // 12: Dog (bright green)
78+
0xFF9A6324, // 13: Horse (brown)
79+
0xFFFFD8B1, // 14: Motorbike (peach)
80+
0xFFFF0000, // 15: Person (red)
81+
0xFF800000, // 16: Potted Plant (maroon)
82+
0xFF0000FF, // 17: Sheep (blue)
83+
0xFF808000, // 18: Sofa (olive)
84+
0xFFE6BEFF, // 19: Train (lavender)
85+
0xFFAA6E28, // 20: TV/Monitor (tan)
86+
};
6387

6488
private void checkAndRequestStoragePermission() {
6589
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
@@ -198,6 +222,7 @@ protected void onCreate(Bundle savedInstanceState) {
198222
mImageView = findViewById(R.id.imageView);
199223
mButtonXnnpack = findViewById(R.id.xnnpackButton);
200224
mProgressBar = findViewById(R.id.progressBar);
225+
mInferenceTimeText = findViewById(R.id.inferenceTimeText);
201226

202227
populateImagePathFromAssets();
203228
showImage();
@@ -223,10 +248,9 @@ public void onClick(View v) {
223248
mButtonXnnpack.setOnClickListener(
224249
new View.OnClickListener() {
225250
public void onClick(View v) {
226-
mModule.destroy();
227-
mModule = Module.load("/data/local/tmp/dl3_xnnpack_fp32.pte");
228251
mButtonXnnpack.setEnabled(false);
229252
mProgressBar.setVisibility(ProgressBar.VISIBLE);
253+
mInferenceTimeText.setVisibility(View.INVISIBLE);
230254
mButtonXnnpack.setText(getString(R.string.run_model));
231255

232256
Thread thread = new Thread(MainActivity.this);
@@ -262,32 +286,38 @@ public void run() {
262286
boolean imageSegementationSuccess = false;
263287
final long startTime = SystemClock.elapsedRealtime();
264288
Tensor outputTensor = mModule.forward(EValue.from(inputTensor))[0].toTensor();
265-
final long inferenceTime = SystemClock.elapsedRealtime() - startTime;
266-
Log.d("ImageSegmentation", "inference time (ms): " + inferenceTime);
289+
mInferenceTime = SystemClock.elapsedRealtime() - startTime;
290+
Log.d("ImageSegmentation", "inference time (ms): " + mInferenceTime);
267291

268292
final float[] scores = outputTensor.getDataAsFloatArray();
269293
int width = mBitmap.getWidth();
270294
int height = mBitmap.getHeight();
271295

296+
// Get original pixels for blending
297+
int[] originalPixels = new int[width * height];
298+
mBitmap.getPixels(originalPixels, 0, width, 0, 0, width, height);
299+
272300
int[] intValues = new int[width * height];
273301
for (int j = 0; j < height; j++) {
274302
for (int k = 0; k < width; k++) {
275-
int maxi = 0, maxj = 0, maxk = 0;
303+
int maxi = 0;
276304
double maxnum = -Double.MAX_VALUE;
277305
for (int i = 0; i < CLASSNUM; i++) {
278306
float score = scores[i * (width * height) + j * width + k];
279307
if (score > maxnum) {
280308
maxnum = score;
281309
maxi = i;
282-
maxj = j;
283-
maxk = k;
284310
}
285311
}
286-
if (maxi == PERSON) intValues[maxj * width + maxk] = 0xFFFF0000; // R
287-
else if (maxi == DOG) intValues[maxj * width + maxk] = 0xFF00FF00; // G
288-
else if (maxi == SHEEP) intValues[maxj * width + maxk] = 0xFF0000FF; // B
289-
else intValues[maxj * width + maxk] = 0xFF000000;
290-
if (maxi == PERSON || maxi == DOG || maxi == SHEEP) {
312+
int pixelIndex = j * width + k;
313+
int classColor = CLASS_COLORS[maxi];
314+
315+
if (maxi == 0) {
316+
// Background: show original image
317+
intValues[pixelIndex] = originalPixels[pixelIndex];
318+
} else {
319+
// Blend segmentation color with original at 50% opacity
320+
intValues[pixelIndex] = blendColors(originalPixels[pixelIndex], classColor, 0.5f);
291321
imageSegementationSuccess = true;
292322
}
293323
}
@@ -310,12 +340,14 @@ public void run() {
310340
runOnUiThread(
311341
() -> {
312342
if (showUserIndicationOnImgSegFail) {
313-
Toast.makeText(this, "ImageSegmentation Failed", Toast.LENGTH_SHORT).show();
343+
Toast.makeText(this, "No objects detected", Toast.LENGTH_SHORT).show();
314344
}
315345
mImageView.setImageBitmap(transferredBitmap);
316346
mButtonXnnpack.setEnabled(true);
317347
mButtonXnnpack.setText(R.string.run_xnnpack);
318348
mProgressBar.setVisibility(ProgressBar.INVISIBLE);
349+
mInferenceTimeText.setText("Inference: " + mInferenceTime + " ms");
350+
mInferenceTimeText.setVisibility(View.VISIBLE);
319351
});
320352
}
321353

@@ -326,4 +358,18 @@ public void run() {
326358
}
327359
});
328360
}
361+
362+
// Blend two colors with given alpha for the overlay
363+
private int blendColors(int background, int foreground, float alpha) {
364+
int bgR = (background >> 16) & 0xFF;
365+
int bgG = (background >> 8) & 0xFF;
366+
int bgB = background & 0xFF;
367+
int fgR = (foreground >> 16) & 0xFF;
368+
int fgG = (foreground >> 8) & 0xFF;
369+
int fgB = foreground & 0xFF;
370+
int r = (int) (bgR * (1 - alpha) + fgR * alpha);
371+
int g = (int) (bgG * (1 - alpha) + fgG * alpha);
372+
int b = (int) (bgB * (1 - alpha) + fgB * alpha);
373+
return 0xFF000000 | (r << 16) | (g << 8) | b;
374+
}
329375
}

dl3/android/DeepLabV3Demo/app/src/main/java/org/pytorch/executorchexamples/dl3/TensorImageUtils.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,6 @@ public static void bitmapToFloatBuffer(
7070
bitmap.getPixels(pixels, 0, width, x, y, width, height);
7171
final int offset_g = pixelsCount;
7272
final int offset_b = 2 * pixelsCount;
73-
for (int i = 0; i < 100; i++) {
74-
final int c = pixels[i];
75-
Log.i("Image", ": " + i + " " + ((c >> 16) & 0xff));
76-
}
7773
for (int i = 0; i < pixelsCount; i++) {
7874
final int c = pixels[i];
7975
float r = ((c >> 16) & 0xff) / 255.0f;

dl3/android/DeepLabV3Demo/app/src/main/res/layout/activity_main.xml

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,28 @@
3030
app:layout_constraintStart_toStartOf="parent"
3131
app:layout_constraintEnd_toEndOf="parent" />
3232

33+
<!-- Inference Time Display -->
34+
<TextView
35+
android:id="@+id/inferenceTimeText"
36+
android:layout_width="wrap_content"
37+
android:layout_height="wrap_content"
38+
android:text="@string/inference_time_placeholder"
39+
android:textSize="16sp"
40+
android:textStyle="bold"
41+
android:textColor="#2196F3"
42+
android:visibility="invisible"
43+
app:layout_constraintTop_toBottomOf="@+id/progressBar"
44+
app:layout_constraintStart_toStartOf="parent"
45+
app:layout_constraintEnd_toEndOf="parent" />
46+
3347
<!-- Row 1: Next and Reset buttons side by side -->
3448
<Button
3549
android:id="@+id/nextButton"
3650
android:layout_width="0dp"
3751
android:layout_height="wrap_content"
3852
android:text="@string/next"
3953
android:textAllCaps="false"
40-
app:layout_constraintTop_toBottomOf="@+id/progressBar"
54+
app:layout_constraintTop_toBottomOf="@+id/inferenceTimeText"
4155
app:layout_constraintStart_toStartOf="parent"
4256
app:layout_constraintEnd_toStartOf="@+id/resetImage"
4357
app:layout_constraintHorizontal_weight="1" />
@@ -48,7 +62,7 @@
4862
android:layout_height="wrap_content"
4963
android:text="@string/reset"
5064
android:textAllCaps="false"
51-
app:layout_constraintTop_toBottomOf="@+id/progressBar"
65+
app:layout_constraintTop_toBottomOf="@+id/inferenceTimeText"
5266
app:layout_constraintStart_toEndOf="@+id/nextButton"
5367
app:layout_constraintEnd_toEndOf="parent"
5468
app:layout_constraintHorizontal_weight="1" />

dl3/android/DeepLabV3Demo/app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@
1111
<string name="reset">Reset</string>
1212
<string name="load_and_refresh">Load And Refresh</string>
1313
<string name="run">Run</string>
14+
<string name="inference_time_placeholder">Inference: -- ms</string>
1415
</resources>

0 commit comments

Comments
 (0)