Можно ли использовать шейдер, чтобы найти разницу между двумя текстурами? (XNA/HLSL)

Я сделал простое приложение на основе веб-камеры, которое обнаруживает «края движения», поэтому рисует текстуру, которая показывает, где пиксели текущего кадра значительно отличаются от предыдущего кадра. Это мой код:

// LastTexture is a Texture2D of the previous frame.
// CurrentTexture is a Texture2D of the current frame.
// DifferenceTexture is another Texture2D.
// Variance is an int, default 100;

Color[] differenceData = new Color[CurrentTexture.Width * CurrentTexture.Height];
Color[] currentData = new Color[CurrentTexture.Width * CurrentTexture.Height];
Color[] lastData = new Color[LastTexture.Width * LastTexture.Height];

CurrentTexture.GetData<Color>(currentData);
LastTexture.GetData<Color>(lastData);

for (int i = 0; i < currentData.Length; i++)
{
    int sumCD = ColorSum(currentData[i]); // ColorSum is the same as c.R + c.B + c.G where c is a Color.
    int sumLD = ColorSum(lastData[i]);
    if ((sumCD > sumLD - Variance) && (sumCD < sumLD + Variance))
        differenceData[i] = new Color(0, 0, 0, 0); // If the current pixel is within the range of +/- the Variance (default: 100) variable, it has not significantly changes so is drawn black.
    else
        differenceData[i] = new Color(0, (byte)Math.Abs(sumCD - sumLD), 0); // This has changed significantly so is drawn a shade of green.
}

DifferenceTexture = new Texture2D(game1.GraphicsDevice, CurrentTexture.Width, CurrentTexture.Height);
DifferenceTexture.SetData<Color>(differenceData);

LastTexture = new Texture2D(game1.GraphicsDevice,CurrentTexture.Width, CurrentTexture.Height);
LastTexture.SetData<Color>(currentData);

Есть ли способ перенести этот расчет на графический процессор с помощью шейдеров (он может работать со скоростью около 25/26 кадров в секунду, используя описанный выше метод, но это немного медленно)? У меня есть базовое понимание того, как работают шейдеры HLSL, и я не ожидаю полного решения, я просто хочу знать, возможно ли это и как получить «разницу» данных текстуры из шейдера и будет ли это на самом деле быстрее .

Заранее спасибо.


person Callum Rogers    schedule 25.07.2009    source источник


Ответы (3)


Что касается вашего комментария выше о решении использовать двухпоточный подход к вашей проблеме, ознакомьтесь с .Net Parallel Extensions CTP от Microsoft. microsoft.com

Если вы не планируете выполнять развертывание на XBox360, эта библиотека отлично работает с XNA, и я заметил значительные улучшения скорости в определенных циклах и итерациях.

В основном вам нужно будет изменить только пару строк кода, например:

for (int i = 0; i < currentData.Length; i++)
{
    // ...
}

изменится на:

Parallel.For(0, currentData.Length, delegate(int i)
{
   // ...
});

чтобы каждое ядро ​​​​в вашем процессоре автоматически помогало с обработкой чисел. Это быстро и отлично.

Удачи!

person bufferz    schedule 26.07.2009
comment
Я попробую это, выглядит намного лучше, чем моя текущая параллельная попытка с использованием массивов делегатов (тьфу...) - person Callum Rogers; 27.07.2009
comment
МОЙ БОГ. Это невероятно. Простота настройки и молниеносная скорость; Я никогда не видел ничего подобного. Это увеличилось со 110 мс (исходный вариант) до 60 мс (мой метод многопоточности), 2 мс (шейдеры, но это было слишком сложно, неработоспособно и ужасно неточно) и 15 мс (Threading.For()). Теперь это работает быстрее, чем 16 мс, необходимые для 60 кадров в секунду, и нет задержки. Спасибо! - person Callum Rogers; 27.07.2009
comment
@CallumRogers: как версия шейдера может быть слишком сложной, неработоспособной и ужасно неточной? Каждый компонент R/G/B/A преобразуется из 8-битного целого числа (байта) в 32-битное число с плавающей запятой. Таким образом, вы получаете гораздо больший динамический диапазон для работы внутри шейдера, и когда вы выводите цвет (опять же, нормализованный до 32-битного 1f), он преобразуется обратно в байтовые компоненты в коде вашего процессора. Что касается сложности, она не должна сильно отличаться от вашего кода выше. Вам даже не нужен вершинный шейдер, просто нарисуйте эффект, используя SpriteBatch. - person Lou; 06.07.2015
comment
@LousyCoder Понятия не имею, я задавал этот вопрос 6 лет назад?! Кто знает! - person Callum Rogers; 09.07.2015

Вы можете сэмплировать две текстуры внутри пиксельного шейдера, а затем записывать разницу как значение цвета. Если вы настроили Render Target, цвет информация, которую вы выводите из шейдера, будет храниться в этой текстуре, а не в фреймбуфере.

Я не знаю, какой прирост скорости вы ожидаете увидеть, но я бы сделал именно так.

* редактировать - Да, и я забыл сказать, помните о типе выборки, который вы используете, так как это повлияет на результаты. Если вы хотите, чтобы ваш алгоритм напрямую транслировался в GPU, для начала используйте точечную выборку.

person Mark Simpson    schedule 25.07.2009
comment
Кажется, это почти работает, за исключением того, что Color в .NET имеет четыре значения (0..255,0..255,0..255,0..255), которые относятся к float4 в HLSL (0..1,0 ..1,0..1,0..1), что, кажется, все портит. - person Callum Rogers; 26.07.2009
comment
Просто хотел уточнить; этот метод работает невероятно быстро (нахождение разницы между двумя текстурами 640x480 менее чем за 2 мс), но его рендеринг — полный кошмар (но, что удивительно, шейдеры — нет), поэтому я отказался от этого пути, вместо этого сосредоточившись на попытках двухпоточная операция (один поток сравнивает половину данных, другой — другую половину). Я наткнулся на проблему, когда он отрисовывал первый кадр, а затем переставал делать что-либо еще. - person Callum Rogers; 26.07.2009
comment
Что касается маршрута шейдера: ни одна из этих вещей не должна быть серьезной проблемой; Вы сможете их решить, если будете продолжать затыкаться :) - person Mark Simpson; 26.07.2009

оператор Собеля или что-то подобное используется в игровых движках и других приложениях реального времени для обнаружения границ. . Написать пиксельный шейдер тривиально.

person Community    schedule 18.04.2011