Palette tirée d'une image
Une photo déposée dans la page, et ses cinq couleurs dominantes en ressortent, avec leur part et leur code à copier. Le bon exercice pour lire les pixels d'une image et les regrouper.
- Lire les pixels d'une image avec getImageData
- Regrouper des couleurs avec les k-moyennes
- Ouvrir un fichier local sans rien envoyer
Pourquoi une palette tirée d'une image ? Pour une affiche, un site ou une carte de visite, on part souvent d'une photo qu'on aime. Retrouver ses couleurs à la pipette est long et approximatif.
L'image est dessinée dans un canvas, qui donne accès à chaque pixel. On en prélève quelques milliers, puis on les regroupe en cinq familles avec l'algorithme des k-moyennes : chaque couleur rejoint le centre le plus proche, chaque centre se déplace au milieu de sa famille, et on recommence.
Pourquoi en JavaScript simple ? FileReader n'est même pas nécessaire : URL.createObjectURL suffit, et la photo ne quitte jamais l'ordinateur.
CHARGER LA PHOTO
L'image reste dans le navigateur : rien n'est envoyé à un serveur.
function charger(fichier) { const url = URL.createObjectURL(fichier); const img = new Image(); img.onload = () => { cv.width = 360; cv.height = Math.round(img.height * 360 / img.width); ctx.drawImage(img, 0, 0, cv.width, cv.height); URL.revokeObjectURL(url); // on libère la mémoire analyser(); }; img.src = url; }LIRE LES PIXELS
getImageData renvoie r, g, b, a à la suite. On saute des pixels pour aller vite.
function pixels() { const d = ctx.getImageData(0, 0, cv.width, cv.height).data, px = []; const pas = Math.max(1, Math.floor(cv.width * cv.height / 6000)); for (let i = 0; i < d.length; i += 4 * pas) if (d[i + 3] > 128) px.push([d[i], d[i + 1], d[i + 2]]); return px; }LA DISTANCE ENTRE DEUX COULEURS
L'œil est plus sensible au vert : on lui donne plus de poids.
const dist = (a, b) => 2 * (a[0] - b[0]) ** 2 + 4 * (a[1] - b[1]) ** 2 + 3 * (a[2] - b[2]) ** 2;
LES K-MOYENNES
Chaque pixel rejoint le centre le plus proche, puis chaque centre va au milieu de son groupe.
function kmoyennes(px, centres, passes = 8) { for (let n = 0; n < passes; n++) { const g = centres.map(() => ({ s: [0, 0, 0], n: 0 })); for (const p of px) { let j = 0; centres.forEach((c, k) => { if (dist(p, c) < dist(p, centres[j])) j = k; }); g[j].s = g[j].s.map((v, i) => v + p[i]); g[j].n++; } centres = g.map((x, k) => x.n ? x.s.map(v => v / x.n) : centres[k]); } return centres; }EN HEXADÉCIMAL
const hex = c => '#' + c.map(v => Math.round(v).toString(16).padStart(2, '0')).join('');
PIÈGE 1 : Analyser tous les pixels
Mauvais : passer les 12 millions de pixels d'une photo de téléphone dans la boucle. La page se fige plusieurs secondes.
Correct : réduire l'image dans le canvas, puis ne prélever qu'un pixel sur plusieurs.
PIÈGE 2 : Des centres de départ au hasard
Mauvais : tirer les cinq centres au hasard. Deux tombent dans le même ciel bleu, et une couleur forte disparaît.
Correct : partir de couleurs bien espacées, en prenant à chaque fois le pixel le plus éloigné des centres déjà choisis.
PIÈGE 3 : Une image d'un autre site
Mauvais : dessiner une image venue d'un autre domaine, puis lire ses pixels. Le canvas est « contaminé » et getImageData lève une erreur.
Correct : utiliser un fichier local, ou une image servie avec l'en-tête CORS et crossOrigin="anonymous".
- Triez la palette par clarté plutôt que par part, pour un dégradé naturel.
- Ignorez les blancs et les noirs presque purs, souvent du fond.
- Exportez la palette en fichier .ase pour la reprendre dans un logiciel de dessin.
- Pour de grandes images, lancez l'analyse dans un Web Worker : l'interface reste fluide.
- Calculez dans l'espace OKLab plutôt qu'en RGB : les familles de couleurs correspondent mieux à ce que l'œil voit.
Le canvas est accessible par un useRef, l'analyse se lance quand le fichier change.
La palette est un useState, rendue en liste de boutons avec une key par couleur.
Pensez à révoquer l'URL de l'objet dans le nettoyage du useEffect.
Chargement des commentaires…