Быстрое обнаружение точек в подпиксельной лазерной точке

Я использую XNA для создания проекта, где я могу нарисовать "граффити" на моей стене с помощью ЖК-проектора и монохромной камеры, которая фильтруется, чтобы видеть только ручные лазерные точки. Я хочу использовать любое количество лазерных указателей - на самом деле не важно их дифференцировать в данный момент.

Стена 10 'x 10', а камера - только 640x480, поэтому я пытаюсь использовать субпиксельное измерение с использованием кривой сплайна, как описано здесь: tpub.com

Камера работает со скоростью 120 кадров в секунду (8 бит), поэтому мой вопрос для вас - это самый быстрый способ найти центр подпиксельной лазерной точки. В настоящее время я использую 2D-поиск грубой силы, чтобы найти самый яркий пиксель на изображении (0 - 254), прежде чем выполнять сплайн-интерполяцию. Этот метод не очень быстрый, и каждый кадр занимает больше времени, чем компьютер.

Изменить: Чтобы уточнить, в конце мои данные камеры представлены двумерным массивом байтов, указывающим яркость пикселей.

Что бы я хотел сделать, это использовать шейдер XNA, чтобы хрустнуть изображение для меня. Это практично? Из того, что я понимаю, на самом деле не существует способа сохранить постоянные переменные в пиксельном шейдере, такие как текущие итоги, средние значения и т.д.

Но для аргументов, скажем, я нашел самые яркие пиксели с использованием грубой силы, а затем сохранил их и их соседние пиксели для кривой сплайна в X число вершин, используя texcords. Имеет ли смысл использовать HLSL для вычисления сплайновой кривой с использованием texcords?

Я также открыт для предложений за пределами моего окна XNA, будь то DX10/DX11, возможно, какой-то FPGA и т.д. У меня просто нет большого опыта в том, как хрустать данные таким образом. Я полагаю, что если они могут сделать что-то подобное на Wii-Mote, используя 2 батареи AA, я, вероятно, ошибаюсь.

Любые идеи?

Ответ 1

Вы имеете дело с некоторыми довольно сложными математиками, если вам нужна субпиксельная точность. Я думаю, что этот документ - это что-то, что нужно учитывать. К сожалению, вам придется заплатить, чтобы увидеть его с помощью этого сайта. Если у вас есть доступ к подходящей библиотеке, они могут получить ее для вас.

Ссылка в исходном сообщении предложила сделать 1000 сплайн-вычислений для каждой оси - она ​​обрабатывала x и y независимо, что нормально для круговых изображений, но немного отброшено, если изображение является искаженным эллипсом. Вы можете использовать следующее, чтобы получить разумную оценку:

x c= sum (x n.f(x n))/sum (f (x nсуб > ))

где x c - среднее значение, x n - это точка вдоль оси x, а f (x n) - это значение в точке x n. Итак, для этого:

          *
       *  *
       *  *
       *  *
       *  *
       *  *
       *  *  *
    *  *  *  *
    *  *  *  *
 *  *  *  *  *  *
------------------
 2  3  4  5  6  7 

дает:

sum (x n.f(x n)) = 1 * 2 + 3 * 3 + 4 * 9 + 5 * 10 + 6 * 4 + 7 * 1

sum (f (x n)) = 1 + 3 + 9 + 10 + 4 + 1

x c= 128/28 = 4.57

и повторите для оси y.

Ответ 2

Если по принуждению Brute вы имеете в виду смотреть на каждый пиксель самостоятельно, это в основном единственный способ сделать это. Вам придется сканировать все пиксели изображений, независимо от того, что вы хотите сделать с изображением. Хотя вам может не потребоваться найти самые яркие пиксели, вы можете отфильтровать изображение по цвету (например, если вы используете красный лазер). Это легко сделать с использованием цветного кодированного изображения HSV. Если вы ищете некоторые более быстрые алгоритмы, попробуйте OpenCV. Он был оптимизирован снова и снова для обработки изображений, и вы можете использовать его в С# через обертку:

