【问题标题】:How to create an edge preserving blur (similar to a bilateral filter) using a limited set of primitive operations如何使用一组有限的原始操作创建边缘保持模糊(类似于双边滤波器)
【发布时间】:2014-01-11 06:29:52
【问题描述】:

我一直在尝试使用现有 SVG 滤镜工具箱中有限的一组图元来复制双边滤镜效果(边缘保留、颜色范围感知)。我尝试了多种方法。我迄今为止最成功的是一个三部分操作,它进行 Sobel 边缘检测,扩大 Sobel 边缘,通过合成操作提取与这些边缘相对应的像素,高斯模糊源图像,然后在顶部合成原始边缘像素图像模糊。结果保留了边缘,但不支持颜色范围。

<filter id="surfaceBlur" color-interpolation-filters="sRGB">
        <!-- convert source image to luminance map-->
        <feColorMatrix type="luminanceToAlpha" />
        <!-- sober edge detection-->
        <feConvolveMatrix order="3" kernelMatrix="-1 -2 -1  
                                                    0 0 0  
                                                   1 2 1 "
                          preserveAlpha="true"
                         />
        <feConvolveMatrix order="3" kernelMatrix="-1 0 1  
                                                  -2 0 2 
                                                  -1 0 1 "
                          preserveAlpha="true"
                          />
      <!-- dilate the edges to produce a wider mask-->
      <feMorphology operator="dilate" radius="1"
                     result="mask"/>
      <!-- extract just the detail from the source graphic using the dilated edges -->
      <feComposite operator="in" in="SourceGraphic" in2="mask" result="detail" />
      <!-- blur the source image -->
      <feGaussianBlur stdDeviation="3" in="SourceGraphic" result="backblur"/>
       <!-- slap the detail back on top of the blur! -->
      <feComposite operator="over" in="detail" in2="backblur"/>

您可以看到原始的 gaussianBlur,这个过滤器,在右下角,一个真正的双边过滤器:

http://codepen.io/mullany/details/Dbyxt

如您所见,这不是一个糟糕的结果,但它与双边过滤器不是很接近。此方法也仅适用于灰度图像,因为它使用亮度差异来查找边缘 - 因此不会检测到相似亮度颜色之间的边缘。

所以问题是是否有边缘保留颜色范围感知过滤器(引导边缘视图、双边等)的算法变体 - 可以使用 SVG 中可用的有限原语构建 - 对于不熟悉 SVG 的人来说是:

  • 高斯模糊
  • 卷积(任何内核大小)
  • 侵蚀/扩张
  • 颜色矩阵
  • 所有 porter duff 合成操作
  • 基本混合操作(乘法、加网、变亮、变暗)
  • 一种组件传输原语,它允许通过表格查找(以及将底限/上限转换为特定值)来转换颜色通道

只有 RGB 颜色空间可用。多次迭代没问题,这些操作的任何有向图都可以构造出来。

更新:

我成功创建了一个中值过滤器,使用 feBlend lighten 和 darken 作为冒泡排序中的 Max 和 Min 运算符(感谢 cs.stackexchange.com 的帮助)。然而这是低效的:http://codepen.io/mullany/pen/dmbvz,并且缺乏双边滤镜的颜色范围感知。

【问题讨论】:

  • 您总是可以建议在dev.w3.org/fxtf/filters 中添加额外的过滤器@ Firefox 和 Chrome 正在实现此规范中的功能。
  • 是的,罗伯特,我有一封很长的电子邮件,我正在为需要的过滤器撰写邮件,现在自定义过滤器已被取消。

标签: algorithm image-processing svg


【解决方案1】:

我应该说我没有任何图形经验来证明这一点,但从数学的角度来看,我认为这可以模仿the equation that defines the bilateral filter

  • 给定您的图像I,使用颜色矩阵生成图像Intensity,该图像保存单个通道中每个像素的强度,例如 R。通道 G 和 B 被归零。

  • 对于双边过滤器窗口中的每个非中心像素,构造一个卷积矩阵,该矩阵获取特定像素和中心像素之间的差异。例如,对于 3x3 窗口,您将拥有矩阵

     0 0 0   -1 0 0    0-1 0    0 0-1    0 0 0    0 0 0    0 0 0    0 0 0        
    -1 1 0    0 1 0    0 1 0    0 1 0    0 1-1    0 1 0    0 1 0    0 1 0    
     0 0 0    0 0 0    0 0 0    0 0 0    0 0 0    0 0-1    0-1 0   -1 0 0
    

    您可以根据需要在此处缩放1s 和-1s,以模拟双边滤波器的空间内核。

  • 将每个卷积矩阵应用于Intensity 映射,获得(在 3x3 示例中)8 幅图像,这些图像表示中心像素与其相邻像素之间的强度变化。

  • 对于 8 幅图像中的每幅图像,使用一个模拟双边滤波器范围内核的表格,将组件转移原语应用于 R。

  • 使用另一个颜色矩阵设置 G 和 B 通道以匹配所有 8 幅图像中的 R 通道。

  • 对 8 个和原始图像中的每一个使用乘法运算符得到 8 个新图像,它们代表双边滤波器总和中的 8 个项。

  • 使用 Porter-Duff 算子叠加 8 幅图像,有效地取双边滤波器中 8 项的总和。这将为您提供最终图像。

