在不同视角的图像中找到射箭目标

我正试图找到一种方法来识别射箭目标和照片上的所有环,这些环可能由不同的视角组成:
在此处输入图像描述

我的目标是确定目标,然后在箭头击中目标的位置自动计算其得分。 推定如下:

  • 相机的位置不固定,可能会改变
  • 射箭目标也可能会轻微移动或旋转
  • 目标可能具有不同的大小并且具有不同的圆圈数量
  • 目标中可能有许多洞(有时是大划痕)

我已经尝试过使用OpenCV来查找轮廓,但即使使用预处理(灰度 – >模糊( – >阈值) – >边缘检测),我仍然会发现一些houndred轮廓,它们都被箭头或其他障碍物(孔)分散注意力。目标,所以不可能找到一个漂亮的圆形线。 使用霍夫找到圆圈也不起作用,因为它会给我我的结果,因为霍夫只能找到完美的圆而不是椭圆。

通过预处理图像,这是我迄今为止的最佳结果:

预处理图像

我正在考虑椭圆和圆拟合,但由于我不知道目标的半径,位置和姿势,这可能是一个非常麻烦的任务。 另一个想法是关于使用模板识别,但目标的位置和旋转经常变化。

现在我有想法跟随图像上的每一行来检查它是否是一条曲线然后猜测哪些曲线属于一起形成一个圆/椭圆(椭圆因为透视)。 问题是线条可能在短距离内被箭头或孔相交,因此线条太短而无法检查它是否是曲线。 如果目标上的圆圈较小,则根本无法识别它的机会很高。 此外,如您所见,圆圈8,7和6在左侧没有清晰的线条。

我认为只要能够清楚地识别目标中的所有环,就不需要进行透视校正来完成这项任务。

我google了很长时间,发现一些论文都没有完全专注于这个特定的任务,也太过数学让我无法理解。

是否有可能实现这一任务? 你能和我分享一个如何解决这个问题的想法吗? 任何事情都非常感激。

我在Java中这样做,但编程语言是次要的。 如果您需要更多详细信息,请与我们联系。

对于初学者来说

  • 从纸张目标中检测圆圈和镜头 。

如果您在图像上使用标准化目标(顺便说一句。我也使用这些也是我的弓:))然后不要切断颜色。 您可以选择蓝色红色和黄色像素的区域以简化检测。 看到:

  • 足迹拟合

从那你需要适合圈子。 但是当你获得透视时,物体不是圆形也不是椭圆形。 你有两个选择:

  1. 透视校正

    使用右下表矩形区域作为标记(或整个目标)。 它是已知宽高比的矩形。 所以在图像上测量它并构造将改变图像的变换,使其再次变成矩形。 关于这一点有很多东西: 3D场景重建,所以谷歌/阅读/实施。 基础仅基于De-skew + scaling。

  2. 椭圆形近似圆(不是轴对齐!)

    所以将椭圆拟合到找到的边而不是圆。 这不会那么精确,但仍然足够接近。 看到:

    • 椭圆拟合

[编辑1]抱歉没有时间/心情这一段时间

由于你无法在这里自己调整我的方法,它是:

  1. 消除噪音

    你需要重新着色你的图像,以消除噪音,以缓解其余…我将其转换为HSV并通过简单的阈值检测你的4种颜色(圆圈+纸张),并将图像重新着色为4种颜色(圆圈,纸张,背景)进入RGB空间。

    结果

  2. 填补空白

    在一些临时图像中,我填补了由箭头和东西创建的圆圈中的空白。 只需从图像的相对两侧(每行/每行)扫描像素,如果点击选定的圆形颜色就停止(你需要从外圆到内部不要覆盖以前的那些……)。 现在,用您选择的圆形颜色填充这两个点之间的空间。 (我从纸张开始,然后是蓝色,红色和黄色):

    结果

  3. 现在你可以使用链接的方法

    因此,找到每种颜色的平均点,即大约圆心。 然后做一个radius-es直方图并选择最大的直方图。 从这里只是从圆圈中抛出线条并找到圆圈真正停止的位置并从中计算椭圆半轴并更新中心(处理透视扭曲)。 为了目测检查,我将每个圆圈的十字和圆形渲染到#1的图像中:

    结果

    你可以看到它非常接近。 如果你需要更好的匹配,那么投射更多的线(不仅仅是90度H,V线)来获得更多的点并用代数计算椭圆或通过近似拟合它(第二个链接)

C ++代码(解释见第一个链接):

picture pic0,pic1,pic2; // pic0 - source // pic1 - output // pic2 - temp DWORD c0; int x,y,i,j,n,m,r,*hist; int x0,y0,rx,ry; // ellipse const int colors[4]=// color sequence from center { 0x00FFFF00, // RGB yelow 0x00FF0000, // RGB red 0x000080FF, // RGB blue 0x00FFFFFF, // RGB White }; // init output as source image and resize temp to same size pic1=pic0; pic2=pic0; pic2.clear(0); // recolor image (in HSV space -> RGB) to avoid noise and select target pixels pic1.rgb2hsv(); for (y=0;y100) // bright enough pixels? { i=25; // treshold if (abs(h- 40)+abs(s-225)Canvas->Pen->Width=3; pic1.bmp->Canvas->Pen->Color=0x0000FF00; pic1.bmp->Canvas->Brush->Style=bsClear; m=(pic1.xs+pic1.ys)*2; hist=new int[m]; if (hist==NULL) return; for (j=3;j>=0;j--) { // select color per pass c0=colors[j]; // fill the gaps with H,V lines into temp pic2 for (y=0;y x0)&&(pic1.p[y][x].dd!=c0);x--); for (;x0 y0)&&(pic1.p[y][x].dd!=c0);y--); for (;y0= 0)&&(pic2.p[y][x].dd==c0);x--); rx=x; // scan left for (x=x0+r,y=y0;(x>1; rx=(x-rx)>>1; for (x=x0,y=y0-r;(y>= 0)&&(pic2.p[y][x].dd==c0);y--); ry=y; // scan up for (x=x0,y=y0+r;(y>1; ry=(y-ry)>>1; i=10; pic1.bmp->Canvas->MoveTo(x0-i,y0); pic1.bmp->Canvas->LineTo(x0+i,y0); pic1.bmp->Canvas->MoveTo(x0,y0-i); pic1.bmp->Canvas->LineTo(x0,y0+i); //rx=r; ry=r; pic1.bmp->Canvas->Ellipse(x0-rx,y0-ry,x0+rx,y0+ry); } pic2.save("out1.png"); pic1.save("out2.png"); pic1.bmp->Canvas->Pen->Width=1; pic1.bmp->Canvas->Brush->Style=bsSolid; delete[] hist;