算法挑战:从图像生成颜色方案

时间:2009-07-09 20:10:26

标签: image algorithm colors

背景

所以,我正在研究一个新的Web应用程序迭代。并且,我们发现我们的用户沉迷于懒惰。真的很懒。事实上,我们为他们做的工作越多,他们就越喜欢这项服务。现有应用程序的一部分要求用户选择要使用的配色方案。但是,我们有一个图像(用户网站的屏幕截图),为什么我们不能只是满足他们的懒惰并为他们做呢?答:我们可以,这将是一个有趣的编程练习! :)

挑战

给定图像,如何创建相应的颜色方案?换句话说,如何选择图像中的主要X颜色(其中X由Web应用程序定义)。在我们的特定情况下使用的图像是用户网站的屏幕截图,以全分辨率(例如1280x1024)拍摄。 (注意:请简单描述您的算法 - 不需要发布实际的伪代码。)

奖励积分(街道积分,而不是实际的积分):

  • 描述一种简单而有效的算法。代码是我们创造的方式 - 保持简洁和美观。
  • 允许用户根据各种“情绪”调整配色方案,例如“炫彩”,“明亮”,“静音”,“深度”等(a la Kuler
  • 描述一种可靠地确定网站截图中使用的主文字颜色的方法(可能需要自己的,单独的算法)。

启示

有几个现有网站执行类似的功能。请随意查看它们并问自己:“我将如何复制它?我怎样才能改进它?”

11 个答案:

答案 0 :(得分:26)

  1. 要查找主要X颜色,请触摸应用程序。在图像上运行颜色直方图。直方图中的前X个颜色是主题。编辑:如果使用渐变,您将需要选择不同的颜色“峰值”;也就是说,如果橙色是渐变中使用的主要颜色之一,您可能会在“橙色”周围拥有一大堆颜色。实际上,只需在直方图中选择的颜色之间实施一定的距离。

  2. 最好在HSV空间中调整配色方案;将您的颜色转换为HSV空间,如果用户希望它更“明亮”,请增加值,如果他们希望它更“彩色”,增加饱和度等。

  3. 通过表征高变异性区域(傅立叶空间中的高频率),可以最好地确定文本颜色。在这些区域内,你应该有:两种颜色,文字和背景,在这种情况下你的文字是较少使用的颜色;或者您将拥有多种颜色,文本和背景图像颜色,在这种情况下,文本颜色是最常见的颜色。

答案 1 :(得分:8)

你可以看看:

https://github.com/dcollien/Dreamcoat

在CoffeeScript中做到这一点(有文化的咖啡,所以它有详细记录)

在这里演示:http://dcollien.github.io/Dreamcoat/test.html

它结合了颜色量化方法和KMeans方法。

答案 2 :(得分:7)

我这样做是为了找到用于图像(艺术品)的调色板。

  1. 我从imagemagick开始,将大图像调整到可调整大小(即最大尺寸为400像素)。这实际上有助于将细微的局部色差转换为具有这些色彩平均值的较少像素。

  2. 循环调整大小后的图像中的每个像素,读取每个像素的RGB值,将RGB转换为HSB并将HSB值存储到数组中。

  3. 对于找到的每个像素颜色,我将Hue范围(0,255)除以16,将饱和度范围(0,100)除以10,将亮度范围(0,100)除以10.将结果向上或向下舍入到整数。这有助于将像素分组为相似颜色的类别。

    因此,HSB为223,64,76的像素将属于类别14,6,8

    在每个类别中,您仍然可以找到每个像素的精确颜色,但在大多数情况下,类别本身与源图像的颜色匹配很近。

    如果您希望从类别中进行更好的颜色复制,请选择将HSB划分为更精细的分区。即。将每个H,S,B除以8,5,5而不是16,10,10。

  4. 计算最流行的颜色类别,排序和显示。我丢弃的像素数很少的颜色类别。

  5. 注意:这是专为具有相同颜色值的像素非常少的艺术品(即带有阴影和渐变的绘画)而设计的。

    在大多数情况下,HTML页面可能具有与特定颜色值完全匹配的更多像素(即背景颜色,文本颜色等,无论它们出现在哪里都将是相同的颜色。)

答案 3 :(得分:6)

Color quantization与用于选择低色彩GIF调色板的过程相同。为了从摄影图像中获取调色板,我使用了Nick Rabinowitz'quantize.js,它基于Leptonica的MMCQ(修改后的中间切割量化)。

meemoo screen shot Live web appabout

答案 4 :(得分:5)

  1. 将屏幕图像划分为一个r多个矩形的网格,以n×m“网格”为单位,每个都有宽度(总宽度/ n)和高度(总高度/ m)。

    1a上。为屏幕的高调区域指定权重,例如左偏离中心区域。

    1b中。对于每个矩形,将像素分配到(颜色频率)的空间

  2. 对于每个矩形R,频率分布f_R和权重W_R:

    2a上。通过扫描“顶部频率”,“第二频率”( f_R)确定 i 第一方案颜色(例如,i = 1 - 背景颜色)每个块都有[ i ,:])。

    2B。对于每个 i ,将其放入得分表( color_i 得分),其中得分= f_R [ i ,“频率”] * W_R

    2c中。每个 i 的最佳得分手将是 i -th方案颜色。

  3. 理论上,如果你有很多“蓝色白色”或“红色黑色”,你应该得到白色的初级,蓝色次级或黑色初级,红色次级,例如。

    对于文本颜色,可以直接根据背景颜色计算,也可以选择二次颜色,如果HSV的V差太小,则将颜色偏离计算出的方案颜色,但增加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];
            }
    