【讨论】:

  • 嗨 - Andy 只是对第 5 步的简要说明(“使用另一个颜色矩阵..”)你的意思是重复我们刚刚对 G 和 B 的 R 通道执行的步骤吗?
  • 要么重复这些步骤,要么将 R 通道复制到 G 和 B 通道。
  • 现在想想,我并不完全清楚将两个像素相乘是如何工作的。它只是将每个通道中的值相乘吗?可能还需要添加标准化步骤,我不确定。整个方法对您有意义吗?
  • yes SVG 将每个通道的每个像素值的单位化值相乘。我现在正在编写方法。我不完全确定如何用表格模拟范围内核,但我会试一试
  • 是的,您刚刚让我意识到,以我的天真想法将强度值和颜色值存储为数字 0-255,直接乘法会产生一些严重的溢出问题.缩放的简单解决方案,因此强度可以在 0-15 范围内,颜色可以在 0-16 范围内,看起来也不是很吸引人。嗯。
【解决方案2】:

【讨论】:

  • 这无法使用 SVG 原语实现,因为存在每个像素的协方差和方差计算。
【解决方案3】:

这是一种使用纯图像处理的方法:-

  1. 使用不锐化遮罩(它基本上会锐化边缘)。

    Unsharp Masking

    可以通过将原始图像的拉普拉斯算子添加到原始图像来完成。

  2. 对锐化的图像使用模糊处理。

其背后的概念是,由于模糊也会降低边缘的强度,因此我们会增加所有锐边的强度,然后应用模糊来抵消效果。

注意:- 我不知道 SVG 遮罩

【讨论】:

    【解决方案4】:

    以下文档说明了如何使用不同像素强度级别的空间滤波器插值(仅限插值 + 高斯滤波器)来实现双边滤波器的恒定时间近似:

    [Qingxiong Yang、Kar-Han Tan 和 Narendra Ahuja,实时 O(1) 双边滤波, IEEE 计算机视觉和模式识别会议 (CVPR) 2009]

    这里有一个java实现:https://code.google.com/p/kanzi/source/browse/java/src/kanzi/filter/FastBilateralFilter.java

    要查看过滤器的结果:

    java -cp kanzi.jar kanzi.test.TestEffects -filter=FastBilateral -file=...

    原始 C 代码和其他好东西可在 http://www.cs.cityu.edu.hk/~qiyang 获得

    【讨论】:

    • 我尝试了这种方法的一种变体(将图像分割成亮度桶并将空间内核应用于每个范围,然后混合结果)。不幸的是,它似乎需要很多范围才能有效 - 我用 8 个水桶进行的试验导致了一只相当令人毛骨悚然的猫。
    【解决方案5】:

    虽然答案已经被接受并获得了赏金,但我想试试anisotropic diffusion algorithm。它将像素强度的扩散定律应用于图像中的平滑纹理。防止跨边缘发生扩散,因此它保留了图像中的边缘。 我对SVG不是很熟悉,只是在Matlab中写了一个非常简单的灰度图像代码。但我认为它在 SVG 中是可行的,因为只需要基本的差异操作(像素 i+1i 之间的差异,在所有 4 个方向上)和电源/添加操作。 代码:

    diff = I; % original image
    lambda = 0.25; 
    niter = 10;
    Co = 20;
    for i = 1:niter  % iterations
    
      % Construct diffl which is the same as diff but
      % has an extra padding of zeros around it.
      diffl = zeros(rows+2, cols+2);
      diffl(2:rows+1, 2:cols+1) = diff;
    
      % North, South, East and West differences
      deltaN = diffl(1:rows,2:cols+1)   - diff;  
      deltaS = diffl(3:rows+2,2:cols+1) - diff;  
      deltaE = diffl(2:rows+1,3:cols+2) - diff;  
      deltaW = diffl(2:rows+1,1:cols)   - diff;
    
      cN = 1./(1 + (deltaN/Co).^2);
      cS = 1./(1 + (deltaS/Co).^2);
      cE = 1./(1 + (deltaE/Co).^2);
      cW = 1./(1 + (deltaW/Co).^2);
    
      diff = diff + lambda*(cN.*deltaN + cS.*deltaS + cE.*deltaE + cW.*deltaW);
    
    end
    

    我得到的结果图:

    希望对您有所帮助。谢谢

    【讨论】:

    • 遗憾的是,SVG 过滤器中没有电源操作 - 这是一个奇怪的受限工具箱 - 因此是原始问题。
    • 那么你可以对每个像素使用乘法得到 xi * xi
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-26
    相关资源
    最近更新 更多