Оператор Sobel & Convolution с WriteableBitmapEx

Така че използвам WriteableBitmapEx за приложение в Windows RT. Опитвам се да внедря откриване на ръбове на изображение с помощта на оператора sobel. Успешно приложих и двете ядра за откриване на x и y върху изображението с помощта на .Convolute(), но сега съм заседнал в добавянето на двете изображения към едно. Проблемът е, че всички пиксели на двете изображения изглежда имат стойност 0 за прозрачност (така че A в ARGB). Мога да покажа и двете изображения самостоятелно без проблем, но добавянето им ми дава само черна картина. Та въпросите ми са:

  • Защо прозрачността е зададена на 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;
    }

person Thomas    schedule 12.02.2013    source източник


Отговори (3)


Конволюцията се прилага върху всички налични канали. Обработват се не само червено, зелено и синьо (което искате в този случай), но и алфа каналът. Това води до алфа стойност нула (100% прозрачност). Разгледайте следния пример:

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

Технически всичко е наред, не открива никакви ръбове над алфа канала. Но функционално това не е това, което искате в този случай. Ако това поведение не е желано, или можете да пропуснете обработката на алфа канала (ако източникът ви позволява) или да нулирате алфа на 255 след това.

Ще спекулирам за черното изображение, което ще се покаже на екрана, защото нямам опит с използваната технология. Много рамки първо нулират изображението до плътен цвят (ако приемем, че в този случай е черно). Това е необходимо, така че предишното изображение да не избледнява, ако имате работа с прозрачни изображения (или части от него). Добавянето на извито (прозрачно) изображение към този плътен цвят ще доведе до същия плътен цвят. Следователно изображението ще бъде показано изцяло черно.

Забележка: combineSobel използва всички канали, но тъй като преди беше преобразуван в скала на сивото, може да искате да оптимизирате създаването на цветове.

person Caramiriel    schedule 17.02.2013

Има ли по-добър начин за комбиниране на две изображения? Blit, за съжаление, изглежда не поддържа този вид >добавяне на пиксели. Но ForEach наистина е бавен...

Поради тази причина трябва да използвате опасен код, вижте това: Защо моят опасен кодов блок е по-бавен от моя безопасен код?

Що се отнася до скоростта, помислете, че извиквате 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.

person Balázs Szántó    schedule 20.02.2013

има 2 проблема във вашия код:

1) Опитах и ​​методът на convolute не работи за мен - той просто създава празно изображение. Предполагам, че вината не е твоя. Когато комбинирате 2 празни изображения, получавате празно едно.

2) Има малък проблем с внедряването на комбайна. Можете да зададете прозрачност на 255 без sqrt calc, както правите на r, g, b

Надявам се това да помогне

person Anatoly Lubarsky    schedule 17.02.2013