前言

像素风最早出现在8bit的电子游戏中,受制于电脑内存大小以及显示色彩单一, 只能使用少量像素来呈现内容,却成就了不少经典的像素游戏。随着内存容量与屏幕分辨率的提升,内存与显示媒介的限制不再是问题,而像素风也慢慢演变成一种独特的创作风格。

像素画的一般的绘制流程包括了勾线、填色等,而逐个像素的绘制需要大量时间。一些流行的艺术方式,比如线描与绘画领域,都逐渐出现了自动化或半自动化生成的方法。本文将从零开始实现SLIC[1]算法,并实现一款生成像素画工具。

什么是SLIC算法

像素画的绘制之所以不简单,是因为直接的下采样并不能准确的捕获关键像素,且容易导致丢失边缘信息,生成的像素画往往不尽人意。手工的勾线、填色,都是为了选取合适的像素点。由此,我们的问题变成了如何选取合适的像素点进行填色。

首先,引入一个概念——超像素。超像素是 2003 年 Xiaofeng Ren 提出和发展起来的图像分割技术,是指具有相似纹理、颜色、亮度等特征的相邻像素构成的有一定视觉意义的不规则像素块[1]。

通过将图片分割为超像素,可以得到相似的像素簇,相似的像素使用同一个颜色进行填充,得到的像素画会更合理。

超像素点分割的方法包括了提取轮廓、聚类、梯度上升等多种。论文[1]提出的SLIC超像素点分割算法(简单线性迭代聚类,simple linear iterative clustering)就是其中一种,它基于K-means聚类算法,根据像素的颜色和距离特征进行聚类来实现良好的分割结果,与若干种超像素点分割算法相比,SLIC具有简单灵活、效果好、处理速度快等优势。

如何实现SLIC算法

SLIC的基本流程如下:

  1. 图像预处理。

    将图像从RGB颜色空间转换到CIE-Lab颜色空间,Lab颜色空间更符合人类对颜色的视觉感知。这个空间里的距离能反映人感觉到的颜色差别,相关计算更为准确。

    Lab颜色空间同样具有三个通道,分别是lab,其中l代表亮度,数值范围为[0,100]a表示从绿色到红色的分量,数值范围为[-128,127]b表示蓝色到黄色的分量,数值范围为[-128,127]

    RGBLAB之间没有直接的转换公式,需要将RGB转为XYZ颜色空间再转为LAB,代码见文末完整代码。

  2. 初始化聚类中心。

    根据参数确定超像素的数目,也就是需要划分为多少个区域。假设图片有N个像素点,预计分割为K个超像素,每个超像素大小为N/K,相邻中心距离为S=Sqr(N/K),得到K个聚类坐标。

  3. 优化初始聚类中心。在聚类中心的3*3邻域内选择梯度最小的像素点作为新的聚类中心。

    把图像看成二维离散函数,梯度也就是这个函数的求导,当相邻像素值有变化就会存在梯度,而在边缘上的像素点的梯度最大。将聚类中心挪到梯度最小的地方可以避免其落到边缘轮廓上,影响聚类效果。

    离散梯度的梯度计算这里不做详细推导了,由于其中包含了若干*方与开方,计算量较大,一般会简化为用绝对值来*似*方和*方根的操作。简化后的计算坐标为(i,j)的像素点的梯度公式为:

    其中(i+1,j)(i,j+1)为像素右侧点与像素下方点的坐标。l(a,b)(a,b)坐标上像素的亮度通道值l

  4. 计算像素点与聚类中心的距离。

    在聚类中心距离S的区域内 2S*2S的邻域内计算像素点与每个聚类中心的距离。

    这里的距离使用的是欧式距离,总距离Ddc颜色距离与ds空间距离两部分组成。公式如下:

    如果直接将labxy拼接成一个矢量计算距离,当超像素的大小变化时,xy的值可以取到非常大 ,比如如果一张图1000*1000,空间距离可以达到1000*Sqr(2),而颜色距离最大仅10*Sqr(2),导致最终计算得到的距离值中,空间距离ds权重占比过大。

    所以需要进行归一化,除以最大值即超像素点的初始宽度S,将值映射到[0,1]

    而颜色空间距离也会给到一个固定的值m来调节颜色距离与空间距离的影响权重,m取值范围为[1,40]

    距离公式即变成了

    m越大,颜色空间除以m后的值越小,即空间距离的权重越大,生成的像素会更为形状规则,当m越小,颜色距离权重更大,超像素会在边缘更为紧凑,而形状大小较为不规则。

  5. 像素点分类。

    标记每个像素点的类别为距离其最小的聚类中心的类别。

  6. 重新计算聚类中心。

    计算属于同一个聚类的所有像素点的*均向量值,重新得到聚类中心 。

  7. 迭代4~6的过程。

    直到旧聚类中心与新聚类中心的距离小于一定阈值或者达到一定迭代次数,一般来说,当迭代次数到达10,算法能够达到收敛。

  8. 聚类优化。

    迭代到最后,可能会出现与聚类中心不属于同一连通域的孤立像素点,可以使用到连通算法将其分配到最*的聚类标签。

    论文中并未给出具体的实现算法。而本文的应用场景是生成像素画,会对像素进行下取样,并不会细化到每个像素,由此,本文不做聚类优化处理。

