美文网首页
K-平均聚类算法(k -means Clustering)(k-

K-平均聚类算法(k -means Clustering)(k-

作者: 原上的小木屋 | 来源:发表于2020-06-12 00:13 被阅读0次

    主要思想

    • k-平均聚类算法在类别数已知时使用。在质心不断明确的过程中完成特征量的分类任务。

    具体步骤

    1. 为每个数据随机分配类;
    2. 计算每个类的重心;
    3. 计算每个数据与重心之间的距离,将该数据分到重心距离最近的那一类;
    4. 重复步骤2和步骤3直到没有数据的类别再改变为止。

    以减色化和直方图作为特征量来执行以下的算法

    1. 对图像进行减色化处理,然后计算直方图,将其用作特征量;
    2. 对每张图像随机分配类别0或类别1(在这里,类别数为2,以np.random.seed(1)作为随机种子生成器。当np.random.random小于th时,分配类别0;当np.random.random大于等于th时,分配类别1,在这里th=0.5);
    3. 分别计算类别0和类别1的特征量的质心(质心存储在gs = np.zeros((Class, 12), dtype=np.float32)中);
    4. 对于每个图像,计算特征量与质心之间的距离(在此取欧氏距离),并将图像指定为质心更接近的类别。
    5. 重复步骤3和步骤4直到没有数据的类别再改变为止。
    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    from glob import glob#导入三个关于图像处理主要的库,opencv、numpy、matplotlib以及关于路径操作的glob库
    # Dicrease color对图像减色处理
    def dic_color(img):
        img //= 63
        img = img * 64 + 32
        return img
    # Database
    def get_DB():
        # get training image path
        train = glob("123//test//*")#准备测试数据集
        train.sort()
        # prepare database
        db = np.zeros((len(train), 13), dtype=np.int32)
        pdb = []
        # each train
        for i, path in enumerate(train):#对于每一个图像
            # read image
            img = dic_color(cv2.imread(path))#先对其进行减色化处理
            # histogram绘制直方图
            for j in range(4):每一个通道给4个占位用来存放32、96、160、224的像素点
                db[i, j] = len(np.where(img[..., 0] == (64 * j + 32))[0])#对像素点进行计数
                db[i, j + 4] = len(np.where(img[..., 1] == (64 * j + 32))[0])#对像素点进行计数
                db[i, j + 8] = len(np.where(img[..., 2] == (64 * j + 32))[0])#对像素点进行计数,这一步骤完成,db与KNN中的db一样,每一个图片就成为了一个1行13列的特征向量
            # get class
            if 'akahara' in path:
                cls = 0
            elif 'madara' in path:
                cls = 1
            # store class label
            db[i, -1] = cls#对db的第13位上的索引打上标签
            # add image path
            pdb.append(path)
        return db, pdb#返回图像集的特征向量和路径
    # k-Means step1#k-Means第一步
    def k_means_step1(db, pdb, Class=2):#划分的类为两类,将参数输入
        # copy database
        feats = db.copy()#db备份
        # initiate random seed
        np.random.seed(1)#np.random.seed()函数可以保证生成的随机数具有可预测性。
        # assign random class
        for i in range(len(feats)):#先随机把图片的标签打乱,让其自动产生聚类
            if np.random.random() < 0.5:
                feats[i, -1] = 0
            else:
                feats[i, -1] = 1
        # prepare gravity
        gs = np.zeros((Class, 12), dtype=np.float32)#准备一个2行12列的数组
        # get gravity
        for i in range(Class):#对于每一个类别
            gs[i] = np.mean(feats[np.where(feats[..., -1] == i)[0], :12], axis=0)#对打第一类标签的图片的特征向量取均值,这就是所谓的每个类的质心
        print("assigned label")
        print(feats)
        print("Grabity")
        print(gs)#将每个类的质心打印出来
    db, pdb = get_DB()
    k_means_step1(db, pdb)
    

    如代码中分析的那样,上述代码只是计算了每个类别的质心在哪里,只算是完成了K-means的第一个步骤,接下来我们进行后续步骤

    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    from glob import glob
    # Dicrease color#引入四个包
    def dic_color(img):#减色
        img //= 63
        img = img * 64 + 32
        return img
    # Database
    def get_DB():
        # get training image path#准备图像数据
        train = glob("123//test//*")
        train.sort()
        # prepare database
        db = np.zeros((len(train), 13), dtype=np.int32)
        pdb = []
        # each train
        for i, path in enumerate(train):
            # read image
            img = dic_color(cv2.imread(path))
            # histogram
            for j in range(4):
                db[i, j] = len(np.where(img[..., 0] == (64 * j + 32))[0])
                db[i, j+4] = len(np.where(img[..., 1] == (64 * j + 32))[0])
                db[i, j+8] = len(np.where(img[..., 2] == (64 * j + 32))[0])
            # get class
            if 'akahara' in path:
                cls = 0
            elif 'madara' in path:
                cls = 1
            # store class label
            db[i, -1] = cls
            # add image path
            pdb.append(path)
        return db, pdb#返回图像特征向量和路径
    # k-Means step2#k-means第二步
    def k_means_step2(db, pdb, Class=2):
        # copy database
        feats = db.copy()
        # initiate random seed
        np.random.seed(1)
        # assign random class
        for i in range(len(feats)):
            if np.random.random() < 0.5:
                feats[i, -1] = 0
            else:
                feats[i, -1] = 1#到这里还和上面的代码类似,先把标签打乱
        while True:
            # prepare greavity
            gs = np.zeros((Class, 12), dtype=np.float32)
            change_count = 0
            # compute gravity#计算每个类别的质心
            for i in range(Class):
                gs[i] = np.mean(feats[np.where(feats[..., -1] == i)[0], :12], axis=0)
            # re-labeling
            for i in range(len(feats)):#对于每个图像,计算他们与两个质心的距离
                # get distance each nearest graviry
                dis = np.sqrt(np.sum(np.square(np.abs(gs - feats[i, :12])), axis=1))
                # get new label
                pred = np.argmin(dis, axis=0)#得到最小的距离索引标签
                # if label is difference from old label
                if int(feats[i, -1]) != pred:#如果标签和最小距离的标签不一致
                    change_count += 1#同时交换次数加1,循环结束时判断交换次数看有没有增加,没有增加说明聚类趋于稳定,终止循环
                    feats[i, -1] = pred#就把标签换掉
            if change_count < 1:#如果循环次数没增加即终止循环
                break
        for i in range(db.shape[0]):
            print(pdb[i], " Pred:", feats[i, -1])#输出预测结果
    db, pdb = get_DB()
    k_means_step2(db, pdb)
    

    使用K-means进行图像减色

    • 在之前的代码中,我们涉及到了减色处理,但都是固定减多少颜色。这里,我们使用k−平均聚类算法用于动态确定要减少的颜色。
    • 算法如下
    1. 从图像中随机选取K个RGB分量(这我们称作类别)。
    2. 将图像中的像素分别分到颜色距离最短的那个类别的索引中去,色彩距离按照下面的方法计算:dis=\sqrt{(R−R′)^2+(G−G′)^2+(B−B′)^2}
    3. 计算各个索引下像素的颜色的平均值,这个平均值成为新的类别;
    4. 如果原来的类别和新的类别完全一样的话,算法结束。如果不一样的话,重复步骤2和步骤3;
    5. 将原图像的各个像素分配到色彩距离最小的那个类别中去。
    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    from glob import glob#照例导入四个包
    # K-means step1#k-means第一步
    def k_means_step1(img, Class=10):#将图像和聚类划分的类别数作为参数传入
        #  get shape获取图像尺寸
        H, W, C = img.shape
        # initiate random seed
        np.random.seed(0)
        # reshape重塑图像矩阵,将其拉成1列,共计H*W行,每一行代表一个像素点的BGR值
        img = np.reshape(img, (H * W, -1))
        # select one index randomly随机选取class个索引,对于我们的代码来说,就是随机选取10个值
        i = np.random.choice(np.arange(H * W), Class, replace=False)
        Cs = img[i].copy()
        print(Cs)
        clss = np.zeros((H * W), dtype=int)#构造一个与图像尺寸一样大的clss数组,拍平,一行H*W列的数组
        # each pixel#对于每一个像素
        for i in range(H * W):
            # get distance from base pixel#分别计算像素与10个基类之间的距离
            dis = np.sqrt(np.sum((Cs - img[i]) ** 2, axis=1))
            # get argmin distance#选取最小的距离将基类的索引做标记
            clss[i] = np.argmin(dis)
        # show
        out = np.reshape(clss, (H, W)) * 50#将clss作为像素值绘制出来
        out = out.astype(np.uint8)
        return out#返回图像
    # read image
    img = cv2.imread("123.jpg").astype(np.float32)
    # K-means step2
    out = k_means_step1(img)
    cv2.imwrite("out.jpg", out)
    cv2.imshow("result", out)
    cv2.waitKey(0)
    

    上述代码仅仅迭代了一次,聚类还没达到稳定,我们增加一些代码,使聚类趋于稳定

    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    from glob import glob#依旧引入四个包
    def k_means(img, Class=5):
        # get shape获取图像的尺寸
        H, W, C = img.shape
        # initiate random seed
        np.random.seed(0)
        # reshape image
        img = np.reshape(img, (H * W, -1))
        # get index randomly
        i = np.random.choice(np.arange(H * W), Class, replace=False)
        Cs = img[i].copy()#直至这一步,一切代码还和上面相同
        while True:
            # prepare pixel class label#对于准备的每一个标签
            clss = np.zeros((H * W), dtype=int)
            # each pixel#对于每一个元素
            for i in range(H * W):
                # get distance from index pixel
                dis = np.sqrt(np.sum((Cs - img[i]) ** 2, axis=1))#计算与不同标签像素点的距离
                # get argmin distance
                clss[i] = np.argmin(dis)#获取最小标签索引
            # selected pixel values选定像素值
            Cs_tmp = np.zeros((Class, 3))#创建临时数组,用来存放class个质点的值
            # each class label#对于每一个标签
            for i in range(Class):
                Cs_tmp[i] = np.mean(img[clss == i], axis=0)#计算每个类的均值,将其放入Cs_tmp这个临时数组中,待质点值不再发生变化,聚类结束
            # if not any change
            if (Cs == Cs_tmp).all():
                break
            else:
                Cs = Cs_tmp.copy()
        # prepare out image
        out = np.zeros((H * W, 3), dtype=np.float32)#创建输出图像
        # assign selected pixel values
        for i in range(Class):#对于每一个类
            out[clss == i] = Cs[i]#在输出图像中对属于同一类的像素进行赋值
        print(Cs)
        out = np.clip(out, 0, 255)#将像素值在0-255之外截断
        # reshape out image
        out = np.reshape(out, (H, W, 3))#输出图像
        out = out.astype(np.uint8)
        return out
    # read image
    img = cv2.imread("123.jpg").astype(np.float32)
    # K-means
    out = k_means(img)
    cv2.imwrite("out.jpg", out)
    cv2.imshow("result", out)
    cv2.waitKey(0)
    

    相关文章

      网友评论

          本文标题:K-平均聚类算法(k -means Clustering)(k-

          本文链接:https://www.haomeiwen.com/subject/dacitktx.html