快速亚像素激光点检测

时间:2009-07-20 14:08:20

标签: c# visual-studio-2008 xna

我正在使用XNA构建一个项目,我可以使用液晶投影仪和单色相机在墙上绘制“涂鸦”,过滤后只能看到手持激光点指针。我想使用任意数量的激光指针 - 此时并不关心区分它们。

墙面为10'x 10',相机仅为640x480,因此我尝试使用如下所示的样条曲线进行子像素测量:tpub.com

相机以120fps(8位)运行,所以我的问题是找到亚像素激光点中心的最快方法。目前我正在使用强力2D搜索在进行样条插值之前找到图像上最亮的像素(0 - 254)。这种方法速度不是很快,而且每一帧在计算机上的使用时间都比它们要长。

编辑:为了澄清,最后我的相机数据由表示像素亮度的2D字节数组表示。

我想做的是使用XNA着色器为我压缩图像。这有用吗?根据我的理解,实际上没有办法在像素着色器中保留持久变量,例如运行总计,平均值等。

但是为了论证,让我说我使用强力发现了最亮的像素,然后使用texcoords将它们和样条曲线的相邻像素存储到X个顶点中。那么使用HLSL来计算使用texcoords的样条曲线是否可行?

我也对我的XNA盒子之外的建议持开放态度,无论是DX10 / DX11,还是某种FPGA等等。我对这种方式处理数据的方式并不十分了解。我想他们是否可以在使用2节AA电池的Wii-Mote上做这样的事情,而不是我可能会采用错误的方式。

有什么想法吗?

7 个答案:

答案 0 :(得分:5)

如果通过暴力强迫你的意思是独立地观察每个像素,它基本上是唯一的方法。无论您想对图像做什么,都必须扫描所有图像像素。虽然您可能不需要找到最亮的像素,但您可以按颜色过滤图像(例如:如果使用红色激光)。使用HSV彩色编码图像可以轻松完成。如果您正在寻找更快的算法,请尝试OpenCV。它已经被反复优化用于图像处理,您可以通过包装器在C#中使用它:

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

OpenCV还可以帮助您轻松找到点中心并跟踪每个点。

你有使用120fps相机的原因吗?你知道人眼只能看到大约30fps吗?我猜测它是跟随非常快速的激光运动...你可能想要考虑降低它,因为120fps的实时处理将很难实现。

答案 1 :(得分:4)

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

这是我的头脑,对错误感到抱歉; ^)

答案 2 :(得分:3)

蛮力是唯一真正的方式,但是你使用着色器的想法很好 - 你要从CPU卸载强力检查,它只能同时查看少量像素(大约1个)每个核心),GPU,可能有100多个哑核心(流水线),可以同时比较像素(你的算法可能需要稍微修改一点,以适应GPU的1指令多核心安排)。 / p>

我看到的最大问题是你是否能够足够快地将数据移动到GPU上。

答案 3 :(得分:3)

如果您想要亚像素精度,那么您正在处理一些非常复杂的数学运算。我认为this paper是需要考虑的事情。不幸的是,你必须付钱才能看到它使用该网站。如果您可以访问合适的图书馆,他们可能会为您抓住它。

原帖中的链接建议对每个轴进行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轴。

答案 4 :(得分:2)

要考虑的另一个优化:如果你正在绘图,那么指针的当前位置可能接近指针的最后位置。记住帧之间指针的最后记录位置,并且只扫描靠近该位置的区域...比如1'x1'区域。只有在该区域找不到指针时,才应扫描整个表面。

显然,在相机“丢失”指针并且必须进行慢速全图扫描之前,程序可以扫描的速度和移动鼠标的速度之间会有一个权衡取舍。一点实验可能会揭示出最佳价值。

顺便说一句酷项目。

答案 5 :(得分:1)

将相机略微偏离焦​​点并对中性样品进行bitblt。您可以快速扫描非0值的行。此外,如果您是8位并且一次拾取4个字节,则可以更快地处理图像。正如其他人指出的那样,你可能会降低帧速率。如果保真度低于生成的图像,则高扫描速率没有多大意义。

(如果您的表面很忙,那么轻微的失焦相机将有助于获得最亮点并减少误报...当然假设您没有拍摄光滑/平坦的表面)

答案 6 :(得分:1)

从黑色输出缓冲区开始。暂时忘掉子像素。每个帧,每个像素,都这样做:

outbuff = MAX(outbuff,inbuff);

完成图像后,将子像素过滤到第三个“干净”缓冲区。或者一次一帧地做一大块或一行屏幕。优点:绘图的实时“粗略”视图,随时清理。

当您从粗略输出缓冲区转换为“干净”第三个缓冲区时,您可以清除粗糙到黑色。这样可以让您永久保持绘图而不会减速。

通过在“粗糙”上方绘制“干净”,或许颜色略有不同,你将拥有两全其美的效果。

这类似于绘画程序所做的 - 如果画得非常快,你会看到一个粗略的版本,那么绘画程序会在有时间时“清理”图像。


对算法的一些评论:

我在这个舞台上看过很多作弊。我在Sega Genesis仿真器上播放了Sonic,它可以进行上采样。它有一些非常疯狂的算法可以很好地工作并且非常快。

您实际上可以获得一些优势,因为您可能知道点上的亮度和半径。

您可能只看每个像素及其8个邻居,并根据子像素所在的亮度让这9个像素“投票”。


其他想法

当您控制激光指示器时,您的手并不准确。尝试每隔10帧左右获取所有点,确定哪些光束(基于先前的运动,并考虑新的点,关闭的激光,以及进入或离开视野的点),然后只绘制高分辨率曲线。不要担心输入中的子像素 - 只需将曲线绘制到高分辨率输出中。

使用Catmull-Rom样条曲线,它通过所有控制点。