Gonzalez R. C. and Woods R. E. Digital Image Processing (Forth Edition).

单个像素的意义其实很小, 于是有了superpixel的概念, 即一簇pixels的集合(且这堆pixels共用一个值), 这会导致图片有非常有趣的艺术风格(下图便是取不同的superpixel大小形成的效果, 有种抽象画的感觉?):

经过superpixel的预处理后, 图片可以变得更加容易提取edge, region, 毕竟superpixel已经率先提取过一次了.

SLIC Superpixel algorithm

SLIC (simple linear iterative clustering) 算法是基于k-means的一种聚类算法.

Given: 需要superpixels的个数\(n_{sp}\); 图片\(f(x, y) = (r, g, b), x = 1,2,\cdots M, y = 1, 2, \cdots, N\);

  1. 根据图片以及其位置信息生成数据:

    \[\bm{z} = [r, g, b, x, y]^T,
    \]

    其中\(r, g, b\)是颜色编码, \(x, y\)是位置信息.

  2. 令\(n_{tp} = MN\)表示pixels的个数, 并计算网格大小:

    \[s = [n_{tp} / n_{sp}]^{1/2}.
    \]
  3. 将图片均匀分割为大小\(s\)的网格, 初始化superpixels的中心:

    \[\bm{m}_i = [r_i, g_i, b_i, x_i, y_i]^T, i=1,2,\cdots, n_{sp},
    \]

    为网格的中心. 或者, 为了防止噪声的影响, 选择中心\(3 \times 3\)领域内梯度最小的点.

  4. 将图片的每个pixel的类别标记为\(L(p) = -1\), 距离\(d(p) = \infty\);

  5. 重复下列步骤直到收敛:

    1. 对于每个像素点\(p\), 计算其与\(2s \times 2s\)邻域内的中心点\(\bm{m}_i\)之间的距离\(D_i(p)\), 倘若\(D_i(p) < d(p)\):

      \[d(p) = D_i, L(p) = i.
      \]
    2. 令\(C_i\)表示\(L(p) = i\)的像素点的集合, 更新superpixels的中心:

      \[\bm{m}_i = \frac{1}{|C_i|} \sum_{\bm{z} \in C_i} \bm{z}, i=1, 2, \cdots, n_{sp}.
      \]
  6. 将以\(\bm{m}_i\)为中心的区域中的点的(r, g, b)设定为与\(\bm{m}_i\)一致.

距离函数的选择

倘若\(D\)采用的是和普通K-means一样的\(\|\cdot\|_2\)显然是不合适的, 因为\((r, g, b)\)和\((x, y)\)显然不是一个尺度的. 故采用如下的距离函数:

\[D = [(\frac{d_c}{d_{cm}})^2 + (\frac{d_s}{d_{sm}})^2]^{1/2}, \\
d_c = [(r_j - r_i)^2 + (g_j - g_i)^2 + (b_j - b_i)^2]^{1/2}, \\
d_s = [(x_j - x_i)^2 + (y_j - y_i)^2]^{1/2},
\]

其中\(d_{cm}, d_{sm}\)分别是\(d_c, d_s\)可能取到的最大值, 相当于标准化了.

代码

skimage.segmentation.slic

import numpy as np

