如何从Image中检测机器人方向?

Sar*_*oon 6 java image-processing computer-vision boofcv

我正在开发可以在玉米植物中运行并由罗盘传感器引导的机器人,但我想将相机应用为机器人的眼睛,并使用图像处理来检测运动的误差角度.

这是图像示例.

处理过的图像 处理过的图像 原始图像 原始图像 分割图像 分割图像

我使用以下步骤

  1. 第1步:我使用的当前技术是将颜色值转换为从此代码修改的HSV

  2. 步骤2:所以它将检测所选择的颜色,即褐色或污垢颜色,然后我收集最左右两个棕色或两个阵列中每个图像行的选定颜色(红点).

  3. 步骤3:我将2线性回归线绘制为蓝点,并将交点计算为粉红点
  4. 第4步:绘制绿线以将粉红点与其他图像进行比较.我不知道该如何处理这条绿线
  5. 问题是玉米叶子之间也存在污垢或棕色,然后我让我的代码错过计算

问题是如何过滤掉玉米叶之间的棕色像素或不在玉米路径中的其他区域?我应该在这个问题上学习或应用哪种算法或方法?

编辑1:使用Spektre的答案,它看起来更好

这是我用JAVA + Boofcv应用它之后的结果

  • 第1步:阈值处理或颜色分割 阈值处理或颜色分割

  • 第2步:模糊(使用高斯和中值滤波器) 所迷离

  • 第3步:绘制线性回归 绘制线性回归

更多信息

完全代码在这里

LinearRegression类

具有相同过程的10个示例图像

Spe*_*tre 3

对于您的源图像

来源

我会:

  1. 创建棕色东西的面具

    只要阈值H,S并忽略V,你已经拥有了这个。我使用整数255而不是颜色(蓝色)进行后面的计算。

    面具

  2. 模糊面具

    这将删除小簇错误选择的零件。之后,您应该再次对蒙版进行阈值处理,因为除非您完全选择了区域,否则蒙版值会稍小一些255。面积越大,值越大(越接近255)。我与>=150

  3. 通过水平线扫描掩模

  4. 对于每条线找到所有选定像素的重心

    再次模糊和阈值处理后,使用过的蒙版位于Aqua中。x因此计算每行中所有屏蔽像素的平均点坐标。该点用白色标记。

  5. 通过所有重心的回归线

    我使用我的近似搜索来进行此搜索,但您可以使用您想要的任何回归。回归线用红色标记

    我使用了直线方程x=x0+y*dx,其中y=<0,pic1.ys>. 并按间隔搜索解:

    x0=<-pic1.xs,+2*pic1.xs>
    dx=<-10,+10>
    
    Run Code Online (Sandbox Code Playgroud)

pic1.xs,pic1.ys图像分辨率在哪里。正如您所看到的,我没有涵盖所有角度,但我认为这无论如何都不适用于这些边缘情况(接近水平方向)。对于这种情况,您应该在垂直线上执行此操作并使用x=y0+x*dy

最后结果

这里是我使用的C++源代码:

    picture pic0,pic1;
        // pic0 - source img
        // pic1 - output img
    int x,y,h,s,v,px,pn,*p;
    color c;
    // copy source image to output
    pic1=pic0;
    pic1.save("cornbot0.png");
    // create brown stuff mask
    for (y=0;y<pic1.ys;y++)             // scan all H lines
     for (x=0;x<pic1.xs;x++)            // scan actual H line
        {
        c=pic1.p[y][x];                 // get pixel color
        rgb2hsv(c);                     // in HSV
        h=WORD(c.db[picture::_h]);
        s=WORD(c.db[picture::_s]);
        v=WORD(c.db[picture::_v]);
        // Treshold brownish stuff
        if ((abs(h- 20)<10)&&(abs(s-200)<50)) c.dd=255; else c.dd=0;
        pic1.p[y][x]=c;
        }
    pic1.save("cornbot1.png");
    pic1.smooth(10);                    // blur a bit to remove small clusters as marked
    pic1.save("cornbot2.png");

    // compute centers of gravity
    p=new int[pic1.ys];                 // make space for points
    for (y=0;y<pic1.ys;y++)             // scan all H lines
        {
        px=0; pn=0;                     // init center of gravity (avg point) variables
        for (x=0;x<pic1.xs;x++)         // scan actual H line
         if (pic1.p[y][x].dd>=150)      // use marked points only
            {
            px+=x; pn++;                // add it to avg point
            pic1.p[y][x].dd=0x00004080; // mark used points (after smooth) with Aqua
            }
        if (pn)                         // finish avg point computation
            {
            px/=pn;
            pic1.p[y][px].dd=0x00FFFFFF;// mark it by White
            p[y]=px;                    // store result for line regression
            } else p[y]=-1;             // uncomputed value
        }

    // regress line
    approx x0,dx;
    double ee;
    for (x0.init(-pic1.xs,pic1.xs<<1,100,3,&ee); !x0.done; x0.step())   // search x0
     for (dx.init(-10.0   ,+10.0     ,1.0,3,&ee); !dx.done; dx.step())  // search dx
      for (ee=0.0,y=0;y<pic1.ys;y++)                                    // compute actua solution distance to dataset
       if (p[y]!=-1)                                                    // ignore uncomputed values (no brown stuff)
        ee+=fabs(double(p[y])-x0.a-(double(y)*dx.a));
    // render regressed line with Red
  for (y=0;y<pic1.ys;y++)
    {
    x=double(x0.aa+(double(y)*dx.aa));
    if ((x>=0)&&(x<pic1.xs))
     pic1.p[y][x].dd=0x00FF0000;
    }
    pic1.save("cornbot2.png");
    delete[] p;
