Spaces:
Runtime error
Runtime error
import quantize from 'quantize'; | |
import * as d3 from 'd3-color'; | |
import type { Color } from 'd3-color'; | |
import { dev } from '$app/environment'; | |
export function randomSeed() { | |
return BigInt(13248873089935215612 & (((1 << 63) - 1) * Math.random())); | |
} | |
function sortColors(colors: Color[]): Color[] { | |
const reverse = true; | |
return colors | |
.map((color) => d3.hcl(color)) | |
.sort((a, b) => { | |
const aa = a.h; | |
const bb = b.h; | |
return !reverse ? aa - bb || isNaN(aa) - isNaN(bb) : bb - aa || isNaN(bb) - isNaN(aa); | |
}); | |
} | |
function createPixelArray(imgData: Uint8ClampedArray, pixelCount: number, quality: number) { | |
// from https://github.com/lokesh/color-thief | |
const pixels = imgData; | |
const pixelArray = []; | |
for (let i = 0, offset, r, g, b, a; i < pixelCount; i = i + quality) { | |
offset = i * 4; | |
r = pixels[offset + 0]; | |
g = pixels[offset + 1]; | |
b = pixels[offset + 2]; | |
a = pixels[offset + 3]; | |
// If pixel is mostly opaque and not white | |
if (typeof a === 'undefined' || a >= 125) { | |
if (!(r > 250 && g > 250 && b > 250)) { | |
pixelArray.push([r, g, b]); | |
} | |
} | |
} | |
return pixelArray; | |
} | |
export function extractPalette( | |
base64image: string, | |
colorCount = 5, | |
quality = 1 | |
): Promise<{ colors: Color[]; imgBlob: Blob }> { | |
return new Promise((resolve) => { | |
const img = new Image(); | |
img.onload = async () => { | |
const w = img.width; | |
const h = img.height; | |
const canvas = document.createElement('canvas'); | |
canvas.width = w; | |
canvas.height = h; | |
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D; | |
ctx.drawImage(img, 0, 0, w, h); | |
const imageData = ctx.getImageData(0, 0, w, h); | |
const pixelArray = createPixelArray(imageData.data, w * h, quality); | |
const cmap = quantize(pixelArray, colorCount); | |
const colors: number[][] = cmap.palette(); | |
const tempCanvas = document.createElement('canvas'); | |
tempCanvas.width = w / 5; | |
tempCanvas.height = h / 5; | |
const tempCtx = tempCanvas.getContext('2d') as CanvasRenderingContext2D; | |
tempCtx.drawImage(img, 0, 0, w, h, 0, 0, w / 5, h / 5); | |
const imgBlob: Blob = await new Promise((_resolve) => | |
tempCanvas.toBlob(_resolve, 'image/jpeg', 0.8) | |
); | |
const colorsRGB = colors.map((color) => d3.rgb(...(color as [number, number, number]))); | |
resolve({ | |
colors: sortColors(colorsRGB), | |
imgBlob | |
}); | |
}; | |
img.src = base64image; | |
}); | |
} | |
export async function uploadImage(imagBlob: Blob, prompt: string): string { | |
// simple regex slugify string for file name | |
const promptSlug = slugify(prompt); | |
const UPLOAD_URL = dev ? 'moon/uploads' : 'https://huggingface.co/uploads'; | |
const hash = crypto.randomUUID().split('-')[0]; | |
const fileName = `color-palette-${hash}-${promptSlug}.jpeg`; | |
const file = new File([imagBlob], fileName, { type: 'image/jpeg' }); | |
console.log('uploading image', file); | |
const response = await fetch(UPLOAD_URL, { | |
method: 'POST', | |
headers: { | |
'Content-Type': file.type, | |
'X-Requested-With': 'XMLHttpRequest' | |
}, | |
body: file /// <- File inherits from Blob | |
}); | |
const url = await response.text(); | |
console.log('uploaded images', url); | |
return url; | |
} | |
function slugify(text: string) { | |
if (!text) return ''; | |
return text | |
.toString() | |
.toLowerCase() | |
.replace(/\s+/g, '-') | |
.replace(/[^\w\-]+/g, '') | |
.replace(/\-\-+/g, '-') | |
.replace(/^-+/, '') | |
.replace(/-+$/, ''); | |
} | |