SLIC超像素分割

作者: 雨幻逐光 | 来源:发表于2020-02-14 18:12 被阅读0次

在计算机视觉领域里,图像分割(Segmentation)是指将数字图像分割成一些列像素集合的过程。这些集合也被成为超像素。因为分割是基于一定标准进行像素划分,归类的,所以形成的超像素中的像素一般具有位置相邻且颜色、亮度、纹理等特征相似等特点。这些小区域大多保留了进一步进行图像处理的有效信息,且一般不会破坏图像中物体的边界信息。因此超像素分割可以作为一些算法的前处理。接下来介绍一种常用的超像素分割算法SLIC(simple linear iterativeclustering)即简单的线性迭代聚类。
SLIC算法流程是:

  1. 将彩色图像转化为LAB颜色空间。
  2. 接着,由LAB颜色空间向量和图像本身的位置(row, col) 向量构建新的5维特征向量(即 new_vector[l, a, b, row, col])。
  3. 然后,根据针对5维向量构建的距离标准来聚类。

聚类算法流程如下:

  1. 初始化种子点(聚类中心):按照设定的超像素个数,在图像内均匀的分配种子点。假设图片总共有 N 个像素点,预分割为 K 个相同尺寸的超像素,那么每个超像素的大小为N/ K ,则相邻种子点的距离(步长)近似为S=\sqrt{N/K}

  2. 在种子点的n*n邻域内重新选择种子点(一般取n=3)。具体方法为:计算该邻域内所有像素点的梯度值,将种子点移到该邻域内梯度最小的地方。这样做的目的是为了避免种子点落在梯度较大的轮廓边界上,以免影响后续聚类效果。

  3. 在每个种子点周围的邻域内为每个像素点分配类标签(即属于哪个聚类中心)。和标准的k-means在整张图中搜索不同,SLIC的搜索范围限制为2S*2S,可以加速算法收敛。在此注意一点:期望的超像素尺寸为S*S,但是搜索的范围是2S*2S

  4. 距离度量。包括颜色距离和空间距离。对于每个搜索到的像素点,分别计算它和该种子点的距离。距离计算方法如下:

距离计算公式

其中,dc代表颜色距离,ds代表空间距离,Ns是类内最大空间距离,定义为Ns=S=\sqrt{N/K},适用于每个聚类。最大的颜色距离Nc既随图片不同而不同,也随聚类不同而不同,一般取值范围是取值范围[1,40]。D'就是最后5维向量的距离,是由颜色距离和空间距离构成的2维向量的欧氏距离。

由于每个像素点都会被多个种子点搜索到,所以每个像素点都会有一个与周围种子点的距离,取最小值对应的种子点作为该像素点的聚类中心。

  1. 迭代优化。理论上上述步骤不断迭代直到误差收敛(可以理解为每个像素点聚类中心不再发生变化为止),实践发现10次迭代对绝大部分图片都可以得到较理想效果,所以一般迭代次数取10。

下面是简易的python实现:

import math
from skimage import io, color
import numpy as np

class Cluster(object):

    cluster_index = 1

    def __init__(self, row, col, l=0, a=0, b=0):
        self.row = row
        self.col = col
        self.l = l
        self.a = a
        self.b = b

        self.pixels = []
        self.no = self.cluster_index
        Cluster.cluster_index += 1

    def update(self, row, col, l, a, b):
        self.row = row
        self.col = col
        self.l = l
        self.a = a
        self.b = b


