Skip to content

Commit fde1777

Browse files
authored
[Fix] Support fetching images when using worker engine (#574)
The previous implementation relied on the HTMLImageElement constuctor which is not available in worker contexts.
1 parent dc2d5ea commit fde1777

File tree

2 files changed

+32
-31
lines changed

2 files changed

+32
-31
lines changed

examples/vision-model/src/vision_model.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ function setLabel(id: string, text: string) {
99
label.innerText = text;
1010
}
1111

12+
const USE_WEB_WORKER = false;
13+
1214
const proxyUrl = "https://cors-anywhere.herokuapp.com/";
1315
const url_https_street = "https://www.ilankelman.org/stopsigns/australia.jpg";
1416
const url_https_tree = "https://www.ilankelman.org/sunset.jpg";
@@ -23,16 +25,25 @@ async function main() {
2325
setLabel("init-label", report.text);
2426
};
2527
const selectedModel = "Phi-3.5-vision-instruct-q4f16_1-MLC";
26-
const engine: webllm.MLCEngineInterface = await webllm.CreateMLCEngine(
27-
selectedModel,
28-
{
29-
initProgressCallback: initProgressCallback,
30-
logLevel: "INFO", // specify the log level
31-
},
32-
{
33-
context_window_size: 6144,
34-
},
35-
);
28+
29+
const engineConfig: webllm.MLCEngineConfig = {
30+
initProgressCallback: initProgressCallback,
31+
logLevel: "INFO", // specify the log level
32+
};
33+
const chatOpts = {
34+
context_window_size: 6144,
35+
};
36+
37+
const engine: webllm.MLCEngineInterface = USE_WEB_WORKER
38+
? await webllm.CreateWebWorkerMLCEngine(
39+
new Worker(new URL("./worker.ts", import.meta.url), {
40+
type: "module",
41+
}),
42+
selectedModel,
43+
engineConfig,
44+
chatOpts,
45+
)
46+
: await webllm.CreateMLCEngine(selectedModel, engineConfig, chatOpts);
3647

3748
// 1. Single image input (with choices)
3849
const messages: webllm.ChatCompletionMessageParam[] = [

src/support.ts

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -411,28 +411,18 @@ export const IMAGE_EMBED_SIZE = 1921;
411411
/**
412412
* Given a url, get the image data. The url can either start with `http` or `data:image`.
413413
*/
414-
export function getImageDataFromURL(url: string): Promise<ImageData> {
415-
return new Promise((resolve, reject) => {
416-
// Converts img to any, and later `as CanvasImageSource`, otherwise build complains
417-
const img: any = new Image();
418-
img.crossOrigin = "anonymous"; // Important for CORS
419-
img.onload = () => {
420-
const canvas = document.createElement("canvas");
421-
const ctx = canvas.getContext("2d");
422-
if (!ctx) {
423-
reject(new Error("Could not get 2d context"));
424-
return;
425-
}
426-
canvas.width = img.width;
427-
canvas.height = img.height;
428-
ctx.drawImage(img as CanvasImageSource, 0, 0);
414+
export async function getImageDataFromURL(url: string): Promise<ImageData> {
415+
const response = await fetch(url, { mode: "cors" });
416+
const img = await createImageBitmap(await response.blob());
417+
const canvas = new OffscreenCanvas(img.width, img.height);
418+
const ctx = canvas.getContext("2d");
419+
if (!ctx) {
420+
throw new Error("Could not get 2d context");
421+
}
422+
ctx.drawImage(img, 0, 0);
429423

430-
const imageData = ctx.getImageData(0, 0, img.width, img.height);
431-
resolve(imageData);
432-
};
433-
img.onerror = () => reject(new Error("Failed to load image"));
434-
img.src = url;
435-
});
424+
const imageData = ctx.getImageData(0, 0, img.width, img.height);
425+
return imageData;
436426
}
437427

438428
/**

0 commit comments

Comments
 (0)