def _generate_data(img):
img = img.astype(np.float64)
if len(img.shape) == 2:
img = img[..., None]
M, N = img.shape[0], img.shape[1]
loc = np.stack(np.meshgrid(range(M), range(N), indexing='ij'), axis=-1)
classes = -np.ones((M, N))
distances = np.ones((M, N)) * np.float('inf')
data = np.concatenate((img, loc), axis=-1)
return data, classes, distances def _generate_means(data, size: int):
M, N = data.shape[0], data.shape[1]
x_splits = np.arange(0, M + size, size)
y_splits = np.arange(0, N + size, size)
means = []
for i in range(len(x_splits) - 1):
for j in range(len(y_splits) - 1):
r1, r2 = x_splits[i:i+2]
c1, c2 = y_splits[j:j+2]
region = data[r1:r2, c1:c2]
means.append(region.mean(axis=(0, 1)))
return np.array(means) def _unit_step(data, means, classes, distances, size, dis_fn):
M, N = data.shape[0], data.shape[1]
size = 2 * size
for i, m in enumerate(means):
# ..., x, y
x, y = np.round(m[-2:])
x, y = int(x), int(y)
xl, xr = max(0, x - size), min(x + size, M)
yb, yt = max(0, y - size), min(y + size, N)
p = data[xl:xr, yb:yt]
_dis = dis_fn(p, m)
indices = _dis < distances[xl:xr, yb:yt]
distances[xl:xr, yb:yt][indices] = _dis[indices]
classes[xl:xr, yb:yt][indices] = i # update
for i in range(len(means)):
x_indices, y_indices = np.where(classes == i)
if len(x_indices) == 0:
continue
means[i] = data[x_indices, y_indices].mean(axis=0) def slic(img, size, max_iters=10, compactness=10):
data, classes, distances = _generate_data(img)
means = _generate_means(data, size)
dsm = size
dcm = (img.max(axis=(0, 1)) - img.min(axis=(0, 1))) * compactness
dsc = np.concatenate((dcm, [dsm] * 2))
def dis_func(p, m):
_dis = ((p - m) / dsc) ** 2
return _dis.sum(axis=-1)
for _ in range(max_iters):
_unit_step(data, means, classes, distances, size, dis_func)
new_img = np.zeros_like(img, dtype=np.float)
for i, m in enumerate(means):
x_indices, y_indices = np.where(classes == i)
if len(x_indices) == 0:
continue
new_img[x_indices, y_indices] = m[:-2]
return new_img.astype(img.dtype)
from skimage import io, segmentation, filters
from freeplot.base import FreePlot

img = io.imread(r"Lenna.png") ours = slic(img, size=50, compactness=0.5) def mask2img(mask, img):
new_img = img.astype(np.float)
masks = np.unique(mask)
for m in masks:
x, y = np.where(mask == m)
mcolor = new_img[x, y].mean(axis=0)
new_img[x, y] = mcolor
return new_img.astype(img.dtype) mask = segmentation.slic(img)
yours = mask2img(mask, img) fp = FreePlot((1, 3), (10.3, 5), titles=('Lenna', 'ours', 'skimage.segmentation.slic'))
fp.imageplot(img, index=(0, 0))
fp.imageplot(ours, index=(0, 1))
fp.imageplot(yours, index=(0, 2))
fp.set_title()
fp.show()

skimage上实现的代码还有强制连通性, 我想这个是为什么它看起来这么流畅的原因. Compactness 越大, 聚类越倾向于空间信息, 所以越容易出现块状结构.

SuperPixel的更多相关文章

  1. Superpixel Based RGB-D Image Segmentation Using Markov Random Field——阅读笔记

    1.基本信息 题目:使用马尔科夫场实现基于超像素的RGB-D图像分割: 作者所属:Ferdowsi University of Mashhad(Iron) 发表:2015 International ...

  2. {Links}{Matting}{Saliency Detection}{Superpixel}Source links

    自然图像抠图/视频抠像技术发展情况梳理(image matting, alpha matting, video matting)--计算机视觉专题1 http://blog.csdn.net/ansh ...

  3. SLIC superpixel算法

    标题 SLIC superpixel算法 作者 YangZheng 联系方式 263693992 SLIC算法是simple linear iterative cluster的简称,该算法用来生成超像 ...

  4. 跑superpixel的程序

    知乎上对superpixel的讲解还不错:https://www.zhihu.com/question/27623988 superpixel的算法有很多,opencv中也包含了很多,我找了一个比较经 ...

  5. 【深度聚类】Superpixel Sampling Networks

    Superpixel Sampling Networks 原始文档:https://www.yuque.com/lart/papers/ssn 本文的思想很简单,传统的超像素算法是一种有效的低/中级的 ...

  6. SLIC superpixel实现分析

    http://infoscience.epfl.ch/record/149300这是SLIC算法的官网,网站有和SLIC相关的资源. SLIC主要运用K-means聚类算法进行超像素的处理,聚类算法中 ...

  7. 超像素经典算法SLIC的代码的深度优化和分析。

    现在这个社会发展的太快,到处都充斥着各种各样的资源,各种开源的平台,如github,codeproject,pudn等等,加上一些大型的官方的开源软件,基本上能找到各个类型的代码.很多初创业的老板可能 ...

  8. paper 116:自然图像抠图/视频抠像技术梳理(image matting, video matting)

    1. Bayesian Matting, Chuang, CVPR 2001.http://grail.cs.washington.edu/projects/digital-matting/paper ...

  9. {Reship}{Code}{CV}

    UIUC的Jia-Bin Huang同学收集了很多计算机视觉方面的代码,链接如下: https://netfiles.uiuc.edu/jbhuang1/www/resources/vision/in ...