class SLICProcessor(object):
    @staticmethod
    def open_image(path):
        rgb = io.imread(path)
        lab_arr = color.rgb2lab(rgb)
        return lab_arr

    @staticmethod
    def save_lab_image(path, lab_arr):
        rgb_arr = color.lab2rgb(lab_arr)
        io.imsave(path, rgb_arr)

    def make_cluster(self, row, col):
        row=int(row)
        col=int(col)
        return Cluster(row, col,
                       self.data[row][col][0],
                       self.data[row][col][1],
                       self.data[row][col][2])

    # K是超像素的个数,M是用于计算lab空间颜色向量距离的参数
    def __init__(self, filename, K, M):
        self.K = K
        self.M = M

        self.data = self.open_image(filename)
        self.rows = self.data.shape[0]
        self.cols = self.data.shape[1]
        self.N = self.rows * self.cols
        self.S = int(math.sqrt(self.N / self.K))

        self.clusters = []
        self.label = {}
        self.dis = np.full((self.rows, self.cols), np.inf)

    def init_clusters(self):
        row = self.S / 2
        col = self.S / 2
        while row < self.rows:
            while col < self.cols:
                self.clusters.append(self.make_cluster(row, col))
                col+= self.S
            col = self.S / 2
            row += self.S

    def get_gradient(self, row, col):
        if col + 1 >= self.cols:
            col = self.cols - 2
        if row + 1 >= self.rows:
            row = self.rows - 2

        gradient = (self.data[row + 1][col][0] + self.data[row][col+1][0] - 2 * self.data[row][col][0]) + \
                   (self.data[row + 1][col][1] + self.data[row][col+1][1] - 2 * self.data[row][col][1]) + \
                   (self.data[row + 1][col][2] + self.data[row][col+1][2] - 2 * self.data[row][col][2])

        return gradient

    def move_clusters(self):
        for cluster in self.clusters:
            cluster_gradient = self.get_gradient(cluster.row, cluster.col)
            new_row = cluster.row
            new_col = cluster.col

            for dh in range(-1, 2):
                for dw in range(-1, 2):
                    _row = cluster.row + dh
                    _col = cluster.col + dw
                    new_gradient = self.get_gradient(_row, _col)
                    if new_gradient < cluster_gradient:
                        cluster_gradient = new_gradient
                        new_row = _row
                        new_col = _col

            cluster.update(new_row, new_col, self.data[new_row][new_col][0], self.data[new_row][new_col][1], self.data[new_row][new_col][2])

    def assignment(self):
        for cluster in self.clusters:
            for h in range(cluster.row - 2 * self.S, cluster.row + 2 * self.S):
                if h < 0 or h >= self.rows: continue
                for w in range(cluster.col - 2 * self.S, cluster.col + 2 * self.S):
                    if w < 0 or w >= self.cols: continue
                    L, A, B = self.data[h][w]
                    Dc = math.sqrt(
                        math.pow(L - cluster.l, 2) +
                        math.pow(A - cluster.a, 2) +
                        math.pow(B - cluster.b, 2))
                    Ds = math.sqrt(
                        math.pow(h - cluster.row, 2) +
                        math.pow(w - cluster.col, 2))
                    D = math.sqrt(math.pow(Dc / self.M, 2) + math.pow(Ds / self.S, 2))
                    if D < self.dis[h][w]:
                        if (h, w) not in self.label:
                            self.label[(h, w)] = cluster
                            cluster.pixels.append((h, w))
                        else:
                            self.label[(h, w)].pixels.remove((h, w))
                            self.label[(h, w)] = cluster
                            cluster.pixels.append((h, w))
                        self.dis[h][w] = D

    def update_cluster(self):
        for cluster in self.clusters:
            sum_h = sum_w = number = 0
            for p in cluster.pixels:
                sum_h += p[0]
                sum_w += p[1]
                number += 1
                _h =int(sum_h / number)
                _w =int(sum_w / number)
                cluster.update(_h, _w, self.data[_h][_w][0], self.data[_h][_w][1], self.data[_h][_w][2])

    def save_current_image(self, name):
        image_arr = np.copy(self.data)
        for cluster in self.clusters:
            for p in cluster.pixels:
                image_arr[p[0]][p[1]][0] = cluster.l
                image_arr[p[0]][p[1]][1] = cluster.a
                image_arr[p[0]][p[1]][2] = cluster.b
            # 迭代后的聚合点 -- 如果要显示请反注释下面的代码
            # image_arr[cluster.row][cluster.col][0] = 0
            # image_arr[cluster.row][cluster.col][1] = 0
            # image_arr[cluster.row][cluster.col][2] = 0
        self.save_lab_image(name, image_arr)

    def iterates(self, output_path):
        self.init_clusters()
        self.move_clusters()
        #考虑到效率和效果,折中选择迭代10次
        for i in range(10):
            self.assignment()
            self.update_cluster()
        self.save_current_image(output_path)



if __name__ == '__main__':
    p = SLICProcessor("path of the image you want to process", 200, 40)
    p.iterates("path of the image you want to save")

效果如下:


输入图
输出图

相关文章

  • SLIC超像素分割

    SLIC(simple linear iterativeclustering) ,即简单线性迭代聚类。将彩色图像转...

  • SLIC超像素分割

    在计算机视觉领域里,图像分割(Segmentation)是指将数字图像分割成一些列像素集合的过程。这些集合也被成为...

  • SLIC超像素分割详解(一):简介

    SLIC超像素分割详解(一) 超像素概念是2003年Xiaofeng Ren提出和发展起来的图像分割技术,是指具有...

  • SLIC 超像素分割(C++)

    摘要: SLIC:simple linear iterative clustering,简单的线性迭代聚类,它使用...

  • 超像素SLIC算法

    超像素算法有很多,SLIC是效果比较好的一种,今天介绍SLIC算法。SLIC算法与K-means有些类似。 主要步...

  • 超像素(Superpixels)

    超像素(Superpixels) 超像素是外观相似的一组相连像素。超像素分割将图像划分为数百个不重叠的超像素(而不...

  • 论文学习笔记 SLIC Superpixels Compared

    这篇论文提出的SLIC算法,是一个基于梯度下降的超像素生成算法。原理很简单好懂,而且似乎也很有道理。读完这篇后bo...

  • 论文泛读:《Automatic Skin Lesion Segm

    简 介: 超像素合并算法 (superpixel merging)用于皮肤组织分割, 做到了 非深度方法 中的 ...

  • RGB-D语义分割结果汇总

    1 语义分割常用指标 (1)像素准确率(pixel accuracy, PA):分割正确的像素总量除以像素总数。(...

  • 语义分割

    (一)语义分割和数据集 (1)什么是语义分割? 语义分割将图片的每一个像素分类到对应的类别。神经网络能够在像素级别...

网友评论

    本文标题:SLIC超像素分割

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