2009-07-20 39 views
10

我使用XNA构建了一个项目,在该项目中,我可以使用LCD投影仪和单色相机在墙上绘制“涂鸦”,该相机仅过滤手持激光点指针。我想使用任何数量的激光指示器 - 在这一点上并不真正在意区分它们。快速亚像素激光点检测

壁为10' ×10' ,并且摄像头只有640×480,所以我尝试所概述此处使用样条曲线以使用子像素测量:tpub.com

相机以120fps的运行(8所以我的问题都是找到亚像素激光点中心的最快方法。目前,我正在使用蛮力二维搜索在样条插值之前在图像上找到最亮的像素(0 - 254)。这种方法不是非常快,每个帧花费的时间比计算机长得多。

编辑:为了说明问题,最后我的相机数据由表示像素亮度的二维数组表示。

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

但是出于参数的原因,我们假设我使用蛮力发现了最亮的像素,然后使用texcoords将它们及其相邻像素的样条曲线存储到X个顶点中。那么使用HLSL来计算使用texcoords的样条曲线是否可行?

我也对我的XNA盒以外的建议持开放态度,不管是DX10/DX11,也可能是某种FPGA,等等。我只是没有太多关于如何以这种方式处理数据的经验。我想,如果他们可以在使用2节AA电池的Wii-Mote上做这样的事情,那么我可能会以错误的方式进行这样的尝试。

任何想法?

+0

什么是慢,做扫描或做样条插值之后? – Nosredna 2009-07-20 14:24:11

+0

样条插值到目前为止是它的最慢分量,取决于我用什么样的delta来评估样条。在这种情况下,我想要0.1px的精度。 – bufferz 2009-07-20 14:46:45

+0

我应该补充说,如果我有20个激光器,那么样条计算会变得非常非常昂贵。 – bufferz 2009-07-20 14:48:23

回答

3

如果你想要子像素的准确性,你正在处理一些非常复杂的数学。我认为this paper是需要考虑的事情。不幸的是,您必须付费才能使用该网站进行查看。如果您有权访问合适的图书馆,他们可能会为您抓住它。

原始帖子中的链接建议为每个轴进行1000次样条计算 - 它独立处理x和y,这对圆形图像是正确的,但如果图像是偏斜的椭圆,则会有点偏离。你可以使用以下方法来得到一个合理的估计:

X Ç = SUM(X ñ .F(X ñ))/总和(F(X ñ))

其中x ç是均值,X ñ是沿x轴和F(X ñ)的一个点在点x ñ的值。因此,对于这样的:

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

给出:

总和(X Ñ .F(X Ñ))= 1 * 2 + 3 * 3 + 4 * 9 + 5×10 + 6 * 4 + 7 * 1个

总和(F(X ñ))= 1 + 3 + 9 + 10 + 4 + 1

X ç =28分之128= 4.57

并对y轴重复。

5

如果通过暴力强制你的意思是独立地看每个像素,它基本上是唯一的方法。无论您想要如何处理图像,您都必须扫描所有图像像素。尽管您可能不需要查找最亮的像素,但可以按照颜色过滤图像(例如:如果使用红色激光)。这很容易使用HSV颜色编码的图像完成。如果您正在寻找更快的算法,请尝试OpenCV。它已经一次又一次地优化了图像处理,你可以在C#中通过包装使用它:

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

的OpenCV也可以帮助你轻松地找到点中心和跟踪每一个点。

您有使用120fps相机的原因吗?你知道人眼只能看到约30fps的权利?我猜这是为了追随非常快的激光运动......你可能想考虑把它降下来,因为120fps的实时处理将非常难以达到。

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

这是从我的头顶这么难过错误; ^)

3

蛮力是唯一真正的方法,但是你使用着色器的想法是好的 - 你会卸载CPU的蛮力检查,它只能查看少量的像素(大约每个核心1个)到GPU,这很可能有100多个哑核(管道)可以同时比较像素(你的算法可能需要修改一点,以配合1指令 - GPU的多核安排)。

我看到的最大问题是您是否可以将数据快速移动到GPU。

1

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

(轻微的失焦相机将帮助只是最亮的点,减少误报,如果你有一个繁忙的面......当然,假设你是不是拍摄光滑/平坦表面)

2

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

很显然,在程序可以扫描的速度有多快,在摄像头“丢失”指针之前移动鼠标的速度有多快,并且必须转到缓慢的全图像扫描。一点实验可能会揭示最佳值。

很酷的项目,顺便说一下。

1

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

outbuff = max(outbuff,inbuff);

当您完成图像时,对第三个“干净”缓冲区进行亚像素过滤。或者实时地做一个块或一行屏幕。优点:图形的实时“粗糙”视图,随时清理。

当您从粗略输出缓冲区转换为“干净”第三缓冲区时,您可以将粗糙清除为黑色。这可以让你永远保持绘画而不会放慢速度。

通过在“粗糙”顶部绘制“干净”,也许颜色略有不同,您将拥有两全其美的效果。

这与绘制程序所做的相似 - 如果绘制速度非常快,则会看到粗糙的版本,然后绘图程序会在图像有时间时“清理”图像。


对算法的一些评论:

我已经看到了很多作弊的在这个舞台上。我在上世纪的Sega Genesis仿真器上玩过Sonic。它有一些非常漂亮的算法,工作得很好,而且速度非常快。

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

你可能只是看看每个像素及其8个邻居,并让这9个像素根据它们的亮度来“投票”子像素在哪里。


其他的想法

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

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