Skip to content

Add BiSeNet face parsing sample (CompiledModel GPU)#201

Open
john-rocky wants to merge 6 commits into
google-ai-edge:mainfrom
john-rocky:bisenet-face-parsing-sample
Open

Add BiSeNet face parsing sample (CompiledModel GPU)#201
john-rocky wants to merge 6 commits into
google-ai-edge:mainfrom
john-rocky:bisenet-face-parsing-sample

Conversation

@john-rocky

Copy link
Copy Markdown
Contributor

BiSeNet (zllrunning/face-parsing.PyTorch, MIT) — real-time 19-class CelebAMask-HQ face parsing (skin, brows, eyes, nose, lips, ears, hair, hat, glasses, neck, cloth, …), running fully on the LiteRT CompiledModel GPU at ~22 ms/frame on a Pixel 8a. For AR / beauty / makeup. Fills the face-parsing gap in the samples.

BiSeNet is a pure CNN (ResNet18 backbone + context path + feature fusion). Three re-authoring patches make it a fully GPU-compatible graph — 74/74 nodes on the delegate, 1 partition (device corr 0.99999, argmax 99.96% vs PyTorch):

  1. align_corners=TrueFalse — the output upsamples use align_corners=True, which the GPU delegate rejects.
  2. global avg_pool2d(x, x.size()[2:])mean([2,3]) — full-spatial-kernel pooling is rejected by the Mali delegate as AVERAGE_POOL_2D; a MEAN reduce is supported.
  3. zero-pad maxpool — the ResNet stem MaxPool2d(padding=1) lowers to a -inf PADV2 (PADV2: src has wrong size on Mali); an explicit 0-pad + unpadded maxpool is exact (post-ReLU input ≥ 0).

These are on-device-only rejections (the op inventory is clean and CPU parity is 1.0). CPU-exact vs PyTorch (corr 0.99999999999).

Model: litert-community/BiSeNet-Face-Parsing-LiteRT (53 MB).

Sample at compiled_model_api/face_parsing/bisenet_kotlin_gpu (android app + conversion scripts), with a deterministic bundled face demo.

BiSeNet (zllrunning/face-parsing.PyTorch, MIT) — real-time 19-class CelebAMask-HQ face parsing (skin, brows, eyes, nose, lips, ears, hair, ...), running fully on the LiteRT CompiledModel GPU at ~22 ms/frame on a Pixel 8a. For AR / beauty / makeup.

BiSeNet is a pure CNN (ResNet18 backbone). Three re-authoring patches make it a fully GPU-compatible graph (74/74 nodes on the delegate, 1 partition): align_corners=True->False; global avg_pool2d(x,x.size()[2:])->mean([2,3]) (Mali rejects a full-spatial-kernel AVERAGE_POOL_2D); ZeroPadMaxPool (the ResNet stem maxpool's -inf PADV2 is rejected as 'PADV2: src has wrong size', replaced by an explicit 0-pad + unpadded maxpool, exact post-ReLU). These are on-device-only rejections (op inventory clean, CPU parity 1.0). Device Mali GPU corr 0.99999, argmax 99.96%.

Sample at compiled_model_api/face_parsing/bisenet_kotlin_gpu (android app + conversion scripts), with a deterministic bundled face demo.
@john-rocky john-rocky force-pushed the bisenet-face-parsing-sample branch from eeb54a9 to 3e2a320 Compare July 6, 2026 20:58
Merge hard-wrapped lines (paragraphs, list items, blockquotes) into single lines so the markdown renders as normal prose. Tables, code blocks, and content are unchanged.
… conversion script, document all three GPU patches, copyright 2026
Wrap every line in the conversion scripts to the 80-column limit of the
Google Python Style Guide and add Args/Returns sections to the
module-level function docstrings. No behavior changes.
Restructure the app to the image_segmentation reference: a thin
ComponentActivity host over a MainViewModel + immutable UiState, UI strings
moved to res/values, a Gradle version catalog, and status-bar inset padding.
Inference/model code is unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant