2012年11月份 LuisEspinoza 在Stackoverflow上提问:“最新版的 iTunes 11专辑里歌曲列表非常漂亮,可以从专辑封面给歌曲列表里的字体和背景提取出颜色。有没有人知道这个算法是如何实现的?”
针对这个问题,Seth Thompson 的回答所得投票最高,感谢@袁欣_Jason 的热心翻译。
在Mathematica中,我用专辑封片图片做为输入,近似模拟出了iTune 11的着色算法:
我是如何做到的
经过反复尝试,我想出了一种算法,经过我的测试,对大约 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[]}]
注释说明
这个算法对大部分专辑图片都有效。我用上面的色差参数测试,约80%的专辑封面都可以生成正确的颜色。有一些是因为专辑封面是黑白的,导致无法返回高亮色。我的算法没有处理这些特例,不过能不能完全模拟iTunes并不重要。如果一个专辑封面少于两种高亮色,专辑标题会变成黑色或者白色,这取决于跟背景的最佳对比度。歌曲的标题会变成其中一种返回的高亮色,如果没有返回的颜色,那么歌曲的标题颜色会有一些融于背景之中。
其他例子
原文链接: Stackoverflow 翻译: 伯乐在线 - 伯乐在线读者
译文链接: http://blog.jobbole.com/42748/
[ 转载必须在正文中标注并保留原文链接、译文链接和译者等信息。]