从纸质靶标检测圆圈和射击点

我正在做一个小项目,需要从给定的纸质靶标图像中检测得分情况。这类似于iPhone上的TargetScan应用程序。

我使用OpenCV来处理图像,项目分为两个部分:一部分是检测靶标上的圆圈(使用霍夫圆变换效果很好),另一部分是检测射击点。我需要一些关于如何从给定图像中检测这些射击点的主意。以下是一个带有圆圈检测的示例图像(绿色线条表示检测到的圆圈,红色点表示中心)。OpenCV中可以使用哪些算法来检测这些射击点?enter image description here

这里是另一个示例图像enter image description here


回答:

算法:

  1. 创建/清除图像的掩码
  2. 二值化图像(根据某个强度阈值转换为黑白)
  3. 处理所有像素
  4. 计算x,y方向上相同颜色的像素数量

    称之为wx,wy

  5. 检测圆圈、射击点和中间部分

    圆圈较细,因此wxwy应小于细阈值,而另一个应较大。射击点较大,因此wxwy都必须在射击点直径范围内。中间部分是黑色的,且wx,wy都超过所有阈值(可以在这里计算平均点)。将这些信息存储到掩码中

  6. 使用掩码信息重新着色图像

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

    中心是中间部分区域的平均点,现在处理所有绿色点并计算其半径。对所有找到的半径进行直方图统计,并按计数降序排序。计数应与2*PI*r一致,否则忽略此类点。

  8. 将射击点像素分组

    因此,进行分割或泛洪填充重新着色每个命中点,以避免对单个射击点进行多次计数

我用C++编写了#1..#6部分的代码,供娱乐使用,代码如下:

    picture pic0,pic1,pic2;        // pic0 - 源图像        // pic1 - 输出图像        // pic2 - 掩码    int x,y,i,n,wx,wy;    int r0=3;           // 细曲线宽度阈值 [像素]    int r1a=15;         // 射击点直径最小阈值 [像素]    int r1b=30;         // 射击点直径最大阈值 [像素]    int x0,y0;          // 平均点 == 中心    // 初始化输出图像为源图像,但仅保留灰度强度    pic1=pic0;    pic1.rgb2i();    // 初始化掩码(与源图像大小相同)    pic2.resize(pic0.xs,pic0.ys);    pic2.clear(0);    // 二值化图像并转换回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) // 黑/白阈值 <0,765>           pic1.p[y][x].dd=0x00000000; // RGB中的黑色      else pic1.p[y][x].dd=0x00FFFFFF; // RGB中的白色    // 处理像素    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;   // 计算列中相同颜色的像素数量        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;   // 计算行中相同颜色的像素数量        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 ((wx>=r0)||(wy>=r0))    // 但仍然是线            {            pic2.p[y][x].dd=1;      // 细线            }        if (pic1.p[y][x].dd==0)     // 黑色         if ((wx>=r0)&&(wy>=r0))    // 且在两个轴上都较厚            {            pic2.p[y][x].dd=2;      // 中间部分            x0+=x; y0+=y; n++;            }        if (pic1.p[y][x].dd)        // 白色(背景颜色)        if ((wx>r1a)&&(wy>r1a))     // 大小在射击点范围内         if ((wx<r1b)&&(wy<r1b))            {            pic2.p[y][x].dd=3;      // 射击点            }        }     if (n) { x0/=n; y0/=n; }    // 将掩码数据(重新着色)添加到输出图像中//  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; // 绿色细线        if (pic2.p[y][x].dd==2) pic1.p[y][x].dd=0x000000FF; // 蓝色中间部分        if (pic2.p[y][x].dd==3) pic1.p[y][x].dd=0x00FF0000; // 红色射击点        }    // 中心十字线    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);

我使用自己的picture类来处理图像,因此一些成员如下:

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

这是重新着色后的结果

example

  • 绿色 – 细圆圈
  • 蓝色 – 中间部分
  • 红色十字 – 圆圈中心
  • 红色 – 射击点

如您所见,它需要进一步处理第#7、#8步的操作,并且您的图像中没有中间部分以外的射击点,因此可能还需要调整以检测中间部分以外的射击点

[edit1] 半径

// 创建并清除半径直方图n=xs; if (n<ys) n=ys;int *hist=new int[n];for (i=0;i<n;i++) hist[i]=0;// 计算直方图for (y=0;y<pic2.ys;y++) for (x=0;x<pic2.xs;x++)  if (pic2.p[y][x].dd==1)   // 细像素    {    i=sqrt(((x-x0)*(x-x0))+((y-y0)*(y-y0)));    hist[i]++;    }// 合并邻近半径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;    }// 绘制有效圆圈pic1.bmp->Canvas->Pen->Color=0xFF00FF;  // 品红色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;

radius circles

检测到的圆圈是品红色…我觉得效果不错。中间部分稍微影响了一些结果。您可以计算平均半径步长并插值缺失的圆圈…

Related Posts

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注