Оператор Собеля и свертка с 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) Я попробовал, и метод свертки у меня не работает - он просто создает пустой образ. Думаю, это не твоя вина. Когда вы объединяете 2 пустых изображения, вы получаете пустое.

2) Есть небольшая проблема с реализацией вашего комбината. Вы можете установить прозрачность на 255 без sqrt calc, как вы это делаете на r, g, b

надеюсь это поможет

person Anatoly Lubarsky    schedule 17.02.2013