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

标签 image opencv image-processing computer-vision artificial-intelligence

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

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

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

最佳答案

算法:

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

    称之为wx,wy

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

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

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

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

    中心是中间区域的平均点,现在处理所有的绿点并计算它的半径。对所有找到的半径做直方图并按计数降序排序。如果不忽略这些点,计数应该与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);

我使用我自己的图像类,所以一些成员是:


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

这是重新着色的结果

example

  • 绿色 - 细圆圈
  • 蓝色中间部分
  • 红十字(圆心)
  • 红色 - 镜头

如您所见,它需要对项目符号 #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;

radius circles

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

关于image - 检测纸质目标的圆圈和射击,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30422840/

相关文章:

opencv - 在 OpenCV 中针对最大值和最小值范围对图像进行阈值处理

c# - 从字节数组数据中获取图像的宽度和高度

css:浏览器缩放时图像出现问题

Java AffinetransformOp filter() 搞砸了我的形象

android - 如何在 Android 的 ImageView 中显示 gif 图像?

python - 如何将pygame中的3d数组转换为opencv python中的有效输入?

c++ - 与 CAM 的距离无关的人脸识别

iphone - 识别绘制图像的最佳方法

c++ - 检查一张图片是否是另一张图片的移位

c++ - 如何检测rgb图像格式真的是灰色的?