寒夏摸鱼站

教你造一台光棱坦克

1.9k字 …阅 @2025年11月29日 §技术 #图像处理

幻影坦克已经满足不了你了?接下来到达战场的是光棱坦克!

与幻影坦克不同,光棱坦克 是命令与征服系列游戏中的一种盟军高科单位 是一种需要依赖于特定图片调整算法才能看到底图的藏图方法,通常会使用 曝光度调整亮度调整 来显影。

为了让你理解光棱坦克的实现原理,接下来我们会通过基础概念循序渐进地教你光棱坦克的制造细节。

注:本文中所有的交互小工具需要依赖一些特殊浏览器 API(WorkerOffscreenCanvas),如果无法使用,请考虑更换浏览器

图像直方图

与幻影坦克依赖透明度魔法不同,光棱坦克依赖图片的亮度魔法。为了观察一张图片的亮度信息,通常会使用直方图

以下是帮你计算一张输入图片直方图的小工具,你可以添加图片试试看。

直方图是一张二维图,横坐标是 像素值,根据图片的位深有着不同的范围(8 位深为 256 格,16 位深为 65536 格)
纵坐标是一个统计量,即在 整个图片中有多少个像素与横坐标像素值一样,白色的柱越高,那么对应像素的数量越多。

// img: number[][]
// w: number
// h: number
const getHistogram = (img, w, h) => {
  const yOut = Array(256).fill(0);

  for (let col = 0; col < w; col++) {
    for (let row = 0; row < h; row++) {
      const grey = img[w][h];
      yOut[grey]++;
    }
  }

  return yOut;
};

从直方图中我们同样还可以获得整张图片的信息 —— 如果白色柱大多集中于左边,则图片整体更暗(暗色像素数量多),反之更亮。

需要注意的是,直方图如果不特别说明 通道,则默认是将图片转换为灰度后再进行统计计算,以此来体现亮度信息,如果指定了通道,那么直方图反应的就是对应色彩通道的像素分布信息了。

色阶控制

在通过直方图获取图片的亮度分布信息之后,我们就可以通过 色阶 来对修改图片的像素亮度分布了。

在专业的图片编辑软件中,色阶的参数可以变得很多很复杂,但是对于制造光棱坦克,我们只需要调整 4 个参数:

  • 输入黑场
  • 输入白场
  • 输出黑场
  • 输出白场

黑场和白场

在图片编辑软件的色阶控制中,你能看到一个拖动条,两边分别有一个黑块和白块,控制着像素的亮度范围,这被分别称为 黑场白场


对于输入,黑场和白场做的是 钳制 + 压缩

简单地来说,就是将比黑场还要暗的像素置为纯黑,比白场还要亮的像素置为纯白,而黑场和白场之间的像素重新缩放到纯黑和纯白之间。

我们此处默认 ,且图片是 8 位深,共 256 级

尝试一下对黑场和白场的调整会对图片和直方图产生什么影响:

从直方图的比较可以看到,小于黑场的像素被全部压缩到了 0 值,大于白场的像素被全部压缩到了 255,而剩下中间夹着的像素则被均匀拉伸到整个 0 到 255 的范围内。
图片除了动态范围被压缩以外,还损失了暗部和亮部细节。


对于输出,黑场和白场做的是 重映射

简单地来说,就是将原来 0 到 255 范围内的像素值重新映射到 范围。

尝试一下对黑场和白场的调整会对图片和直方图产生什么影响:

从直方图的比较可以看到,整个图片最终的像素直方图被压缩到了黑场和白场之间的区域,图片的动态范围被收窄了,但是相关的细节有所保留。

组合变换

色阶依次组合了输入/输出的黑场/白场变换,最终可以产生如下的变换式:

经过组合变换得到的就是我们所需要的色阶参数控制结果:

光棱藏图

光棱坦克图片意在通过提高曝光度或抬高亮度来显示隐藏图,它们虽然在视觉效果上相似,但是在数学和物理模型上并不相同。

亮度

亮度操作是一种 非物理 的简单偏移操作,用于直接操作图片的整体明暗感觉,一般有三种类型:

加法模型

直接对每个像素加一个偏移量 ,然后钳制数值范围:

这种简单的计算容易造成高光溢出和细节损失。

仿射模型

通过乘法和偏移量在调整亮度,在图像编辑软件中常用(即“亮度/对比度”调节):

用于控制对比度, 用于调整亮度,这种方法相比加法可以减少细节损失。

HSV/HSL 色彩空间调整 V/L 通道

利用这两个色彩空间中天然包含的亮度通道来进行调整:

这种变换保留了色彩的色相和饱和度信息,同时更加符合人眼的感知,但是需要经过色彩空间变换,产生计算复杂度。

曝光度

曝光度调整模拟了真实相机的感光过程,因此这是一种 物理 操作,它是一个线性+指数缩放过程:

需要注意的是,如果图片不是 HDR 或 RAW 直出,那么色彩空间往往是经过 Gamma 调整的非线性空间,而曝光度调整需要在线性空间中,因此这类图片需要先进行逆 Gamma 变换之后,再调整曝光度。

基于亮度/曝光度算法的藏图

以上两种方案对于直方图来说,可以大致看作 把白场调低,让亮部被压缩成纯白,把暗部拉伸填充范围。

因此,对于表图,我们需要 提高黑场 来给里图创造像素空间;对于里图,我们需要将它们压缩到暗部,即 降低白场,这样,当曝光度/亮度抬高时,大部分的表图像素会被压缩到纯白,使得暗部的里图被凸显出来。

那么我们应该怎么融合这两张图呢?

棋盘交错拼图

光棱坦克不像幻影坦克利用多余的透明度通道藏匿信息,而是使用 国际象棋棋盘 排列,交错放置表图和里图的像素。

这样一来,两张图首先可以保持位置重叠,同样又均分了像素信息,保留了足够的分辨率,一台光棱坦克就这样诞生了。

小工具

表图色阶调整
里图色阶调整