调色板生成算法



在为网站寻找漂亮的调色板吗? 最近在家中安装的RGB背光灯,还是想用新颜色粉刷房间? 还是购买了带有彩色背光的键盘并想充分利用它? 无论您处于哪种情况,都一定要不断调整配色方案。

作为一名程序员,我很快写了几行代码来生成随机的调色板。 几分钟后,我立即意识到这种方法可能无法获得最佳效果,因此我实施了调色板的“重新加载”按钮。 在我看来,要制定一个好的计划,您只需要一点运气和耐心。

我错了 随机调色板生成很烂。 有时,美丽的色彩与丑陋,肮脏的棕色或黄色阴影并存。 颜色集合总是太暗,太亮,对比度低,或者集合由非常相似的颜色组成。 有必要提出不同的解决方案。

色彩空间


让我们从理论开始。 如今,色彩空间已广泛用于对颜色进行分类:

RGB


RGB代表Red Green Blue 。 显示器就是这样工作的:它们以三个颜色通道发光,这些颜色通道以不同的比例混合以产生各种颜色。 每个通道中的值在0到255之间变化R:0, G:0, B:0 (或十六进制表达式中的#000000)为黑色,并且R:255, G:255, B:255 (或#ffffff) )-白色。

CIE实验室


CIE Lab的色彩空间比sRGB宽,并且包含人类感知的所有颜色。 它是在普遍感知的期望下创建的。 换句话说,颜色之间的距离对应于主观差异:如果两种颜色的值彼此接近,则它们看起来相似。 另一方面,彼此隔开很远的两种颜色也被认为是完全不同的。 在CIE Lab中,为饱和色分配的空间比为黑暗和明亮分配的空间更多。 顺便说一下,对于人眼来说,非常深的绿色几乎无法与黑色区分开。 此外,此颜色空间是三维的: L表示亮度(0.0到1.0), ab (约-1.0到1.0)是颜色通道。

盐酸


如果RGB描述了显示器如何显示颜色,而CIE Lab描述了我们如何感知颜色,则HCL是最紧密地描述我们对颜色的看法的颜色空间。 它也是三维的, H代表色相(0到360度), 代表色度, L代表亮度(均从0.0到1.0测量)。

我建议使用CIE Lab进行计算,并使用HCL来向用户表示调色板。 如果需要,可以将这些空间中的值转换为RGB。

色彩空间分解


由于我需要获得一组独特的,独立的颜色,因此首先我们丢弃那些看起来非常相似的颜色。 颜色空间将是三维的,并且k均值聚类算法非常适合分离此类低维数据集。 他正在尝试将数据(在我们的示例中为颜色空间)分解为k个单独的区域。 然后从这些区域中群集的中心点组装调色板。 gif在CIE Lab的三维空间中显示算法的二维显示。

编写代码


使用在Go上实现的k-means方法,只需几行代码即可解决该问题。 首先,在CIE Lab空间中准备颜色值:

 var d clusters.Observations for l := 0.2; l <= 0.8; l += 0.05 { for a := -1.0; a < 1.0; a += 0.1 { for b := -1.0; b < 1.0; b += 0.1 { d = append(d, clusters.Coordinates{l, a, b}) } } } 

我已经选择了几个参数,并对生成的颜色施加了一定的限制。 在此示例中,我们将排除太暗(亮度<0.2)和太亮(亮度> 0.8)的颜色。

扩展新创建的色彩空间:

 km := kmeans.New() clusters, _ := km.Partition(d, 8) 

Partition函数将返回八个簇的切片。 每个群集都有一个中心点,该中心点是给定空间中的单独颜色。 它的坐标可以轻松地转换为十六进制RGB值:

 col := colorful.Lab(c.Center[0], c.Center[1], c.Center[2]) col.Clamped().Hex() 

请记住,CIE Lab比RGB宽,这意味着某些Lab值无法转换为RGB。 可以使用“ Clamped将这些值转换为RGB空间中最接近的颜色。

完整代码


 package main import ( "github.com/muesli/kmeans" "github.com/muesli/clusters" colorful "github.com/lucasb-eyer/go-colorful" ) func main() { // Create data points in the CIE L*a*b* color space // l for lightness, a & b for color channels var d clusters.Observations for l := 0.2; l <= 0.8; l += 0.05 { for a := -1.0; a <= 1.0; a += 0.1 { for b := -1.0; b <= 1.0; b += 0.1 { d = append(d, clusters.Coordinates{l, a, b}) } } } // Partition the color space into 8 clusters km := kmeans.New() clusters, _ := km.Partition(d, 8) for _, c := range clusters { col := colorful.Lab(c.Center[0], c.Center[1], c.Center[2]) fmt.Println("Color as Hex:", col.Clamped().Hex()) } } 

此代码生成的一组八种(不是这样)随机颜色:



定义自己的色彩空间


增加对颜色生成的控制。 我们可以轻松地管理用于进一步计算的数据,从而选择适合我们需要的色彩空间。 生成一个柔和的调色板:

 func pastel(c colorful.Color) bool { _, s, v := col.Hsv() return 0.2 <= s && s <= 0.4 && 0.7 <= v && v <= 1.0 } for l := 0.0; l <= 1.0; l += 0.05 { for a := -1.0; a <= 1.0; a += 0.1 { for b := -1.0; b <= 1.0; b += 0.1 { col := colorful.Lab(l, a, b) if col.IsValid() && pastel(col) { d = append(d, clusters.Coordinates{l, a, b}) } } } } 

HSV是另一种颜色空间,名称中的字母表示色调,饱和度和亮度。 在此空间中,柔和的颜色通常具有高亮度和低饱和度。

这是发生了什么:



您还可以按颜色的饱和度(色度)和亮度过滤颜色,以获得一组“暖色调”:

 func warm(col colorful.Color) bool { _, c, l := col.Hcl() return 0.1 <= c && c <= 0.4 && 0.2 <= l && l <= 0.5 } 

结果:



色域包


我正在使用一个名为gamut的库,它将在Go上的一个方便的软件包中收集此处描述的所有内容,从而使您可以生成和管理调色板和主题。 您已经可以尝试,但仍在工作。

Source: https://habr.com/ru/post/zh-CN414455/


All Articles