개발일기

[대표색상 뽑기] 색상 히스토그램 & Kmeans 알고리즘으로 색상 정보 추출하기 본문

Project Portfolio

[대표색상 뽑기] 색상 히스토그램 & Kmeans 알고리즘으로 색상 정보 추출하기

츄98 2025. 4. 28. 18:00

- Kmeans 알고리즘만으로 대표 색상 뽑기

2025.04.23 - [Project Portfolio] - [대표색상 뽑기] Kmeans로 색상 정보 추출하기

 

[대표색상 뽑기 - 연구일지1] Kmeans로 색상 정보 추출하기

목차1. 개요2. K-means 알고리즘3. 거리계산(유클리드, CIEDE2000, 코사인 유사도) 알고리즘4. 색상 정보 추출 코드5. 대표 색상 추출 코드 1. 개요K-means는 데이터의 특징을 바탕으로 유사한 데이터끼리 K

developer908.tistory.com

 

Kmeans알고리즘에 이어 이번에는 히스토그램을 활용하여 대표색상을 뽑아보려고한다.

그 다음으로 히스토그램과 Kmeans 알고리즘을 합쳐서 색상을 뽑아보면 어떨까?

 

목차
1. 색상 히스토그램으로 대표색 추출
2. 문제점 파악 - 색상 히스토그램, K-means

3. 개선) K-Means + 히스토그램 가중합 기반 대표색 추출

 

1. 색상 히스토그램

구성요소

 

Pixel Value (x축)

  • 각 픽셀의 색상 강도 값
  • 보통 0~255 범위의 정수 값으로 표현되며, 채널마다 다음과 같은 의미를 갖는다.
  • Grayscale: 밝기 (0 = 검정, 255 = 흰색)
  • RGB 이미지: R (빨강 채널), G (초록 채널), B (파랑 채널)
  • x축은 일반적으로 256개의 bin(구간)으로 나뉘어 있으며,
  • 각 bin은 특정 pixel value를 나타낸다.

 

Frequency (y축)

  • 해당 pixel value를 가지는 픽셀의 개수(빈도수) 를 의미

 

어떻게 대표 색상을 뽑는가? => Peak 찾기

 

  • 히스토그램에서 가장 높은 빈도를 갖는 pixel value를 찾기
  • RGB 각각의 채널에 대해 peak를 찾아 (R_peak, G_peak, B_peak)를 조합

 

코드

# 이미지 색상 히스토그램 그리기
colors = ('r', 'g', 'b')
for i, color in enumerate(colors):
    hist = cv2.calcHist([image], [i], None, [256], [0, 256])
    plt.plot(hist, color=color)
    plt.xlim([0, 256])
plt.title("Color Histogram")
plt.xlabel("Pixel Value")
plt.ylabel("Frequency")
# 가장 높은 빈도를 갖는 pixel value를 찾아서 대표색상 유추하기
peaks = []
for i, c in enumerate(colors):
    hist = cv2.calcHist([image], [i], None, [256], [0, 256])
    peak = np.argmax(hist)
    peaks.append(int(peak))
rgb_color = np.array([peaks[2], peaks[1], peaks[0]], dtype=np.uint8).reshape(1, 1, 3) # RGB order
rgb_color_img = np.tile(rgb_color, (100, 100, 1))
plt.imshow(rgb_color_img)
plt.axis('off')
plt.title("Dominant Color")

 

결과

왼쪽부터 crop 이미지, 색상 히스토그램, 유추된 대표 색상, RGB 채널 팔레트

 

이렇게 대표색상이 잘 유추된 경우도 있고,

조금.. 이상한 색상이 유추되기도 한다. 

 

2. 문제점 파악

- 색상 히스토그램

색상 히스토그램만으로 대표색을 추출했을 때의 문제점
항목 설명
채널 독립성 문제 일반적인 색상 히스토그램은 보통 R, G, B 각각 채널별로 따로 본다.
전체 색상 조합을 고려하지 못함. (R, G, B 독립 가정)
색 조합 표현 불가 예를 들어 (200, 150, 100) 같은 색은
단일 채널(R/G/B) peak만 보고는 제대로 찾기 어렵다.
다수의 색 혼합 무시 복잡한 장면(여러 색 혼합)에서는 주요 색을 정확히 뽑기 힘들다. (밝기나 명암 차이로 왜곡 가능)
그림자/광량 영향 큼 밝은 부분(High Value)나 어두운 부분(Low Value)만 많아도 peak에 영향받는다.

 

 "R, G, B 각각 peak는 찾지만, 진짜 의미 있는 색 조합은 못 잡음."

 