小小总结一下,SLIC算法流程大体与K-means是一致的,不断迭代计算距离最小的聚类簇,不同的是只对聚类中心的S距离内像素点进行计算,减少了不少的计算量。

生成像素画

基于SLIC算法,我们已经可以把一张图划分为N个超像素点。每个超像素中像素都是相*的。也就是说,每个像素都被归类为一个超像素,有一个聚类中心。那么将像素的颜色赋值为其聚类中心的颜色即得到我们想要的效果。

设定一定步长stride,使用Canvas,每隔stride个像素,将像素赋值为其聚类中心的颜色,即得到最终的像素化结果。

而每个人对于像素画的主观感受是不一致的,为了让用户有更多的选择,得到自己满意的结果。可以暴露更多的人工干预参数,比如取消聚类优化的终止条件,改为由用户来设置迭代次数,以及最终取像素值的步长。人工设定的参数包括了

  • 超像素点大小blocksizeblocksize越小,超像素点分割越细腻。
  • 迭代次数itersiters越大,分割结果更精准,计算时间越长。
  • 颜色空间权重weightweight越大,颜色对于分割结果的影响越大。
  • 取像素点步长stridestride越小,生成的像素图越接*超像素点,也就越细腻。

实现用户交互界面

作为一个工具,自然需要用户交互界面,前端界面基于HTML/Javascript/CSS搭建,使用Canvas API绘制图像内容,而用户交互面板选择的是dat.gui [3] 库。dat.gui是一个轻量级的图像化界面库,非常适用于参数的修改,常用作可视化 Demo 的演示。支持的参数类型包括了NumberStringBoolean、自定义函数等。可以为不同的属性绑定相应的响应事件,当属性值改变时自动触发事件。

为生成像素化工具添加以下属性与事件:

  • iters、stride、blockSize、weight(颜色空间权重m)参数变化时重新进行SLIC算法的计算,并重新绘制计算结果;
  • 添加Upload imageExport image按钮,支持用户上传图片与下载像素化后的图片;

在绘制图像的Canvas画布层上叠加一层Canvas画布,对算法的结果进行可视化,添加以下功能

  • grid开关控制是否绘制像素网格;
  • Centers开关控制是否显示聚类中心;
  • Contours开关控制是否显示聚类边缘轮廓;

其中聚类中心点Centers的绘制直接使用ctx.fillRect 传入中心点坐标即可。

超像素轮廓Contours的绘制则需要先计算得到轮廓点。

可以对每个像素点与周围的8个像素点进行比较,如果聚类中心不同的像素点个数大于2,则代表着这个像素点周围有两个以上不同类别的点,则这个点为轮廓。效果如下:

最后,就得到一个简单的生成像素画工具了。

体验地址

完整版代码地址(JS版)

参考文献

[1] Achanta R, Shaji A, Smith K, Lucchi A, Fua P, Su ̈sstrunk S. SLIC superpixels. Technical Report. IVRG CVLAB; 2010.

[2] Gerstner T , Decarlo D , Alexa M , et al. Pixelated image abstraction with integrated user constraints[J]. Computers & graphics, 2013.

[3] https://github.com/dataarts/dat.gui

欢迎关注凹凸实验室博客:aotu.io

或者关注凹凸实验室公众号(AOTULabs),不定时推送文章:

