机器学习在图像识别方面具有很好的效果,今天在网易云课堂黑板可老师视频中学习了一下Kmeans算法,是一种非监督类学习算法,具体步骤如下
1,选取K个点作为初始中心
2,将每个点最近的中心,形成K个簇
3,重新计算每个簇的中心
4,如果簇中心发生明显的变化或者未达到最大迭代次数,则返回到第二部
如上图,有很多点,需要将这些点分成类簇,我们的方法是
1,先随机选择三个点Cluster
2,判断所有点距离这三个点的距离,并将点加入到该Cluster中
3,重新计算每个簇的中心
4,迭代2
下面讲一个实际的问题,对于一张图片,我们想找出图片的K个主要色彩,然后将图片换成包含主要色彩的图片
具体步骤如下:
1,将一个图片转化成像素集合
2,随机在这些像素集合中选取K个点,也就是三个簇
3,计算每个像素点距离这三个簇最近的簇,并把它放入该簇中
4,第三步形成了新的簇,然后计算新簇的中心,计算簇中心的方法是求每个簇的RGB三原色的平均颜色,根据平均颜色选取新的pixel,也就是簇的中心
5,重复步骤2至迭代次数
6,迭代完成后得到图片的中心pixel
7,遍历原图的每个pixel,求出距离该pixel最近的中心pixel,记为pixelCenter并将原图的pixel置换为pixelCenter
8,7步骤完成后得到新的图片
Python示例代码如下,(从github上down的)
# -*- 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
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()
self.clusters[idx].centroid = randomPixels[idx]
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.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())
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 = './ml_kmean.pypics/'
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.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()
原图和处理之后的图对比如下: