< 8-3-2. K-Means Clustering in OpenCV >
cv2.kmeans() 함수를 사용하는 법을 알아볼 것 이다.
Understanding Parameters
Input parameters
- samples : 데이터 타입은 np.float32여야하고, 각 특성들은 단일 열(column) 형태로 입력되어야 한다.
- nclusters(K) : 끝에 원하는 클러스터의 수 이다.
- criteria : 반복을 어느 정도 할지 정해주는 기준이다. 기준에 충족이 된다면 알고리즘 반복이 멈추게 된다. 사실 튜플 형태로 3가지로 이루어져 있다.
- 3.a - termination criteria의 종류 : 3가지 flags가 있다 :
- cv2. TERM_CRITERIA_EPS - 특정 정확도에 다다르면 알고리즘 반복을 멈춘다.
- cv2.TERM_CRITERIA_MAX_ITER - 특정 반복 횟수를 지나면 알고리즘을 멈춘다.
- cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER - 위의 어느 조건이라도 만족하면 반복을 멈춘다.
- 3.b - max iter - 최대 반복 횟수를 지정하는 정수
- 3.c - epsilon - 요구되는 특정 정확도
- 3.a - termination criteria의 종류 : 3가지 flags가 있다 :
- attempts : 다른 초기 라벨을 사용하여 알고리즘을 실행하는 횟수를 지정하는 flag 이다. 이 알고리즘은 가장 작은(긴밀한,compactness) 라벨을 반환한다. 이 라벨은 출력으로 반환된다.
- flags : 이 flag는 어떻게 초기 중심값을 정할지에 대한 것이다. 보통 2가지 flag가 자주 사용된다 : cv2.KMEANS_PP_CENTERS / cv2.KMEANS_RANDOM_CENTERS
Output parameters
- compactness : 각 점과 이에 대응하는 중심과의 거리 제곱의 합이다.
- labels : 이는 라벨값 배열이다. ‘0’, ‘1’ 처럼 나타낸다.
- centers : 클러스터의 중심들의 배열이다.
이제 3가지 예시를 통해 어떻게 K-Means algorithm을 적용하는지 살펴보자.
1. Data with Only One Feature
오직 한 가지의 특성만을 가지는 데이터를 가지고 있다고 생각해보자. 1차원이 될 것이다. 예를 들어서, 위의 티셔츠 문제에서 “키” 값만을 가지고 사이즈를 결정해야 하는 문제와 같은 것이다.
먼저 Matplotlib을 통해서 데이터를 뿌려보자.
import cv2
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(43)
x = np.random.randint(25,100,25)
y = np.random.randint(175,255,25)
z = np.hstack((x,y)) # z.shape = (50,)
z = z.reshape((50,1)) # 단일 column을 입력으로 넣어야 하기 때문
z = np.float32(z) # input은 np.float32 형태여야함
plt.hist(z,256,[0,256])
plt.show()
‘z’ 배열의 사이즈가 50이고, 값들은 0~255 까지다. ‘z’를 열벡터로 reshape 했다.
아래의 이미지를 얻을 수 있게 된다.
이제 KMeans 함수를 적용해보자. 이전에 criteria를 설정해야한다. 기준은 최대 10번 반복, 요하는 정확도는 1.0으로 설정해서 알고리즘을 멈춰보도록 하자.
# (type, max_iter, epsilon)
criteria = (cv2.TERM_CRITERIA_EPS+cv2.TERM_CRITERIA_MAX_ITER,10,1.0)
flags = cv2.KMEANS_RANDOM_CENTERS
# KMeans ( attmepts = 10 )
compactness, labels, centers = cv2.kmeans(z,2,None,criteria,10,flags)
3가지 결과를 얻을 수 있다. 71.92와 215.519의 중심을 얻었다. 이제 주어진 라벨에 따라서 다른 클러스터로 분리해보자.
A = z[labels==0]
B = z[labels==1]
A를 빨간색, B를 파란색, 그들의 중심을 노란색으로 그려보자.
plt.hist(A,256,[0,256],color='r')
plt.hist(B,256,[0,256],color='b')
plt.hist(centers,32,[0,256],color='y')
plt.show()
2. Data with Multiple Features
이전 예시에서, 오직 하나의 특성인 “키”만 보았다. 여기서는 두 가지 특성 모두를 이용해 볼 것이다.
이전에는 하나의 열벡터로 구성을 했었다. 행이 입력 테스트 샘플 하나를 의미하는 것이다.
예를 들어서, 이 사례에서, 50x2크기의 테스트 데이터를 가지고 있다고 해보자. 첫 열은 키, 두 번째 열은 몸무게이다. 행은 키와 몸무게 두 가지 요소를 가진다. 나머지 행들도 마찬가지이다.
바로 코드로 가보자.
np.random.seed(42)
# (50,2) 형태로 만들어야 하기 때문에 난수 생성시 (25,2)로
x = np.random.randint(25,100,(25,2))
y = np.random.randint(175,255,(25,2))
z = np.vstack((x,y)) # 이번엔 vstack
z = np.float32(z)
criteria = (cv2.TERM_CRITERIA_EPS+cv2.TERM_CRITERIA_MAX_ITER,10,1.0)
flags = cv2.KMEANS_RANDOM_CENTERS
compact,labels,centers = cv2.kmeans(z,2,None,criteria,10,flags)
# 데이터를 분리하여 flatten()으로 펴주기 (= ravel())
A = z[labels.ravel() == 0]
B = z[labels.ravel() == 1]
# 데이터 뿌리기
plt.scatter(A[:,0],A[:,1]) # 한 열씩
plt.scatter(B[:,0],B[:,1], c='r')
plt.scatter(centers[:,0],centers[:,1],s=80,c='y',marker='s')
plt.xlabel('Height'), plt.ylabel('Weight')
plt.show()
산점도 결과는 아래와 같다.
3. Color Quantization
컬러 양자화는 이미지에서 사용되는 뚜렷한 색상의 수를 줄이는 프로세스이다. 이렇게 하는 이유는 메모리를 줄이기 위해서이다. 가끔 어떤 기기들은 색상을 표현하는데 한계가 있다. 이러한 경우에, 컬러 양자화가 실행되는 것이다. 여기서는 컬러 양자화를 위해 k-means clustering을 사용한다.
새로 설명할 것은 없다. 3가지 특성인 R,G,B가 있고, 이미지를 Mx3 사이즈로 reshape하면 된다. (M은 이미지내의 픽셀 수 이다.) 클러스터링 이후, centroid 값을 모든 픽셀들에게 적용시켜, 이미지는 특정 색상의 수 만을 가지게 된다. 그리고 다시 원본 이미지의 형태로 reshape 해주면 된다.
img = cv2.imread('./images/irene.png')
z = img.reshape((-1,3)) # flatten()
z = np.float32(z)
criteria = (cv2.TERM_CRITERIA_EPS+cv2.TERM_CRITERIA_MAX_ITER,10,1.0)
flags = cv2.KMEANS_RANDOM_CENTERS
K =[2,4,8]
for i,k in enumerate(K):
compact , labels, centers = cv2.kmeans(z,k,None,criteria,10,flags)
centers = np.uint8(centers)
res = centers[labels.flatten()] # labels 값 만을 추출
res2 = res.reshape((img.shape))
plt.figure(figsize=(12,8))
plt.subplot(1,3,i+1), plt.imshow(res2[:,:,::-1]),plt.title("K = {0}".format(k))
plt.savefig("K_means_face(K={0}).jpg".format(k))
사람 얼굴에 대해 컬러 양자화를 진행해봤다.
먼저 원본이미지는 아래와 같다.
< 원본 이미지 >
K의 값(2,4,8)을 바꿔가며 진행한 결과는 다음과 같다.
K가 증가하면서 원본 이미지와 유사해지는 것을 볼 수 있다. K에 따라 포함하는 색이 달라지게 되기 때문이다!