Oh MyUtils

이미지 색상 추출기 - 이미지에서 팔레트 추출 온라인

이미지에서 주요 색상을 추출하여 색상 팔레트 생성. HEX, RGB, HSL 값 제공 — 100% 클라이언트, 이미지 업로드 없음.

자주 묻는 질문

이미지 색상 추출기란 무엇이며 왜 필요한가요?

이미지 색상 추출기는 업로드된 이미지의 픽셀을 분석하여 가장 지배적인 색상을 식별하고 색상 팔레트를 생성합니다. 사진, 아트워크 또는 브랜드 이미지에서 일관된 색상 체계를 도출해야 하는 디자이너에게 유용합니다. 스포이트로 수동으로 색상을 샘플링하는 대신, 도구가 자동으로 가장 대표적인 색상을 식별하고 CSS, 디자인 도구 또는 브랜딩 가이드라인에서 즉시 사용할 수 있는 웹 형식(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; --color-2: #3498DB; ... } 형태로 CSS에 바로 사용할 수 있습니다. JSON은 hex, rgb, hsl 값이 포함된 객체 배열을 프로그래밍 용도로 내보냅니다. 일반 텍스트는 한 줄에 하나씩 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}%`));
});

관련 도구