我正在使用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上做这样的事情,而不是我可能会采用错误的方式。
有什么想法吗?
答案 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样条曲线,它通过所有控制点。