随机推荐

  1. [web安全] 利用pearcmd.php从LFI到getshell

    有一段时间没写blog了,主要是事多,加上学的有些迷茫,所以内耗比较大.害,沉下心好好学吧. 漏洞利用背景: 允许文件包含,但session等各种文件包含都已经被过滤了.ctf题中可以关注regist ...

  2. 【STM32】WS2812介绍、使用SPI+DMA发送数据

    这篇要使用到SPI+DMA,需要了解的话,可以参考我另两篇博客 时钟:https://www.cnblogs.com/PureHeart/p/11330967.html SPI+DMA通信:https ...

  3. 03-Collection用例管理及批量执行

    当我们对一个或多个系统中的很多用例进行维护时,首先想到的就是对用例进行分类管理,同时还希望对这批用例做回归测试 .在postman也提供了这样一个功能,就是Collection .通过这个Collec ...

  4. 【Matlab】find函数用法

    find(A):返回向量中非零元素的位置 注意返回的是位置的脚标 //类似python,还是很好用的 如果是二维矩阵,是先横行后列的 b=find(a),a是一个矩阵,查询非零元素的位置 如果X是一个 ...

  5. 项目集成seata和mybatis-plus冲突问题解决方案:(分页插件失效, 自动填充失效, 自己注入的id生成器失效 找不到mapper文件解决方案)

    项目集成seata和mybatis-plus,seata与mybatis-plus冲突问题(所有插件失效,自动填充失效,找不到mapper文件解决方案) 自动填充代码: package com.fro ...

  6. Spring框架源码干货分享之三级缓存和父子工厂

    记录并分享一下本人学习spring源码的过程,有什么问题或者补充会持续更新.欢迎大家指正! 环境: spring5.X + idea 建议:学习过程中要开着源码一步一步过 Spring中对象的创建宏观 ...

  7. .NET内存性能分析宝典

    .NET Memory Performance Analysis 知道什么时候该担心,以及在需要担心的时候该怎么做 译者注 **作者信息:Maoni Stephens ** - 微软架构师,负责.NE ...

  8. [BUUCTF]REVERSE——[MRCTF2020]Transform

    [MRCTF2020]Transform 附件 步骤: 例行检查,64位程序,无壳 64位ida载入,找到关键函数 一开始让我们输入一个长度为33位的字符串,之后使用数组dword_40F040打乱了 ...

  9. [BUUCTF]PWN——CmmC_Simplerop

    cmcc_simplerop 附件 步骤 例行检查,32位,开启了nx保护 本地试运行一下程序,查看一下大概的情况 32位ida载入,习惯性的检索程序里的字符串,看了个寂寞,从main函数开始看程序 ...

  10. 日程表(Project)

    <Project2016 企业项目管理实践>张会斌 董方好 编著 Project默认打开时,在功能区下面会有一个[日程表],如果不见了,那肯定里什么时候手贱关掉的,不要紧,还可以到[视图] ...