为什么我的 SVG feTurbulence 输出中有细黑线?
Why are there thin, dark lines in my SVG feTurbulence output?
在试验 feTurbulence
滤镜原语时,我在意想不到的地方出现了细细的暗线。它们在 numOctaves="1"
时最为明显。他们为什么在那里?
假设我从 https://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement 中的参考代码开始(修复它以便编译)。我称之为
turbulence(
0, /* color channel */,
point, /* {x,y} */
1.0, 1.0, /* fBaseFreqX and Y */
1, /* numOctaves */
0, /* bFractalSum */
0, /* bDoStitching */
0.0, 0.0, /* fTileX and Y */
0.0, 0.0, /* fTileWidth and Height */
)
(我的完整资源可在 https://gitlab.com/AlanDeSmet/svg-1.1-feturbulence 获得)
从 0.0 到 10.0 迭代 x 和 y,获取 300 个样本,并将每个样本乘以 256 创建一个 300x300 灰度图像:
这是我希望看到的。它看起来类似于
等程序生成的 Perlin 湍流
Adobe Flash (source):
3ds Max (source):
但是,如果我使用 feTurbulence 创建 SVG 并在 Firefox、Chromium 或 Inkscape(我相信这是 3 个独立的实现)中查看它,我会得到:
来源:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg height="10" width="10" version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<filter color-interpolation-filters="sRGB"
id="test-turbulence" x="0" y="0" width="1" height="1">
<feTurbulence type="turbulence" numOctaves="1" baseFrequency="1" />
<feColorMatrix
values="1 0 0 0 0
1 0 0 0 0
1 0 0 0 0
0 0 0 0 1 " />
</filter>
<rect width="10" height="10" style="filter:url(#test-turbulence)" x="0" y="0" />
</svg>
(我正在使用 color-interpolation-filters="sRGB"
来更接近地匹配我的简单程序的输出。它不会改变结构,它只是使图像“变暗”。
图像中有细的暗线,这是我没有预料到的。这是并排比较;我在左侧(或上方)使用标准参考实现,在右侧(或下方)使用 Chromium 的输出(看起来与 Firefox 和 Inkscape 相同)。
这似乎符合标准,因为三个不同的渲染器都同意,但我认为这不是标准参考实现所做的,也不是其他一些程序所做的。
为什么我尝试使用标准的参考实现与 Firefox、Chrome 和 Inkscape 所做的有所不同?该标准是否应该与为 Perlin 湍流实施的其他程序不同?如果有,有什么区别?
这不是所使用的数学。这是Chromium源码的相关注释:
/**
About the noise types : the difference between the first 2 is just minor tweaks to the
algorithm, they're not 2 entirely different noises. The output looks different, but once the
noise is generated in the [1, -1] range, the output is brought back in the [0, 1] range by doing :
* kFractalNoise_Type : noise * 0.5 + 0.5
* kTurbulence_Type : abs(noise)
Very little differences between the 2 types, although you can tell the difference visually.
好吧,我不知道它是否有帮助,但是当您将所有位置的不透明度设置为 1 时,这些线程的不透明度区域为零或接近零,这些区域将转换为完全不透明。您可以通过添加绿色背景并将 alpha 乘以 128 来看到它。
svg{
background: green;
}
<svg height="600px" width="800px" viewBox="0 0 800 600">
<filter color-interpolation-filters="sRGB"
id="test-turbulence" x="0%" y="0%" width="100%" height="100%">
<feTurbulence type="turbulence" numOctaves="1" baseFrequency=".02" />
<feColorMatrix
values="1 0 0 0 0
1 0 0 0 0
1 0 0 0 0
0 0 0 128 0 " />
</filter>
<rect width="800" height="600" filter="url(#test-turbulence)" x="0" y="0" />
</svg>
总结
这是预期的行为。 您可以使用 alpha 来避免它
湍流通道,而不是任何颜色通道。
您可以使用 feColorMatrix
从 alpha 通道创建灰度湍流:
<filter id="turbulence-alpha" x="0" y="0" width="1" height="1">
<feTurbulence type="turbulence" baseFrequency="0.02" />
<feColorMatrix
values="0 0 0 1 0
0 0 0 1 0
0 0 0 1 0
0 0 0 0 1 " />
</filter>
但是为什么?
该行为令人惊讶,但它符合 SVG 规范并且可能是
正确用于某些用途。
意外的线条来自 alpha 通道,尽管已将其丢弃!
例如,这里是所有四个频道。观察线程跨越所有
颜色通道并与接近零的部分匹配
alpha 通道。
(用于生成它的 SVG 在下面的“Alpha Comparison SVG”下。)
SVG specification (backup link) 说:
Unless otherwise stated, all image filters operate on premultiplied RGBA
samples. Filters which work more naturally on non-premultiplied data
(feColorMatrix and feComponentTransfer) will temporarily undo and redo
premultiplication as specified.
“预乘”在这种情况下是指预乘 alpha,其中
颜色通道由 alpha 调整。预乘是好的,因为它
允许合成和过滤正常工作。它发生在
场景,你可以忽略它...除非你修改 alpha 通道。
问题是预乘丢失数据。而当 alpha 值
approach 0(完全透明),数据丢失特别严重。什么时候
feColorMatrix 或 feComponentTransfer "暂时撤消和重做
premultiplication", 撤销操作只是一个近似值。那个数据
损失表现为整个图像中的意外线条。
例如,给定一个颜色通道为
的输入图像
其alpha通道为
颜色通道的预乘版本将是
尝试撤消预乘会产生以下结果:
整个图像都有损坏(略低于 50% 的像素不匹配),
但当 alpha 接近于零时,与原始版本的差异最为显着。
(这些图像是由下面“比较图像生成器”下的 Python 代码创建的。premul_alpha
和 unpremul_alpha
基于
Inkscape's implementation)
type="fractalNoise"
呢?
以上都适用于<feTurbulence type="fractalNoise">
,那为什么没有问题呢?
因为 <feTurbulence type="fractalNoise" numOctaves="1">
是原始 Perlin 2d 噪声,并且 Perlin noise is in the range −0.707 through 0.707 (backup link)。它被视为 −1 到 1 的范围。将该范围重新映射到 0 到 255,所有值最终都在 37 到 217 之间。损坏存在,但是因为 alpha 永远不够接近 0,所以您看不到它。
它在 type="turbulence"
时变得可见,因为 Perlin 湍流使用原始噪声的绝对值。因此范围变为 0.000 到 0.707,最终在 0 到 217 范围内。这也是为什么 fractalNoise
没有任何纯黑色而 turbulence
有(以及为什么两者都没有任何纯白色)的原因。
(这方面的来源在下面的“湍流与噪声”中。)
脚注
Alpha 比较 SVG
这是比较 feTurbulence
发出的四个通道的 SVG。
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="230"
width="800"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<filter id="turbulence-red" x="0" y="0" width="1" height="1">
<feTurbulence type="turbulence" baseFrequency="0.02" />
<feColorMatrix
values="1 0 0 0 0
1 0 0 0 0
1 0 0 0 0
0 0 0 0 1 " />
</filter>
<filter id="turbulence-green" x="0" y="0" width="1" height="1">
<feTurbulence type="turbulence" baseFrequency="0.02" />
<feColorMatrix
values="0 1 0 0 0
0 1 0 0 0
0 1 0 0 0
0 0 0 0 1 " />
</filter>
<filter id="turbulence-blue" x="0" y="0" width="1" height="1">
<feTurbulence type="turbulence" baseFrequency="0.02" />
<feColorMatrix
values="0 0 1 0 0
0 0 1 0 0
0 0 1 0 0
0 0 0 0 1 " />
</filter>
<filter id="turbulence-alpha" x="0" y="0" width="1" height="1">
<feTurbulence type="turbulence" baseFrequency="0.02" />
<feColorMatrix
values="0 0 0 1 0
0 0 0 1 0
0 0 0 1 0
0 0 0 0 1 " />
</filter>
<text x="100" y="220" text-anchor="middle">Red Channel</text>
<rect x="0" y="0" width="200" height="200"
style="filter:url(#turbulence-red)" />
<text x="300" y="220" text-anchor="middle">Green Channel</text>
<rect x="200" y="0" width="200" height="200"
style="filter:url(#turbulence-green)" />
<text x="500" y="220" text-anchor="middle">Blue Channel</text>
<rect x="400" y="0" width="200" height="200"
style="filter:url(#turbulence-blue)" />
<text x="700" y="220" text-anchor="middle">Alpha Channel</text>
<rect x="600" y="0" width="200" height="200"
style="filter:url(#turbulence-alpha)" />
</svg>
比较图像生成器
此代码生成了上面的四个正方形示例图像。
#! /usr/bin/python3
from PIL import Image
def premul_alpha(color,alpha):
temp = alpha * color + 128
res = (temp + (temp >> 8)) >> 8
return res
def unpremul_alpha(color, alpha):
if alpha == 0: return color # Nonsensical operation
res = int((255 * color + alpha/2) / alpha)
return res
originalimg = Image.new("L",(256,256))
original_px = originalimg.load()
alphaimg = Image.new("L",(256,256))
alpha_px = alphaimg.load()
premulimg = Image.new("L",(256,256))
premul_px = premulimg.load()
restoredimg = Image.new("L",(256,256))
restored_px = restoredimg.load()
damagedimg = Image.new("L",(256,256),0)
damaged_px = damagedimg.load()
total = 0
dmg_count =0
for color in range(256):
for alpha in range(0,256):
original_px[color,alpha] = color;
alpha_px[color,alpha] = alpha;
during = premul_alpha(color,alpha)
premul_px[color,alpha] = during
restored = unpremul_alpha(during,alpha)
restored_px[color,alpha] = restored
total += 1
if restored != color:
dmg_count += 1
damaged_px[color,alpha] = 255
print(f"{dmg_count}/{total} -> {dmg_count/total}")
originalimg.save("original.png")
alphaimg.save("alpha.png")
premulimg.save("premul.png")
restoredimg.save("restored.png")
damagedimg.save("damaged.png")
湍流与噪声
噪音:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="200"
width="200"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<filter id="turbulence-alpha" x="0" y="0" width="1" height="1">
<feTurbulence type="fractalNoise" baseFrequency="0.02" />
<feColorMatrix
values="0 0 0 1 0
0 0 0 1 0
0 0 0 1 0
0 0 0 0 1 " />
</filter>
<rect x="0" y="0" width="200" height="200"
style="filter:url(#turbulence-alpha)" />
</svg>
湍流:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="200"
width="200"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<filter id="turbulence-alpha" x="0" y="0" width="1" height="1">
<feTurbulence type="turbulence" baseFrequency="0.02" />
<feColorMatrix
values="0 0 0 1 0
0 0 0 1 0
0 0 0 1 0
0 0 0 0 1 " />
</filter>
<rect x="0" y="0" width="200" height="200"
style="filter:url(#turbulence-alpha)" />
</svg>
在试验 feTurbulence
滤镜原语时,我在意想不到的地方出现了细细的暗线。它们在 numOctaves="1"
时最为明显。他们为什么在那里?
假设我从 https://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement 中的参考代码开始(修复它以便编译)。我称之为
turbulence(
0, /* color channel */,
point, /* {x,y} */
1.0, 1.0, /* fBaseFreqX and Y */
1, /* numOctaves */
0, /* bFractalSum */
0, /* bDoStitching */
0.0, 0.0, /* fTileX and Y */
0.0, 0.0, /* fTileWidth and Height */
)
(我的完整资源可在 https://gitlab.com/AlanDeSmet/svg-1.1-feturbulence 获得)
从 0.0 到 10.0 迭代 x 和 y,获取 300 个样本,并将每个样本乘以 256 创建一个 300x300 灰度图像:
这是我希望看到的。它看起来类似于
等程序生成的 Perlin 湍流Adobe Flash (source):
3ds Max (source):
但是,如果我使用 feTurbulence 创建 SVG 并在 Firefox、Chromium 或 Inkscape(我相信这是 3 个独立的实现)中查看它,我会得到:
来源:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg height="10" width="10" version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<filter color-interpolation-filters="sRGB"
id="test-turbulence" x="0" y="0" width="1" height="1">
<feTurbulence type="turbulence" numOctaves="1" baseFrequency="1" />
<feColorMatrix
values="1 0 0 0 0
1 0 0 0 0
1 0 0 0 0
0 0 0 0 1 " />
</filter>
<rect width="10" height="10" style="filter:url(#test-turbulence)" x="0" y="0" />
</svg>
(我正在使用 color-interpolation-filters="sRGB"
来更接近地匹配我的简单程序的输出。它不会改变结构,它只是使图像“变暗”。
图像中有细的暗线,这是我没有预料到的。这是并排比较;我在左侧(或上方)使用标准参考实现,在右侧(或下方)使用 Chromium 的输出(看起来与 Firefox 和 Inkscape 相同)。
这似乎符合标准,因为三个不同的渲染器都同意,但我认为这不是标准参考实现所做的,也不是其他一些程序所做的。
为什么我尝试使用标准的参考实现与 Firefox、Chrome 和 Inkscape 所做的有所不同?该标准是否应该与为 Perlin 湍流实施的其他程序不同?如果有,有什么区别?
这不是所使用的数学。这是Chromium源码的相关注释:
/** About the noise types : the difference between the first 2 is just minor tweaks to the algorithm, they're not 2 entirely different noises. The output looks different, but once the noise is generated in the [1, -1] range, the output is brought back in the [0, 1] range by doing :
* kFractalNoise_Type : noise * 0.5 + 0.5 * kTurbulence_Type : abs(noise)
Very little differences between the 2 types, although you can tell the difference visually.
好吧,我不知道它是否有帮助,但是当您将所有位置的不透明度设置为 1 时,这些线程的不透明度区域为零或接近零,这些区域将转换为完全不透明。您可以通过添加绿色背景并将 alpha 乘以 128 来看到它。
svg{
background: green;
}
<svg height="600px" width="800px" viewBox="0 0 800 600">
<filter color-interpolation-filters="sRGB"
id="test-turbulence" x="0%" y="0%" width="100%" height="100%">
<feTurbulence type="turbulence" numOctaves="1" baseFrequency=".02" />
<feColorMatrix
values="1 0 0 0 0
1 0 0 0 0
1 0 0 0 0
0 0 0 128 0 " />
</filter>
<rect width="800" height="600" filter="url(#test-turbulence)" x="0" y="0" />
</svg>
总结
这是预期的行为。 您可以使用 alpha 来避免它 湍流通道,而不是任何颜色通道。
您可以使用 feColorMatrix
从 alpha 通道创建灰度湍流:
<filter id="turbulence-alpha" x="0" y="0" width="1" height="1">
<feTurbulence type="turbulence" baseFrequency="0.02" />
<feColorMatrix
values="0 0 0 1 0
0 0 0 1 0
0 0 0 1 0
0 0 0 0 1 " />
</filter>
但是为什么?
该行为令人惊讶,但它符合 SVG 规范并且可能是 正确用于某些用途。
意外的线条来自 alpha 通道,尽管已将其丢弃! 例如,这里是所有四个频道。观察线程跨越所有 颜色通道并与接近零的部分匹配 alpha 通道。
(用于生成它的 SVG 在下面的“Alpha Comparison SVG”下。)
SVG specification (backup link) 说:
Unless otherwise stated, all image filters operate on premultiplied RGBA samples. Filters which work more naturally on non-premultiplied data (feColorMatrix and feComponentTransfer) will temporarily undo and redo premultiplication as specified.
“预乘”在这种情况下是指预乘 alpha,其中 颜色通道由 alpha 调整。预乘是好的,因为它 允许合成和过滤正常工作。它发生在 场景,你可以忽略它...除非你修改 alpha 通道。
问题是预乘丢失数据。而当 alpha 值 approach 0(完全透明),数据丢失特别严重。什么时候 feColorMatrix 或 feComponentTransfer "暂时撤消和重做 premultiplication", 撤销操作只是一个近似值。那个数据 损失表现为整个图像中的意外线条。
例如,给定一个颜色通道为
的输入图像其alpha通道为
颜色通道的预乘版本将是
尝试撤消预乘会产生以下结果:
整个图像都有损坏(略低于 50% 的像素不匹配), 但当 alpha 接近于零时,与原始版本的差异最为显着。
(这些图像是由下面“比较图像生成器”下的 Python 代码创建的。premul_alpha
和 unpremul_alpha
基于
Inkscape's implementation)
type="fractalNoise"
呢?
以上都适用于<feTurbulence type="fractalNoise">
,那为什么没有问题呢?
因为 <feTurbulence type="fractalNoise" numOctaves="1">
是原始 Perlin 2d 噪声,并且 Perlin noise is in the range −0.707 through 0.707 (backup link)。它被视为 −1 到 1 的范围。将该范围重新映射到 0 到 255,所有值最终都在 37 到 217 之间。损坏存在,但是因为 alpha 永远不够接近 0,所以您看不到它。
它在 type="turbulence"
时变得可见,因为 Perlin 湍流使用原始噪声的绝对值。因此范围变为 0.000 到 0.707,最终在 0 到 217 范围内。这也是为什么 fractalNoise
没有任何纯黑色而 turbulence
有(以及为什么两者都没有任何纯白色)的原因。
(这方面的来源在下面的“湍流与噪声”中。)
脚注
Alpha 比较 SVG
这是比较 feTurbulence
发出的四个通道的 SVG。
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="230"
width="800"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<filter id="turbulence-red" x="0" y="0" width="1" height="1">
<feTurbulence type="turbulence" baseFrequency="0.02" />
<feColorMatrix
values="1 0 0 0 0
1 0 0 0 0
1 0 0 0 0
0 0 0 0 1 " />
</filter>
<filter id="turbulence-green" x="0" y="0" width="1" height="1">
<feTurbulence type="turbulence" baseFrequency="0.02" />
<feColorMatrix
values="0 1 0 0 0
0 1 0 0 0
0 1 0 0 0
0 0 0 0 1 " />
</filter>
<filter id="turbulence-blue" x="0" y="0" width="1" height="1">
<feTurbulence type="turbulence" baseFrequency="0.02" />
<feColorMatrix
values="0 0 1 0 0
0 0 1 0 0
0 0 1 0 0
0 0 0 0 1 " />
</filter>
<filter id="turbulence-alpha" x="0" y="0" width="1" height="1">
<feTurbulence type="turbulence" baseFrequency="0.02" />
<feColorMatrix
values="0 0 0 1 0
0 0 0 1 0
0 0 0 1 0
0 0 0 0 1 " />
</filter>
<text x="100" y="220" text-anchor="middle">Red Channel</text>
<rect x="0" y="0" width="200" height="200"
style="filter:url(#turbulence-red)" />
<text x="300" y="220" text-anchor="middle">Green Channel</text>
<rect x="200" y="0" width="200" height="200"
style="filter:url(#turbulence-green)" />
<text x="500" y="220" text-anchor="middle">Blue Channel</text>
<rect x="400" y="0" width="200" height="200"
style="filter:url(#turbulence-blue)" />
<text x="700" y="220" text-anchor="middle">Alpha Channel</text>
<rect x="600" y="0" width="200" height="200"
style="filter:url(#turbulence-alpha)" />
</svg>
比较图像生成器
此代码生成了上面的四个正方形示例图像。
#! /usr/bin/python3
from PIL import Image
def premul_alpha(color,alpha):
temp = alpha * color + 128
res = (temp + (temp >> 8)) >> 8
return res
def unpremul_alpha(color, alpha):
if alpha == 0: return color # Nonsensical operation
res = int((255 * color + alpha/2) / alpha)
return res
originalimg = Image.new("L",(256,256))
original_px = originalimg.load()
alphaimg = Image.new("L",(256,256))
alpha_px = alphaimg.load()
premulimg = Image.new("L",(256,256))
premul_px = premulimg.load()
restoredimg = Image.new("L",(256,256))
restored_px = restoredimg.load()
damagedimg = Image.new("L",(256,256),0)
damaged_px = damagedimg.load()
total = 0
dmg_count =0
for color in range(256):
for alpha in range(0,256):
original_px[color,alpha] = color;
alpha_px[color,alpha] = alpha;
during = premul_alpha(color,alpha)
premul_px[color,alpha] = during
restored = unpremul_alpha(during,alpha)
restored_px[color,alpha] = restored
total += 1
if restored != color:
dmg_count += 1
damaged_px[color,alpha] = 255
print(f"{dmg_count}/{total} -> {dmg_count/total}")
originalimg.save("original.png")
alphaimg.save("alpha.png")
premulimg.save("premul.png")
restoredimg.save("restored.png")
damagedimg.save("damaged.png")
湍流与噪声
噪音:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="200"
width="200"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<filter id="turbulence-alpha" x="0" y="0" width="1" height="1">
<feTurbulence type="fractalNoise" baseFrequency="0.02" />
<feColorMatrix
values="0 0 0 1 0
0 0 0 1 0
0 0 0 1 0
0 0 0 0 1 " />
</filter>
<rect x="0" y="0" width="200" height="200"
style="filter:url(#turbulence-alpha)" />
</svg>
湍流:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="200"
width="200"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<filter id="turbulence-alpha" x="0" y="0" width="1" height="1">
<feTurbulence type="turbulence" baseFrequency="0.02" />
<feColorMatrix
values="0 0 0 1 0
0 0 0 1 0
0 0 0 1 0
0 0 0 0 1 " />
</filter>
<rect x="0" y="0" width="200" height="200"
style="filter:url(#turbulence-alpha)" />
</svg>