Oh MyUtils

画像カラー抽出ツール - 画像からパレット抽出 オンライン

画像から主要な色を抽出してカラーパレットを生成。HEX、RGB、HSL値を取得 — 100%クライアント、画像アップロードなし。

よくある質問

画像カラー抽出ツールとは何ですか?なぜ必要ですか?

画像カラー抽出ツールは、アップロードされた画像のピクセルを分析し、最も支配的な色を特定してカラーパレットを生成します。写真、アートワーク、ブランド画像から一貫したカラースキームを導き出す必要があるデザイナーに便利です。スポイトで手動で色をサンプリングする代わりに、ツールが自動的に最も代表的な色を特定し、CSS、デザインツール、ブランドガイドラインですぐに使用できるWeb対応フォーマット(HEX、RGB、HSL)で提供します。

この画像カラー抽出ツールの使い方は?

1. アップロードエリアに画像をドラッグ&ドロップします(またはクリックしてファイルを参照)。2. ツールが自動的に支配的な色を抽出し、パレットとして表示します。3. パレットサイズスライダーで色の数を調整します(2-16色)。4. カラースウォッチをクリックしてHEX、RGB、またはHSL値をコピーします。5. エクスポートオプションを使用して、パレット全体をCSS変数、JSON、またはプレーンテキストとしてコピーします。6. ソートコントロールで頻度、色相、明度別に色を並べ替えることができます。

画像データは安全ですか?画像はサーバーにアップロードされますか?

画像は100%安全で、ブラウザの外に出ることはありません。すべてのカラー抽出はHTML5 Canvas APIを使用してクライアントサイドで実行されます。ツールはブラウザ内で直接画像のピクセルデータを読み取り、ローカルでカラー量子化アルゴリズムを適用して結果を表示します。画像データがサーバーに送信されることは一切なく、完全にプライベートです。

どのカラー量子化アルゴリズムが使用されていますか?

このツールは、確立されたカラー量子化技術であるMedian Cutアルゴリズムを使用しています。画像の色空間を再帰的に小さな領域に分割し、最も範囲の広いチャンネル(赤、緑、または青)の中央値で分割します。各領域を平均化して代表色を生成します。Median Cutは高速で決定論的であり、支配的な色を正確に表現する視覚的に優れたパレットを生成します。

どの画像フォーマットがサポートされていますか?

PNG、JPEG(JPG)、WebP、GIF画像をサポートしており、ファイルサイズは最大10MBです。PNG、WebP、GIF画像の透明ピクセルはカラー抽出時に自動的に無視され、パレットに影響しません。GIF画像の場合、最初の(静的な)フレームのみが分析されます。

画像から何色抽出できますか?

2色から16色の間で抽出できます。デフォルトは8色で、ほとんどのケースに適したバランスを提供します。シンプルな画像やミニマルなパレットには4-6色が適しています。多くの異なる色領域を持つ複雑な写真には、10-16色でより多くのニュアンスをキャプチャできます。スライダーを調整するとリアルタイムで更新されます。

頻度、色相、明度でのソートの違いは何ですか?

頻度(デフォルト)は画像内で占める面積に基づいて色を並べ、最も支配的な色が最初に表示されます。色相は色相環上の位置(0-360度)に基づいて並べ、赤からオレンジ、黄、緑、青、紫への虹のようなシーケンスを作ります。明度はHSL明度値に基づいて最も明るい色から最も暗い色の順に並べ、グラデーション作成やコントラスト範囲の把握に便利です。

カラーパレットをコードで使用するためにエクスポートできますか?

はい。開発者向けに最適化された複数のエクスポートフォーマットを提供しています:CSS変数は :root { --color-1: #FF5733; ... } を生成し、CSSですぐに使用できます。JSONはhex、rgb、hsl値を含むオブジェクト配列をプログラム用途でエクスポートします。プレーンテキストは1行に1つのHEXコードのシンプルなリストを提供します。

コード例

// Extract dominant colors from an image using Canvas API + Median Cut
function extractColorsFromImage(imageSource, colorCount = 8) {
  return new Promise((resolve, reject) => {
    const img = new Image();

    img.onload = () => {
      const maxPixels = 10000;
      const scale = Math.min(1, Math.sqrt(maxPixels / (img.width * img.height)));
      const width = Math.max(1, Math.floor(img.width * scale));
      const height = Math.max(1, Math.floor(img.height * scale));

      const canvas = document.createElement("canvas");
      canvas.width = width;
      canvas.height = height;

      const ctx = canvas.getContext("2d");
      ctx.drawImage(img, 0, 0, width, height);

      const imageData = ctx.getImageData(0, 0, width, height);
      const pixels = [];

      for (let i = 0; i < imageData.data.length; i += 4) {
        if (imageData.data[i + 3] < 128) continue;
        pixels.push({
          r: imageData.data[i],
          g: imageData.data[i + 1],
          b: imageData.data[i + 2],
        });
      }

      const colors = medianCut(pixels, colorCount);
      resolve(colors);
    };

    img.onerror = () => reject(new Error("Failed to load image"));

    if (imageSource instanceof File) {
      img.src = URL.createObjectURL(imageSource);
    } else {
      img.src = imageSource;
    }
  });
}

function medianCut(pixels, targetCount) {
  if (pixels.length === 0) return [];
  let buckets = [pixels];

  while (buckets.length < targetCount) {
    let maxRange = -1, maxIndex = 0, splitChannel = "r";

    for (let i = 0; i < buckets.length; i++) {
      if (buckets[i].length < 2) continue;
      for (const ch of ["r", "g", "b"]) {
        const values = buckets[i].map((p) => p[ch]);
        const range = Math.max(...values) - Math.min(...values);
        if (range > maxRange) {
          maxRange = range;
          maxIndex = i;
          splitChannel = ch;
        }
      }
    }

    if (maxRange <= 0) break;

    const toSplit = buckets[maxIndex];
    toSplit.sort((a, b) => a[splitChannel] - b[splitChannel]);
    const mid = Math.floor(toSplit.length / 2);
    buckets.splice(maxIndex, 1, toSplit.slice(0, mid), toSplit.slice(mid));
  }

  const totalPixels = pixels.length;
  return buckets.map((bucket) => {
    const r = Math.round(bucket.reduce((s, p) => s + p.r, 0) / bucket.length);
    const g = Math.round(bucket.reduce((s, p) => s + p.g, 0) / bucket.length);
    const b = Math.round(bucket.reduce((s, p) => s + p.b, 0) / bucket.length);
    const hex = "#" + [r, g, b].map((v) => v.toString(16).padStart(2, "0")).join("").toUpperCase();
    return { hex, rgb: { r, g, b }, percentage: ((bucket.length / totalPixels) * 100).toFixed(1) };
  });
}

// Usage
const input = document.querySelector('input[type="file"]');
input.addEventListener("change", async (e) => {
  const file = e.target.files[0];
  const colors = await extractColorsFromImage(file, 8);
  colors.forEach((c) => console.log(`${c.hex} - ${c.percentage}%`));
});

関連ツール