[http://www.codeproject.com/KB/cs/Intel_OpenCV.aspx][1]

OpenCV также поможет вам легко найти центры точек и отслеживать каждую точку.

Есть ли причина, по которой вы используете камеру с частотой 120 кадров в секунду? вы знаете, что человеческий глаз может видеть только около 30 кадров в секунду? Я предполагаю, что это следует за очень быстрыми лазерными движениями... Возможно, вы захотите рассмотреть вопрос о его сокращении, потому что обработка в реальном времени 120 кадров в секунду будет очень сложной.

Ответ 3

проходящий через 640 * 480 байт, чтобы найти самый старший байт, должен работать в пределах ms. Даже на медленных процессорах. Не нужно идти по пути шейдеров.

Я бы советовал оптимизировать ваш цикл. например: это очень медленно (потому что он выполняет умножение с каждым поиском массива):

byte highest=0;
foundX=-1, foundY=-1;
for(y=0; y<480; y++)
{
    for(x=0; x<640; x++)
    {
        if(myBytes[x][y] > highest)
        {
            highest = myBytes[x][y];
            foundX = x;
            foundY = y;
        }
    }
}

это намного быстрее:

byte [] myBytes = new byte[640*480];
//fill it with your image

byte highest=0;
int found=-1, foundX=-1, foundY=-1;
int len = 640*480;
for(i=0; i<len; i++)
{
    if(myBytes[i] > highest)
    {
        highest = myBytes[i];
        found = i;
    }
}
if(found!=-1)
{
    foundX = i%640;
    foundY = i/640;
}

Это от головы, так жаль ошибок; ^)

Ответ 4

Brute-force - единственный реальный способ, однако ваша идея использования шейдера хороша - вы будете выгружать проверку грубой силы из CPU, которая может смотреть только на небольшое количество пикселей одновременно (примерно 1 на ядро), на GPU, который, вероятно, имеет более 100 немых ядер (конвейеров), которые могут одновременно сравнивать пиксели (ваш алгоритм, возможно, потребуется немного модифицировать, чтобы хорошо работать с 1 инструкцией по многим ядрам графического процессора).

Самая большая проблема, которую я вижу, заключается в том, можно ли быстро переместить эти данные на GPU.

Ответ 5

Еще одна оптимизация: если вы рисуете, то текущее местоположение указателя, вероятно, закрывает последнее местоположение указателя. Помните последнюю записанную позицию указателя между кадрами и просматривайте только область, расположенную ближе к этой позиции... скажем, область 1'x1 '. Только если указатель не найден в этой области, вы должны сканировать всю поверхность.

Очевидно, что будет компромисс между тем, как быстро ваша программа может сканировать, и как быстро вы сможете перемещать мышь, прежде чем камера "теряет" указатель и должна перейти к медленному сканированию полного изображения, Небольшое экспериментирование, вероятно, покажет оптимальное значение.

Прохладный проект, кстати.

Ответ 6

Поместите камеру немного не в фокус и битбит против нейтрального образца. Вы можете быстро сканировать строки для значений, отличных от 0. Также, если вы в 8 бит и получаете 4 байта за раз, вы можете быстрее обрабатывать изображение. Как указывалось другим, вы можете уменьшить частоту кадров. Если у вас меньше верности, чем в полученном изображении, в высокой скорости сканирования не так много.

(Небольшая вне фокуса камера поможет получить самые яркие точки и уменьшить ложные срабатывания, если у вас занятая поверхность... конечно, если предположить, что вы не снимаете ровную/плоскую поверхность)

Ответ 7

Начните с черного выходного буфера. Забудьте о субпикселе пока. Каждый кадр, каждый пиксель, делает это:

outbuff = макс (outbuff, inbuff);

Сделайте субпиксельную фильтрацию в третий "чистый" буфер, когда вы закончите с изображением. Или сделать кусок или линию экрана одновременно в режиме реального времени. Преимущество: "грубый" просмотр в режиме реального времени чертежа, очищенный, когда вы идете.

Когда вы конвертируете из грубого выходного буфера в "чистый" третий буфер, вы можете очистить грубую до черной. Это позволяет сохранить рисунок навсегда без замедления.

Выбрав "чистый" поверх "грубо", возможно, немного отличающийся цвет, вы получите лучшее из обоих миров.

Это похоже на то, что делают программы рисования - если вы рисуете очень быстро, вы видите грубую версию, тогда программа рисования "очищает" изображение, когда оно имеет время.


Некоторые комментарии к алгоритму:

Я видел много мошенников на этой арене. Я играл в Sonic на эмуляторе Sega Genesis, который был выше. и у него есть довольно дикие алгоритмы, которые работают очень хорошо и очень быстрые.

У вас есть некоторые преимущества, которые вы можете получить, потому что вы можете знать яркость и радиус точки.

Вы можете просто взглянуть на каждый пиксель и его 8 соседей и позволить этим 9 пикселям "проголосовать" в соответствии с их яркостью, где лежит подпиксель.


Другие мысли

Ваша рука не так точна, когда вы управляете лазерной указкой. Попытайтесь получить все точки каждые 10 кадров или около того, указав, какие лучи являются (на основе предыдущего движения и учета новых точек, выключенных лазеров и точек, которые вошли или покинули поле зрения), а затем просто рисуя высокий кривая разрешения. Не беспокойтесь о субпикселе на входе - просто нарисуйте кривую на выходе с высоким разрешением.

Используйте сплайн Catmull-Rom, который проходит через все контрольные точки.