在 RGB 图像上模拟彩色滤光片

信息处理 图像处理 过滤器 图片 颜色
2022-02-10 23:07:35

我的问题与这个问题非常相似,但我相信它略有不同,这就是我发布一个新问题的原因。

给定 RGB 格式的图像,我想知道如果在拍摄照片时在镜头上使用了彩色滤镜,是否可以(至少近似地)模拟图像的外观。

我从上面链接的问题的答案中了解到这是不可能的,但我想知道这样的程序是否有意义:

  1. 假设图像中的每个像素都是纯色的叠加,这些纯色具有特定的波长。
  2. 找到导致每种颜色的叠加(例如,RGB 颜色[255 255 255]将是可见光谱中所有波长的叠加)。
  3. 删除我们希望过滤掉的波长。
  4. 生成的图像是近似过滤的图像。

我对第二步有疑问,因为我不知道是否有办法确定哪些纯色会形成每个可能的 RGB 值。如果可以实现,那么我相信我上面描述的程序是有意义的。

  • 是否有可能找到应该叠加哪些波长以实现每个 RGB 值?
  • 如果这是可能的,那么我的方法难道不是一种明智的方法,可以近似模拟在拍照时使用彩色滤光片的效果吗?
1个回答

我的答案是基于Fast RGB to Spectrum Conversion for Reflectances由于 RGB 数据文件中只有 3 个自由度 (DOF),并且在连续光谱中实际上存在无限自由度,因此该映射不会是唯一的。

输出光谱由下式给出: 其中是归一化为的 RGB 三元组范围 0 到 1,以及如下所示。

ρ(λ)=rρR(λ)+gρG(λ)+bρB(λ)
rgbρR(λ)ρG(λ)ρB(λ)

rho_R
0.021592459, 0.020293111, 0.021807906, 0.023803297, 0.025208132,
0.025414957, 0.024621282, 0.020973705, 0.015752802, 0.01116804,
0.008578277, 0.006581877, 0.005171723, 0.004545205, 0.00414512,
0.004343112, 0.005238155, 0.007251939, 0.012543656, 0.028067132,
0.091342277, 0.484081092, 0.870378324, 0.939513128, 0.960926994,
0.968623763, 0.971263883, 0.972285819, 0.971898742, 0.972691859,
0.971734812, 0.972344540, 0.971503390, 0.970857997, 0.970553866, 0.969671404

rho_G
0.010542406, 0.010878976, 0.011063512, 0.010736566, 0.011681813,
0.012434719, 0.014986907, 0.020100392, 0.030356263, 0.063388962,
0.173423837, 0.568321142, 0.827791998, 0.916560468, 0.952002841,
0.964096452, 0.970590861, 0.972502542, 0.969148203, 0.955344651,
0.892637233, 0.500364100, 0.116236717, 0.047951391, 0.027873526, 
0.020057963, 0.017382174, 0.015429109, 0.015438080, 0.014546826,
0.015197773, 0.014285896, 0.015069123, 0.015506263, 0.015545797, 0.016302839

rho_B
0.967865135, 0.968827912, 0.967128582, 0.965460137, 0.963110055,
0.962150324, 0.960391811, 0.958925903, 0.953890935, 0.925442998,
0.817997886, 0.425096960, 0.167036273, 0.078894327, 0.043852038,
0.031560435, 0.024170984, 0.020245519, 0.018308140, 0.016588218, 
0.01602049, 0.0155548080, 0.013384959, 0.012535491, 0.011199484,
0.011318274, 0.011353953, 0.012285073, 0.012663188, 0.012761325,
0.013067426, 0.013369566, 0.013427487, 0.013635740, 0.013893597, 0.014025757

在这些表中,第一个条目是 380 nm,整体之间有 10 nm 步长。

现在您处于频谱域中,您可以执行过滤。

要返回 RGB 域,我通常会进行从频谱到XYZ的转换,然后再到 RGB。我在下面包含了我的代码。有关更多背景信息,您可以访问 Wikipedia CIE 1931 颜色空间页面。

频谱到 XYZ

double piecewise_gaussian(double x, double alpha, double mu, double sigma1,
                          double sigma2) {
    double t = (x - mu) / (x < mu ? sigma1 : sigma2);

    return alpha * std::exp(-(t * t) / 2);
}

color_xyz wavelength_to_xyz(double lambda) {
    color_xyz xyz;

    xyz.c[0] = piecewise_gaussian(lambda, 1.056, 599.8, 37.9, 31.0)
        + piecewise_gaussian(lambda, 0.362, 442.0, 16.0, 26.7) 
        + piecewise_gaussian(lambda, -0.065, 501.1, 20.4, 26.2);

    xyz.c[1] = piecewise_gaussian(lambda, 0.821, 568.8, 46.9, 40.5) 
        + piecewise_gaussian(lambda, 0.286, 530.9, 16.3, 31.1);

    xyz.c[2] = piecewise_gaussian(lambda, 1.217, 437.0, 11.8, 36.0) 
        + piecewise_gaussian(lambda, 0.681, 459.0, 26.0, 13.8);

    return xyz;
}

XYZ 为 RBG

color_rgb xyz_to_rgb(color_xyz xyz) {
    color_rgb rgb;

    rgb.c[0] = 0.4184700 * xyz.c[0] - 0.1586600 * xyz.c[1] - 0.082835 * xyz.c[2];
    rgb.c[1] = -0.0911690 * xyz.c[0] + 0.2524300 * xyz.c[1] + 0.015708 * xyz.c[2];
    rgb.c[2] = 0.0009209 * xyz.c[0] - 0.0025498 * xyz.c[1] + 0.178600 * xyz.c[2];

    return rgb;
}

从上面的 RBG -> 频谱 -> 到 RBG 的完整循环是未归一化的。对于我使用它的应用程序来说,这并不重要。我推荐的合理标准化策略如下:

  1. 从 RGB 白色值 (1, 1, 1) 开始
  2. 转换为等效光谱
  3. 转换回 RGB 空间
  4. 计算新 RGB 值的“亮度”,RGB 值可能不再是所有三个分量相同值的三倍(这就是我使用引号的原因)
  5. 使用新值的计算亮度的倒数作为所有转换的恒定比例因子