【问题标题】:Sobel operator & Convolution with WriteableBitmapEx使用 WriteableBitmapEx 的 Sobel 算子和卷积
【发布时间】:2013-02-12 15:48:03
【问题描述】:

所以我将 WriteableBitmapEx 用于 Windows RT 上的应用程序。我正在尝试使用 sobel 运算符对图像进行边缘检测。我已经使用 .Convolute() 成功地将用于 x 和 y 检测的两个内核应用到图像上,但现在我坚持将两个图像都添加到一个图像中。 问题是,两个图像的所有像素似乎都具有透明度值 0(因此 ARGB 中的 A)。我可以毫无问题地单独显示这两个图像,但是添加它们只会给我一张黑色的图片。 所以我的问题是:

  • 为什么卷积后每个像素的透明度都设置为 0?
  • 为什么我仍然可以显示图像而不是全黑?
  • 为什么我添加两个图像时是黑色的?
  • 有没有更好的组合两个图像的方法? Blit unfortunatley 似乎不支持这种像素添加。但是 ForEach 确实很慢...

为了验证,这是我到目前为止的代码。我可以同时显示 wbmpY 和 wbmpX,但是 finalbmp 是全黑的。

    public int[,] sobelY = new int[3, 3] { { 1, 2, 1 }, { 0, 0, 0 }, { -1, -2, -1 } };
    public int[,] sobelX = new int[3, 3] { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } };

    public void trim(WriteableBitmap wbmp)
    {
        var graybmp = wbmp.Clone();
        graybmp.ForEach(toGrayscale);

        var wbmpY = graybmp.Clone();
        var wbmpX = graybmp.Clone();

        wbmpY = wbmpY.Convolute(sobelY, 1, 0);
        wbmpX = wbmpX.Convolute(sobelX, 1, 0);

        var finalbmp = combineSobel(wbmpX, wbmpY);           
     }

    public WriteableBitmap combineSobel(WriteableBitmap img, WriteableBitmap img2)
    {
        int height = img.PixelHeight;
        int width = img.PixelWidth;
        WriteableBitmap result = img.Clone();
        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                Color imgColor = img.GetPixel(x, y);
                Color img2Color = img2.GetPixel(x, y);
                Color newColor = Color.FromArgb(
                    Math.Min((byte)Math.Sqrt(Math.Pow(imgColor.A, 2) + Math.Pow(img2Color.A, 2)), (byte)255),
                    Math.Min((byte)Math.Sqrt(Math.Pow(imgColor.R, 2) + Math.Pow(img2Color.R, 2)), (byte)255),
                    Math.Min((byte)Math.Sqrt(Math.Pow(imgColor.G, 2) + Math.Pow(img2Color.G, 2)), (byte)255),
                    Math.Min((byte)Math.Sqrt(Math.Pow(imgColor.B, 2) + Math.Pow(img2Color.B, 2)), (byte)255)
                );

                result.SetPixel(x, y, newColor);
            }
        }
        return result;
    }

【问题讨论】:

    标签: c# edge-detection writeablebitmap writeablebitmapex


    【解决方案1】:

    卷积应用于所有可用通道。不仅处理红色、绿色和蓝色(在这种情况下是您想要的),还处理 Alpha 通道。这导致 alpha 值为零(100% 透明)。考虑以下示例:

    1 0 -1 255 255 255 2 0 -2 超过 255 255 255 1 0 -1 255 255 255 1*255 0*255 -1*255 255 0 -255 2*255 0*255 -2*255 = 510 0 -510 1*255 0*255 -1*255 255 0 -255 2*255 + 510 + 3*0 - 2*255 - 510 = 0 对于所有像素

    从技术上讲,一切都很好,它没有检测到 Alpha 通道上的任何边缘。但是,在这种情况下,这不是您想要的功能。如果不需要这种行为,您可以跳过处理 Alpha 通道(如果源允许)或之后将 Alpha 重置为 255。

    我将推测屏幕上将显示的黑色图像,因为我没有使用技术的经验。许多框架首先将图像重置为纯色(假设在这种情况下为黑色)。这是必需的,这样如果您正在处理透明图像(或其中的一部分),前一个图像就不会渗出。将卷积(透明)图像添加到此纯色中,将产生相同的纯色。因此,图像将显示为全黑。

    注意:combineSobel 使用所有通道,但由于之前已转换为灰度,您可能需要优化颜色的创建。

    【讨论】:

      【解决方案2】:

      有没有更好的方法来组合两个图像? Blit unfortunatley 似乎不支持这种 >pixel-adding。但是 ForEach 真的很慢……

      出于这个原因,您应该使用不安全的代码,请查看:Why is my unsafe code block slower than my safe code?

      考虑到速度问题,请考虑为每个像素调用 16 个函数(4xMath.Min、4xMath.Pow、4xMath.Sqrt)。这是一个巨大的开销。

      像素值在 [0,255] 范围内 Math.Pow([0,255],2) + Mat.Pow([0,255],2) 结果范围为 [0,2*Math.Pow(255,2)] => [0,130 050] 我会构建一个这样的查找表:

      byte[] LUT4Sqrt = new byte[130051];
      
      for (int i = 0; i < 130051; i++)
      {
          LUT4Sqrt[i] = (byte)(Math.Sqrt(i));
      }
      

      也可以制作 Math.Pow 的查找表。

      【讨论】:

        【解决方案3】:

        您的代码中有 2 个问题:

        1) 我尝试过卷积方法对我不起作用 - 它只会创建一个空图像。我想这不是你的错。当您合并 2 张空图像时,您会得到一张空图像。

        2) 您的 combine 实现存在一个小问题。您可以将透明度设置为 255,而无需像在 r、g、b 上那样进行 sqrt calc

        希望对你有帮助

        【讨论】:

          猜你喜欢
          • 2017-11-04
          • 2020-04-06
          • 2019-09-20
          • 2018-11-27
          • 1970-01-01
          • 1970-01-01
          • 2019-12-04
          • 2022-01-23
          • 2018-01-31
          相关资源
          最近更新 更多