实现SLIC算法生成像素画的更多相关文章

  1. 技术期刊 · 天光台高未百尺 | Uber 工程师的 JS 算法课;大数据时代的个人隐私;设计师的 Github;告别 PPT 工程师;从零开始实现的像素画

    蒲公英 · JELLY技术期刊 Vol.42 这是一个最好的时代,多样化的平台给了所有人成长发展的机会,各种需求和解决需求的人让人大开眼界:但这也并不是完美的时代,"前端还需要懂什么算法?& ...

  2. 【资源分享】Gmod-Expression2 - 自定义像素画生成

    *作者:BUI* 可自定义制作属于你的像素画(默认为Sans) 第77行的COLOR可编辑你想要的颜色(RGB值) 1,2,3,4分别代表第77行所定义的颜色(0代表不显示) 视频地址:传送链接 @n ...

  3. python生成字符画

    python生成字符画 这个idea来自于实验楼,非常适合练习PIL的像素处理,更重要的是非常有意思. 环境配置 依赖的第三方库就是PIL(Python Image Library),可以直接使用pi ...

  4. 算法生成N芒星

    前面两个图像生成算法是:道教的太极八卦图和佛教的卐和卍字图.这一节整个洋气的图像:芒星.但愿我别召唤出什么恐怖的禁忌,尤其今晚还是万圣节之夜.平时看玄幻小说,经常读到有关六芒星,七芒星,九芒星的技法. ...

  5. 机器学习:simple linear iterative clustering (SLIC) 算法

    图像分割是图像处理,计算机视觉领域里非常基础,非常重要的一个应用.今天介绍一种高效的分割算法,即 simple linear iterative clustering (SLIC) 算法,顾名思义,这 ...

  6. ZeroMQ接口函数之 :zmq_z85_decode – 从一个用Z85算法生成的文本中解析出二进制密码

    ZeroMQ 官方地址 :http://api.zeromq.org/4-0:zmq_z85_decode zmq_z85_decode(3)         ØMQ Manual - ØMQ/4.1 ...

  7. PHP HMAC_SHA1 算法 生成算法签名

    HMAC_SHA1(Hashed Message Authentication Code, Secure Hash Algorithm)是一种安全的基于加密hash函数和共享密钥的消息认证协议. 它可 ...

  8. 根据twitter的snowflake算法生成唯一ID

    C#版本 /// <summary> /// 根据twitter的snowflake算法生成唯一ID /// snowflake算法 64 位 /// 0---0000000000 000 ...

  9. 在 iPad 上试验从用算法生成法线贴图-到法线映射光照效果

    在 iPad 上试验从用算法生成法线贴图-到法线映射光照效果 目录 概述 一般来说, 法线贴图是用高模的法线图, 低模的纹理图, 来生成较好的渲染效果. 而法线图通常是通过图像处理软件来生成的, 这里 ...

随机推荐

  1. Spring中@Import注解的使用

    Spring中@Import注解的使用 @Import注解算是SpringBoot自动配置原理中一个很重要的注解 认识@Import注解 先看一下源码 @Target(ElementType.TYPE ...

  2. Jenkins 基础篇 - Server 配置

    我们使用 Jenkins 部署服务之前要先配置我们的目标服务器,配置目标服务器也很简单,就是将服务器的 IP.账号密码或者账号密钥配置在 Jenkins中.在演示服务器配置之前我们要先知道 Linux ...

  3. canal 环境搭建 canal 与kafka通信(三)

    canal 占用了生产者 .net core端 使用消费者获取canal 消息 安装 Confluent.Kafka  demo使用 1.3.0 public static void Consumer ...

  4. VulnHub系列(一)DC-1

    环境 kali linux 和 DC-1 都是搭建在VMware上的虚拟机,都是NAT模式. 主机发现 NAT模式下虚拟机没有被分配真实的ip地址,他们通过共享宿主机的ip地址访问互联网.我们可以通过 ...

  5. Linux进阶之软件管理

    本节内容 一.rpm:管理linux软件程序的 特点:安装方便 不能解决依赖关系 1.安装软件: -i: -v: -h: rpm -ivh 包名 2.卸载软件 -e: 清楚 rpm -e 程序名 3. ...

  6. 6.5 scp:远程文件复制

    scp命令 用于在不同的主机之间复制文件,它采用SSH协议来保证复制的安全性.scp命令每次都是全量完整复制,因此效率不高,适合第一次复制时使用,增量复制建议使用rsync命令替代.     scp ...

  7. Centos6.9以下查看端口占用情况和开启端口命令

    Centos查看端口占用情况命令,比如查看80端口占用情况使用如下命令:   lsof -i tcp:80   列出所有端口   netstat -ntlp   1.开启端口(以80端口为例)     ...

  8. Java基础之概述

    1. 什么是程序 程序是计算机执行某些操作或解决某个问题而编写的一系列有序指令的集合 2. Java三大版本 Java SE 标准版 Java EE 企业版 Java ME 小型版 3. Java重要 ...

  9. JVM-垃圾收集算法基础

    目录 目录 前言 手动释放内存导致的问题 垃圾判定方法 哪些对象是垃圾? 引用计数算法 可达性分析法 垃圾收集算法 标记-清除 优点 缺点 优化 标记-复制 优点 缺点 优化 标记-整理 优点 缺点 ...

  10. Django(46)drf序列化类的使用(ModelSerializer)

    前言 我们上篇文章使用到了Serializer类,可能有小伙伴说太过复杂,那么本篇就为大家带来更加简便的序列化类ModelSerializer ModelSerializer 先来看下ModelSer ...