svg 中的黑白(非灰度)滤镜,最好是 snap.svg

Posted

技术标签:

【中文标题】svg 中的黑白(非灰度)滤镜,最好是 snap.svg【英文标题】:Black and white (not grayscale) filter in svg, ideally snap.svg 【发布时间】:2017-05-21 21:59:44 【问题描述】:

编辑 1: 重写问题,以便更清晰易懂。

这个真的让我心碎。

我想对图像应用过滤器,使超过某个阈值的值显示为全白,而所有等于或小于阈值的值都显示为全黑。

这是一张表示 3x3 像素图像的 RGB 值的地图:

|(255,255,255)|(220,220,220)|(100,100,100)|  
|(254,254,254)|(12 ,12 ,12 )|(38 ,38 ,38 )|  
|(201,201,201)|(105,105,105)|(60 ,60 ,60 )|

应用我的过滤器后,我希望收到一个图像,其中所有大于 200 的值都转换为 (255,255,255),所有等于或小于 200 的值都转换为 (0,0,0),例如:

|(255,255,255)|(255,255,255)|(0  ,0  ,0  )|
|(255,255,255)|(0  ,0  ,0  )|(0  ,0  ,0  )|
|(255,255,255)|(0  ,0  ,0  )|(0  ,0  ,0  )|

关于我应该从哪里开始的任何想法?这在svg中甚至可能吗? 我知道我可以在 svg 的过滤器中创建矩阵乘法,但我不知道如何解决这个问题。

谢谢!

编辑 1: 我在数学交流中发布的相关问题: https://math.stackexchange.com/questions/2087482/creating-binary-matrix-for-threshold-as-a-result-of-matrix-multiplcation

编辑 2::从 Snap.svg 代码中提取:此矩阵会将彩色图像转换为灰度:

<feColorMatrix type="matrix" values="0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0"/>

我想修改此处的值以获得黑白,而不是灰度。我想选择阈值,高于该阈值返回为白色,低于返回为黑色

附加信息:根据 MSDN,乘法是这样发生的:

Resulting vector   my coveted matrix     original vector
    | R' |     | a00 a01 a02 a03 a04 |   | R |
    | G' |     | a10 a11 a12 a13 a14 |   | G |
    | B' |  =  | a20 a21 a22 a23 a24 | * | B |
    | A' |     | a30 a31 a32 a33 a34 |   | A |
    | 1  |     |  0   0   0   0   1  |   | 1 |

这又会应用于输入图像的每个像素。

【问题讨论】:

您可以查看 svg 过滤器。很难看出你在追求什么。是否有将被擦除的初始图像或 svg,或者什么? 没有特定的 svg 或图像。基本上我需要(创建)一个过滤器来获取图像并返回单色图像(不是灰度,而是严格的黑白)。让所有大于 x 的值显示为白色,所有小于或等于 x 的值显示为黑色。 如果您知道内置的 svg 过滤器等,那就太好了。如果没有,我将不得不解决这个数学问题。 math.stackexchange.com/questions/2087482/… 我知道您想将 RGB 图像转换为黑白图像。正如您稍后所说,您可能想要一个应用于每个像素的应用程序,返回 (0,0,0) 或 (255,255,255)(A 代表不透明度)。我的问题是:如果你得到一个像素(203,97,150),输出会是什么? 我没有像素 (203,97,150)。在我的输入中,所有三个字段都是相等的,格式始终为 (x,x,x) 【参考方案1】:

您可以使用&lt;feComponentTransfer type="discrete"&gt; 过滤器原语实现阈值。

这是一个例子。

<svg   style="background-color: linen">
  <defs>
    <linearGradient id="gradient">
      <stop offset="0" stop-color="white"/>
      <stop offset="1" stop-color="black"/>
    </linearGradient>
    
    <filter id="threshold" color-interpolation-filters="sRGB">
      <feComponentTransfer>
        <feFuncR type="discrete" tableValues="0 1"/>
        <feFuncG type="discrete" tableValues="0 1"/>
        <feFuncB type="discrete" tableValues="0 1"/>
      </feComponentTransfer>
    </filter>
  </defs>

  <rect x="50" y="50"   fill="url(#gradient)"/>
  <rect x="50" y="300"   fill="url(#gradient)" filter="url(#threshold)"/>
</svg>

