Sobel operator & Convolution with WriteableBitmapEx

倾然丶 夕夏残阳落幕 提交于 2019-12-10 18:06:40

问题


So I'm using WriteableBitmapEx for an app on Windows RT. I'm trying to implement edge detection on an image using the sobel operator. I have successfully applied both kernels for x and y detection onto the image using .Convolute(), but now I'm stuck adding both images to one. The problem is, that all the pixels of both images seem to have the value 0 for transparency (so the A in ARGB). I can display both images on their own without a Problem, but adding them gives me just a black picture. So my questions are:

  • Why is the transparency set to 0 for every pixel after the convolution?
  • Why can I still display the Image without it being all black?
  • Why is it black when I add two Images?
  • Is there a better way of combining two Images? Blit unfortunatley doesn't seem to support this kind of pixel-adding. But ForEach really is slow...

For calrification, here's my code so far. I can display both wbmpY and wbmpX, but finalbmp is completely black.

    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;
    }

回答1:


Convolution is applied over all the available channels. Not only red, green and blue (which is what you want in this case) are processed, but also the alpha channel. This results in an alpha value of zero (100% transparent). Consider the following example:

1 0 -1      255 255 255
2 0 -2 over 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 for all pixels

Technically it's all fine, it doesn't detect any edges over the alpha channel. However functionally this is not what you want in this case. If this behavior is not desired, either you can skip processing the alpha channel (if the source allows you to) or reset alpha to 255 afterwards.

I'm going to speculate about the black image that will be shown on screen, because I do not have experience of the used technology. A lot of frameworks first reset the image to a solid color (assuming it's black in this case). This is required so that the previous image will not bleed through if you're dealing with transparent images (or parts of it). Adding the convoluted (transparent) image to this solid color, will result in the same solid color. Therefor the image will be shown all black.

Note: combineSobel uses all channels, but since it was converted to greyscale before, you might want to optimize the creation of colors.




回答2:


Is there a better way of combining two Images? Blit unfortunatley doesn't seem to support this kind of >pixel-adding. But ForEach really is slow...

You should use unsafe code for that reason, check this out: Why is my unsafe code block slower than my safe code?

As the matter of speed, think about that you call 16 functions (4xMath.Min, 4xMath.Pow, 4xMath.Sqrt) for every pixel. It's a huge overhead.

Pixel values are in the range of [0,255] Math.Pow([0,255],2) + Mat.Pow([0,255],2) results in the range of [0,2*Math.Pow(255,2)] => [0,130 050] I would construct a lookup table like this:

byte[] LUT4Sqrt = new byte[130051];

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

Lookup table for the Math.Pow can be made too.




回答3:


there are 2 issues in your code:

1) I tried and convolute method doesn't work for me - it just creates an empty image. I guess it's not your fault. When you combine 2 empty images you get empty one.

2) There is a minor issue with your combine implementation. You can set transparency to 255 without sqrt calc as you do on r, g, b

hope this helps



来源:https://stackoverflow.com/questions/14836299/sobel-operator-convolution-with-writeablebitmapex

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!