算法:
- 为图像创建/清除掩模
- 将图像二值化(通过某个强度阈值转为黑白)
- 处理所有像素
计算同一颜色像素在 x、y 方向上有多少个
称之为 wx, wy
检测圆形、射击和中间部分
圆形很细,因此 wx
或 wy
应小于细阈值,另一个应较大。 射击很大,因此 wx
和 wy
都必须在射击直径范围内。 中间部分为黑色,wx, wy
均超过阈值(可以在此处计算平均点)。将此信息存储在掩模中
使用掩模信息重新着色图像
从找到的点计算圆的中心和半径
中心是中间部分区域的平均点,现在处理所有绿色点并为其计算半径。对于所有找到的半径进行直方图,按计数降序排序。如果计数与 2*PI*r
不一致,则忽略此类点。
将射击像素组合在一起
因此,对于每个命中点进行分割或泛洪重新着色,以避免单个射击的多次计算
我用 C++ 乐在其中编写了 #1..#6 的代码,以下是代码:
picture 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);
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)
pic1.p[y][x].dd=0x00000000;
else pic1.p[y][x].dd=0x00FFFFFF;
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; }
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);
我使用自己的图片类处理图像,其中一些成员如下:
xs,ys
图像大小(以像素为单位)
p[y][x].dd
是位于(x,y)
位置的像素,类型为32位整数
clear(color)
- 清除整个图像
resize(xs,ys)
- 将图像调整为新分辨率
这是重新上色的结果
- 绿色 - 细圆圈
- 蓝色 - 中间部分
- 红色 - 十字架(圆圈中心)
- 红色 - 子弹
正如您所看到的,它需要从#7、#8子弹进一步处理,而且您的图像在中间部分之外没有射击,因此可能需要对中间部分之外的射击检测进行一些微调。
[编辑1] 半径
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;
检测到的圆是品红色的...我认为这很不错。中间部分有些问题。您可以计算平均半径步长并插值缺失的圆...