在不同视角的图像中找到射箭目标 [英] find archery target in image of different perspectives

查看:167
本文介绍了在不同视角的图像中找到射箭目标的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正试图找到一种方法来识别射箭目标以及照片上可能由不同视角构成的所有环:



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



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



我认为进行透视校正不是必要的任务,只要我能清楚地识别目标中的所有戒指。



我搜索了很长时间,发现一些论文都没有完全专注于这个特定的任务和对我来说太过数学了解。



是否有机会完成这项任务?你能和我分享一个如何解决这个问题的想法吗?非常感谢。



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

解决方案

首发参见





  • 填补空白



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




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



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





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


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

     图片pic0,pic1,pic2; 
    // pic0 - source
    // pic1 - 输出
    // pic2 - temp
    DWORD c0;
    int x,y,i,j,n,m,r,* hist;
    int x0,y0,rx,ry; // ellipse
    const int colors [4] = //来自中心的颜色序列
    {
    0x00FFFF00,// RGB yelow
    0x00FF0000,// RGB red
    0x000080FF ,// RGB蓝色
    0x00FFFFFF,// RGB白色
    };

    //初始输出为源图像,并将temp调整为相同大小
    pic1 = pic0;
    pic2 = pic0; pic2.clear(0);

    //重新着色图像(在HSV空间中 - > RGB)以避免噪音并选择目标像素
    pic1.rgb2hsv();
    for(y = 0; y< pic1.ys; y ++)
    for(x = 0; x< pic1.xs; x ++)
    {
    color c;
    int h,s,v;
    c = pic1.p [y] [x];
    h = c.db [picture :: _ h];
    s = c.db [picture :: _ s];
    v = c.db [picture :: _ v];
    if(v> 100)//足够亮的像素?
    {
    i = 25; // treshold
    if(abs(h-40)+ abs(s-225)< i)c.dd = colors [0]; // RGB yelow
    else if(abs(h-250)+ abs(s-165)< i)c.dd = colors [1]; // RGB red
    else if(abs(h-145)+ abs(s-215)< i)c.dd = colors [2]; // RGB blue
    else if(abs(h-145)+ abs(s-10)< i)c.dd = colors [3]; // RGB white
    else c.dd = 0x00000000; // RGB黑色表示未选择的像素
    }否则c.dd = 0x00000000; // RGB black
    pic1.p [y] [x] = c;
    }
    pic1.save(out0.png);
    //拟合椭圆:
    pic1.bmp-> 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)返回;
    for(j = 3; j> = 0; j--)
    {
    //每次通过选择颜色
    c0 = colors [j];
    //用H,V线填充间隙为临时pic2
    为(y = 0; y< pic1.ys; y ++)
    {
    for(x = 0; (X LT; pic1.xs)及及(pic1.p [Y] [X] .DD = C0!); X ++); X0 = X;
    for(x = pic1.xs-1;(x> x0)&&(pic1.p [y] [x] .dd!= c0); x--);
    for(; x0< x; x0 ++)pic2.p [y] [x0] .dd = c0;
    }
    for(x = 0; x< pic1.xs; x ++)
    {
    for(y = 0;(y for(y = pic1.ys-1;(y> y0)&&(pic1.p [y] [x] .dd!= c0); y--);
    for(; y0< y; y0 ++)pic2.p [y0] [x] .dd = c0;
    }
    如果(j == 3)继续; //不要继续使用边框
    //平均点(可能的中心)
    x0 = 0; Y0 = 0; N = 0;
    for(y = 0; y< pic2.ys; y ++)
    for(x = 0; x< pic2.xs; x ++)
    if(pic2.p [y] [x ] .dd == c0)
    {x0 + = x; Y0 + = Y;的n ++; }
    如果(!n)继续; //没有找到积分
    x0 / = n; Y0 / = N; // center
    //半径为
    的直方图(i = 0; i< m; i ++)hist [i] = 0;
    n = 0;
    for(y = 0; y< pic2.ys; y ++)
    for(x = 0; x< pic2.xs; x ++)
    if(pic2.p [y] [x ] .dd == c0)
    {
    r = sqrt(((x-x0)*(x-x0))+((y-y0)*(y-y0)));的n ++;
    hist [r] ++;
    }
    //为(r = 0,i = 0; i< m; i ++)
    if(hist [r]< hist [i])
    r = i;
    //从可能的中心投射线以找到边(并重新计算rx,ry)
    for(x = x0-r,y = y0;(x> = 0)&&(pic2。 p [Y] [X] .DD == C0); x--); RX = X; //扫描左边
    (x = x0 + r,y = y0;(x x0 =(rx + x)>> 1; RX =(X-RX)>大于1;
    for(x = x0,y = y0-r;(y> = 0)&&(pic2.p [y] [x] .dd == c0); y--); RY = Y; //向上扫描
    (x = x0,y = y0 + r;(y y0 =(ry + y)>> 1; RY =(Y-RY)GT;大于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;


    I'm trying to find a way to identify an archery target and all of its rings on a photo which might be made of different perspectives:

    My goal is to identify the target and later on also where the arrows hit the target to automatically count their score. Presumptions are as follows:

    • The camera's position is not fixed and might change
    • The archery target might also move or rotate slightly
    • The target might be of different size and have different amount of circles
    • There might be many holes (sometimes big scratches) in the target

    I have already tried OpenCV to find contours, but even with preprocessing (grayscale -> blur (-> threshold) -> edge detection) I still find a few houndred contours which are all distracted by the arrows or other obstacles (holes) on the target, so it is impossible to find a nice circular line. Using Hough to find circles doesn't work either as it will give me weired results as Hough will only find perfect circles and not ellipses.

    With preprocessing the image this is my best result so far:

    I was thinking about ellipse and circle fitting, but as I don't know radius, position and pose of the target this might be a very cpu consuming task. Another thought was about using recognition from a template, but the position and rotation of the target changes often.

    Now I have the idea to follow every line on the image to check if it is a curve and then guess which curves belong together to form a circle/ellipse (ellipse because of the perspective). The problem is that the lines might be intersected by arrows or holes in a short distance so the line would be too short to check if it is a curve. With the smaller circles on the target the chance is high that it isn't recognised at all. Also, as you can see, circle 8, 7 and 6 have no clear line on the left side.

    I think it is not neccessary to do perspective correction to achieve this task as long as I can clearly identify all the rings in the target.

    I googled a long time and found some thesis which are all not exactly focussed on this specific task and also too mathematical for me to understand.

    Is it by any chance possible to achieve this task? Could you share with me an idea how to solve this problem? Anything is very appreciated.

    I'm doing this in Java, but the programming language is secondary. Please let me know if you need more details.

    解决方案

    for starters see

    If you are using standardized target as on the image ( btw. I use these same too for my bow :) ) then do not cut off the color. You can select the regions of blue red and yellow pixels to ease up the detection. see:

    From that you need to fit the circles. But as you got perspective then the objects are not circles nor ellipses. You got 2 options:

    1. Perspective correction

      Use right bottom table rectangle area as marker (or the whole target). It is rectangle with known aspect ratio. so measure it on image and construct transformation that will change the image so it became rectangle again. There are tons of stuff about this: 3D scene reconstruction so google/read/implement. The basic are based just on De-skew + scaling.

    2. Approximate circles by ellipses (not axis aligned!)

      so fit ellipses to found edges instead circles. This will not be as precise but still close enough. see:

    [Edit1] sorry did not have time/mood for this for a while

    As you were unable to adapt my approach yourself here it is:

    1. remove noise

      you need to recolor your image to remove noise to ease up the rest... I convert it to HSV and detect your 4 colors (circles+paper) by simple tresholding and recolor the image to 4 colors (circles,paper,background) back into RGB space.

    2. fill the gaps

      in some temp image I fill the gaps in circles created by arrows and stuff. It is simple just scan pixels from opposite sides of image (in each line/row) and stop if hit selected circle color (you need to go from outer circles to inner not to overwrite the previous ones...). Now just fill the space between these two points with your selected circle color. (I start with paper, then blue,red and yellow last):

    3. now you can use the linked approach

      So find avg point of each color, that is approx circle center. Then do a histogram of radius-es and chose the biggest one. From here just cast lines out of the circle and find where the circle really stops and compute the ellipse semi-axises from it and also update the center (that handles the perspective distortions). To visually check I render cross and circle for each circle into the image from #1:

      As you can see it is pretty close. If you need even better match then cast more lines (not just 90 degree H,V lines) to obtain more points and compute ellipse algebraically or fit it by approximation (second link)

    C++ code (for explanations look into first link):

    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;y<pic1.ys;y++)
     for (x=0;x<pic1.xs;x++)
        {
        color c;
        int h,s,v;
        c=pic1.p[y][x];
        h=c.db[picture::_h];
        s=c.db[picture::_s];
        v=c.db[picture::_v];
        if (v>100)  // bright enough pixels?
            {
            i=25; // treshold
                 if (abs(h- 40)+abs(s-225)<i) c.dd=colors[0]; // RGB yelow
            else if (abs(h-250)+abs(s-165)<i) c.dd=colors[1]; // RGB red
            else if (abs(h-145)+abs(s-215)<i) c.dd=colors[2]; // RGB blue
            else if (abs(h-145)+abs(s- 10)<i) c.dd=colors[3]; // RGB white
            else                              c.dd=0x00000000; // RGB black means unselected pixels
            } else                            c.dd=0x00000000; // RGB black
        pic1.p[y][x]=c;
        }
    pic1.save("out0.png");
    // fit ellipses:
    pic1.bmp->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<pic1.ys;y++)
            {
            for (x=        0;(x<pic1.xs)&&(pic1.p[y][x].dd!=c0);x++); x0=x;
            for (x=pic1.xs-1;(x>     x0)&&(pic1.p[y][x].dd!=c0);x--);
            for (;x0<x;x0++) pic2.p[y][x0].dd=c0;
            }
        for (x=0;x<pic1.xs;x++)
            {
            for (y=        0;(y<pic1.ys)&&(pic1.p[y][x].dd!=c0);y++); y0=y;
            for (y=pic1.ys-1;(y>     y0)&&(pic1.p[y][x].dd!=c0);y--);
            for (;y0<y;y0++) pic2.p[y0][x].dd=c0;
            }
        if (j==3) continue; // do not continue for border
        // avg point (possible center)
        x0=0; y0=0; n=0;
        for (y=0;y<pic2.ys;y++)
         for (x=0;x<pic2.xs;x++)
          if (pic2.p[y][x].dd==c0)
           { x0+=x; y0+=y; n++; }
        if (!n) continue;               // no points found
        x0/=n; y0/=n;                   // center
        // histogram of radius
        for (i=0;i<m;i++) hist[i]=0;
        n=0;
        for (y=0;y<pic2.ys;y++)
         for (x=0;x<pic2.xs;x++)
          if (pic2.p[y][x].dd==c0)
            {
            r=sqrt(((x-x0)*(x-x0))+((y-y0)*(y-y0))); n++;
            hist[r]++;
            }
        // select most occurent radius (biggest)
        for (r=0,i=0;i<m;i++)
         if (hist[r]<hist[i])
          r=i;
        // cast lines from possible center to find edges (and recompute rx,ry)
        for (x=x0-r,y=y0;(x>=     0)&&(pic2.p[y][x].dd==c0);x--); rx=x; // scan left
        for (x=x0+r,y=y0;(x<pic2.xs)&&(pic2.p[y][x].dd==c0);x++);       // scan right
        x0=(rx+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<pic2.ys)&&(pic2.p[y][x].dd==c0);y++);       // scan down
        y0=(ry+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;
    

    这篇关于在不同视角的图像中找到射箭目标的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