Public
Edited
Mar 6, 2023
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
formData = {
const formData = new FormData()
formData.append("init_image", await blobify(canvasImage));
// formData.append("init_image_mode", "IMAGE_STRENGTH");
// formData.append("image_strength", 0.35);
formData.append("mask_source", "MASK_IMAGE_BLACK")
formData.append("mask_image", await blobify(mask));
formData.append("text_prompts[0][text]", form.pos);
formData.append("text_prompts[0][weight]", 1.0);
formData.append("text_prompts[1][text]", form.neg);
formData.append("text_prompts[1][weight]", -1.0);
formData.append("cfg_scale", 7);
formData.append("clip_guidance_preset", "FAST_BLUE");
// The API seems to use the dimensions of the input image over these
// formData.append("height", 512);
// formData.append("width", 512);
// formData.append("samples", form.samples);
formData.append("samples", 2);
formData.append("steps", 40);
formData.append('sampler', 'K_EULER_ANCESTRAL')
return formData;
}
Insert cell
new Map(formData.entries())
Insert cell
// The submit button in the form at the top of the notebook just calls the following function:
// stabilityI2Ibasic(engine, formData)
Insert cell
Insert cell
submit = function* (formData) {
if(!formData) return null;
// Observable specific thing
yield "loading";
yield fetch(`https://api.stability.ai/v1beta/generation/${engine}/image-to-image/masking`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
// DO NOT set content-type, prevents browser from setting an important boundary property
// 'Content-Type': 'multipart/form-data',
'Accept': "application/json",
},
body: formData
})
.then(response => {
if(response.status !== 200) {
return response.text()
}
return response.json()
})
}
Insert cell
Insert cell
blobify = (canvas) => {
return new Promise((resolve, reject) => {
canvas.toBlob((blob) => {
resolve(blob)
}, 'image/png')
})
}
Insert cell
canvasImage = resizeToCanvas(url)
Insert cell
maskify(maskInput, canvasImage)
Insert cell
// mask = {
maskify = function(maskInput, canvasImage) {
let w = canvasImage.width
let h = canvasImage.height
let canvas = DOM.canvas(w, h);
let context = canvas.getContext("2d");
// we collect our strokes here and dispatch

let maskColor = "black"
let backgroundColor = "white"
function render() {
context.clearRect(0, 0, w, h);
context.fillStyle = backgroundColor;
context.fillRect(0, 0, w, h)
context.lineWidth = 10;
context.lineCap = "round"
context.fillStyle = maskColor
maskInput.forEach(stroke => {
stroke.forEach(p => {
context.beginPath()
context.arc(p[0], p[1], maskInput.strokeWidth, 0, Math.PI * 2)
context.fill()
})
// context.beginPath();
// context.moveTo(stroke[0][0], stroke[0][1]);
// stroke.slice(1).forEach(p => context.lineTo(p[0], p[1]));
// context.stroke();
})
}

canvas.style.border = "solid 1px #ccc";

render();
return canvas;
}
Insert cell
url = inputImage ? await inputImage.url() : await FileAttachment("homer.png").url()
Insert cell
Insert cell
async function resizeToCanvas(url, scale = 1) {
let img = new Image();
img.crossOrigin = '*';
img.src = url;
await new Promise(resolve => img.addEventListener('load', resolve));
let w = img.width
let h = img.height

// resize the image appropriately
let f = factorize(w, h, 512)
let sw = Math.floor(f.width * scale)
let sh = Math.floor(f.height * scale)
let ctx = DOM.canvas(sw, sh).getContext('2d');
ctx.drawImage(img, 0, 0, sw, sh);
ctx.canvas.originalWidth = img.width
ctx.canvas.originalHeight = img.height
return ctx.canvas;
}
Insert cell
function factorize(w, h, target) {
// target should be power of 2 probably, like 512 or 1024
let factor = target / Math.max(w, h)
factor = Math.ceil(Math.min(w, h) * factor / 64) * 64 / Math.min(w, h)
w = Math.floor((w * factor) / 64) * 64
h = Math.floor((h * factor) / 64) * 64
return { width: w , height: h }
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
f = factorize(sampleWidth, sampleHeight, factor)
Insert cell
cf = factorize(canvasImage.originalWidth, canvasImage.originalHeight, factor)
Insert cell
Insert cell
{
result; // retrigger this call every time the result changes
return stability("v1beta/user/balance")
}
Insert cell
function stability(endpoint, params) {
let url = `https://api.stability.ai/${endpoint}`
return fetch(url, {
headers: {
"Authorization": `Bearer ${apiKey}`
}
}).then(response => {
if(response.status !== 200) {
return response.text()
}
return response.json()
})
}
Insert cell
engines = stability("v1beta/engines/list")
Insert cell
Insert cell
Insert cell
import { loading as spinner } from "@mateh/loading"
Insert cell
import {textcolor} from "@observablehq/text-color-annotations-in-markdown"
Insert cell
// this lets us avoid resubmitting our request everytime our form changes
import {guard} from "@mootari/inputs-submit"
Insert cell
// https://talk.observablehq.com/t/when-do-event-listeners-need-to-be-manually-removed/7160
// this lets us cancel a fetch request if our cell's recalculate before the fetch is complete
function toSignal(invalidation) {
const controller = new AbortController;
invalidation.then(() => controller.abort());
return controller.signal;
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more