Run ExecuTorch and validate the output

ExecuTorchSegmenter creates the input tensor from the shape in model_spec.json, runs the model, and rejects an unexpected output shape:

    

        
        
Tensor inputTensor = Tensor.fromBlob(input.tensor, spec.inputShape);
Tensor outputTensor = module.forward(EValue.from(inputTensor))[0].toTensor();

if (!Arrays.equals(outputTensor.shape(), spec.outputShape)) {
    throw new IllegalArgumentException("Unexpected model output shape");
}

    
ExecuTorch API Highlight

Tensor.fromBlob() associates the Java float array with the shape expected by the exported program. EValue.from() converts the tensor to the value type used for ExecuTorch method arguments. Module.forward() invokes the exported forward method and returns an array of EValue outputs; [0].toTensor() selects the segmentation-logit tensor. Class selection, mask resizing, and cutout creation remain application code.

The shape check prevents the postprocessor from applying NCHW indexing to an incompatible output.

Create the cutout

For flattened NCHW output, all pixels for one class are contiguous. The score for a class and pixel is therefore read at classIndex * pixels + pixel:

    

        
        
for (int classIndex = 1; classIndex < classes; classIndex++) {
    float score = logits[classIndex * pixels + pixel];
    if (score > bestScore) {
        bestScore = score;
        bestClass = classIndex;
    }
}

    

After counting the winning class at every pixel, MaskPostprocessor chooses the largest non-background class. It removes the letterbox padding and sets alpha to 255 for the selected class and 0 elsewhere:

    

        
        
int tensorIndex = (y + padTop) * tensorWidth + x + padLeft;
alphaMask[contentIndex] =
        squareMask[tensorIndex] == selectedClass ? (byte) 255 : 0;

    

Keep runtime work off the UI thread

MainActivity runs model loading and inference on a single background executor. Only the UI update is sent back to the main thread:

    

        
        
executor.execute(() -> {
    CutoutResult result = segmenter.cutout(selectedBitmap);
    runOnUiThread(() -> {
        lastResult = result;
        displayLastResult();
    });
});

    

When the Activity is destroyed, Module.close() is queued after any active inference and the executor is shut down. This releases the native runtime without closing it while a model call is still running.

ExecuTorch API Highlight

Module.close() releases the native runtime resources owned by the Java Module. It must run after outstanding inference work has completed so that an active forward() call does not lose the native state it is using.

You should now understand how you can use the same ExecuTorch preparation workflow from the Edge Linux modules and apply it to an Android application. The model is still prepared ahead of time, exported, lowered, and packaged as a .pte artifact. What changes is how the application packages the artifact, loads it, prepares input tensors, runs inference, handles output, and keeps the UI responsive.

Previous page Complete and continue