为什么我的 SVG feTurbulence 输出中存在细黑线?

Ala*_*met 5 svg perlin-noise svg-filters

当尝试使用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 */
            )
Run Code Online (Sandbox Code Playgroud)

(我的完整来源可在https://gitlab.com/AlanDeSmet/svg-1.1-feturbulence获取)

从 0.0 到 10.0 迭代 x 和 y,获取 300 个样本,并将每个样本乘以 256 创建一个 300x300 灰度图像:

在此输入图像描述

这就是我期望看到的。它看起来类似于由程序产生的柏林湍流,例如

Adobe Flash(来源):在此输入图像描述

3ds Max(来源):在此输入图像描述

但是,如果我使用 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>
Run Code Online (Sandbox Code Playgroud)

(我用来color-interpolation-filters="sRGB"更紧密地匹配我的简单程序的输出。它不会改变结构,只是使图像“变暗”。

整个图像中有细密的暗线,这是我没有预料到的。这是并排比较;我使用左侧(或上方)的标准参考实现,以及右侧(或下方)的 Chromium 输出(看起来与 Firefox 和 Inkscape 相同)。

在此输入图像描述 在此输入图像描述

由于三个不同的渲染器都同意,它似乎可能是正确的标准,但这不是我认为标准参考实现所做的事情,也不是其他一些程序所做的事情。

为什么我尝试使用标准的参考实现与 Firefox、Chrome 和 Inkscape 的做法存在差异?该标准是否应该与针对柏林湍流实施的其他计划有所不同?如果是这样,有什么区别?

Ala*_*met 5

概括

\n

这是预期的行为。您可以通过使用 alpha\n通道来避免湍流,而不是任何颜色通道。

\n

您可以使用feColorMatrixAlpha 通道创建灰度湍流:

\n
<filter id="turbulence-alpha" x="0" y="0" width="1" height="1">\n  <feTurbulence type="turbulence" baseFrequency="0.02" />\n  <feColorMatrix\n     values="0 0 0 1 0\n             0 0 0 1 0\n             0 0 0 1 0\n             0 0 0 0 1 " />\n</filter>\n
Run Code Online (Sandbox Code Playgroud)\n

但为什么?

\n

该行为令人惊讶,但它符合 SVG 规范,并且\n对于某些用途可能是正确的。

\n

意外的线条来自 Alpha 通道,尽管已丢弃它!\n例如,这里是所有四个通道。观察到线程跨越所有颜色通道并与 Alpha 通道的接近零的部分完全匹配。

\n

在此输入图像描述

\n

(用于生成此内容的 SVG 位于下面的“Alpha Comparison SVG”下。)

\n

SVG 规范(备份链接)说:

\n
\n

除非另有说明,所有图像过滤器都对预乘的 RGBA\n样本进行操作。对非预乘数据(feColorMatrix 和 feComponentTransfer)更自然地工作的过滤器将根据指定暂时撤消和重做预乘。

\n
\n

在这种情况下,“预乘”指的是预乘 Alpha,其中颜色通道由 Alpha 进行调整。预乘很好,因为它可以使合成和过滤正常工作。它发生在场景后面,您可以忽略它...除非您修改 Alpha 通道。

\n

问题是预乘会丢失数据。当 alpha 值接近 0(完全透明)时,数据丢失尤其严重。当feColorMatrix或feComponentTransfer“暂时撤消和重做预乘”时,撤消操作只是一个近似值。该数据丢失表现为整个图像中出现意外的线条。

\n

例如,给定一个输入图像,其颜色通道为

\n

在此输入图像描述

\n

其 alpha 通道是

\n

在此输入图像描述

\n

颜色通道的预乘版本将是

\n

在此输入图像描述

\n

尝试撤销预乘会产生以下结果:

\n

在此输入图像描述

\n

整个图像都有损坏(略低于 50% 的像素不匹配),\n但当 Alpha 接近零时,与原始图像的差异最为显着。

\n

(这些图像是由下面的“比较图像生成器”下的 Python 代码创建的。premul_alphaunpremul_alpha基于
\n Inkscape\ 的实现

\n

关于什么type="fractalNoise"

\n

上述所有内容都适用于<feTurbulence type="fractalNoise">,那么为什么它不是一个问题呢?

\n

因为<feTurbulence type="fractalNoise" numOctaves="1">是原始 Perlin 2d 噪声,而Perlin 噪声的范围为 \xe2\x88\x920.707 到 0.707备份链接)。它被视为 \xe2\x88\x921 到 1 的范围。将该范围重新映射到 0 到 255,所有值最终都在 37 到 217 之间。损坏是存在的,但因为 alpha 永远不够接近 0,所以您看不到它。

\n

它变得可见,type="turbulence"因为柏林湍流使用原始噪声的绝对值。因此范围变为 0.000 到 0.707,最终范围为 0 到 217。这也是为什么fractalNoise没有纯黑色而turbulence有的原因(以及为什么两者都没有纯白色)。

\n

在此输入图像描述\n在此输入图像描述

\n

(其来源在下面的“湍流与噪声”中。)

\n

脚注

\n

Alpha 比较 SVG

\n

这是比较 发出的四个通道的 SVG feTurbulence

\n
<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n<svg\n   height="230"\n   width="800"\n   version="1.1"\n   xmlns="http://www.w3.org/2000/svg"\n   xmlns:svg="http://www.w3.org/2000/svg">\n  <filter id="turbulence-red" x="0" y="0" width="1" height="1">\n    <feTurbulence type="turbulence" baseFrequency="0.02" />\n    <feColorMatrix\n       values="1 0 0 0 0\n               1 0 0 0 0\n               1 0 0 0 0\n               0 0 0 0 1 " />\n  </filter>\n  <filter id="turbulence-green" x="0" y="0" width="1" height="1">\n    <feTurbulence type="turbulence" baseFrequency="0.02" />\n    <feColorMatrix\n       values="0 1 0 0 0\n               0 1 0 0 0\n               0 1 0 0 0\n               0 0 0 0 1 " />\n  </filter>\n  <filter id="turbulence-blue" x="0" y="0" width="1" height="1">\n    <feTurbulence type="turbulence" baseFrequency="0.02" />\n    <feColorMatrix\n       values="0 0 1 0 0\n               0 0 1 0 0\n               0 0 1 0 0\n               0 0 0 0 1 " />\n  </filter>\n  <filter id="turbulence-alpha" x="0" y="0" width="1" height="1">\n    <feTurbulence type="turbulence" baseFrequency="0.02" />\n    <feColorMatrix\n       values="0 0 0 1 0\n               0 0 0 1 0\n               0 0 0 1 0\n               0 0 0 0 1 " />\n  </filter>\n  <text x="100" y="220" text-anchor="middle">Red Channel</text>\n  <rect x="0" y="0" width="200" height="200"\n     style="filter:url(#turbulence-red)" />\n\n  <text x="300" y="220" text-anchor="middle">Green Channel</text>\n  <rect x="200" y="0" width="200" height="200"\n     style="filter:url(#turbulence-green)" />\n\n  <text x="500" y="220" text-anchor="middle">Blue Channel</text>\n  <rect x="400" y="0" width="200" height="200"\n     style="filter:url(#turbulence-blue)" />\n\n  <text x="700" y="220" text-anchor="middle">Alpha Channel</text>\n  <rect x="600" y="0" width="200" height="200"\n     style="filter:url(#turbulence-alpha)" />\n</svg>\n\n
Run Code Online (Sandbox Code Playgroud)\n

比较图像生成器

\n

此代码生成了上面的四个方形示例图像。

\n
#! /usr/bin/python3\n\nfrom PIL import Image\n\ndef premul_alpha(color,alpha):\n    temp = alpha * color + 128\n    res = (temp + (temp >> 8)) >> 8\n    return res\n\ndef unpremul_alpha(color, alpha):\n    if alpha == 0: return color # Nonsensical operation\n    res = int((255 * color + alpha/2) / alpha)\n    return res\n\noriginalimg = Image.new("L",(256,256))\noriginal_px = originalimg.load()\nalphaimg = Image.new("L",(256,256))\nalpha_px = alphaimg.load()\npremulimg = Image.new("L",(256,256))\npremul_px = premulimg.load()\nrestoredimg = Image.new("L",(256,256))\nrestored_px = restoredimg.load()\ndamagedimg = Image.new("L",(256,256),0)\ndamaged_px = damagedimg.load()\n\n\ntotal = 0\ndmg_count =0\nfor color in range(256):\n    for alpha in range(0,256):\n        original_px[color,alpha] = color;\n        alpha_px[color,alpha] = alpha;\n        during  = premul_alpha(color,alpha)\n        premul_px[color,alpha] = during\n        restored = unpremul_alpha(during,alpha)\n        restored_px[color,alpha] = restored\n        total += 1\n        if restored != color:\n            dmg_count += 1\n            damaged_px[color,alpha] = 255\nprint(f"{dmg_count}/{total} -> {dmg_count/total}")\n\noriginalimg.save("original.png")\nalphaimg.save("alpha.png")\npremulimg.save("premul.png")\nrestoredimg.save("restored.png")\ndamagedimg.save("damaged.png")\n
Run Code Online (Sandbox Code Playgroud)\n

湍流与噪声

\n

噪音:

\n
<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n<svg\n   height="200"\n   width="200"\n   version="1.1"\n   xmlns="http://www.w3.org/2000/svg"\n   xmlns:svg="http://www.w3.org/2000/svg">\n  <filter id="turbulence-alpha" x="0" y="0" width="1" height="1">\n    <feTurbulence type="fractalNoise" baseFrequency="0.02" />\n    <feColorMatrix\n       values="0 0 0 1 0\n               0 0 0 1 0\n               0 0 0 1 0\n               0 0 0 0 1 " />\n  </filter>\n  <rect x="0" y="0" width="200" height="200"\n     style="filter:url(#turbulence-alpha)" />\n</svg>\n
Run Code Online (Sandbox Code Playgroud)\n

湍流:

\n
<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n<svg\n   height="200"\n   width="200"\n   version="1.1"\n   xmlns="http://www.w3.org/2000/svg"\n   xmlns:svg="http://www.w3.org/2000/svg">\n  <filter id="turbulence-alpha" x="0" y="0" width="1" height="1">\n    <feTurbulence type="turbulence" baseFrequency="0.02" />\n    <feColorMatrix\n       values="0 0 0 1 0\n               0 0 0 1 0\n               0 0 0 1 0\n               0 0 0 0 1 " />\n  </filter>\n  <rect x="0" y="0" width="200" height="200"\n     style="filter:url(#turbulence-alpha)" />\n</svg>\n
Run Code Online (Sandbox Code Playgroud)\n