verisight / samples.js
snarmatha's picture
Deploy VeriSight browser edition
bcef53c verified
Raw History Blame Contribute Delete
3.63 kB
// Builds the three demo files in the browser from CC0 scikit-image photos
// (coffee.png by Rachel Michetti, chelsea.png by Stefan van der Walt), so the
// Space needs no binary assets. Mirrors scripts/make_samples.py.
const SK = "https://cdn.jsdelivr.net/gh/scikit-image/scikit-image@v0.24.0/skimage/data/";
const fetchBitmap = async (name) => createImageBitmap(await (await fetch(SK + name)).blob());
const toBlob = (c, type, q) => new Promise((r) => c.toBlob(r, type, q));
function canvasOf(w, h) {
const c = document.createElement("canvas");
c.width = w; c.height = h;
return [c, c.getContext("2d")];
}
async function jpegBitmap(source, w, h, quality) {
const [c, ctx] = canvasOf(w, h);
ctx.drawImage(source, 0, 0);
return createImageBitmap(await toBlob(c, "image/jpeg", quality));
}
// ---- PNG tEXt chunk insertion (to mimic Stable Diffusion's "parameters" metadata)
const CRC = (() => {
const t = new Uint32Array(256);
for (let n = 0; n < 256; n++) { let c = n; for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1; t[n] = c >>> 0; }
return t;
})();
function crc32(bytes) {
let c = 0xffffffff;
for (const b of bytes) c = CRC[(c ^ b) & 0xff] ^ (c >>> 8);
return (c ^ 0xffffffff) >>> 0;
}
function withTextChunk(png, keyword, text) {
const data = new TextEncoder().encode(`${keyword}\0${text}`);
const chunk = new Uint8Array(12 + data.length), dv = new DataView(chunk.buffer);
dv.setUint32(0, data.length);
chunk.set([0x74, 0x45, 0x58, 0x74], 4); // "tEXt"
chunk.set(data, 8);
dv.setUint32(8 + data.length, crc32(chunk.subarray(4, 8 + data.length)));
const at = 33; // after the 8-byte signature + 25-byte IHDR chunk
const out = new Uint8Array(png.length + chunk.length);
out.set(png.subarray(0, at), 0); out.set(chunk, at); out.set(png.subarray(at), at + chunk.length);
return out;
}
export async function makeSample(kind) {
if (kind === "photo") {
const coffee = await fetchBitmap("coffee.png");
const [c, ctx] = canvasOf(coffee.width, coffee.height);
ctx.drawImage(coffee, 0, 0);
return new File([await toBlob(c, "image/jpeg", 0.92)], "photo.jpg", { type: "image/jpeg" });
}
if (kind === "edited") {
const [coffee, cat] = await Promise.all([fetchBitmap("coffee.png"), fetchBitmap("chelsea.png")]);
const base = await jpegBitmap(coffee, coffee.width, coffee.height, 0.85);
const patch = await jpegBitmap(cat, cat.width, cat.height, 0.3); // different compression history
const [c, ctx] = canvasOf(coffee.width, coffee.height);
ctx.drawImage(base, 0, 0);
ctx.drawImage(patch, 100, 50, 200, 200, 300, 100, 200, 200);
return new File([await toBlob(c, "image/jpeg", 0.85)], "edited.jpg", { type: "image/jpeg" });
}
// "ai-tagged": a procedurally generated image carrying Stable-Diffusion-style metadata
const N = 512, [c, ctx] = canvasOf(N, N), img = ctx.createImageData(N, N);
for (let y = 0; y < N; y++) for (let x = 0; x < N; x++) {
const xx = x / N, yy = y / N, i = (y * N + x) * 4;
img.data[i] = (0.5 + 0.5 * Math.sin(6 * xx + 3 * Math.sin(4 * yy))) * 255;
img.data[i + 1] = (0.5 + 0.5 * Math.sin(5 * yy + 2 * Math.cos(3 * xx))) * 255;
img.data[i + 2] = (0.5 + 0.5 * Math.cos(4 * (xx + yy))) * 255;
img.data[i + 3] = 255;
}
ctx.putImageData(img, 0, 0);
const png = new Uint8Array(await (await toBlob(c, "image/png")).arrayBuffer());
const tagged = withTextChunk(png, "parameters",
"abstract flowing colors\nSteps: 30, Sampler: DPM++ 2M, CFG scale: 7, Model: sd_xl_base_1.0 (demo file)");
return new File([tagged], "ai-tagged.png", { type: "image/png" });
}