Run Code Online (Sandbox Code Playgroud)

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

  • xs,ys图像大小(以像素为单位)
  • p[y][x].dd(x,y)32 位整数类型位置处的像素
  • p[y][x].dw[2]2D字段(x,y)的 2x16 位整数类型位置处的像素
  • p[y][x].db[4]位置处的像素(x,y)为 4x8 位整数类型,以便于通道访问
  • clear(color)- 清除整个图像
  • resize(xs,ys)- 将图像大小调整为新分辨率
  • bmp- VCL封装GDI位图,可访问 Canvas
  • smooth(n)- 快速模糊图像n时间

您可以通过基于区域和位置的分割(删除小簇)来进一步改进这一点。您也可以忽略邻居之间平均点过大的峰值。您还可以检测天空并忽略天空存在的整个区域。

[编辑1] 平滑

这就是我的平滑的样子:

void picture::smooth(int n)
    {
    color   *q0,*q1;
    int     x,y,i,c0[4],c1[4],c2[4];
    bool _signed;
    if ((xs<2)||(ys<2)) return;
    for (;n>0;n--)
        {
        #define loop_beg for (y=0;y<ys-1;y++){ q0=p[y]; q1=p[y+1]; for (x=0;x<xs-1;x++) { dec_color(c0,q0[x],pf); dec_color(c1,q0[x+1],pf); dec_color(c2,q1[x],pf);
        #define loop_end enc_color(c0,q0[x  ],pf); }}
        if (pf==_pf_rgba) loop_beg for (i=0;i<4;i++) { c0[i]=(c0[i]+c0[i]+c1[i]+c2[i])>>2; clamp_u8(c0[i]);  } loop_end
        if (pf==_pf_s   ) loop_beg                   { c0[0]=(c0[0]+c0[0]+c1[0]+c2[0])/ 4; clamp_s32(c0[0]); } loop_end
        if (pf==_pf_u   ) loop_beg                   { c0[0]=(c0[0]+c0[0]+c1[0]+c2[0])>>2; clamp_u32(c0[0]); } loop_end
        if (pf==_pf_ss  ) loop_beg for (i=0;i<2;i++) { c0[i]=(c0[i]+c0[i]+c1[i]+c2[i])/ 4; clamp_s16(c0[i]); } loop_end
        if (pf==_pf_uu  ) loop_beg for (i=0;i<2;i++) { c0[i]=(c0[i]+c0[i]+c1[i]+c2[i])>>2; clamp_u16(c0[i]); } loop_end
        #undef loop_beg
        #define loop_beg for (y=ys-1;y>0;y--){ q0=p[y]; q1=p[y-1]; for (x=xs-1;x>0;x--) { dec_color(c0,q0[x],pf); dec_color(c1,q0[x-1],pf); dec_color(c2,q1[x],pf);
        if (pf==_pf_rgba) loop_beg for (i=0;i<4;i++) { c0[i]=(c0[i]+c0[i]+c1[i]+c2[i])>>2; clamp_u8(c0[i]);  } loop_end
        if (pf==_pf_s   ) loop_beg                   { c0[0]=(c0[0]+c0[0]+c1[0]+c2[0])/ 4; clamp_s32(c0[0]); } loop_end
        if (pf==_pf_u   ) loop_beg                   { c0[0]=(c0[0]+c0[0]+c1[0]+c2[0])>>2; clamp_u32(c0[0]); } loop_end
        if (pf==_pf_ss  ) loop_beg for (i=0;i<2;i++) { c0[i]=(c0[i]+c0[i]+c1[i]+c2[i])/ 4; clamp_s16(c0[i]); } loop_end
        if (pf==_pf_uu  ) loop_beg for (i=0;i<2;i++) { c0[i]=(c0[i]+c0[i]+c1[i]+c2[i])>>2; clamp_u16(c0[i]); } loop_end
        #undef loop_beg
        #undef loop_end
        }
    }
Run Code Online (Sandbox Code Playgroud)

它只是对 3 个像素进行加权平均

(x,y)=(2*(x,y)+(x-1,y)+(x,y-1))/4
Run Code Online (Sandbox Code Playgroud)

然后做同样的事情

(x,y)=(2*(x,y)+(x+1,y)+(x,y+1))/4
Run Code Online (Sandbox Code Playgroud)

以避免图像移动。然后这整个事情就循环了n很多次,仅此而已。在这种情况下,您可以忽略钳制和像素格式选项pf==_pf_rgba,但无论如何它只使用单个通道...dec_color,enc_color只需将颜色通道解压缩到变量数组中或从变量数组中打包,以避免 8 位通道上的截断和溢出问题,并且也可以更好地格式化/简化代码(针对不同的像素格式支持)

顺便说一句,平滑基与卷积相同

0.00 0.25 0.00
0.25 0.50 0.00
0.00 0.00 0.00
Run Code Online (Sandbox Code Playgroud)

0.00 0.00 0.00
0.00 0.50 0.25
0.00 0.25 0.00
Run Code Online (Sandbox Code Playgroud)