使用SVG滤波器实现Sobel滤波器

时间:2016-01-29 15:59:33

标签: svg svg-filters

乍一看,我觉得这很简单。谷歌发现this有以下代码,但它不是真正的索贝尔过滤器。

<filter id="edge">
<feColorMatrix type="luminanceToAlpha" />
<feConvolveMatrix order="3" kernelMatrix="-1 -2 -1 0 0 0 1 2 1" />
<feConvolveMatrix order="3" kernelMatrix="-1 -2 -1 0 0 0 1 2 1" />
</filter>

wikipedia page后,我需要一种方法来查找两个feConvolveMatrix结果之间的距离。我可以通过合成来平方并添加它们,但似乎没有任何方法可以获得平方根运算符。有可能吗?

我在下面添加了我当前的解决方案,但它与参考资料不完全匹配。

2 个答案:

答案 0 :(得分:1)

feDiffuseLighting实际上会计算一个sobel过滤器来生成法线。我将以下内容放在一起,将RGB提取为三个白色图像,每个图像的R,G和B为高度图的alpha。然后使用位于图像上方的定向光提取法线的z分量(1 - sqrt(dx^2 + dy^2))。最后它被反转(仅获得sqrt(dx^2 + dy^2))并且所有三个通道被组合。

我不太确定color-interpolation-filters="sRGB"图像是否非常暗,所以我设置surfaceScale="8.0"以提高增量的变化。它仍然看起来不像参考,但它很接近。此外,无论出于何种原因,过滤器向外扩展以覆盖HTML的其他部分(编辑:请参阅this)。

this page测试图像:
enter image description here enter image description here

.sobelme {
  -webkit-filter: url('#sobel');
  -moz-filter: url('#sobel');
  -ms-filter: url('#sobel');
  -o-filter: url('#sobel');
  filter: url('#sobel');
}
<img class="sobelme" src="http://i.stack.imgur.com/sld5Y.png" />
<img src="http://i.stack.imgur.com/wq077.png" />

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="0">
  <filter id="sobel" color-interpolation-filters="sRGB">
    <feColorMatrix type="matrix" in="SourceGraphic" result="RA" values="0 0 0 0 1
          0 0 0 0 1
          0 0 0 0 1
          1 0 0 0 0"></feColorMatrix>
    <feColorMatrix type="matrix" in="SourceGraphic" result="GA" values="0 0 0 0 1
          0 0 0 0 1
          0 0 0 0 1
          0 1 0 0 0"></feColorMatrix>
    <feColorMatrix type="matrix" in="SourceGraphic" result="BA" values="0 0 0 0 1
          0 0 0 0 1
          0 0 0 0 1
          0 0 1 0 0"></feColorMatrix>
    <feDiffuseLighting in="RA" result="R" surfaceScale="8.0">
      <feDistantLight elevation="90"></feDistantLight>
    </feDiffuseLighting>
    <feDiffuseLighting in="GA" result="G" surfaceScale="8.0">
      <feDistantLight elevation="90"></feDistantLight>
    </feDiffuseLighting>
    <feDiffuseLighting in="BA" result="B" surfaceScale="8.0">
      <feDistantLight elevation="90"></feDistantLight>
    </feDiffuseLighting>
    <feColorMatrix type="matrix" in="R" result="RS" values="-1 0 0 0 1
          0 0 0 0 0
          0 0 0 0 0
          0 0 0 0 1"></feColorMatrix>
    <feColorMatrix type="matrix" in="G" result="GS" values="0 0 0 0 0
          0 -1 0 0 1
          0 0 0 0 0
          0 0 0 0 1"></feColorMatrix>
    <feColorMatrix type="matrix" in="B" result="BS" values="0 0 0 0 0
          0 0 0 0 0
          0 0 -1 0 1
          0 0 0 0 1"></feColorMatrix>
    <feComposite in="RS" in2="GS" result="RSGS" operator="arithmetic" k1="0" k2="1" k3="1" k4="0"></feComposite>
    <feComposite in="RSGS" in2="BS" operator="arithmetic" k1="0" k2="1" k3="1" k4="0"></feComposite>
  </filter>
</svg>

更新:

测试图像是一种近似值,忽略水平和垂直增量之间的距离并简单地对它们求和。使用上述feDiffuseLighting进行正常计算的Sobel滤波器被归一化,这就是需要按比例缩放8的原因。简单地添加水平和垂直增量的绝对值,而sqrt()类似于@MichaelMullany's answer,可以如下实现。一个优点是不会发生过滤器在边界外的恼人膨胀。 color-interpolation-filters是必要的,否则会应用伽玛,会产生奇怪的结果。它仍然与测试图像不完全匹配,但它越来越简单。