- K-means

K-means만으로 대표색을 추출했을 때의 문제점
항목 설명
작은 군집 문제 데이터가 적은 군집(=거의 없는 색)도 똑같이 중요한 것처럼 클러스터를 만든다.
비율 반영 안됨 각 색상(클러스터)이 이미지 전체에서 차지하는 비율을 고려하지 않음.
대표성 오류 실제로는 이미지에 거의 없는 색이 클러스터 중심으로 잡힐 수 있음.
Overfitting 가능성 K를 너무 크게 잡으면, 비슷한 색끼리도 억지로 나눠서 대표색이 많아짐.

 

"색상은 나누지만, 많이 쓰인 색인지 적게 쓰인 색인지 구분을 못 한다."

 

3. K-means와 색상 히스토그램 가중합 기반 대표색 추출

 

  • 이미지 전체를 K-Means로 군집화
  • 각 클러스터가 가진 픽셀 수 비율을 히스토그램처럼 사용하여 대표색 결정
  • (히스토그램처럼 각 클러스터에 속한 픽셀 수를 세서, 많이 쓰인 색을 중요하게 다룸)
  • np.unique(labels, return_counts=True)를 통해 각 클러스터의 빈도를 계산
    → 가장 높은 비율을 가진 클러스터의 중심을 대표색으로 채택
"KMeans는 색상을 잘 나누고, 히스토그램은 사용량을 반영한다.
둘을 같이 쓰면 '정확하고 의미 있는 대표색'을 뽑을 수 있다."


코드

K-means를 사용하여 대표색을 추출했을 때의 코드처럼기준이 되는 색상을 초기 중심점으로 주었다.

initial_centroids = np.array([
    [0, 0, 0],          # black (검정색)
    [0, 0, 128],        # navy blue (남색)
    [128, 128, 128],    # gray (회색)
    [255, 255, 255],    # white (흰색)
    [220, 20, 60],      # crimson (진한 빨간색)
    [0, 128, 0],        # dark green (진녹색)
    [255, 215, 0],      # gold/yellow (금색, 노란색 계열)
])

# KMeans 적용 (initial_centroids 사용)
kmeans = KMeans(n_clusters=len(initial_centroids), init=initial_centroids, n_init=1, random_state=42)
kmeans.fit(img_data)

# 각 클러스터의 중심 색상과 그 비율
unique, counts = np.unique(kmeans.labels_, return_counts=True)
total_pixels = np.sum(counts)
proportions = counts / total_pixels

# 비율 기준 내림차순 정렬
sorted_indices = np.argsort(-proportions)

# Top-3 dominant colors
top_3_colors = kmeans.cluster_centers_[sorted_indices[:3]].astype(int)

 

 

n_init = 1로 준 이유는, 처음 설정한 중심점이 변하지 않도록 하기 위함이다.

내가 원하는 목적은, 제시된 색들을 기준으로 하여 분류하고자 하기 때문에,
초기 중심점(initial_centroids)을 한 번만 사용하여 클러스터링을 수행하고 결과를 그대로 사용하도록 한다.

K-means로 군집화한 후,

각 클러스터가 가진 픽셀 수 비율을 구하여 제일 높은 비율을 가진 클러스터의 중심점 3개를 뽑아 사용했다.

 

결과

지금까지 실험한 것 중에서 가장 정확하게 나왔다.

엉뚱한 색도 없이 Top-3 Color가 잘 추출된 것을 확인할 수 있었다.

 

창문, 도로 등 차량의 대표색 추출을 방해하는 요소들을 제거한다면, 정확도가 더 올라갈 것으로 예상된다.

 

따라서 segmentation 모델을 만들어서 창문, 도로 등을 제외하고,

색상을 뽑고자 하는 객체만을 마스킹하는 모델을 만들어 사용해보려고 한다.