答案 5 :(得分:4)

您想要的算法类型的名称是Color Quantization

很遗憾,我没有任何可用的源代码,但我确信google search可能会有所改善。

特别是关于这个主题的Dr. Dobb's Journal article似乎很有希望。

答案 6 :(得分:2)

与McWafflestix的解决方案类似,细节需要调整,但我的一般方法是......

(我同意HSV是合适的空间)

  1. 抓取图像的直方图,对其进行滤波以平滑噪点,并找到V和S处于可能的“主题”颜色(可能是动态的)色域的最高分。蓝天上的一只红鸟要求我们足够聪明,不要将我们的计划建立在蓝色上,而是放在红色上。这可能需要对照片构图进行一些猜测,例如“框架中心”和“三分法”分析可以为您提供颜色相关的概率。 无论如何,这是我们的基色。

  2. 沿着Kuler的路线,通过在色轮周围移动来计算与基座相配的颜色。计算赞美的额外分数,如果它也出现在步骤1的直方图中的显着位置。

  3. 使用基色和计算的赞美来获得令人愉悦的辅助颜色,例如每个颜色较浅或较暗,或多或少饱和等。

答案 7 :(得分:2)

已经有很多很好的建议如何找到原色,我会尝试类似的方法。为了找到文字颜色,我有另一个建议。

从上到下计算图像中每一行的直方图。每次到达线的基线时,文本颜色的频率应该会大幅下降。当你达到小写字母时,频率将保持低,直到你达到下一行的大写字母,然后是第二步。

如果在击中基线时有另一个强烈的峰值变得更大,则找到背景颜色。渐变背景将平滑此峰值,峰值的变化 - 当您进入或离开新行时 - 将通过抗锯齿平滑。

答案 8 :(得分:1)

我有点晚了,但我会在3d色彩空间中实现Kohonen Map(http://en.wikipedia.org/wiki/Self-organizing_map)。地图上的点数将是您为调色板所需的不同颜色的数量,然后使用图像中的所有像素训练地图。我自己没试过,但我确定其他人已经考虑过了。

答案 9 :(得分:1)

以下是围绕从图像生成配色方案的不同方法的一些建议和讨论:

首先,在一些色彩空间中嵌入/绘制像素。这可以是RGB,HSL或其他一些颜色空间。然后,您可以使用以下之一生成颜色方案:

  1. 创建颜色空间的直方图 - 这涉及将空间分割成网格,并计算每个网格单元格中的像素。选择具有最多像素的前N个单元格(直方图桶),并平均每个像素中的像素以生成每个单元格的颜色。这可以是你的配色方案。

  2. Median Cut 或其他一些空间分区技术 - 这是对#1的一个很好的改进,因为它会通过查看数据来分割空间。

  3. 聚类像素 - 使用多种聚类技术(k-means,mean-shift等)之一将像素聚类成组。然后平均每组中的像素以生成颜色方案。

  4. 我写了一篇关于上述三种方法here

    的更详细的帖子

    我还写了一个interactive web app,它允许你使用上述三种方法之一加载图像和创建颜色调色板。您可以在github

    上找到相应的代码

答案 10 :(得分:0)

在保持最小/最大值的同时分别平均色调,饱和度和亮度。

将所有颜色的目标色调锁定为平均值,并插入边界之间的x点的饱和度和亮度。这应该返回一个方案,其颜色与foto相同,但具有简单的变化。也许你甚至会得到苹果的外观。

希望你不要得到3种色情的狗呕吐。