使用 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 = 2
和 intercept = -(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
合并到其中
我在后端使用 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 = 2
和 intercept = -(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
合并到其中