检测纸质目标的圆圈和射击

Detecting circles and shots from paper target

我正在做一个小项目,我必须检测从给定的纸质目标图像中得分的点。类似于 iPhone 的 TargetScan 应用程序。

我正在使用 openCV 处理图像,基本上我有两个部分,一个是检测来自目标的圆(使用 Hough Circle Transform 效果很好),第二个部分是检测镜头。我需要一些想法如何从给定图像中检测这些镜头。这是一个启用了圆圈检测的示例图像(绿线表示检测到的圆圈,红点表示中心)。 openCV 的哪些算法可用于检测这些芽?

这是另一个示例图片

算法:

  1. create/clear 图像遮罩
  2. 二值化图像(通过某个强度阈值变为黑白)
  3. 处理所有像素
  4. 计算在x,y个方向上有多少个相同颜色的像素

    称之为wx,wy

  5. 检测圆、镜头和中段

    圆圈 很薄,所以 wxwy 应该小于薄阈值,另一个应该更大。 射门 很大,因此展位 wxwy 必须在射门直径范围内。 中间部分 是黑色的,展位 wx,wy 高于所有阈值(您可以在此处计算平均点)。将此信息存储到 mask

  6. 使用遮罩信息重新着色图像

  7. 根据找到的点计算圆心和半径

    center是mid section area的平均点,现在处理所有的绿点并计算它的半径。对所有找到的半径做直方图并按计数降序排序。如果不忽略这些点,计数应该与2*PI*r一致。

  8. 将拍摄像素组合在一起

    所以分割或洪水填充重新着色每个命中以避免单次射击的多重计算

我为了好玩用 C++ 编写了#1..#6,代码如下:

    picture pic0,pic1,pic2;
        // pic0 - source
        // pic1 - output
        // pic2 - mask
    int x,y,i,n,wx,wy;
    int r0=3;           // thin curve wide treshod [pixels]
    int r1a=15;         // shot diameter min treshod [pixels]
    int r1b=30;         // shot diameter max treshod [pixels]
    int x0,y0;          // avg point == center
    // init output as source image but in grayscale intensity only
    pic1=pic0;
    pic1.rgb2i();
    // init mask (size of source image)
    pic2.resize(pic0.xs,pic0.ys);
    pic2.clear(0);
    // binarize image and convert back to RGB
    for (y=r0;y<pic1.ys-r0-1;y++)
     for (x=r0;x<pic1.xs-r0-1;x++)
      if (pic1.p[y][x].dd<=500) // Black/White treshold <0,765>
           pic1.p[y][x].dd=0x00000000; // Black in RGB
      else pic1.p[y][x].dd=0x00FFFFFF; // White in RGB
    // process pixels
    x0=0; y0=0; n=0;
    for (y=r1b;y<pic1.ys-r1b-1;y++)
     for (x=r1b;x<pic1.xs-r1b-1;x++)
        {
        wy=1;   // count the same color pixels in column
        for (i=1;i<=r1b;i++) if (pic1.p[y-i][x].dd==pic1.p[y][x].dd) wy++; else break;
        for (i=1;i<=r1b;i++) if (pic1.p[y+i][x].dd==pic1.p[y][x].dd) wy++; else break;
        wx=1;   // count the same color pixels in line
        for (i=1;i<=r1b;i++) if (pic1.p[y][x-i].dd==pic1.p[y][x].dd) wx++; else break;
        for (i=1;i<=r1b;i++) if (pic1.p[y][x+i].dd==pic1.p[y][x].dd) wx++; else break;
        if ((wx<r0)||(wy<r0))       // if thin
         if ((wx>=r0)||(wy>=r0))    // but still line
            {
            pic2.p[y][x].dd=1;      // thin line
            }
        if (pic1.p[y][x].dd==0)     // black
         if ((wx>=r0)&&(wy>=r0))    // and thick in both axises
            {
            pic2.p[y][x].dd=2;      // middle section
            x0+=x; y0+=y; n++;
            }
        if (pic1.p[y][x].dd)        // white (background color)
        if ((wx>r1a)&&(wy>r1a))     // size in range of shot
         if ((wx<r1b)&&(wy<r1b))
            {
            pic2.p[y][x].dd=3;      // shot
            }
        }
     if (n) { x0/=n; y0/=n; }

    // add mask data (recolor) to output image
//  if (0)
    for (y=0;y<pic1.ys;y++)
     for (x=0;x<pic1.xs;x++)
        {
        if (pic2.p[y][x].dd==1) pic1.p[y][x].dd=0x0000FF00; // green thin line
        if (pic2.p[y][x].dd==2) pic1.p[y][x].dd=0x000000FF; // blue midle section
        if (pic2.p[y][x].dd==3) pic1.p[y][x].dd=0x00FF0000; // red shots
        }

    // Center cross
    i=25;
    pic1.bmp->Canvas->Pen->Color=0x0000FF;
    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);

我用的是自己的图片class所以部分会员是:


xs,ys 图像大小(以像素为单位)
p[y][x].dd(x,y) 位置的像素,为 32 位整数类型
clear(color) - 清除整个图像
resize(xs,ys) - 将图像调整为新分辨率

这是重新着色的结果

  • 绿色 - 细圆圈
  • 蓝色中段
  • 红叉(圆心)
  • 红色 - 镜头

如您所见,它需要对项目符号 #7、#8 进行进一步处理,而且您的图像在中间部分之外没有拍摄,因此可能也需要对中间部分以外的拍摄检测进行一些调整

[edit1] 半径

// create & clear radius histogram
n=xs; if (n<ys) n=ys;
int *hist=new int[n];
for (i=0;i<n;i++) hist[i]=0;
// compute histogram
for (y=0;y<pic2.ys;y++)
 for (x=0;x<pic2.xs;x++)
  if (pic2.p[y][x].dd==1)   // thin pixels
    {
    i=sqrt(((x-x0)*(x-x0))+((y-y0)*(y-y0)));
    hist[i]++;
    }
// merge neigbour radiuses
for (i=0;i<n;i++)
 if (hist[i])
    {
    for (x=i;x<n;x++) if (!hist[x]) break;
    for (wx=0,y=i;y<x;y++) { wx+=hist[y]; hist[y]=0; }
    hist[(i+x-1)>>1]=wx; i=x-1;
    }
// draw the valid circles
pic1.bmp->Canvas->Pen->Color=0xFF00FF;  // magenta
pic1.bmp->Canvas->Pen->Width=r0;
pic1.bmp->Canvas->Brush->Style=bsClear;
for (i=0;i<n;i++)
 if (hist[i])
    {
    float a=float(hist[i])/(2.0*M_PI*float(i));
    if ((a>=0.3)&&(a<=2.1))
     pic1.bmp->Canvas->Ellipse(x0-i,y0-i,x0+i,y0+i);
    }
pic1.bmp->Canvas->Brush->Style=bsSolid;
pic1.bmp->Canvas->Pen->Width=1;
delete[] hist;

检测到的圆圈是洋红色的……我觉得很好。中间部分稍微拧一下。您可以计算平均半径步长并插入缺失的圆...