이번에 도전해 볼 예제는 K-means clustering을 활용한 Image Segmentation입니다. 이미지는 pixel이라는 단위로 구성되어 있는데요, 이 한 pixel당 Red, Green, Blue의 세가지 색상이 각 8bits(0~255)의 값을 가져서 24bits True Color를 완성하게 됩니다. 물론 gray scale로 되어 있는 흑백 사진의 경우 단순히 8bits(0~255)의 값만으로 pixel이 구성되게 됩니다. 그렇다면 그림은 (r, g, b)의 3차원 값을 지닌 dataset이라고 볼 수 있습니다. 만약 이 색깔들을 $K$개의 cluster로 뽑아낸다면, 비슷한 색깔을 지닌 pixel들을 묶어낼 수 있겠죠? 그것 뿐만이 아니라 segmentation을 통해 pixel당 24bits대신에 1~$K$의 index만으로 정보를 축소하여 보낼 수 있겠죠? 그럼 가장 먼저 R에서 아래의 이미지를 불러와 보겠습니다. img

library(jpeg) #jpeg이미지를 로드하기 위해 라이브러리가 필요합니다.
image = readJPEG('stanford.jpeg') #이미지를 불러와 변수에 담습니다.

굉장히 간단히 jpeg 이미지를 불러올 수 있죠? 그럼 이 이미지가 정말 pixel들로 구성되어 있는지 확인해 보죠.

dim(image)
## [1] 532 800   3

Dimension이 가로 X 세로 X 3(rgb)라는 것을 확인하실 수 있죠? 그럼 R에서 어떻게 이 그림을 볼 수 있을까요?

plot(1:2, type="n", xlab='', ylab='') #일단 비어있는 plot을 만들고,
rasterImage(image, 1, 1, 2, 2) #원하는 크기에 이미지를 불러옵니다.

center

여기서는 임의로 1~2까지의 x, y축을 정의해서 image를 (1, 1)에서 (2, 2)까지의 공간에 집어넣었습니다. 실제로 픽셀 값을 확인해 보시면,

image[1,1,] #(1,1) pixel의 rgb값을 확인합니다.
## [1] 0.5725490 0.7450980 0.7960784

이런 값들을 가지고 있네요. 그럼 이제 K-means clustering을 적용하기 위해서 data를 조금 변형시켜 보겠습니다. 어차피 segmentation에서 가로나 세로의 개념은 의미가 없기 때문에 1차원 vector로 변형시켜서 (r, g, b)의 3차원 자료형으로 변형시키겠습니다.

image.dataframe = data.frame(r=as.vector(image[,,1]), g=as.vector(image[,,2]), b=as.vector(image[,,3]))
dim(image.dataframe) # = (532*800, 3)
## [1] 425600      3

정확히 data samples 수가 pixels 수와 같다는 것을 확인하실 수 있죠? 간략하게 head함수를 이용해 처음 일부만 확인해 보시면 잘 변형되었다는 것을 확인하실 수 있습니다.

head(image.dataframe)
##           r         g         b
## 1 0.5725490 0.7450980 0.7960784
## 2 0.5686275 0.7411765 0.7921569
## 3 0.5686275 0.7411765 0.7921569
## 4 0.5686275 0.7411765 0.7921569
## 5 0.5725490 0.7450980 0.7960784
## 6 0.5764706 0.7490196 0.8000000

그럼 이제 임의로 $K=6$개의 cluster로 이 pixel들을 분리해 보겠습니다.

kmeans.fit = kmeans(image.dataframe, centers=6, nstart=5)
kmeans.fit$centers
##           r         g         b
## 1 0.6565849 0.7306594 0.2342360
## 2 0.3884722 0.3862317 0.3052278
## 3 0.1443569 0.2133600 0.1348398
## 4 0.5888124 0.5312907 0.4495308
## 5 0.6287241 0.7683909 0.8192107
## 6 0.8684830 0.7764108 0.6401854

위와 같은 6개의 색깔로 이미지의 pixel들을 분류할 수가 있네요 :) 그러면 이렇게 segmented된 픽셀들이 어떻게 생겼는지 확인해 보시겠습니다. 그러기 위해서는 다시 원래 형태였던 array형태로 데이터 형태를 복원해 주셔야 합니다.

kmeansCompressed.dataframe = kmeans.fit$centers[kmeans.fit$cluster,] #일단 각 pixel의 data를 centroid의 data들로 채워 넣습니다.
dim(kmeansCompressed.dataframe)
## [1] 425600      3
kmeansCompressed = array(kmeansCompressed.dataframe, dim(image)) #dataframe형태를 다시 array형태로 옮겨줍니다.
dim(kmeansCompressed) #다시 (가로, 세로, 3)의 형태가 되었죠? :)
## [1] 532 800   3
plot(1:2, type="n", xlab='', ylab='')
rasterImage(kmeansCompressed, 1, 1, 2, 2)

center

쨔잔! +_+ 정말 신기하지 않습니까? 이렇게 k-means와 같은 clustering algorithm들은 매우 다양하게 활용될 수 있으니 잘 기억하세요 :)