【问题标题】:Fast formula for a "high contrast" curve“高对比度”曲线的快速公式
【发布时间】:2010-12-01 14:05:06
【问题描述】:

我的内部循环包含一个分析显示有问题的计算。

这个想法是采用灰度像素 x (0

  • 对于 x
  • 对于 x > .5,x
  • f(0) = 0
  • f(x) = 1 - f(1 - x),即它应该是“对称的”
  • 最好是函数流畅。

所以图表必须看起来像这样:

.

我有两个实现(它们的结果不同但都是一致的):

float cosContrastize(float i) {
    return .5 - cos(x * pi) / 2;
}

float mulContrastize(float i) {
    if (i < .5) return i * i * 2;
    i = 1 - i;
    return 1 - i * i * 2;
}

因此,我请求对这些实现之一进行微优化,或者您自己的原始、更快的公式。

也许你们中的一个人甚至可以旋转这些位;)

【问题讨论】:

  • 如果您能告诉我们您使用的是什么语言(我假设是 Java)以及所涉及的编译器/运行时是什么,也许我们可以为您提供更好的帮助。
  • 带有 MS 编译器和运行时的 C#,但如果我发现需要,我愿意用 C++ 重写关键算法...
  • C# 带有驼峰式方法名称? :(
  • 许多这样的函数在以零为中心时更快/更简单。我不确定你程序的其余部分,但如果这种代码很关键,你可以考虑重新定位你的数据表示以从 -1..1 而不是 0..1 缩放。

标签: optimization math formula


【解决方案1】:

考虑以下 sigmoid 形函数(正确转换为所需范围):


我使用 MATLAB 生成了上图。如果有兴趣这里的代码:

x = -3:.01:3;
plot(   x, 2*(x>=0)-1, ...
        x, erf(x), ...
        x, tanh(x), ...
        x, 2*normcdf(x)-1, ...
        x, 2*(1 ./ (1 + exp(-x)))-1, ...
        x, 2*((x-min(x))./range(x))-1  )
legend({'hard' 'erf' 'tanh' 'normcdf' 'logit' 'linear'})

【讨论】:

  • OP 的主要问题是速度。这些如何加快速度?
  • 谢谢,至少我知道它们现在被称为“sigmoid”;)我用 tanh 做了一个简单的实现,它和 cos 一样快。其余的需要更多的思考,我认为它们会慢一些,但我们会看到。
  • ++ 不错。就个人而言,我倾向于 logit(实际上是逆 logit 函数),因为您只需调用一次 exp() 和一个除法。您可以通过缩放 X 使其更清晰。
  • @tom10,OP 可以看到使用的公式,并考虑哪些套件最适合。
【解决方案2】:

您可以简单地设置阈值,但我认为这太愚蠢了:

return i < 0.5 ? 0.0 : 1.0;

由于您提到“增加对比度”,我假设输入值是亮度值。如果是这样,并且它们是离散的(可能是 8 位值),您可以使用查找表快速完成此操作。

您的“mulContrastize”看起来相当快。一种优化是使用整数数学。再说一次,您的输入值实际上可以作为 [0..255] 中的 8 位无符号值传递。 (再次,可能是一个很好的假设?)你可以做一些大致像......

int mulContrastize(int i) {
  if (i < 128) return (i * i) >> 7; 
  // The shift is really: * 2 / 256
  i = 255 - i;
  return 255 - ((i * i) >> 7);

【讨论】:

  • 阈值离平滑太远,无法在我的情况下使用。它们是亮度值,是的。它们不是离散值——它们实际上是浮点数,有两个原因。首先,在 OpenGL 中,浮动纹理是最快的。其次,我决定使用 0.0-1.0 浮点数来使我的数学变得简单快。但我从没想过与查找表进行对比,我会研究一下,看看它是否超过了 OpenGL 纹理问题。您发布的实现确实不错,但不如查找表好。而且我的 mulContrastize 确实“相当快”,但不是在如此紧密的内部循环中:)
  • 顺便说一句,你不应该除以 255 两次,一次。所以你应该移动 7 点。
  • 哎呀,你是对的,这标准化了一步太远了。将修复示例。
  • ++ 用于查找表建议。
  • 是的,我最终使用了查找表。就其本身而言,它的改进为零。但它允许我进行其他一些优化 - 所以 - 谢谢!
【解决方案3】:

分段插值可以快速灵活。它只需要几个决定,然后是乘法和加法,并且可以逼近任何曲线。它还避免了查找表可能引入的过程(或两次查找中的额外成本,然后进行插值以消除这种情况),尽管 lut 可能非常适合您的情况。

只需几个片段,您就可以获得非常好的匹配。这里会有颜色 渐变 中的路线,这将比绝对颜色中的路线更难检测。

正如 Eamon Nerbonne 在 cmets 中指出的那样,可以通过“根据诸如二阶导数之类的东西来选择分割点以最大化细节”来优化分割,即斜率变化最大的地方。显然,在我发布的示例中,在五段案例的中间放置三个段并不会增加更多细节。

【讨论】:

  • 如果你真的很时髦,你可以根据像二阶导数这样的东西来选择你的分割点,以最大化细节(在中心、相当笔直的段中区分段没有意义)。跨度>
  • @Eamon:感谢二次衍生的想法。我知道我对中心点很懒惰,但我真的很喜欢对二阶导数的概括。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-22
  • 2017-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-14
相关资源
最近更新 更多