如何使用OpenCV,Python和k-means聚类算法来查找图像中最主要的颜色
该任务可用于分析一张优秀摄影作品的色彩分布,并建立色卡图,将其用于本地调色。
K-Means聚类,那么k-means究竟是什么意思呢?
K-means是一种聚类算法。目标是将n个数据点分成k个簇。 n个数据点中的每一个都将被分配给具有最接近平均值的簇。每个簇的平均值称为“质心”或“中心”。
总的来说,应用k均值产生原始n个数据点的k个单独的簇。特定集群内的数据点被认为与属于其他集群的数据点彼此“更相似”。
在我们的任务中,我们将聚类RGB图像的像素强度。给定MxN大小的图像,我们因此具有MxN个像素,每个像素由三个分量组成:红色,绿色和蓝色。我们将这些MxN像素视为我们的数据点,并使用k-means对它们进行聚类。
k-means的一个条件是我们需要指定我们想要提前生成的集群数量,有些算法会自动选择k的最佳值,但在这里不做讨论。
具体的效果如下图所示:
代码如下所示,需要指定图像位置与聚类类别数目:
#coding:utf-8
#*********************************************************************************************************
'''
说明:利用python/k-means聚类提取图像中的主要色彩,并建立色彩,可用于摄影调色中的图像色彩分析
算法思路:
1)加载RGB图像,并转换为Kmeans可聚类的数据形式;
2)设置聚类中心数目,采用Kmeans聚类;
3)设置色卡图像,显示聚类结果
具体参数:图像resize到(640, 640*ratio),clusters=4
'''
import cv2
import numpy as np
import argparse
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True, help = "Path to the image")
ap.add_argument("-c", "--clusters", required = True, type = int,help = "# of clusters")
args = vars(ap.parse_args())
if __name__ == '__main__':
print( '[INFO] 读取图像......' )
img = cv2.imread( args[ 'image' ] )
if ( img is None ):
print( ' Not read img. ' )
#获取初始图像的宽高比例,以便resize不改变图像比例
ratio = img.shape[0] / img.shape[1]
img_resize = cv2.resize( img, ( 640, int( 640*ratio ) ), interpolation=cv2.INTER_CUBIC )
#将Opencv中图像默认BGR转换为通用的RGB格式
img_rgb = cv2.cvtColor( img_resize, cv2.COLOR_BGR2RGB )
(height, width, channels) = img_rgb.shape
#将图像数据转换为需要进行Kmeans聚类的Data
img_data = img_rgb.reshape( height*width, channels )
print( '[INFO] Kmeans 颜色聚类......' )
#调用sklearn中Kmeans函数
kmeans = KMeans( n_clusters = args[ 'clusters' ] )
kmeans.fit(img_data)
#建立颜色与标签的对应字典
color_label = {}
for i in range( len( kmeans.cluster_centers_ ) ):
color_label[i] = kmeans.cluster_centers_[i]
print( ' 颜色以及其对应的标签为: {}'.format( color_label ) )
#计算聚类结果, 各颜色及其所含像素数目
color_num = {}
for m in range( len( np.unique( kmeans.labels_ ) ) ):
print( np.sum( kmeans.labels_ == m ) )
print( color_label[m] )#标签m对应的色彩
color_num[ np.sum( kmeans.labels_ == m ) ] = color_label[m]
print( ' 色彩排序前字典映射为: {}'.format( color_num ) )
color_num_sorted = sorted( color_num.items(), key = lambda x:x[0], reverse = True )
print( ' 色彩排序后字典映射为: {}'.format( color_num_sorted) )
color_num_ratio = []
for i in range( len( color_num_sorted ) ):
color_num_ratio.append( color_num_sorted[i][0] )
color_num_ratio = color_num_ratio / np.sum( color_num_ratio )
print( ' 色彩数目求取比例之后: {}'.format( color_num_ratio ) )
print( '[INFO] 显示色卡图像......' )
#创建带有色卡的图像
color_card = np.zeros( shape = ( height, width + 100, 3 ), dtype = np.int32 )
#图像左侧区域复制源图像
for i in range(height):
for j in range(width):
color_card[i][j] = img_rgb[i][j]
#图像右侧显示色卡
start = 0
for i in range( len( kmeans.cluster_centers_ ) ):
color = color_num_sorted[i][1]
row_start = int ( color_num_ratio[i] * height )
#由于前面的比例为小数,转为Int导致最后部分区域没有色彩,采用最后一种颜色进行填充
if i == len( kmeans.cluster_centers_ ) - 1:
color_card[start:, width:width+100] = color
color_card[start: start +row_start, width:width+100] = color
start += row_start
plt.imshow( color_card )
plt.show()