这个原语的工作原理是为每个颜色分量创建一个波段表。然而,波段的数量与输入有关,而不是与输出有关。可以把它想象成输入被分成许多大小相等的波段。然后为每个波段分配一个输出值。因此,如果您希望拆分发生在 50%(R、G 或 B = 0.5 (128)),那么您将创建两个波段,“0 1”。第一个波段 (0 -> 0.5) 中的值被分配值 0,第二个波段 (0.5 -> 1) 中的值被分配值 1。

因此,例如,如果您希望阈值为 20%(0.2 或 51),则需要创建五个波段。输出表的值为“0 1 1 1 1”。

<svg   style="background-color: linen">
  <defs>
    <linearGradient id="gradient">
      <stop offset="0" stop-color="white"/>
      <stop offset="1" stop-color="black"/>
    </linearGradient>
    
    <filter id="threshold" color-interpolation-filters="sRGB">
      <feComponentTransfer>
        <feFuncR type="discrete" tableValues="0 1 1 1 1"/>
        <feFuncG type="discrete" tableValues="0 1 1 1 1"/>
        <feFuncB type="discrete" tableValues="0 1 1 1 1"/>
      </feComponentTransfer>
    </filter>
  </defs>

  <rect x="50" y="50"   fill="url(#gradient)"/>
  <rect x="50" y="300"   fill="url(#gradient)" filter="url(#threshold)"/>
</svg>

这种工作方式的缺点是,它确实意味着如果您想在任意组件值处设置阈值,您可能需要在表中拥有多达 256 个值。

为了演示这一点,在最后一个示例中,我使用一些 javascript 根据范围滑块设置的值更新过滤器表。

var selector = document.getElementById("selector");
var funcR = document.getElementById("funcR");
var funcG = document.getElementById("funcG");
var funcB = document.getElementById("funcB");

function updateFilter() 
  // Input value
  var threshold = selector.value;
  // Create the table
  var table = "0 ".repeat(threshold) + "1 ".repeat(256-threshold);
  // Update the filter components
  funcR.setAttribute("tableValues", table);
  funcG.setAttribute("tableValues", table);
  funcB.setAttribute("tableValues", table);


selector.addEventListener('input', updateFilter);

// Call the filter updater once at the start to initialise the filter table
updateFilter();
<svg   style="background-color: linen">
  <defs>
    <filter id="threshold" color-interpolation-filters="sRGB">
      <feComponentTransfer>
        <feFuncR id="funcR" type="discrete" tableValues="0 1 1 1"/>
        <feFuncG id="funcG" type="discrete" tableValues="0 1 1 1"/>
        <feFuncB id="funcB" type="discrete" tableValues="0 1 1 1"/>
      </feComponentTransfer>
    </filter>
  </defs>

  <image xlink:href="https://placekitten.com/g/300/300"
         x="50" y="50" 
         filter="url(#threshold)"/>
</svg>

<form>
  <input id="selector" type="range" min="0" max="256"/>
</form>

【讨论】:

IE/Edge 仅支持 feComponentTransfer 数组中最多 64 个值 - 只是提示 看来不是这样。我稍微修改了脚本以使其在 IE 中运行,它似乎在 IE11 中运行良好。没试过Edge。 jsfiddle.net/ga0xh0oj 曾经是这样。修好了就好了。【参考方案2】:

这就是 SVG 的 feComponentTransfer 原语的用途,type="discrete"。请咨询webplatform documentation 了解如何使用该元素。

【讨论】:

链接好像坏了。【参考方案3】:

您所指的RGB到灰度转换是一个线性过程。它可以通过矩阵乘法来完成。 每个输出值都是 R、G 和 B 值的加权和。如果您为每个输出通道选择相同的权重,则生成的 RGB 图像将显示为灰色。

二值化是一种非线性操作。因此,无法修改该转换矩阵,使其变为黑白而不是灰度。

我不知道您是否可以在 svg 文件中添加标志或其他内容,使其看起来是黑白的,但您可以编写一个适当替换颜色值的小程序。

【讨论】:

以上是关于svg 中的黑白(非灰度)滤镜,最好是 snap.svg的主要内容,如果未能解决你的问题,请参考以下文章

scss 灰度和棕褐色滤镜SASS mixin(支持SVG滤镜)

scss 灰度和棕褐色滤镜SASS mixin(支持SVG滤镜)

sh 将给定文件夹内的所有图像从彩色转换为灰度,调整它的黑白抖动级别并将其追踪到SVG矢量f

Firefox 中的 SVG 过滤器转换

Inkscape svg彩色图转灰度图

如何把网页变成黑白