Aller au contenu
← 1001 Widgets
N° 034 · ●●●

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.

JSCanvas
Ce qu'on apprend
  • Lire les pixels d'une image avec getImageData
  • Regrouper des couleurs avec les k-moyennes
  • Ouvrir un fichier local sans rien envoyer
Pourquoi ce langage

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.

Comment ça marche
  1. 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;
    }
  2. 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;
    }
  3. 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;
  4. 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;
    }
  5. EN HEXADÉCIMAL

    const hex = c => '#' + c.map(v => Math.round(v).toString(16).padStart(2, '0')).join('');
Pièges

PIÈGE 1 : Analyser tous les pixels

Piège

Mauvais : passer les 12 millions de pixels d'une photo de téléphone dans la boucle. La page se fige plusieurs secondes.

Fix

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

Piège

Mauvais : tirer les cinq centres au hasard. Deux tombent dans le même ciel bleu, et une couleur forte disparaît.

Fix

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

Piège

Mauvais : dessiner une image venue d'un autre domaine, puis lire ses pixels. Le canvas est « contaminé » et getImageData lève une erreur.

Fix

Correct : utiliser un fichier local, ou une image servie avec l'en-tête CORS et crossOrigin="anonymous".

PERSONNALISER
  • 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.
OPTIMISATION
  • 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.
VERS REACT

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.

Commentaires
0/1000

Chargement des commentaires…