我在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]];
最后,我将整个图片返回两种主要颜色,并告诉函数过滤掉背景颜色。
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的功能非常简单:当专辑产生少于两个高光时,将标题变为白色或黑色,具体取决于与背景颜色的最佳对比度。然后,如果有一种颜色强烈突出,歌曲就变成这种颜色,否则标题颜色淡化到背景中。
通过@Seth-thompson的答案和@bluedog的评论,我建立了一个小型的Objective-C (Cocoa-Touch)项目,根据图像生成色彩方案。
您可以在以下链接中查看该项目:
https://github.com/luisespinoza/LEColorPicker
目前,LEColorPicker正在执行以下操作:
就目前而言,我将检查ColorTunes项目(https://github.com/Dannvix/ColorTunes)和Wade Cosgrove项目以获取新功能。此外,我还有一些新的想法来改进颜色方案结果。
你可能还可以查看ColorTunes,这是使用MMCQ算法(中位数切割颜色量化)的Itunes专辑视图的HTML实现。
我刚刚编写了一个JS库,实现了与@Seth描述的算法大致相同。它可以在github.com/arcanis/colibrijs上自由获取,并在NPM上以colibrijs
的形式提供。
借助@Seth的答案,我使用PHP和Imagick实现了算法,以获取图片两侧边框中的主要颜色。
https://gist.github.com/philix/5688064#file-simpleimage-php-L81
它被用于填充http://festea.com.br封面照片的背景。
我在不同的背景下问了同样的问题,然后被引导到http://charlesleifer.com/blog/using-python-and-k-means-to-find-the-dominant-colors-in-images/上学习一种算法(k均值),该算法使用图像中的随机起始点粗略地执行相同的操作。这样,算法就可以自己找到主要颜色。