.sobelme {
  -webkit-filter: url('#sobel');
  -moz-filter: url('#sobel');
  -ms-filter: url('#sobel');
  -o-filter: url('#sobel');
  filter: url('#sobel');
}
<img class="sobelme" src="http://i.stack.imgur.com/sld5Y.png" />
<img src="http://i.stack.imgur.com/wq077.png" />

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="0">
  <filter id="sobel" color-interpolation-filters="sRGB">
    <feConvolveMatrix in="SourceGraphic" order="3" preserveAlpha="true" kernelMatrix="-1 -2 -1
0 0 0
1 2 1 " result="VP" />
    <feConvolveMatrix in="SourceGraphic" order="3" preserveAlpha="true" kernelMatrix="1 2 1
0 0 0
-1 -2 -1 " result="VN" />
    <feConvolveMatrix in="SourceGraphic" order="3" preserveAlpha="true" kernelMatrix="-1 0 1
-2 0 2
-1 0 1 " result="HP" />
    <feConvolveMatrix in="SourceGraphic" order="3" preserveAlpha="true" kernelMatrix="1 0 -1
2 0 -2
1 0 -1 " result="HN" />
    <feComposite operator="arithmetic" in="VN" in2="VP" k2="1" k3="1" result="V" />
    <feComposite operator="arithmetic" in="HN" in2="HP" k2="1" k3="1" result="H" />
    <!-- <feComposite operator="arithmetic" in="V" in2="H" k2="1" k3="1" /> -->
    <feBlend mode="lighten" in="H" in2="V" />
  </filter>
</svg>

答案 1 :(得分:1)

示例代码错误。这是一个使用卷积的传统索贝尔。它将通道分离出来并将它们转换为alpha,运行边缘检测,将它们重新转换回RGB并将通道重新添加到一起。要获得确切的参考结果,看起来它们会添加阈值 - 您可以使用feComponentTransfer执行此操作。

<svg width="600px" height="800px" >

<defs>
<filter id="sobel" x="0%" y="0%" width="100%" height="100%">
    <!-- convert source image to luminance map-->
    <feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1 
                                         0 0 0 0 1 
                                         0 0 0 0 1 
                                         1 0 0 0 0" result="RChan" />
  
      <feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1 
                                         0 0 0 0 1 
                                         0 0 0 0 1 
                                         0 1 0 0 0" result="GChan" />
  
  
      <feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1 
                                         0 0 0 0 1 
                                         0 0 0 0 1 
                                         0 0 1 0 0" result="BChan" />
  
    <!-- sobel edge detection-->

  
    <feConvolveMatrix in="RChan" order="3" kernelMatrix="-1 -2 -1  
                                               0 0 0  
                                               1 2 1 "
                 
                   result="Rhor" />
  
    <feConvolveMatrix in="RChan" order="3" kernelMatrix="-1 0 1  
                                                                 -2 0 2 
                                                                 -1 0 1"  result="Rver"  />
  
   <feComposite operator="arithmetic" k2="1" k3="1" in="Rhor" in2="Rver" />
   <feColorMatrix type="matrix" values="0 0 0 1 0
                                    0 0 0 0 0 
                                    0 0 0 0 0 
                                    0 0 0 0 1" result="rededge"/>

  
   <feConvolveMatrix in="GChan" order="3" kernelMatrix="-1 -2 -1  
                                               0 0 0  
                                               1 2 1"
                   result="Ghor" />
  
    <feConvolveMatrix in="GChan" order="3" kernelMatrix="-1 0 1 
                                                         -2 0 2 
                                                         -1 0 1"  result="Gver"  />
  
   <feComposite operator="arithmetic" k2="1" k3="1" in="Ghor" in2="Gver" />
   <feColorMatrix type="matrix" values="0 0 0 0 0
                                    0 0 0 1 0 
                                    0 0 0 0 0 
                                    0 0 0 0 1" result="greenedge"/>
  

   <feConvolveMatrix in="BChan" order="3" kernelMatrix="-1 -2 -1  
                                               0 0 0  
                                               1 2 1 " result="Bhor" />
  
    <feConvolveMatrix in="BChan" order="3" kernelMatrix="-1 0 1  
                                                                 -2 0 2 
                                                                 -1 0 1"  result="Bver"  />
  
   <feComposite operator="arithmetic" k2="1" k3="1" in="Bhor" in2="Bver"/>
   <feColorMatrix type="matrix" values="0 0 0 0 0
                                    0 0 0 0 0 
                                    0 0 0 1 0 
                                    0 0 0 0 1" result="blueedge"/>

  <feComposite operator="arithmetic" in="blueedge" in2="rededge" k2="1" k3="1"/>
  <feComposite operator="arithmetic" in2="greenedge" k2="1" k3="1" result="finaledges"/>
  
  <feFlood flood-color="black" result="black"/>
  <feComposite operator="over" in="finaledges"/>
  </filter>
  
  </defs>

<image width="400" height="300" preserveAspectRatio="xMinYMin meet" xlink:href="http://www.roborealm.com/help/Sobel_src.jpg"/>
  
  <image filter="url(#sobel)" y="400" width="400" height="300" xlink:href="http://www.roborealm.com/help/Sobel_src.jpg"/>
  
</svg>