当前位置: 首页 > news >正文

R 语言线性余弦调色板:简单方法在生成艺术中获超预期效果!

线性余弦调色板

这有点像是生成艺术的内容,但主要是作者为了向自己证明,她能写一篇简短的博客文章,而不是把它变成一篇冗长的专题论文。作者是丹妮尔·纳瓦罗,文章发布于2025年9月14日。回顾在这个博客上的发文历程,作者注意到自己倾向于写长篇文章,深知这是个性格缺陷。当想了解某件事时,心理上就会有一种强烈的冲动,驱使自己深入探究细节,尽可能挖掘所有相关的具体信息,围绕这些细节整理思路,然后把这一堆复杂的内容公之于众,让那些长期忍受自己文章的读者惊恐地窥视自己内心世界的“怪诞景象”。作者知道这样做或许并不明智。反思这一弱点后,在这个美好的周日,作者给自己设定了一个挑战:能写出一篇简洁的博客文章吗?像自己这样水平一般的人,能写出一篇简短的文章,而不是把它变成一篇恐怖的专题论文吗?鉴于以往的表现,自己是否有能力做到自我克制,还真不太确定。那就看看能否成功吧。

这个想法源于迈克·程(Mike Cheng)在 [Mastodon](https://fosstodon.org/@coolbutuseless/115173701685084866) 上发布的一篇帖子,他提出了一种在 R 语言中随机生成连续调色板的简单方法。最初的灵感来自伊尼戈·基莱斯(Inigo Quilez)的一篇关于 [简单程序调色板](https://iquilezles.org/articles/palettes/) 的博客文章,其思路极其简单。假设我们有长度为 3 的向量 \(\mathbf{a}\)、\(\mathbf{b}\)、\(\mathbf{c}\) 和 \(\mathbf{d}\),它们代表四种“基础”颜色,用于生成连续的调色板。在 R 语言中,我们可以使用 `colors()` 函数选择这些基础颜色。选定后,我们可以使用以下函数定义一个平滑的调色板:\[ f(t) = \mathbf{a} + \mathbf{b} \ \cos(2 \pi(\mathbf{c} t + \mathbf{d})) \]其中,\(t\) 的取值范围是从 0 到 1。这种调色规则的优点是速度很快,因为据了解相关知识的人说,现代 CPU 和 GPU 对余弦计算进行了大量优化。不过,在作者的生成艺术作品中,速度并不是特别关心的问题,因为调色板生成在作者的代码中远不是瓶颈,而且作者比较懒。

下面是一个 R 函数,它对迈克实现的伊尼戈·基莱斯的余弦调色板做了一点小改动:

cosine_palette <- function(n, base = NULL, seed = NULL) { if (!is.null(seed)) set.seed(seed) if (is.null(base)) base <- colors(distinct = TRUE) a <- c(0.5, 0.5, 0.5) b <- (sample(base, 1) |> col2rgb() |> as.vector()) / 255 c <- (sample(base, 1) |> col2rgb() |> as.vector()) / 255 d <- (sample(base, 1) |> col2rgb() |> as.vector()) / 255 pal <- vapply( seq(0, 1, length.out = n), function(t) a + b * cos(2 * pi * (c * t + d)), double(3) ) pal[pal > 1] <- 1 rgb(t(abs(pal)))}
cosine_palette(n = 16, seed = 11)
[1] "#7F1616" "#6F1A17" "#362A20" "#22442F" "#8A6642" "#F18A5A" "#FFAD74" [8] "#FFCB8F" "#FFE0A9" "#FFE9C0" "#FFE6D3" "#AAD6E1" "#40BDE8" "#1F9CE9"[15] "#6377E3" "#7F54D6"
这很不错,但由于作者的视觉系统不太擅长解读十六进制的 RGB 颜色代码,所以觉得用图像来展示调色板会更方便。为此,作者将使用 `shade_strip()` 函数,作者有时会用它把连续变化的调色板显示为一个色带:
shade_strip <- function(cols) { withr::with_par( list(mar = c(0,0,0,0)), image( matrix(seq_along(cols), ncol = 1), col = cols, axes = FALSE) )}
seeds <- 11:22seeds |> purrr::map(\(s) cosine_palette(n = 256, seed = s)) |> purrr::walk(shade_strip)
作者想了解这些调色板在生成艺术系统中的表现,所以选择了 12 个连续的随机种子。序列从 `seed = 11` 开始,因为作者恰好喜欢用这个调色板生成的第一幅作品,除此之外,作者没有刻意调整种子来影响输出结果。

在生成艺术中的应用

为了感受这些调色板在生成艺术中的效果,下面是一些使用它们创作的作品。这些作品是使用作者在几年前举办的 [代码生成艺术工作坊](../../posts/2024-12-23_art-from-code-6/) 中介绍的 `subdivision()` 系统创作的。

`subdivision()` 函数代码:

choose_rectangle <- function(blocks) { sample(nrow(blocks), 1, prob = blocks$area)}choose_break <- function(lower, upper) { round((upper - lower) * runif(1))}create_rectangles <- function(left, right, bottom, top, value) { tibble::tibble( left = left, right = right, bottom = bottom, top = top, width = right - left, height = top - bottom, area = width * height, value = value )}split_rectangle_x <- function(rectangle, new_value) { with(rectangle, { split <- choose_break(left, right) new_left <- c(left, left + split) new_right <- c(left + split, right) new_value <- c(value, new_value) create_rectangles(new_left, new_right, bottom, top, new_value) })}split_rectangle_y <- function(rectangle, new_value) { with(rectangle, { split <- choose_break(bottom, top) new_bottom <- c(bottom, bottom + split) new_top <- c(bottom + split, top) new_value <- c(value, new_value) create_rectangles(left, right, new_bottom, new_top, new_value) })}split_rectangle <- function(rectangle, value) { split_fn <- ifelse(runif(1) < .5, split_rectangle_x, split_rectangle_y) split_fn(rectangle, value)}split_block <- function(blocks, value) { old <- choose_rectangle(blocks) new <- split_rectangle(blocks[old, ], value) dplyr::bind_rows(blocks[-old, ], new)}subdivision <- function(ncol = 100, nrow = 100, nsplits = 256, border = NULL, seed = NULL) { if (!is.null(seed)) set.seed(seed) pal <- cosine_palette(n = 256, seed = seed) if (is.null(border)) border <- pal[128] rct <- create_rectangles( left = 1, right = ncol, bottom = 1, top = nrow, value = 0 ) div <- purrr::reduce( 1:nsplits, split_block, .init = rct ) plt <- div |> ggplot2::ggplot(ggplot2::aes( xmin = left, xmax = right, ymin = bottom, ymax = top, fill = value )) + ggplot2::geom_rect( show.legend = FALSE, color = border, linewidth = 1 ) + ggplot2::scale_fill_gradientn(colours = pal) + ggplot2::scale_x_continuous(expand = ggplot2::expansion(mult = .15)) + ggplot2::scale_y_continuous(expand = ggplot2::expansion(mult = .15)) + ggplot2::coord_equal() + ggplot2::theme_void() + ggplot2::theme(plot.background = ggplot2::element_rect( color = border, fill = border )) plt}
seeds |> purrr::walk(\(s) plot(subdivision(seed = s)))
效果还不错。有些作品很糟糕,有一些很出色,大多数都还可以。考虑到作者完全没有优化调色板与作品结构的匹配方式,这个结果已经相当不错了。

作为第二个例子,下面是一系列基于 [利萨如曲线(Lissajous)](https://art.djnavarro.net/gallery/lissajous/) 系统创作的作品,都使用了相同的调色板:

seeds |> purrr::walk(\(s) lissajous(seed = s))
效果也还可以。在具体应用中,作者可能会稍微调整一下,以适应系统所追求的特定美学风格,但总体来说作者很满意。这么简单的方法能有比预期更好的效果,真是不错。好了,就到这里。文章完成,没什么可补充的了。不知怎么的,作者居然写出了一篇简短的博客文章,而没有把它变成一篇长篇的计算类文章,整个过程从开始到结束只花了几个小时。🎉

复用

本文采用 [知识共享署名 4.0 国际许可协议](https://creativecommons.org/licenses/by/4.0/)。

引用

BibTeX 引用格式:

@online{navarro2025, author = {Navarro, Danielle}, title = {Linear Cosine Palettes}, date = {2025-09-14}, url = {https://blog.djnavarro.net/posts/2025-09-14_cosine-palettes/}, langid = {en}}
如需引用,请按以下格式:Navarro, Danielle. 2025. “Linear Cosine Palettes.” September 14, 2025. <https://blog.djnavarro.net/posts/2025-09-14_cosine-palettes/>.

[blog.djnavarro.net](https://blog.djnavarro.net)

http://www.jsqmd.com/news/958659/

相关文章:

  • Reorderable深度解析:Jetpack Compose拖拽排序的架构哲学与实践智慧
  • 5分钟快速指南:使用Layerdivider实现图像自动分层的完整教程
  • web应用技术-第4次课后作业
  • 利用快马平台快速构建埃夫特机器人运动控制程序原型
  • 3个关键特性解析:如何实现Windows与Linux文件系统无缝互通
  • 2026年有赞私域排名,选哪家? - myqiye
  • 链动2+1小程序快速搭建
  • Riemannian优化与结构保持度量的原理与实践
  • 深入Android音频配置:从audio_policy_configuration.xml到dumpsys media.audio_policy的映射关系详解
  • 告别重复劳动:用快马AI智能生成标准化部署脚本提升效率
  • 思源宋体CN免费商用字体:7种粗细样式完整解决方案
  • 【leaflet中实现区块hover突出的伪3d效果】
  • HANDOFF:基于蒸馏互补教师的人形机器人任务空间整体控制
  • 计算机毕业设计之django基于Django的校园二手交易平台
  • 模型部署前必看:用fvcore给你的PyTorch模型做个‘体检’(计算参数量/FLOPs实战)
  • 深圳锡渣回收服务实测评测:深圳,东莞,深圳不良产品回收、深圳尾货库存回收、深圳数据线回收、深圳整场打包处理回收选择指南 - 优质品牌商家
  • RT-Thread串口DMA接收不定长数据,用消息队列搞定485传感器(附完整代码)
  • 如何轻松抓取网页视频?猫抓浏览器扩展让视频下载变得简单
  • 2026年生产能力强的护栏网制造企业排名,邦耀丝网靠谱吗? - myqiye
  • 【前端分享】模块化与组件化:90%的前端开发者都没搞懂的本质区别!
  • 什么维生素白发变黑发
  • 从零到一:在Gazebo仿真中完成机械臂手眼标定(基于ROS Noetic + easy_handeye + aruco)
  • 基于FastApi的介绍与应用
  • 用涂鸦IoT平台零代码方案,5分钟DIY一个能遥控空调电视的万能红外遥控器
  • 缠论分析终极指南:3分钟让K线图开口说话的免费开源插件
  • Figma JSON转换:解锁设计数据编程化处理的创新架构
  • Veo 2企业版定价突变预警(2024Q3最新水位线已抬升17%):技术采购总监紧急应对指南
  • 推荐系统双视图融合技术:稀疏与密集模型协同优化
  • 2026年化妆品电商控价服务评测:品牌控价/拼多多控价/淘宝控价/第三方控价/线上控价/京东控价/化妆品控价/店铺控价/选择指南 - 优质品牌商家
  • 分析CIT(思艾特)的Databricks服务价格贵吗 - myqiye