新的iTunes 11有一个非常好的查看专辑歌曲列表的视图,在专辑封面的功能中为字体和背景选择颜色。有人知道算法是怎么运作的吗?


我以专辑封面作为输入,在Mathematica中近似地使用了iTunes 11的颜色算法:

我是怎么做到的

经过反复试验,我想出了一个算法,它适用于我测试过的80%的专辑。

颜色的差异

该算法的主要工作是寻找图像的主色调。然而,找到主色的先决条件是计算两种颜色之间的可量化差异。计算两种颜色之间差异的一种方法是计算它们在RGB颜色空间中的欧几里得距离。然而,人类的颜色感知与RGB颜色空间中的距离并不是很匹配。

因此,我写了一个函数来将RGB颜色(形式为{1,1,1})转换为更适合近似颜色感知的颜色空间YUV:

(编辑:@cormullion和@Drake指出Mathematica内置的CIELAB和CIELUV颜色空间同样适合……看起来我在这里重新发明了一些轮子)

convertToYUV[rawRGB_] :=
    Module[{yuv},
        yuv = {{0.299, 0.587, 0.114}, {-0.14713, -0.28886, 0.436},
            {0.615, -0.51499, -0.10001}};
        yuv . rawRGB
    ]

接下来,我写了一个函数,用上面的转换来计算颜色距离:

ColorDistance[rawRGB1_, rawRGB2_] := 
    EuclideanDistance[convertToYUV @ rawRGB1, convertToYUV @ rawRGB2]

主要的颜色

我很快发现,内置的Mathematica函数DominantColors不允许足够细粒度的控制来近似iTunes使用的算法。我写了自己的函数…

计算一组像素中主色的一个简单方法是将所有像素收集到颜色相似的桶中,然后找到最大的桶。

DominantColorSimple[pixelArray_] :=
    Module[{buckets},
        buckets = Gather[pixelArray, ColorDistance[#1,#2] < .1 &];
        buckets = Sort[buckets, Length[#1] > Length[#2] &];
        RGBColor @@ Mean @ First @ buckets
    ]

请注意,.1是不同颜色必须被视为独立的容忍度。还要注意的是,尽管输入是一个原始三元组形式的像素数组({{1,1,1},{0,0,0}}),但我返回了一个Mathematica RGBColor元素,以更好地近似内置的DominantColors函数。

我的实际函数DominantColorsNew添加了在过滤掉给定的其他颜色后返回最多n个主色的选项。它还暴露了每种颜色比较的公差:

DominantColorsNew[pixelArray_, threshold_: .1, n_: 1, 
    numThreshold_: .2, filterColor_: 0, filterThreshold_: .5] :=
    Module[
        {buckets, color, previous, output},
        buckets = Gather[pixelArray, ColorDistance[#1, #2] < threshold &];
        If[filterColor =!= 0, 
        buckets = 
            Select[buckets, 
                ColorDistance[ Mean[#1], filterColor] > filterThreshold &]];
        buckets = Sort[buckets, Length[#1] > Length[#2] &];
        If[Length @ buckets == 0, Return[{}]];
        color = Mean @ First @ buckets;
        buckets = Drop[buckets, 1];
        output = List[RGBColor @@ color];
        previous = color;
        Do[
            If[Length @ buckets == 0, Return[output]];
            While[
                ColorDistance[(color = Mean @ First @ buckets), previous] < 
                    numThreshold, 
                If[Length @ buckets != 0, buckets = Drop[buckets, 1], 
                    Return[output]]
            ];
            output = Append[output, RGBColor @@ color];
            previous = color,
            {i, n - 1}
        ];
        output
    ]

算法的其余部分

首先,我调整了专辑封面的大小(36px, 36px),并用双边滤镜减少了细节

image = Import["http://i.imgur.com/z2t8y.jpg"]
thumb = ImageResize[ image, 36, Resampling -> "Nearest"];
thumb = BilateralFilter[thumb, 1, .2, MaxIterations -> 2];

iTunes通过查找相册边缘的主色来选择背景色。但是,它通过裁剪图像忽略了狭窄的专辑封面边框。

thumb = ImageCrop[thumb, 34];

接下来,我找到主色(使用上面的新函数)沿着图像的最外层边缘,默认容差为.1。

border = Flatten[
    Join[ImageData[thumb][[1 ;; 34 ;; 33]] , 
        Transpose @ ImageData[thumb][[All, 1 ;; 34 ;; 33]]], 1];
background = DominantColorsNew[border][[1]];

最后,我返回图像中的2个主色作为一个整体,告诉函数过滤掉背景色。

highlights = DominantColorsNew[Flatten[ImageData[thumb], 1], .1, 2, .2, 
    List @@ background, .5];
title = highlights[[1]];
songs = highlights[[2]];

以上公差值如下:.1为“分开”颜色之间的最小差值;.2是众多主色之间的最小差异(较低的值可能返回黑色和深灰色,而较高的值确保主色的多样性);.5是主色和背景色之间的最小差值(值越大,颜色组合的对比度越高)

拖鞋!

Graphics[{background, Disk[]}]
Graphics[{title, Disk[]}]
Graphics[{songs, Disk[]}]

笔记

The algorithm can be applied very generally. I tweaked the above settings and tolerance values to the point where they work to produce generally correct colors for ~80% of the album covers I tested. A few edge cases occur when DominantColorsNew doesn't find two colors to return for the highlights (i.e. when the album cover is monochrome). My algorithm doesn't address these cases, but it would be trivial to duplicate iTunes' functionality: when the album yields less than two highlights, the title becomes white or black depending on the best contrast with the background. Then the songs become the one highlight color if there is one, or the title color faded into the background a bit.

更多的例子

你也可以签出ColorTunes,它是Itunes相册视图的HTML实现,它使用MMCQ(中值剪切颜色量化)算法。

Panic的韦德·科斯格罗夫写了一篇不错的博客文章,描述了他的算法实现,类似于iTunes中的算法。它包括Objective-C中的一个示例实现。

根据@Seth-thompson的回答和@bluedog的评论,我建立了一个小的Objective-C (Cocoa-Touch)项目来生成图像功能的配色方案。

你可以在以下网址查看项目:

https://github.com/luisespinoza/LEColorPicker

目前,LEColorPicker正在做:

Image is scaled to 36x36 px (this reduce the compute time). It generates a pixel array from the image. Converts the pixel array to YUV space. Gather colors as Seth Thompson's code does it. The color's sets are sorted by count. The algorithm select the three most dominant colors. The most dominant is asigned as Background. The second and third most dominants are tested using the w3c color contrast formula, to check if the colors has enought contrast with the background. If one of the text colors don't pass the test, then is asigned to white or black, depending of the Y component.

现在,我将检查ColorTunes项目(https://github.com/Dannvix/ColorTunes)和Wade Cosgrove项目的新功能。对于提高配色效果,我也有一些新的想法。

我在不同的上下文中问了同样的问题,并被指向http://charlesleifer.com/blog/using-python-and-k-means-to-find-the-dominant-colors-in-images/的一个学习算法(k Means),它使用图像中的随机起点大致做同样的事情。这样,算法就能自己找到主色调。

根据@Seth的回答,我使用PHP和Imagick实现了在图片的两个横向边界中获得主色调的算法。

https://gist.github.com/philix/5688064#file-simpleimage-php-L81

它被用来填充http://festea.com.br封面照片的背景

我只是写了一个JS库,实现了@Seth所描述的大致相同的算法。可以在github.com/arcanis/colibrijs和NPM上以colibrijs的形式免费获得。