iTunes 11 歌曲列表的着色算法是如何实现的? - 博客

本文由 伯乐在线 - 伯乐在线读者 翻译自 Stackoverflow。转载请参见文章末尾处的要求。

2012年11月份 LuisEspinoza 在Stackoverflow上提问:“最新版的 iTunes 11专辑里歌曲列表非常漂亮,可以从专辑封面给歌曲列表里的字体和背景提取出颜色。有没有人知道这个算法是如何实现的?”

针对这个问题,Seth Thompson 的回答所得投票最高,感谢@袁欣_Jason 的热心翻译。

Third Example

以下是 Seth Thompson 的回答

Example 1

在Mathematica中,我用专辑封片图片做为输入,近似模拟出了iTune 11的着色算法:

Output 1

我是如何做到的

经过反复尝试,我想出了一种算法,经过我的测试,对大约 80% 的专辑都有效。

计算颜色的差异

这个算法就是要寻找图片的主色。能够找到这个主色的先决条件是两种颜色之间的差异是可计量的。其中一种方法就是计算出这两种颜色在RGB颜色空间中的欧式距离(Euclidean distance),不过人对颜色的感知与这种计算出的欧式距离并不匹配。

因此我写了一个函数,用来将 RGB 颜色转换成 YUV 格式,这种颜色空间可以更好的模拟人的色觉:

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 是指将两种颜色区分开的最低色差,虽然我用一个原始像素构成的数组作为输入,为了更好模拟系统内置的DominantColors函数,最后我返回了一个RGBColor类型。

我真正使用的函数 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会从专辑的边缘寻找主色给歌曲列表背景着色,有一点要注意的是,计算时要去除掉专辑封面的一圈窄边框。

接下来我用上面的函数再找出图片最外层主色,使用默认最小色差.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[]}]

Final Output

注释说明

这个算法对大部分专辑图片都有效。我用上面的色差参数测试,约80%的专辑封面都可以生成正确的颜色。有一些是因为专辑封面是黑白的,导致无法返回高亮色。我的算法没有处理这些特例,不过能不能完全模拟iTunes并不重要。如果一个专辑封面少于两种高亮色,专辑标题会变成黑色或者白色,这取决于跟背景的最佳对比度。歌曲的标题会变成其中一种返回的高亮色,如果没有返回的颜色,那么歌曲的标题颜色会有一些融于背景之中。

其他例子

More Examples

原文链接: Stackoverflow   翻译: 伯乐在线 - 伯乐在线读者
译文链接: http://blog.jobbole.com/42748/
[ 转载必须在正文中标注并保留原文链接、译文链接和译者等信息。]