使用 sharp (libvips) 实现 css/svg 对比滤镜

Implementing css/svg contrast filter using sharp (libvips)

我在后端使用 libvips 转换图像,在前端使用 css/svg 预览以节省资源。

我正在努力实现对比度 css/svg 过滤功能。

The specification 将对比度显示为以下形式的线性变换:

out = slope * in + intercept

其中 intercept 应该是:

intercept = - (0.5 * slope) + 0.5

这样,我就可以在css图片修改预览中使用contrast(1.25)了。

但是,通过JS库sharp在libvips中实现这个线性函数:

sharp.linear(contrast, - (0.5 * contrast) + 0.5)

深入观察图像的对比度变化,预期结果是高点被放得更高,低点被放得更低。这看起来与规范矛盾,因为规范应用线性变换,所以它应该总是乘法和相加,使高点更高但也使低点更高。

在 sharp 中使用线性(在 libvips 中也是如此)来改变对比度,输出实际上看起来像一个亮度滤镜,在 css/svg 滤镜的规范中是在形式线性变换而不加法

out = slope * in

在我看来,我可能误解了拦截在 svg 线性函数中的作用。此外,比较 svg 和 css 显示差异。在 css 中使用 contrast(2) 应该模仿 svg 中的 slope = 2intercept = -(0.5 * 2) + 0.5 = -0.5,但在 fiddle:

中不是这种情况

.svg {
  filter: url(#contrast);
}

.css {
  filter: contrast(2);
}
<img src="https://dev-cdn.swbpg.com/o/g/1515254671.jpeg" width="300">
<img class="svg" src="https://dev-cdn.swbpg.com/o/g/1515254671.jpeg" width="300">
<img class="css" src="https://dev-cdn.swbpg.com/o/g/1515254671.jpeg" width="300">

<svg>
  <filter id="contrast">
    <feComponentTransfer>
      <feFuncR type="linear" slope="2" intercept="-0.5"/>
      <feFuncG type="linear" slope="2" intercept="-0.5"/>
      <feFuncB type="linear" slope="2" intercept="-0.5"/>
    </feComponentTransfer>
  </filter>
</svg>

您可以清楚地看到使用 svg 滤镜的第二张图像与使用 css 滤镜的第三张图像看起来不同。

我对过滤器的理解完全错误吗?我希望某处应该有一些阈值将乘法转换为低点的除法。

如何将不同环境中的 css 对比度实现为具有相同结果的线性函数?

您的直觉不正确 :) 对于小于 0.5 的输入值 - 公式会降低亮度 - 为什么?让我们取对比度值为 2,输入值为 0.4

输出 = 2*0.4 - (0.5 *2) + 0.5

输出 = 0.8 - 1 + 0.5

输出=0.3

如您所见,当输入低于 0.5 时,输出将始终小于输入,因为斜率分量和截距的第一个(负)分量之和将等于对比度乘以输入和0.5之间的差异

这是统一值的公式结果(下限和上限为 0/1)。

另外 CSS 滤镜默认使用 sRGB 颜色 space。 SVG 过滤器使用 linearRGB。您需要通过添加属性将 SVG 滤镜颜色 space 设置为 sRGB:color-interpolation-filters="sRGB" 到您的 svg 元素。当你这样做时 - 你的图像看起来是一样的。

.svg {
  filter: url(#contrast);
}

.css {
  filter: contrast(2);
}
<img src="https://dev-cdn.swbpg.com/o/g/1515254671.jpeg" width="300">
<img class="svg" src="https://dev-cdn.swbpg.com/o/g/1515254671.jpeg" width="300">
<img class="css" src="https://dev-cdn.swbpg.com/o/g/1515254671.jpeg" width="300">

<svg color-interpolation-filters="sRGB">
  <filter id="contrast">
    <feComponentTransfer>
      <feFuncR type="linear" slope="2" intercept="-0.5"/>
      <feFuncG type="linear" slope="2" intercept="-0.5"/>
      <feFuncB type="linear" slope="2" intercept="-0.5"/>
    </feComponentTransfer>
  </filter>
</svg>

https://github.com/lovell/sharp/issues/1958

这里有一个来自 css 的工作公式:

filter: `contrast(${contrast})`

锐化:

brightness = 1;
image.linear(brightness * constrast, brightness * (-(128 * contrast) + 128));

尽管如此,我还没有弄清楚如何将 brightness 合并到其中