从纸目标检测圆圈和镜头

Boj*_*ski 5 opencv artificial-intelligence image image-processing computer-vision

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

我正在使用 openCV 来处理图像,基本上我有两个部分,一个是从目标检测圆圈(这与 Hough Circle Transform 一起工作得很好),第二部分是检测镜头。我需要一些想法如何从给定的图像中检测这些镜头。这是一个圆形检测开启的示例图像(检测到的圆圈为绿线,中心为红点)。openCV 中的哪些算法可用于检测这些芽? 在此处输入图片说明

这是另一个示例图像 在此处输入图片说明

Spe*_*tre 5

某物:

  1. 创建/清除图像蒙版
  2. 二值化图像(通过某个强度阈值变成黑白)
  3. 处理所有像素
  4. 计算x,y方向上有多少相同颜色的像素

    称它为 wx,wy

  5. 检测圆,镜头和中间部分

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

  6. 使用蒙版信息重新着色图像

  7. 从找到的点计算圆的中心和半径

    center 是中间截面区域的平均点,现在处理所有绿点并为其计算半径。对所有找到的半径做直方图,并按计数降序对其进行排序。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);
Run Code Online (Sandbox Code Playgroud)

我对图像使用我自己的图片类,因此一些成员是:


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;
Run Code Online (Sandbox Code Playgroud)

半径圆

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