索贝尔算子&;使用WriteableBitmapEx进行卷积

本文关键字:WriteableBitmapEx 使用 卷积 amp 贝尔 | 更新日期: 2023-09-27 17:58:59

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

  • 为什么卷积后每个像素的透明度都设置为0
  • 为什么我仍然可以显示图像而不将其全部显示为黑色
  • 为什么当我添加两个图像时它是黑色的
  • 有没有更好的方法来组合两个图像?不幸的是,Blit似乎不支持这种像素添加。但是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;
    }

索贝尔算子&;使用WriteableBitmapEx进行卷积

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

1 0-1 255 255 2552 0-2超过255 255 2551 0-1 255 255 255
1*255 0*255-1*255 255 0-2552*255 0*255-2*255=510 0-5101*255 0*255-1*255 255 0-2552*255+510+3*0-2*255-510=0(对于所有像素)

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

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

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

有没有更好的方法来组合两个图像?不幸的是,Blit似乎不支持这种>像素添加。但是ForEach真的很慢。。。

出于这个原因,你应该使用不安全的代码,看看这个:为什么我的不安全代码块比我的安全代码慢?

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

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

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

也可以制作数学Pow的查找表。

您的代码中有两个问题:

1) 我试过了,但复杂的方法对我不起作用——它只是创建了一个空的图像。我想这不是你的错。当你组合两个空图像时,你得到的是一个空图像。

2) 联合收割机的实现存在一个小问题。您可以在没有sqrt计算的情况下将透明度设置为255,就像您在r、g、b 上所做的那样

希望这对有帮助