Skip to content

Commit

Permalink
Add selfie segmentation example using builtin TFLite Web (webmachinel…
Browse files Browse the repository at this point in the history
…earning#163)

* Update to 2023 Copyright

* Add selfie segmentation example using builtin TFLite Web
  • Loading branch information
Honry authored Mar 3, 2023
1 parent 23e6f66 commit 998c2cd
Show file tree
Hide file tree
Showing 41 changed files with 1,834 additions and 12 deletions.
4 changes: 3 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
/common/component/component.js
/common/component/component.js
/selfie_segmentation/tflite-support/
/selfie_segmentation/lib/
2 changes: 1 addition & 1 deletion code/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
</div>
<footer class="text-muted text-center text-small mt-5">
<div id="badge"></div>
<p>&copy;2022 <a href="https://webmachinelearning.github.io/">WebNN API</a></p>
<p>&copy;2023 <a href="https://webmachinelearning.github.io/">WebNN API</a></p>
</footer>
<script>
// This workaround is to fix jquery loading issue in electron.
Expand Down
15 changes: 15 additions & 0 deletions common/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,21 @@ a:hover {
font-size: 0.8rem;
}

#selfiesegmentation #outputCanvas {
width: 100%;
max-width: 1539px;
max-height: 1539px;
border: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.2), inset 0 0 1px 0 rgba(255, 255, 255, 1);
}

@media (max-width: 768px) {
#selfiesegmentation #outputCanvas {
max-width: 1539px;
max-height: 1539px;
}
}

#semanticsegmentation #feedElement {
display: none;
}
Expand Down
2 changes: 1 addition & 1 deletion face_recognition/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ <h2 class="text-uppercase text-info">No model selected</h2>
</div>
<footer class="text-muted text-center text-small mt-5">
<div id="badge"></div>
<p>&copy;2022 <a href="https://webmachinelearning.github.io/">WebNN API</a></p>
<p>&copy;2023 <a href="https://webmachinelearning.github.io/">WebNN API</a></p>
</footer>
<script>
// This workaround is to fix jquery loading issue in electron.
Expand Down
2 changes: 1 addition & 1 deletion facial_landmark_detection/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ <h2 class="text-uppercase text-info">No model selected</h2>
</div>
<footer class="text-muted text-center text-small mt-5">
<div id="badge"></div>
<p>&copy;2022 <a href="https://webmachinelearning.github.io/">WebNN API</a></p>
<p>&copy;2023 <a href="https://webmachinelearning.github.io/">WebNN API</a></p>
</footer>
<script>
// This workaround is to fix jquery loading issue in electron.
Expand Down
2 changes: 1 addition & 1 deletion image_classification/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ <h2 class="text-uppercase text-info">No model selected</h2>
</div>
<footer class="text-muted text-center text-small mt-5">
<div id="badge"></div>
<p>&copy;2022 <a href="https://webmachinelearning.github.io/">WebNN API</a></p>
<p>&copy;2023 <a href="https://webmachinelearning.github.io/">WebNN API</a></p>
</footer>
<script>
// This workaround is to fix jquery loading issue in electron.
Expand Down
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ <h1 class="title"><a class="replaceableUrl" href="./face_recognition/index.html"
</div>
</div>
<footer class="text-muted text-center text-small mt-5">
<p>&copy;2022 <a href="https://webmachinelearning.github.io/">WebNN API</a></p>
<p>&copy;2023 <a href="https://webmachinelearning.github.io/">WebNN API</a></p>
</footer>
<script>
// This workaround is to fix jquery loading issue in electron.
Expand Down
2 changes: 1 addition & 1 deletion lenet/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ <h5>Prediction Result:</h5>
</div>
<footer class="text-muted text-center text-small mt-5">
<div id="badge"></div>
<p>&copy;2022 <a href="https://webmachinelearning.github.io/">WebNN API</a></p>
<p>&copy;2023 <a href="https://webmachinelearning.github.io/">WebNN API</a></p>
</footer>
<script>
// This workaround is to fix jquery loading issue in electron.
Expand Down
2 changes: 1 addition & 1 deletion nsnet2/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
</div>
<footer class="text-muted text-center text-small mt-5">
<div id="badge"></div>
<p>&copy;2022 <a href="https://webmachinelearning.github.io/">WebNN API</a></p>
<p>&copy;2023 <a href="https://webmachinelearning.github.io/">WebNN API</a></p>
</footer>
<script>
// This workaround is to fix jquery loading issue in electron.
Expand Down
2 changes: 1 addition & 1 deletion object_detection/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ <h2 class="text-uppercase text-info">No model selected</h2>
</div>
<footer class="text-muted text-center text-small mt-5">
<div id="badge"></div>
<p>&copy;2022 <a href="https://webmachinelearning.github.io/">WebNN API</a></p>
<p>&copy;2023 <a href="https://webmachinelearning.github.io/">WebNN API</a></p>
</footer>
<script>
// This workaround is to fix jquery loading issue in electron.
Expand Down
2 changes: 1 addition & 1 deletion rnnoise/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
</div>
<footer class="text-muted text-center text-small mt-5">
<div id="badge"></div>
<p>&copy;2022 <a href="https://webmachinelearning.github.io/">WebNN API</a></p>
<p>&copy;2023 <a href="https://webmachinelearning.github.io/">WebNN API</a></p>
</footer>
<script>
// This workaround is to fix jquery loading issue in electron.
Expand Down
9 changes: 9 additions & 0 deletions selfie_segmentation/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = {
globals: {
'importScripts': 'readonly',
'tflite_model_runner_ModuleFactory': 'readonly',
},
rules: {
'new-cap': 0,
},
};
17 changes: 17 additions & 0 deletions selfie_segmentation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## WebNN Selfie Segmentation Example

