Быстрое субпиксельное лазерное определение точек - PullRequest
10 голосов
/ 20 июля 2009

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

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

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

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

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

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

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

Есть идеи?

Ответы [ 7 ]

5 голосов
/ 20 июля 2009

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

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

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

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

4 голосов
/ 20 июля 2009

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

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

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

Это не в моей голове, извините за ошибки; ^)

3 голосов
/ 20 июля 2009

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

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

x c = сумма (x n .f (x n )) / сумма (f (x n ))

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

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

дает:

сумма (x n .f (x n )) = 1 * 2 + 3 * 3 + 4 * 9 + 5 * 10 + 6 * 4 + 7 * 1

сумма (f (x n )) = 1 + 3 + 9 + 10 + 4 + 1

x c = 128/28 = 4,57

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

3 голосов
/ 20 июля 2009

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

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

2 голосов
/ 20 июля 2009

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

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

Классный проект, кстати.

1 голос
/ 20 июля 2009

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

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

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

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

Если вы нарисуете «чистый» поверх «грубого», возможно, немного другого цвета, вы получите лучшее из обоих миров.

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


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

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

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

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


Другие мысли

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

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

1 голос
/ 20 июля 2009

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

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

...