Does the model function correctly for the use-case?

Run the validate_cutout.py script on your development machine to try the FP32 XNNPACK artifact on a sample image locally:

    

        
        
module-3-executorch-on-android-semantic-segmentation/scripts/aot/validate_cutout.py \
  --spec module-3-executorch-on-android-semantic-segmentation/artifacts/lraspp_mobilenetv3/model_spec.json \
  --pte module-3-executorch-on-android-semantic-segmentation/artifacts/lraspp_mobilenetv3/lraspp_mobilenetv3_xnnpack.pte \
  --image module-3-executorch-on-android-semantic-segmentation/artifacts/lraspp_mobilenetv3/sample.jpg \
  --out-dir module-3-executorch-on-android-semantic-segmentation/artifacts/lraspp_mobilenetv3/validation

    

The script keeps both the normalized input tensor and the letterbox dimensions needed to map the output back to the visible image:

    

        
        
input_tensor, letterboxed = preprocess_image(image_path, spec)
model = _load_for_executorch(str(args.pte))

with torch.inference_mode():
    output = model.forward((input_tensor,))[0]

    

Before interpreting the logits, it rejects an output that does not match the expected [1, 21, 520, 520] shape:

    

        
        
expected_shape = tuple(spec["output"]["shape"])
if tuple(output.shape) != expected_shape:
    raise ValueError(
        f"Unexpected output shape {tuple(output.shape)}; "
        f"expected {expected_shape}"
    )

    

It then applies argmax across the 21 class planes and selects the largest non-background class:

    

        
        
mask = output[0].argmax(dim=0).cpu().numpy().astype("uint8")
background_class = spec["output"]["postprocessing"]["background_class"]
class_id = largest_non_background(mask, background=background_class)

    

The padding recorded during preprocessing is removed before the binary mask is used as image transparency:

    

        
        
selected = mask[top:bottom, left:right] == class_id
mask_image = Image.fromarray((selected.astype("uint8") * 255), mode="L")
cutout.paste(rgba, mask=mask_image)

    

The selected class should be dog:

    

        
        
Selected class: dog (12)
ExecuTorch forward: 29.18 ms

    

The reported time covers only the ExecuTorch forward() call. It excludes image loading, preprocessing, and postprocessing, and, since it runs on your development machine, should not be confused with Android performance data.

Inspect these files in module-3-executorch-on-android-semantic-segmentation/artifacts/lraspp_mobilenetv3/validation/:

  • mask.png shows the selected class as white pixels.
  • overlay.png applies a blue tint to the selected pixels.
  • cutout.png makes all other pixels transparent.

Check FP32 cutout quality

Inspect mask.png, overlay.png, and cutout.png:

  • confirm that the selected class matches the expected object
  • check whether the mask captures the main foreground object
  • look for missing foreground regions
  • look for background regions that were incorrectly retained

FP32 validation result

The FP32 .pte gives a local quality indication. If the FP32 result is poor locally, it will be poor on Android, and you may require a different model for your use-case. If the FP32 result is useful, it becomes the reference for judging whether later INT8 quantization reduces cutout quality too much.

This single-image check confirms that the model, preprocessing, and postprocessing produce a plausible cutout; it does not measure segmentation accuracy. For production evaluation, you should run the model automatically over a representative labeled validation set, compare each predicted mask with its ground-truth mask, and aggregate the results across all images. You can report per-class intersection over union (IoU) and mean IoU and consider these against acceptance thresholds specific to your use-case and needs.

Previous page Complete and continue