Public
Edited
Mar 7, 2023
Importers
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
result
Insert cell
result.error
Insert cell
Insert cell
Insert cell
formData = {
return {
version: "435061a1b5a4c1e26740464bf786efdfa9cb3a3ac488595a2de23e143fdb0117",
input: {
prompt: form.pos,
n_prompt: form.neg,
ddim_steps: 20,
scale: form.scale,
image_resolution: '512',
image: render(input).toDataURL()
// image: canvasImage.toDataURL()
}
}
}
Insert cell
Insert cell
predict = {
return submit(submitter)
}
Insert cell
submit = function* (formData) {
if(!formData) return null;
// Observable specific thing
yield null;

yield fetch(`https://corsproxy.io/?` + encodeURIComponent(`https://api.replicate.com/v1/predictions`), {
// yield fetch(`https://api.replicate.com/v1/predictions`, {
method: 'POST',
headers: {
'Authorization': `Token ${apiKey}`,
// DO NOT set content-type, prevents browser from setting an important boundary property
// 'Content-Type': 'multipart/form-data',
'Accept': "application/json",
},
// body: formData
body: JSON.stringify(formData)
})
.then(response => {
if(response.status !== 200) {
return response.json()
}
return response.json()
})
}
Insert cell
Insert cell
result = {
let data = predict

yield null
// request every half second until its completed
while(data && !data.completed_at && data.created_at && !data.error) {
// console.log("in the loop", data)
if(!predict.urls) {
yield Promises.delay(500, {})
continue
}
yield Promises.delay(500, replicatePrediction(predict.urls.get).then(response => {
data = response
return data
}))
}
}
Insert cell
function replicatePrediction(url) {
let predictionOptions = {
method: "get",
headers: {
Authorization: 'Token ' + apiKey,
'Content-Type': 'application/json'
}
}
return d3.json(`https://corsproxy.io/?` + encodeURIComponent(url), predictionOptions)
}
Insert cell
Insert cell
Insert cell
drawing = function() {
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 strokes = canvas.value = []
let stroke;
let time;
let newtime;
let strokeWidth = 5;
canvas.value.strokeWidth = strokeWidth

context.lineCap = "round"
context.lineJoin = "round"
canvas.onmousedown = event => {
stroke = []
time = +new Date()
strokes.push(stroke)
canvas.onmousemove(event);
};

canvas.onmouseup = event => {
stroke = null;
};

canvas.onwheel = function(event) {
strokeWidth *= 1 + event.wheelDelta / 10000;
event.preventDefault();
canvas.value.strokeWidth = strokeWidth
canvas.dispatchEvent(new CustomEvent("input"));

render()
// update the cursor
var rect = canvas.getBoundingClientRect();
let ex = event.clientX - rect.left - 2
let ey = event.clientY - rect.top - 1
context.strokeStyle = "white"
context.lineWidth = 1
context.beginPath()
context.arc(ex, ey, strokeWidth, 0, Math.PI * 2)
context.stroke()
context.strokeStyle = "black"
context.lineWidth = 1
context.beginPath()
context.arc(ex, ey, strokeWidth - 1, 0, Math.PI * 2)
context.stroke()
}

canvas.onmousemove = event => {
var rect = canvas.getBoundingClientRect();
let ex = event.clientX - rect.left - 2
let ey = event.clientY - rect.top - 1
if (stroke != null) {
newtime = +new Date();
stroke.push([ex, ey, newtime - time]);
canvas.dispatchEvent(new CustomEvent("input"));
time = newtime;
}
render()
// update the cursor
context.strokeStyle = "white"
context.lineWidth = 1
context.beginPath()
context.arc(ex, ey, strokeWidth, 0, Math.PI * 2)
context.stroke()
context.strokeStyle = "black"
context.lineWidth = 1
context.beginPath()
context.arc(ex, ey, strokeWidth - 1, 0, Math.PI * 2)
context.stroke()
};
function render() {
context.clearRect(0, 0, canvas.width, canvas.height);
if(!strokes.length)
context.drawImage(canvasImage, 0, 0);
context.lineWidth = strokeWidth * 2;
context.strokeStyle = "black"
strokes.forEach(stroke => {
// draw the dots
// stroke.forEach(p => {
// context.beginPath()
// context.arc(p[0], p[1], strokeWidth, 0, Math.PI * 2)
// context.fill()
// })
// connect the dots
context.beginPath();
context.moveTo(stroke[0][0], stroke[0][1]);
stroke.slice(1).forEach(p => context.lineTo(p[0], p[1]));
context.stroke();
})
}

// we reference the "value" of our clear button so that this block reevaluates
// when it changes. even tho a button by default doesn't really have a value the event
// still triggers a reevaluation, effectively clearing the canvas
canvas.clear = function() {
context.clearRect(0, 0, canvas.width, canvas.height);
strokes = canvas.value = []
strokes.strokeWidth = strokeWidth
canvas.dispatchEvent(new CustomEvent("input"));
}

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

render();
return canvas;
}
Insert cell
Insert cell
function render(strokes) {
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 stroke;
let time;
let newtime;
let strokeWidth = strokes.strokeWidth;

context.lineCap = "round";
context.lineJoin = "round";

context.clearRect(0, 0, canvas.width, canvas.height);
if (!strokes.length) context.drawImage(canvasImage, 0, 0);
context.lineWidth = strokeWidth * 2;

context.strokeStyle = "black";

strokes.forEach((stroke) => {
// draw the dots
// stroke.forEach(p => {
// context.beginPath()
// context.arc(p[0], p[1], strokeWidth, 0, Math.PI * 2)
// context.fill()
// })
// connect the dots
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";
canvas.style.cursor = "crosshair";

return canvas;
}
Insert cell
blobify = (canvas) => {
return new Promise((resolve, reject) => {
canvas.toBlob((blob) => {
resolve(blob)
}, 'image/png')
})
}
Insert cell
canvasImage = resizeToCanvas(url)
Insert cell
url = inputImage ? await inputImage.url() : await FileAttachment("bee.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
import { loading as spinner } from "@mateh/loading"
Insert cell
import {textcolor} from "@observablehq/text-color-annotations-in-markdown"
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