栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

基于K均值聚类算法实现数据聚类及二维图像像素分割

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

基于K均值聚类算法实现数据聚类及二维图像像素分割

K均值聚类算法 1.含义及原理

K均值(k-means)聚类算法,顾名思义首先给定K个初始均值作为聚类中心,然后通过分析其他n-k个点同K个聚类中心的距离进行分类,然后对集群求取新的均值作为聚类中心,由此迭代,知道K个不同类别之间的类间距离满足设定阈值条件为止,可应用于一维数据分析和语义分割场景

算法原理及过程:
输入:n个样本的集合
输出:样本集合的聚类
过程:
(1)初始化。随机选择k的样本作为初始聚类的中心。
(2)对样本进行聚类。针对初始化时选择的聚类中心,计算所有样本到每个中心的距离,默认欧式距离,将每个样本聚集到与其最近的中心的类中,构成聚类结果。
(3)计算聚类后的类中心,计算每个类的质心,即每个类中样本的均值,作为新的类中心。
(4)然后重新执行步骤(2)(3),直到聚类结果不再发生改变。

K均值聚类算法的时间复杂度是O(nmk),n表示样本个数,m表示样本维数,k表示类别个数。

2.一维数据进行K均值聚类处理

来自百度百科:
先随机选取K个对象作为初始的聚类中心。然后计算每个对象与各个种子聚类中心之间的距离,把每个对象分配给距离它最近的聚类中心。聚类中心以及分配给它们的对象就代表一个聚类。一旦全部对象都被分配了,每个聚类的聚类中心会根据聚类中现有的对象被重新计算。这个过程将不断重复直到满足某个终止条件。终止条件可以是以下任何一个:
1)没有(或最小数目)对象被重新分配给不同的聚类。
2)没有(或最小数目)聚类中心再发生变化。
3)误差平方和局部最小。

伪代码:
选择k个点作为初始质心。
repeat 将每个点指派到最近的质心,形成k个簇 重新计算每个簇的质心 until 质心不发生变化
#python实现
import numpy as np
import pandas as pd
import random
import sys
import time
class KMeansClusterer:
    def __init__(self,ndarray,cluster_num):
        self.ndarray = ndarray
        self.cluster_num = cluster_num
        self.points=self.__pick_start_point(ndarray,cluster_num)
         
    def cluster(self):
        result = []
        for i in range(self.cluster_num):
            result.append([])
        for item in self.ndarray:
            distance_min = sys.maxsize
            index=-1
            for i in range(len(self.points)):                
                distance = self.__distance(item,self.points[i])
                if distance < distance_min:
                    distance_min = distance
                    index = i
            result[index] = result[index] + [item.tolist()]
        new_center=[]
        for item in result:
            new_center.append(self.__center(item).tolist())
        # 中心点未改变,说明达到稳态,结束递归
        if (self.points==new_center).all():
            return result
         
        self.points=np.array(new_center)
        return self.cluster()
             
    def __center(self,list):
        '''计算一组坐标的中心点
        '''
        # 计算每一列的平均值
        return np.array(list).mean(axis=0)
    def __distance(self,p1,p2):
        '''计算两点间距
        '''
        tmp=0
        for i in range(len(p1)):
            tmp += pow(p1[i]-p2[i],2)
        return pow(tmp,0.5)
    def __pick_start_point(self,ndarray,cluster_num):
        
        if cluster_num <0 or cluster_num > ndarray.shape[0]:
            raise Exception("簇数设置有误")
      
        # 随机点的下标
        indexes=random.sample(np.arange(0,ndarray.shape[0],step=1).tolist(),cluster_num)
        points=[]
        for index in indexes:
            points.append(ndarray[index].tolist())
        return np.array(points)

3.对二维图像像素值进行K均值聚类

需求:
针对某张包含几种颜色的图像,分别统计每类颜色的数量占比,最后将其占比通过饼状图绘制出来,饼状图颜色设置为原图中每类像素的颜色
通过python+opencv实现算法

函数原型:
retval, bestLabels, centers = kmeans(data, K, bestLabels, criteria, attempts, flags, centers=None)

函数参数:
data:  需要分类数据,最好是np.float32的数据,每个特征放一列。
K:  聚类个数 
bestLabels:预设的分类标签或者None
criteria:迭代停止的模式选择,这是一个含有三个元素的元组型数。格式为(type, max_iter, epsilon) 其中,type有如下模式:
cv2.TERM_CRITERIA_EPS :精确度(误差)满足epsilon,则停止。
cv2.TERM_CRITERIA_MAX_ITER:迭代次数超过max_iter,则停止。
cv2.TERM_CRITERIA_EPS+cv2.TERM_CRITERIA_MAX_ITER:两者结合,满足任意一个结束。
attempts:重复试验kmeans算法次数,将会返回最好的一次结果
flags:初始中心选择,可选以下两种:
v2.KMEANS_PP_CENTERS:使用kmeans++算法的中心初始化算法,即初始中心的选择使眼色相差最大.详细可查阅kmeans++算法。(Use kmeans++ center initialization by Arthur and Vassilvitskii)
cv2.KMEANS_RANDOM_CENTERS:每次随机选择初始中心(Select random initial centers in each attempt.)
返回值:
compactness:紧密度,返回每个点到相应重心的距离的平方和
labels:结果标记,每个成员被标记为分组的序号,如 0,1,2,3,4...等
centers:由聚类的中心组成的数组
from glob import glob

