像素风最先出如今8bit的电子游戏中,受制于电脑内存大小以及显示色彩单一, 只能使用少许像素来呈现内容,却成就了很多经典的像素游戏。随着内存容量与屏幕分辨率的提高,内存与显示媒介的限制再也不是问题,而像素风也慢慢演变成一种独特的创做风格。html
像素画的通常的绘制流程包括了勾线、填色等,而逐个像素的绘制须要大量时间。一些流行的艺术方式,好比线描与绘画领域,都逐渐出现了自动化或半自动化生成的方法。本文将从零开始实现SLIC
[1]算法,并实现一款生成像素画工具。前端
像素画的绘制之因此不简单,是由于直接的下采样并不能准确的捕获关键像素,且容易致使丢失边缘信息,生成的像素画每每不尽人意。手工的勾线、填色,都是为了选取合适的像素点。由此,咱们的问题变成了如何选取合适的像素点进行填色。git
首先,引入一个概念——超像素。超像素是 2003 年 Xiaofeng Ren 提出和发展起来的图像分割技术,是指具备类似纹理、颜色、亮度等特征的相邻像素构成的有必定视觉意义的不规则像素块[1]。github
经过将图片分割为超像素,能够获得类似的像素簇,类似的像素使用同一个颜色进行填充,获得的像素画会更合理。算法
超像素点分割的方法包括了提取轮廓、聚类、梯度上升等多种。论文[1]
提出的SLIC
超像素点分割算法(简单线性迭代聚类,simple linear iterative clustering
)就是其中一种,它基于K-means
聚类算法,根据像素的颜色和距离特征进行聚类来实现良好的分割结果,与若干种超像素点分割算法相比,SLIC
具备简单灵活、效果好、处理速度快等优点。ide
SLIC
的基本流程以下:函数
图像预处理。工具
将图像从RGB
颜色空间转换到CIE-Lab
颜色空间,Lab
颜色空间更符合人类对颜色的视觉感知。这个空间里的距离能反映人感受到的颜色差异,相关计算更为准确。优化
Lab
颜色空间一样具备三个通道,分别是l
,a
,b
,其中l
表明亮度,数值范围为[0,100]
,a
表示从绿色到红色的份量,数值范围为[-128,127]
,b
表示蓝色到黄色的份量,数值范围为[-128,127]
。ui
RGB
和LAB
之间没有直接的转换公式,须要将RGB
转为XYZ
颜色空间再转为LAB
,代码见文末完整代码。
初始化聚类中心。
根据参数肯定超像素的数目,也就是须要划分为多少个区域。假设图片有N
个像素点,预计分割为K
个超像素,每一个超像素大小为N/K
,相邻中心距离为S=Sqr(N/K)
,获得K
个聚类坐标。
优化初始聚类中心。在聚类中心的3*3
邻域内选择梯度最小的像素点做为新的聚类中心。
把图像当作二维离散函数,梯度也就是这个函数的求导,当相邻像素值有变化就会存在梯度,而在边缘上的像素点的梯度最大。将聚类中心挪到梯度最小的地方能够避免其落到边缘轮廓上,影响聚类效果。
离散梯度的梯度计算这里不作详细推导了,因为其中包含了若干平方与开方,计算量较大,通常会简化为用绝对值来近似平方和平方根的操做。简化后的计算坐标为(i,j)
的像素点的梯度公式为:
其中(i+1,j)
与(i,j+1)
为像素右侧点与像素下方点的坐标。l(a,b)
为(a,b)
坐标上像素的亮度通道值l
。
计算像素点与聚类中心的距离。
在聚类中心距离S的区域内 2S*2S
的邻域内计算像素点与每一个聚类中心的距离。
这里的距离使用的是欧式距离,总距离D
由dc
颜色距离与ds
空间距离两部分组成。公式以下:
若是直接将l
,a
,b
,x
,y
拼接成一个矢量计算距离,当超像素的大小变化时,x
,y
的值能够取到很是大 ,好比若是一张图1000*1000
,空间距离能够达到1000*Sqr(2)
,而颜色距离最大仅10*Sqr(2)
,致使最终计算获得的距离值中,空间距离ds
权重占比过大。
因此须要进行归一化,除以最大值即超像素点的初始宽度S
,将值映射到[0,1]
。
而颜色空间距离也会给到一个固定的值m
来调节颜色距离与空间距离的影响权重,m
取值范围为[1,40]
。
距离公式即变成了
当m
越大,颜色空间除以m
后的值越小,即空间距离的权重越大,生成的像素会更为形状规则,当m
越小,颜色距离权重更大,超像素会在边缘更为紧凑,而形状大小较为不规则。
像素点分类。
标记每一个像素点的类别为距离其最小的聚类中心的类别。
从新计算聚类中心。
计算属于同一个聚类的全部像素点的平均向量值,从新获得聚类中心 。
迭代4~6
的过程。
直到旧聚类中心与新聚类中心的距离小于必定阈值或者达到必定迭代次数,通常来讲,当迭代次数到达10
,算法可以达到收敛。
聚类优化。
迭代到最后,可能会出现与聚类中心不属于同一连通域的孤立像素点,可使用到连通算法将其分配到最近的聚类标签。
论文中并未给出具体的实现算法。而本文的应用场景是生成像素画,会对像素进行下取样,并不会细化到每一个像素,由此,本文不作聚类优化处理。
小小总结一下,SLIC算法流程大致与K-means
是一致的,不断迭代计算距离最小的聚类簇,不一样的是只对聚类中心的S
距离内像素点进行计算,减小了很多的计算量。
基于SLIC
算法,咱们已经能够把一张图划分为N
个超像素点。每一个超像素中像素都是相近的。也就是说,每一个像素都被归类为一个超像素,有一个聚类中心。那么将像素的颜色赋值为其聚类中心的颜色即获得咱们想要的效果。
设定必定步长stride
,使用Canvas
,每隔stride
个像素,将像素赋值为其聚类中心的颜色,即获得最终的像素化结果。
而每一个人对于像素画的主观感觉是不一致的,为了让用户有更多的选择,获得本身满意的结果。能够暴露更多的人工干预参数,好比取消聚类优化的终止条件,改成由用户来设置迭代次数,以及最终取像素值的步长。人工设定的参数包括了
blocksize
;blocksize
越小,超像素点分割越细腻。iters
;iters
越大,分割结果更精准,计算时间越长。weight
;weight
越大,颜色对于分割结果的影响越大。stride
;stride
越小,生成的像素图越接近超像素点,也就越细腻。做为一个工具,天然须要用户交互界面,前端界面基于HTML/Javascript/CSS
搭建,使用Canvas API
绘制图像内容,而用户交互面板选择的是dat.gui
[3] 库。dat.gui
是一个轻量级的图像化界面库,很是适用于参数的修改,经常使用做可视化 Demo 的演示。支持的参数类型包括了Number
、String
、Boolean
、自定义函数等。能够为不一样的属性绑定相应的响应事件,当属性值改变时自动触发事件。
为生成像素化工具添加如下属性与事件:
iters、stride、blockSize、weight
(颜色空间权重m)参数变化时从新进行SLIC
算法的计算,并从新绘制计算结果;Upload image
与Export image
按钮,支持用户上传图片与下载像素化后的图片;在绘制图像的Canvas
画布层上叠加一层Canvas
画布,对算法的结果进行可视化,添加如下功能
grid
开关控制是否绘制像素网格;Centers
开关控制是否显示聚类中心;Contours
开关控制是否显示聚类边缘轮廓;其中聚类中心点Centers
的绘制直接使用ctx.fillRect
传入中心点坐标便可。
超像素轮廓Contours
的绘制则须要先计算获得轮廓点。
能够对每一个像素点与周围的8
个像素点进行比较,若是聚类中心不一样的像素点个数大于2
,则表明着这个像素点周围有两个以上不一样类别的点,则这个点为轮廓。效果以下:
最后,就获得一个简单的生成像素画工具了。
[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.
欢迎关注凹凸实验室博客:aotu.io
或者关注凹凸实验室公众号(AOTULabs),不定时推送文章。