algorithm挑战:从图像生成颜色scheme
背景
所以,我正在研究一个Web应用程序的新的迭代。 而且,我们发现我们的用户痴迷于懒惰。 真的很懒。 事实上,我们为他们做的工作越多,他们就越喜欢这项服务。 现有应用程序的一部分需要用户select要使用的配色scheme。 但是,我们有一个图片(用户网站的截图),为什么我们不能满足他们的懒惰,为他们做呢? 答:我们可以,这将是一个有趣的编程练习! 🙂
挑战
给定一个图像,你如何创build一个相应的配色scheme? 换句话说,如何select图像中的主要X颜色(其中X由Web应用程序定义)。 在我们的特定情况下使用的图像是用户的网站截图,以全分辨率(例如1280×1024)。 ( 注意:请简单描述你的algorithm – 不需要发布实际的伪代码)
奖励积分(街道积分,不是实际的积分)为:
- 描述一个简单而有效的algorithm。 代码是我们如何创造 – 保持简单和美丽。
- 允许用户根据“多彩”,“明亮”,“静音”,“深”等各种“情绪”来调整配色scheme(a la Kuler )
- 描述一种可靠地确定网站截图中使用的主要文本颜色的方法(可能需要它自己的,单独的algorithm)。
灵感
有几个现有的网站执行类似的function。 随意检查一下,问自己:“我怎么复制这个?我怎么能改进它?
- http://www.pictaculous.com/
- http://www.cssdrive.com/imagepalette/index.php
- http://kuler.adobe.com/#create/fromanimage
-
要find主要的X颜色,screencap应用程序。 在图像上运行颜色直方图。 直方图中的顶部X颜色是主题。 编辑:如果使用渐变,你会想挑选不同的颜色“峰”; 也就是说,如果橙色是渐变中使用的主要颜色之一,则可能在“橙色”周围有一大堆颜色。 实际上,只需在从直方图中select颜色之间执行一定的距离即可。
-
调整配色scheme最好在HSV空间中完成; 将你的颜色转换为HSV空间,如果用户希望它变得“更亮”,增加价值,如果他们想要更多“色彩”,增加饱和度等等。
-
确定文本颜色最好是通过表征高变化区域(傅立叶空间中的高频)来完成。 在这些区域内,你应该有两种颜色,文字和背景,在这种情况下你的文字是较less使用的颜色; 或者你会有几种颜色,文本和背景图像颜色,在这种情况下,文本颜色是最常见的颜色。
我这样做是为了find用于图像(艺术品)的调色板。
-
我从imagemagick开始,调整一个大的图像到一个可行的大小(即最大尺寸为400像素)。这实际上有助于将细微的局部色彩差异转换为较less的像素与这些颜色的平均值。
-
循环显示已resize的图像中的每个像素,读取每个像素的RGB值,将RGB转换为HSB并将HSB值存储到一个数组中。
-
对于find的每个像素颜色,然后将色相范围(0,255)除以16,饱和度范围(0,100)除以10,并将亮度范围(0,100)除以10.将结果向上或向下四舍五入为整数。 这有助于将像素分组成相似颜色的类别。
所以HSB为223,64,76的像素将在14,6,8的类别中
在每个类别中,仍然可以find每个像素的确切颜色 ,但大多数情况下类别本身与源图像是密切匹配的。
如果您希望从类别中更好地进行颜色复制,请select将HSB划分为更细的部分。 即。 将每个H,S,B除以8,5,5而不是16,10,10。
-
计数最stream行的颜色类别,sorting和显示。 我丢弃很less像素数的颜色类别。
注意:这实际上是为具有相同颜色值的像素非常less的作品devise的(即,具有阴影和渐变的绘画)。
大多数情况下,HTML页面可能有更多的像素与特定的颜色值完全匹配(即背景颜色,文本颜色等无论在哪里出现都将是相同的颜色)。
你可以看看:
https://github.com/dcollien/Dreamcoat
这在CoffeeScript(识字咖啡,所以它是有据可查的)
在这里演示: http : //dcollien.github.io/Dreamcoat/test.html
它同时具有颜色量化方法和KMeans方法。
-
将屏幕图像划分为一个宽度为(总宽度/ n)和高度(总高度/ m)的n×m“网格”的r多个矩形网格。
1A。 将权重分配给屏幕的高调区域,例如左侧偏离中心的区域。
1B。 对于每个矩形,将像素分配到( 颜色 , 频率 )
-
对于每个矩形R,频率分布f_R和权重W_R:
2A。 通过扫描每个块的“最高频率”,“第二频率”( 即 f_R [ i ,:])来确定第i个scheme颜色(例如,i = 1 – 背景颜色)。
2B。 对于每个我 ,把它放在一个分数表,( color_i , 分数 )其中score = f_R [ i ,“频率”] * W_R
2C。 每个我的头号射手将是第i个scheme的颜色。
从理论上讲,如果你有很多“白底蓝”或“红底黑”,你应该得到白色的主,蓝色的辅助或黑色的主,红色的辅助,例如。
对于你的文本颜色,要么直接基于背景颜色的计算,要么select二次色,如果HSV的V差太小,则将计算的scheme颜色偏离颜色,但增加V值。
伪代码:
float[][] weights = { { 1.0, 3.0, 5.0, 5.0, 3.0, 1.0, 1.0, 1.0, 1.0 }, { 2.0, 6.0, 7.0, 7.0, 6.0, 2.0, 3.0, 3.0, 2.0 }, { 2.0, 8.0, 9.0, 9.0, 7.0, 3.0, 6.0, 6.0, 3.0 }, { 2.0, 8.0, 9.0, 9.0, 7.0, 2.0, 3.0, 3.0, 2.0 }, { 2.0, 7.0, 9.0, 9.0, 7.0, 2.0, 1.0, 1.0, 1.0 }, { 2.0, 6.0, 7.0, 7.0, 6.0, 2.0, 3.0, 3.0, 1.0 }, { 1.0, 3.0, 5.0, 5.0, 3.0, 2.0, 6.0, 6.0, 2.0 }, { 1.0, 1.0, 2.0, 2.0, 1.0, 2.0, 6.0, 6.0, 2.0 }, { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 3.0, 3.0, 1.0 } }; // Leave the following implementations to the imagination: void DivideImageIntoRegions( Image originalImage, out Image[][] regions ); void GetNthMostCommonColorInRegion( Image region, int n, out Color color ); TKey FindMaximum<TKey, TValue>( Map<TKey, TValue> map ); // The method: Color[] GetPrimaryScheme( Image image, int ncolors, int M = 9, int N = 9 ) { Color[] scheme = new Color[ncolors]; Image[][] regions = new Image[M][N]; DivideImageIntoRegions( image, regions ); for( int i = 0; i < ncolors; i++ ) { Map<Color, float> colorScores = new Map<Color, float>(); for( int m = 0; m < M; m++ ) for( int n = 0; n < N; n++ ) { Color theColor; GetNthMostCommonColorInRegion( region, i, theColor ); if( colorScores[theColor] == null ) { colorScores[theColor] = 0; } colorScores[theColor] += weights[m][n]; } scheme[i] = FindMaximum( colorScores ); } return scheme; }
综上所述,很明显,如果存在一个变异性很小的区域,它将具有与最常见的颜色相同的第二常见的颜色。 为了调整,在这种情况下第二常见的颜色可能是空的,哪一个可以防范:
if( theColor != null ) continue; if( colorScores[theColor] == null ) { colorScores[theColor] = 0; } colorScores[theColor] += weights[m][n]; }
色彩量化与用于select低色彩GIF的调色板的过程相同。 为了从摄影图像中获得调色板,我使用了基于Leptonica的MMCQ(改进的中值切割量化)的Nick Rabinowitz的quantize.js 。
现场的Web应用程序 , 关于 。
你想要的algorithmtypes的名称是色彩量化 。
不幸的是,我没有任何可用的源代码,但我确定谷歌search可能会改变一些东西。
尤其是, Dobb博士关于这个问题的文章似乎很有前途。
已经有很多好的build议如何find原色,我会尝试类似的方法。 为了find文字颜色,我有另一个build议。
从上到下计算图像中每一行的直方图。 每当你到达一条线的底线时,文本颜色的频率应该有很大的下降。 在到达小写字母后,频率将保持较低,直到达到下一行的大写字母,然后是第二步。
如果在击中基线时出现另一个较强的峰值,则会发现背景颜色。 渐变背景将平滑此峰值,并且峰值的变化 – 当您进入或离开新的一行时 – 将通过抗锯齿来平滑。
类似于McWafflestix的解决scheme,细节将需要调整,但我的一般方法是…
(我同意HSV是正确的空间)
-
获取图像的直方图,对其进行滤波以平滑噪点,并findV和S处于可能“主体”色彩(可能是dynamic的)色域中的最高分数。 蓝天上的红色小鸟需要我们足够聪明,不要把我们的计划放在蓝色的地方,而是放在红色的地方。 这可能需要一些关于照片构成的猜测,比如“以框架为中心”和“三分法则”的分析可能会给你一个颜色相关的概率。 无论如何,这是我们的基本色彩。
-
沿着Kuler的线条,通过在色轮周围移动来计算与底座相配的颜色。 计算出来的赞美词的额外分数,如果它也出现在第一步的直方图中。
-
使用基础颜色和计算的赞美来推导令人满意的附属颜色,例如每个颜色的较亮和较暗的版本,或多或less的饱和度等。
我有点晚了,但我会实现一个Kohonen地图(http://en.wikipedia.org/wiki/Self-organizing_map)在3D色彩空间。; 地图上的点数将是您想要的调色板中不同颜色的数量,然后使用图像中的所有像素来训练您的地图。 我没有尝试过这个,但是我确定别人已经想到了。
以下是关于从图像生成颜色scheme的不同方法的一些build议和讨论:
首先,在一些色彩空间中embedded/绘制像素。 这可以是RGB,HSL或其他一些色彩空间。 然后,您可以使用以下任一项来生成配色scheme:
-
创build色彩空间的直方图 – 这包括将空间分割成网格,并计算每个网格单元中的像素。 select像素数最多的前N个单元格(直方图桶),并对每个单元格中的像素进行平均,以产生每个单元格的颜色。 这可以是你的配色scheme。
-
中值切割或其他一些空间分割技术 – 这是对#1的不错改进,因为它将通过查看数据来分割空间。
-
聚类像素 – 使用许多聚类技术之一(k-means,mean-shift等)将像素聚类成组。 然后平均每组中的像素以生成配色scheme。
我在这里写了一个比较详细的post
我还写了一个交互式的networking应用程序 ,允许您使用上述三种方法之一加载图像和创build一个调色板。 你可以在github上find它的代码
分别平均色调,饱和度和亮度,同时保持最小值/最大值。
将所有颜色的目标色调locking到平均值,并为边界之间的x点插入饱和度和亮度。 这应该返回一个与foto相同的颜色的scheme,但是具有简单的变化。 也许你会得到苹果的样子。
只是希望你没有得到3狗屁股阴影。