show-me / src /lib /ai /segmenter.js
PierreRouanet's picture
Add remote robot discovery via Grand Central signaling server
8cec3fe
Raw
History Blame Contribute Delete
2.29 kB
import { pipeline } from "@huggingface/transformers";
/**
* Image segmentation engine β€” works for both panoptic segmentation and face parsing.
* The model ID passed to the constructor determines the behavior.
*
* Events:
* 'loading' β€” model download started
* 'progress' β€” download progress (detail: { file, progress, loaded, total })
* 'ready' β€” model loaded and ready
* 'error' β€” something went wrong (detail: Error)
*/
export class Segmenter extends EventTarget {
#pipe = null;
#status = "idle";
#modelId;
#opts;
/**
* @param {string} modelId e.g. "Xenova/detr-resnet-50-panoptic" or "jonathandinu/face-parsing"
* @param {{ dtype?: string }} opts Extra pipeline options (e.g. { dtype: "q8" })
*/
constructor(modelId, opts = {}) {
super();
this.#modelId = modelId;
this.#opts = opts;
}
get status() {
return this.#status;
}
async load() {
if (this.#status === "ready" || this.#status === "loading") return;
this.#status = "loading";
this.dispatchEvent(new CustomEvent("loading", { detail: { status: "loading" } }));
try {
const device = navigator.gpu ? "webgpu" : "wasm";
// fp16 is ~2-3x faster on WebGPU; q8 is best for WASM CPU
const dtype = this.#opts.dtype ?? (device === "webgpu" ? "fp16" : "q8");
this.#pipe = await pipeline("image-segmentation", this.#modelId, {
device,
...this.#opts,
dtype,
progress_callback: (progress) => {
this.dispatchEvent(new CustomEvent("progress", { detail: progress }));
},
});
this.#status = "ready";
this.dispatchEvent(new CustomEvent("ready"));
} catch (err) {
this.#status = "error";
this.dispatchEvent(new CustomEvent("error", { detail: err }));
throw err;
}
}
/**
* Run segmentation on an image.
* @param {import("@huggingface/transformers").RawImage|string} image
* @returns {Promise<Array<{ label: string, score: number, mask: import("@huggingface/transformers").RawImage }>>}
*/
async segment(image) {
if (!this.#pipe) throw new Error("Segmenter model not loaded β€” call load() first");
return await this.#pipe(image);
}
dispose() {
this.#pipe = null;
this.#status = "idle";
}
}