import cv2
import numpy as np
import matplotlib.pyplot as plt


def get_pixcelstats(name,k_):
    # 读取原始图像
    #img = cv2.imread('./seg/1.jpg')
    img = cv2.imread(name)
    img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    img = cv2.resize(img, (img.shape[1] // 4, img.shape[0] // 4))
    print(img.shape)
    # (486, 654, 3)
    # 图像二维像素转换为一维
    # 转换成3列
    data = img.reshape((-1, 3))
    print("data.shape = ", data.shape)
    data = np.float32(data)

    # 定义终止条件 (type,max_iter,epsilon)
    criteria = (cv2.TERM_CRITERIA_EPS +
                cv2.TERM_CRITERIA_MAX_ITER, 500, 0.1)

    #设置类别数
    k=k_
    # K-Means聚类 聚集成4类
    compactness, labels, centers = cv2.kmeans(data, k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
    print("类别数 = ", len(labels))
    print("聚类中心数:", len(centers))
    print("centers = ", centers)
    labs = np.uint8(labels)
    lab = labs.reshape((img.shape[0], img.shape[1]))
    print("label:",lab.shape)
    label = cv2.cvtColor(lab,cv2.COLOR_GRAY2BGR)
    #cv2.imshow("l", label)

    # 图像转换回uint8二维类型
    centers = np.uint8(centers)
    res = centers[labels.flatten()]
    dst = res.reshape((img.shape))
    #cv2.imshow("img", dst)

    nums = [];#定义空列表,长度为类别数,初始化为0
    for i in range(k):
        nums.append(0)

    #统计每个类别像素数量
    for i in range(lab.shape[0]):
        for j in range(lab.shape[1]):
            for t in range(k):
                if lab[i, j] == t:
                    nums[t] = nums[t] + 1;
    print("nums = ", nums)

    #计算每类像素占比
    total = lab.shape[0] * lab.shape[1]
    name = [];
    data = [];
    colors = [];
    for i in range(k):
        col = ()
        name.append(str(i))
        data.append(nums[i] / total)#计算占比
        cen = centers[i] / 255#聚类中心像素归一化
        col = (cen[0], cen[1], cen[2])
        colors.append(col)#颜色值保存到列表中

    print("data = ", data)
    #print("col = ", colors)

    data1=[]
    colors1=[]
    name1=[]
    #去除白色和黑色
    for i in range(len(data)):
        if data[i]>0.02 and data[i]<0.5:
            name1.append(i)
            data1.append(data[i])
            colors1.append(colors[i])

    plt.subplot(121)
    plt.imshow(img)
    plt.title("Src")
    plt.subplot(122)
    #plt.pie(data, labels=name, colors=colors, autopct='%0.2f%%',wedgeprops={'linewidth': 1, 'edgecolor': "black"})
    plt.pie(data1, labels=name1, colors=colors1, autopct='%0.2f%%', wedgeprops={'linewidth': 1, 'edgecolor': "black"})
    plt.title("Percentage of pixels")
    plt.show()

#图像路径
jpg_list = glob(r'.fengeseg*.jpg')
nums_class=[6,7,7,7,7,6,6,8]#预先定义类别数
for i in range(len(jpg_list)):
    print("图像路径:",jpg_list[i])
    get_pixcelstats(jpg_list[i],nums_class[i])

饼状图的label值为每一个类别的索引值,python调试无法灵活查看标签值,C++调试可以看
注:opencv读取图像颜色通道是BGR,而matplot绘图颜色是RGB,所以要保证颜色统一需要提前对图像使用cv2.COLOR_BGR2RGB

4.C++ opencv应用举例:

算法实现:

    medianBlur(gray, gray, 5);
	int width = gray.cols;
	int height = gray.rows;
	int sampleCount = width * height;
	Mat sample_data = gray.reshape(1, sampleCount);
	Mat data;
	sample_data.convertTo(data, CV_32FC1);

	//k均值分类
	int num = k;//类别数
	Mat label;
	TermCriteria critera = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 0.1);
	kmeans(data, num, label, critera, 8, KMEANS_RANDOM_CENTERS);

	Mat label_ = label.reshape(1,height);//标签图像
	Mat mask(gray.size(), CV_8UC3, Scalar::all(0));
	mask.setTo(Scalar(255, 255, 0), label_ == 1);
	mask.setTo(Scalar(0, 255, 255), label_ == 2);

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1009432.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号