chenjindu's picture
Upload folder using huggingface_hub
4a57260 verified
Raw History Blame Contribute Delete
5.33 kB
<!doctype html><html lang=en><meta charset=utf-8><title>Browser Remove Background — WASM vs WebGPU probe</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><text y='14' font-size='14'>%E2%9C%82%EF%B8%8F</text></svg>">
<script src="./ort/ort.min.js"></script>
<style>
body{font:15px/1.5 system-ui;margin:2rem;max-width:900px}
canvas,img{max-width:100%;border:1px solid #ccc}
pre{background:#f4f4f4;padding:.6rem;white-space:pre-wrap}
button{padding:.5rem 1rem;border:1px solid #999;border-radius:3px;background:#fff;cursor:pointer;font:inherit}
button:hover{background:#f4f4f4}
.filebtn{display:inline-block;padding:.5rem 1rem;border:1px solid #999;border-radius:3px;cursor:pointer}
.filebtn:hover{background:#f4f4f4}
a{color:#0a6}
</style>
<h2>Remove Background in the Browser — WASM vs WebGPU probe</h2>
<p>ISNet (int8 weights, 44 MB) running on onnxruntime-web 1.22. The image is decoded, segmented and composited on your own device — nothing is uploaded.</p>
<p>Pick a photo, then run it with each execution provider and compare the timings. Try a portrait, a product shot and something with a busy background. The first inference is always slower (warm-up and shader compilation) — read the second one.</p>
<p>
<label class="filebtn">Choose image<input type=file id=f accept="image/*" hidden></label>
<span id=fname style="color:#666;margin-right:1rem">No file chosen</span>
<button id=w>Run with WASM</button>
<button id=g>Run with WebGPU</button>
</p>
<pre id=log>Waiting…</pre>
<div style="display:flex;gap:1rem"><img id=img style="width:45%"><canvas id=out style="width:45%;background:repeating-conic-gradient(#ccc 0 25%,#eee 0 50%) 0 0/16px 16px"></canvas></div>
<canvas id=c hidden></canvas>
<p style="margin-top:2rem;color:#666">Production version: <a href="https://is.tools/remove-background">is.tools/remove-background</a> · Source and benchmarks: <a href="https://github.com/chenjindu/browser-remove-background">GitHub</a></p>
<script>
const log = (s) => { document.getElementById('log').textContent += '\n' + s; };
let session = null, sessionEp = '';
document.getElementById('f').onchange = (e) => {
document.getElementById('fname').textContent = (e.target.files[0] && e.target.files[0].name) || 'No file chosen';
};
async function loadModel() {
const t0 = performance.now();
const parts = await Promise.all(['isnet-w8.part00','isnet-w8.part01','isnet-w8.part02'].map(p => fetch('./model/' + p).then(r => r.arrayBuffer())));
const total = parts.reduce((n, p) => n + p.byteLength, 0);
const buf = new Uint8Array(total); let off = 0; for (const p of parts) { buf.set(new Uint8Array(p), off); off += p.byteLength; }
log(`Model fetched (${Math.round(total/1048576)} MB): ${Math.round(performance.now()-t0)} ms`);
return buf;
}
async function run(ep) {
const file = document.getElementById('f').files[0]; if (!file) { log('Pick an image first.'); return; }
document.getElementById('log').textContent = `EP=${ep} · CPU cores ${navigator.hardwareConcurrency} · UA ${navigator.userAgent.slice(0,80)}`;
ort.env.wasm.wasmPaths = new URL('./ort/', document.baseURI).href;
ort.env.wasm.numThreads = 1;
try {
if (!session || sessionEp !== ep) {
const buf = await loadModel();
const t = performance.now();
session = await ort.InferenceSession.create(buf, { executionProviders: [ep], graphOptimizationLevel: 'all' });
sessionEp = ep;
log(`Session created (incl. compilation): ${Math.round(performance.now()-t)} ms`);
}
const img = document.getElementById('img'); img.src = URL.createObjectURL(file); await img.decode();
const c = document.getElementById('c'); c.width = 1024; c.height = 1024; const ctx = c.getContext('2d', { willReadFrequently: true });
ctx.drawImage(img, 0, 0, 1024, 1024);
const d = ctx.getImageData(0, 0, 1024, 1024).data;
const N = 1024 * 1024, x = new Float32Array(3 * N);
for (let i = 0; i < N; i++) { x[i] = d[i*4]/255 - 0.5; x[N+i] = d[i*4+1]/255 - 0.5; x[2*N+i] = d[i*4+2]/255 - 0.5; }
for (let k = 1; k <= 2; k++) {
const t = performance.now();
const out = await session.run({ input_image: new ort.Tensor('float32', x, [1, 3, 1024, 1024]) });
log(`Inference #${k}: ${Math.round(performance.now()-t)} ms`);
if (k === 2) {
const m = out.output_image.data; let mn = 1e9, mx = -1e9; for (const v of m) { if (v < mn) mn = v; if (v > mx) mx = v; }
const mc = document.createElement('canvas'); mc.width = mc.height = 1024; const mctx = mc.getContext('2d');
const id = mctx.createImageData(1024, 1024);
for (let i = 0; i < N; i++) { const a = Math.round(((m[i]-mn)/(mx-mn+1e-8))*255); id.data[i*4]=id.data[i*4+1]=id.data[i*4+2]=255; id.data[i*4+3]=a; }
mctx.putImageData(id, 0, 0);
const o = document.getElementById('out'); o.width = img.naturalWidth; o.height = img.naturalHeight; const octx = o.getContext('2d');
octx.drawImage(img, 0, 0); octx.globalCompositeOperation = 'destination-in'; octx.drawImage(mc, 0, 0, o.width, o.height);
}
}
log('Done.');
} catch (e) { log('Failed: ' + (e && e.message || e)); }
}
document.getElementById('w').onclick = () => run('wasm');
document.getElementById('g').onclick = () => run('webgpu');
</script>