iTunes 11中歌曲列表着色算法是如何工作的?

300
新的iTunes 11为专辑的歌曲列表提供了非常漂亮的视图,根据专辑封面选择字体和背景颜色。有人知道算法是如何工作的吗?

Third Example


10
W3C的颜色对比度公式可能是答案的一部分。我自己的实验测试表明,微软Word使用这个公式来决定其自动着色字体。搜索“颜色亮度由以下公式确定”,即可找到W3C颜色对比度公式的相关信息。 - bluedog
@bluedog,我认为你是对的。我尝试了很多我的专辑封面,总是字体与背景有足够的对比度,可以清晰地看到它。 - LuisEspinoza
1
还有一点需要注意的是,这似乎在Mac OS和Windows之间有所不同:https://twitter.com/grimfrog/status/275187988374380546 - Tom Irving
2
我可以想象,计算中可能不仅包括颜色的数量,还包括它们的饱和度值:我的实验得出结论,虽然高光颜色只出现在图像的少数区域,但它们经常被选为背景颜色。 这就是为什么我认为查看封面图像的直方图及其峰值可能会有所帮助,并且基于一些精细调整的参数来选择颜色。 - Raffael
2
请查看http://www.panic.com/blog/2012/12/itunes-11-and-colors/上的另一个答案。 - Mark Ransom
显示剩余7条评论
7个回答

424

示例1

我在Mathematica中基于专辑封面的输入,对iTunes 11的颜色算法进行了近似:

输出1

实现方法

通过试错,我得出了一个算法,可以在我测试过的大约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]];

最后,我将整个图片返回两种主要颜色,并告诉函数过滤掉背景颜色。

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[]}]

最终输出

注释

该算法可以应用于各种情况。我调整了上述设置和容差值,使其能够正确识别出大约80%的专辑封面的颜色。当DominantColorsNew未找到两个颜色以供高亮显示时(即专辑封面是单色的情况),会出现一些边缘情况。我的算法没有解决这些问题,但复制iTunes的功能非常简单:当专辑产生少于两个高光时,将标题变为白色或黑色,具体取决于与背景颜色的最佳对比度。然后,如果有一种颜色强烈突出,歌曲就变成这种颜色,否则标题颜色淡化到背景中。

更多示例

更多示例


3
好的,@Seth Thompson,看起来非常有前途。我将亲自尝试一下,需要几天时间,请耐心等待。 - LuisEspinoza
6
非常棒的解决方案。现在需要将其从Mathematica转换到Objective-C,这是一项艰苦的挑战。 - loretoparisi
2
这个非常详细的答案值得点赞! - Marius Schulz
1
@cormullion LUV(和LAB)都旨在实现感知均匀性。然而,在任何颜色空间中,我都没有找到使用欧几里得距离的明确参考。我的猜测是,如果没有其他选择,它们两者都比RGB更好。 - Seth Thompson
6
这就是我所谓的“查克·诺里斯式回答”。 - MCKapur
显示剩余8条评论

43

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

您可以在以下链接中查看该项目:

https://github.com/luisespinoza/LEColorPicker

目前,LEColorPicker正在执行以下操作:

  1. 将图像缩放为36x36 px(这会减少计算时间)。
  2. 从图像生成像素数组。
  3. 将像素数组转换为YUV空间。
  4. 按照Seth Thompson的代码收集颜色。
  5. 颜色集按计数排序。
  6. 算法选择三种最显著的颜色。
  7. 最显著的颜色被指定为背景。
  8. 使用w3c颜色对比公式测试第二和第三最显著的颜色,以检查颜色与背景之间是否有足够的对比度。
  9. 如果其中一个文本颜色未通过测试,则根据Y分量分配为白色或黑色。

就目前而言,我将检查ColorTunes项目(https://github.com/Dannvix/ColorTunes)和Wade Cosgrove项目以获取新功能。此外,我还有一些新的想法来改进颜色方案结果。

Screenshot_Mona


2
+1 - 非常酷的东西,也是如何算法开发和应用开发都能成为非常有趣的例子。 - Yuval Karmi
1
+1 用于检查对比度。 - brianmearns
是的,很酷,但你如何对每种颜色的哈希值进行四舍五入?我认为我可以轻松地打破这个算法,只需在右下角添加一个小黑白“显式”标志,你真的增加了黑白的焦点。无论如何,这个算法对于基于剪贴画的图像效果更好,但如果您将图像设置为36x36,则通过抗锯齿处理来减少失败情况。 - Jack Franzen
一个词:太棒了! - Teddy

16

Panic 的 Wade Cosgrove 写了一篇 不错的博客文章,描述了他实现了一个近似于 iTunes 算法的算法。它包括 Objective-C 的示例实现。


15

你可能还可以查看ColorTunes,这是使用MMCQ算法(中位数切割颜色量化)的Itunes专辑视图的HTML实现。


是的,我已经检查过了。不幸的是,似乎几乎没有文档记录。 - LuisEspinoza
ColorTunes中的重要注释是对中位切割量化算法的引用。我只是根据论文中的描述在Python中实现了这个算法,大约花了2个小时的时间,而且我更喜欢它比上面Seth算法的实现。我觉得结果更好一些,但最重要的是它要快得多(当然,我可能没有正确地实现Seth算法)。 - brianmearns
@sh1ftst0rm,你的Python实现在GitHub或其他地方吗?谢谢。 - Anentropic
@Anentropic 抱歉,我没有。那是我正在进行的一个私人项目的一部分,我还没有将其提取出来。如果我有机会,我会尝试在某个地方发布它,但可能不会很快。 - brianmearns

7

我刚刚编写了一个JS库,实现了与@Seth描述的算法大致相同。它可以在github.com/arcanis/colibrijs上自由获取,并在NPM上以colibrijs的形式提供。


5

4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接