問題描述:把給定圖片,用圖片中最主要的三種顏色來表示該圖片
k-means思想:
1、選擇k個點作為初始中心
2、將每個點指派到最近的中心,形成k個簇cluster
3、重新計算每個簇的中心
4、如果簇中心發生明顯變化或未達到最大迭代次數,則回到step2
問題:初始點不對的時候,容易收斂到局部最優值
解決辦法:
1、選擇k個點作為初始中心——canopy,模擬退火,貝葉斯准則
2、將每個點指派到最近的中心,形成k個簇cluster
3、重新計算每個簇的中心
4、如果簇中心發生了明顯的變化或未達到最大迭代次數,則回到step2
例子:給你一幅圖像,找出其中最主要的三種顏色,並將圖片用三種最主要的顏色表示出來
# -*- coding: utf-8 -*- # https://github.com/ZeevG/python-dominant-image-colour # commented by heibanke from PIL import Image import random import numpy class Cluster(object): """ pixels: 主要顏色所依據的像素點 centroid: 主要顏色的RGB值 """ def __init__(self): self.pixels = [] self.centroid = None #cluster有兩個屬性,centroid表示聚類中心,pixels表示依附於該聚類中心的那些像素點 #每個聚類中心都是一個單獨的Cluster對象 def addPoint(self, pixel): self.pixels.append(pixel) def setNewCentroid(self): """ 通過pixels均值重新計算主要顏色 """ R = [colour[0] for colour in self.pixels] G = [colour[1] for colour in self.pixels] B = [colour[2] for colour in self.pixels] R = sum(R) / len(R) G = sum(G) / len(G) B = sum(B) / len(B) self.centroid = (R, G, B) self.pixels = [] return self.centroid class Kmeans(object): def __init__(self, k=3, max_iterations=5, min_distance=5.0, size=400): """ k: 主要顏色的分類個數 max_iterations: 最大迭代次數 min_distance: 當新的顏色和老顏色的距離小於該最小距離時,提前終止迭代 size: 用於計算的圖像大小 """ self.k = k self.max_iterations = max_iterations self.min_distance = min_distance self.size = (size, size) def run(self, image): self.image = image #生成縮略圖,節省運算量 self.image.thumbnail(self.size) self.pixels = numpy.array(image.getdata(), dtype=numpy.uint8) self.clusters = [None]*self.k self.oldClusters = None #在圖像中隨機選擇k個像素作為初始主要顏色 randomPixels = random.sample(self.pixels, self.k) for idx in range(self.k): self.clusters[idx] = Cluster()#生成idx個Cluster的對象 self.clusters[idx].centroid = randomPixels[idx]#每個centroid是隨機采樣得到的 iterations = 0 #開始迭代 while self.shouldExit(iterations) is False: self.oldClusters= [cluster.centroid for cluster in self.clusters] print iterations #對pixel和self.clusters中的主要顏色分別計算距離,將pixel加入到離它最近的主要顏色所在的cluster中 for pixel in self.pixels: self.assignClusters(pixel) #對每個cluster中的pixels,重新計算新的主要顏色 for cluster in self.clusters: cluster.setNewCentroid() iterations += 1 return [cluster.centroid for cluster in self.clusters] def assignClusters(self, pixel): shortest = float('Inf') for cluster in self.clusters: distance = self.calcDistance(cluster.centroid, pixel) if distance < shortest: shortest = distance nearest = cluster#nearest實際上是cluster的引用,不是復制 nearest.addPoint(pixel) def calcDistance(self, a, b): result = numpy.sqrt(sum((a - b) ** 2)) return result def shouldExit(self, iterations): if self.oldClusters is None: return False #計算新的中心和老的中心之間的距離 for idx in range(self.k): dist = self.calcDistance( numpy.array(self.clusters[idx].centroid), numpy.array(self.oldClusters[idx]) ) if dist < self.min_distance: return True if iterations <= self.max_iterations: return False return True # The remaining methods are used for debugging def showImage(self): """ 顯示原始圖像 """ self.image.show() def showCentroidColours(self): """ 顯示主要顏色 """ for cluster in self.clusters: image = Image.new("RGB", (200, 200), cluster.centroid) image.show() def showClustering(self): """ 將原始圖像的像素完全替換為主要顏色后的效果 """ localPixels = [None] * len(self.image.getdata()) #enumerate用於既需要遍歷元素下邊也需要得到元素值的情況,用for循環比較麻煩 for idx, pixel in enumerate(self.pixels): shortest = float('Inf') #正無窮 for cluster in self.clusters: distance = self.calcDistance( cluster.centroid, pixel ) if distance < shortest: shortest = distance nearest = cluster localPixels[idx] = nearest.centroid w, h = self.image.size localPixels = numpy.asarray(localPixels)\ .astype('uint8')\ .reshape((h, w, 3)) colourMap = Image.fromarray(localPixels) return colourMap if __name__=="__main__": from PIL import Image import os k_image=Kmeans(k=3) #默認參數 path = './pics/' fp = open('file_color.txt','w') for filename in os.listdir(path): print path+filename try: color = k_image.run(Image.open(path+filename)) # w_image = k_image.showClustering() w_image = k_image.showCentroidColours() w_image.save(path+'mean_'+filename,'jpeg') fp.write('The color of '+filename+' is '+str(color)+'\n') except: print "This file format is not support" fp.close()
處理前的圖片:
處理后的圖片:
參考:http://blog.zeevgilovitz.com/detecting-dominant-colours-in-python/