This example demonstrates the [MediaPipe Selfie Segmentation](https://google.github.io/mediapipe/solutions/selfie_segmentation) using the TFLite Web XNNPACK delegate and WebNN delegate, which is built by [tflite-support](https://github.com/huningxin/tflite-support/tree/webnn_delegate_side_module).

### Usage

### Segment images

Choose delegate and model, in a very few second you will see the predict result for the test image be presented on the page. Choose the backgrounds to experience variance portrait segmentation.

You could also click 'Pick Image' button to choose your local image to segment it.

### Segment video stream

Here we segments every frame in a live camera, then combined the results. Click 'LIVE CAMERA' tab, allow the browser to use your local camera if there's a prompt, then you will see real-time segmentation results on the page.

Switch delegate or model to check variance predict result.
102 changes: 102 additions & 0 deletions selfie_segmentation/builtin_delegate_worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
'use strict';

/* eslint max-len: ["error", {"code": 120}] */

// built-in webnn delegate
importScripts('./tflite-support/tflite_model_runner_cc_simd.js');

let modelRunnerResult;
let modelRunner;
// Receive the message from the main thread
onmessage = async (message) => {
if (message) {
// Load model or infer depends on the first data
switch (message.data.action) {
case 'load': {
const loadStart = performance.now();
const modelPath = message.data.modelPath;
// Load WASM module and model.
const [module, modelArrayBuffer] = await Promise.all([
tflite_model_runner_ModuleFactory(),
(await fetch(modelPath)).arrayBuffer(),
]);
// Load WASM module and model.
const modelBytes = new Uint8Array(modelArrayBuffer);
const offset = module._malloc(modelBytes.length);
module.HEAPU8.set(modelBytes, offset);

// Create model runner.
modelRunnerResult =
module.TFLiteWebModelRunner.CreateFromBufferAndOptions(
offset,
modelBytes.length,
{
numThreads: 1,
enableWebNNDelegate: message.data.enableWebNNDelegate,
webNNDevicePreference: parseInt(message.data.webNNDevicePreference),
},
);

if (!modelRunnerResult.ok()) {
throw new Error(
`Failed to create TFLiteWebModelRunner: ${modelRunner.errorMessage()}`);
}
modelRunner = modelRunnerResult.value();
const loadFinishedMs = (performance.now() - loadStart).toFixed(2);
postMessage(loadFinishedMs);
break;
}
case 'compute': {
// Get input and output info.

const inputs = callAndDelete(modelRunner.GetInputs(), (results) => convertCppVectorToArray(results));
const input = inputs[0];
const outputs = callAndDelete(modelRunner.GetOutputs(), (results) => convertCppVectorToArray(results));
const output = outputs[0];

// Set input tensor data from the image (224 x 224 x 3).
const inputBuffer = input.data();
inputBuffer.set(message.data.buffer);

// Infer, get output tensor, and sort by logit values in reverse.
const inferStart = performance.now();
modelRunner.Infer();
const inferTime = performance.now() - inferStart;
console.log(`Infer time in worker: ${inferTime.toFixed(2)} ms`);

let outputBuffer = output.data();
outputBuffer = outputBuffer.slice(0);
postMessage({outputBuffer}, [outputBuffer.buffer]);
break;
}
default: {
break;
}
}
}
};

// Helper functions.

// Converts the given c++ vector to a JS array.
function convertCppVectorToArray(vector) {
if (vector == null) return [];

const result = [];
for (let i = 0; i < vector.size(); i++) {
const item = vector.get(i);
result.push(item);
}
return result;
}


// Calls the given function with the given deletable argument, ensuring that
// the argument gets deleted afterwards (even if the function throws an error).
function callAndDelete(arg, func) {
try {
return func(arg);
} finally {
if (arg != null) arg.delete();
}
}
6 changes: 6 additions & 0 deletions selfie_segmentation/images/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Images under this folder are from https://unsplash.com/, which is licensed under the https://unsplash.com/license.

Unsplash photos are made to be used freely. Our license reflects that.
- All photos can be downloaded and used for free 
- Commercial and non-commercial purposes 
- No permission needed (though attribution is appreciated!)
Binary file added selfie_segmentation/images/backgrounds/00.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added selfie_segmentation/images/backgrounds/01.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added selfie_segmentation/images/backgrounds/02.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added selfie_segmentation/images/test.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 998c2cd

Please sign in to comment.