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

2024-01-23

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

这是图像示例。

processed image processed image raw image raw image segmented image segmented image

我使用以下步骤

  1. 步骤 1:我当前使用的技术是将颜色值转换为修改自的 HSV这段代码 http://boofcv.org/index.php?title=Example_Color_Segmentation

  2. 步骤2:所以它将检测选定的颜色,即棕色或泥土颜色,然后我收集两个数组中每个图像行的最左边和最右边的棕色或选定颜色(红点)。

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

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

EDIT1:使用 Spektre 的答案,它看起来更好

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

  • Step 1 : Thresholding or Color Segmentation Thresholding or Color Segmentation

  • Step 2 : Blured(use Gaussian and Median filter) Blured

  • Step 3 : Plot Linear Regression Plot Linear Regression

更多信息

完整代码在这里 https://github.com/pacozaa/Final-Project-Boofcv/blob/master/CornPlantTunnelGuideColorSegmentationV2/src/cornplant/CornPlantTunnelGuide.java

线性回归类 https://github.com/pacozaa/Final-Project-Boofcv/blob/master/CornPlantTunnelGuideColorSegmentationV2/src/cornplant/LinearRegression.java

10 个具有相同过程的示例图像 https://i.stack.imgur.com/LMCBL.jpg


对于您的源图像

I would:

  1. 创建棕色东西的面具

    只是门槛H,S并忽略V,你已经有了这个。我使用整数255而不是颜色(Blue)用于后面的计算。

  2. 模糊面具

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

  3. 通过水平线扫描掩模

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

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

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

    我用我的近似搜索 https://stackoverflow.com/a/36368290/2521214为此,您可以使用任何您想要的回归。回归线标记为Red

    我用的是直线方程x=x0+y*dx where y=<0,pic1.ys>。并按间隔搜索解:

    x0=<-pic1.xs,+2*pic1.xs>
    dx=<-10,+10>
    

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

Here 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;

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

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

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

[编辑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
        }
    }

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

(x,y)=(2*(x,y)+(x-1,y)+(x,y-1))/4

然后做同样的事情

(x,y)=(2*(x,y)+(x+1,y)+(x,y+1))/4

以避免图像移动。然后这整个事情就循环了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

and

0.00 0.00 0.00
0.00 0.50 0.25
0.00 0.25 0.00
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何从图像中检测机器人方向? 的相关文章

随机推荐

  • Codenameone 中使用 split 方法时出错

    我创建了一个新的 Codenameone 项目 它包含以下代码 String values one two tree String v values split Codename One 支持 Java 5 的一个子集String spli
  • 使用 jQuery 调用 Sinatra 删除路由

    我对 Sinatra 还很陌生 正在制作一个利用基本 CRUD 功能的简单待办事项应用程序 在后端 我有工作路线并测试了所有内容 我想合并一些前端功能 并决定使用 jQuery 来帮助实现这一点 我在 jQuery 中有一段当前代码 当单击
  • 具有自定义对象的可过滤适配器

    我想将自动完成文本框添加到 xamarin android 中的列表视图 自定义对象 中 我有一个列表视图 它是从字符串数组填充的 我想使用自定义对象填充我的列表视图 下面的代码适用于字符串数组 任何帮助实现我的自定义对象适配器都会有所帮助
  • 如何更改两层的 ggplot 图例标签和名称?

    我正在使用 ggmap 和 ggplot 包绘制圣保罗地图中两个不同数据帧的经度和纬度坐标 并希望手动标记每个图例图层 更新 我编辑了下面的代码以使其完全可重现 我使用的是地理编码函数而不是 get map 更新 我想在不合并数据帧的情况下
  • addTooltip 间歇性地使用observeEvent 中的两个输入

    我正在构建一个闪亮的应用程序来绘制网络 用户可以选择一个节点 单击切换按钮以显示该节点的自我网络 然后单击相同的按钮返回主网络 我试图获得一个工具提示 将鼠标悬停在按钮上 其中的文本会根据按钮本身的状态以及是否选择节点而变化 问题是工具提示
  • Google Apps 脚本:一天内调用服务次数过多:电子邮件

    我正在尝试循环浏览电子表格 并为每一行发送一封电子邮件 电子邮件发送后 我想删除该行 然而那是行不通的 由于某种原因 它开始疯狂地发送电子邮件 并在某个时候达到限制并退出 它实际上只删除一行 请参阅下面的代码 function sendEm
  • Fortran 与 C++ 相比,如今 Fortran 在数值分析方面是否仍然具有优势? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 随着C 编译器 尤其是intel编译器 的快速发展 以及在C C 代码中直接应用SIMD函数的能力 Fortran在数值计算领域是否仍然具有真正
  • 如何在 App.config 中设置 SQLCommandTimeout

    我已经使用 SQL 数据库开发了一个 Window 服务 目前我的数据库中充满了记录 因此查询执行需要很长时间 而默认命令超时为 30S 但我想将其增加到 120S 一个选项是 com CommandTimeout 120 但我的应用程序中
  • Java 方法中的动态返回类型

    我在这里多次看到类似的问题 但有一个很大的区别 在其他问题中 返回类型由参数确定 我想要 需要做的是通过解析的值确定返回类型byte 根据我收集的信息 以下方法可能有效 public Comparable getParam String p
  • 为什么 _mm_stream_ps 会产生 L1/LL 缓存未命中?

    我正在尝试优化计算密集型算法 但遇到了一些缓存问题 我有一个巨大的缓冲区 它偶尔会随机写入 并且在应用程序结束时只读取一次 显然 写入缓冲区会产生大量缓存未命中 并且还会污染随后再次需要进行计算的缓存 我尝试使用非时间移动内在函数 但缓存未
  • Chrome 中页面加载时的 Popstate

    我正在为我的网络应用程序使用 History API 但遇到一个问题 我执行 Ajax 调用来更新页面上的一些结果并使用history pushState 为了更新浏览器的地址栏而不重新加载页面 然后 当然 我用window popstat
  • Javascript / jQuery - 点击 iPhone 上的元素外部

    我在这个问题中找到了一个关于检测 div 外部点击的很好的答案 如何检测元素外部的点击 https stackoverflow com questions 152975 how to detect a click outside an el
  • 使用之间的区别?和 :prepare 语句中的参数

    假设我想选择记录Id 30 准备好的语句允许两种绑定参数的方式 问号 id 30 q conn gt prepare SELECT FROM pdo db WHERE id gt q gt execute array id Here abo
  • 子div比父div大

    计划是使子 div 与父 div 中的空间一样大 是否可以选择将父级 div 的完整高度应用到子元素上 我只知道 css calc 作为一个选项 但我不想实现这个选项 另外我在图像中解释了这个问题 parent height 250px w
  • Firefox 配置文件首选项 vs Chrome 选项 vs IE 所需功能

    我已经看到 并且实际上回答了 几个特定于硒的问题 其中需要设置一些特定的浏览器首选项来更改其行为 例如 如何使用 Selenium 处理证书 https stackoverflow com questions 24507078 how to
  • Django - 生产中的内存中的sqlite

    我有一个在生产中使用的小型 10MB 只读 sqlite3 数据库 我想加快我的网站速度 因此我尝试在每次 Django 启动时将整个数据库从磁盘加载到内存 这个答案解释了如何在烧瓶中做到这一点 https stackoverflow co
  • 如何创建全局枚举

    如何通过所有类访问枚举 让我解释 enum BottomBackButtonNav 0 BottomNextButtonNav BottomSliderIncreaseNav BottomSliderDcreaseNav PageSwipe
  • 量角器新手,收集选择选项有困难

    自学量角器和解决非角度网络应用程序的问题 并从选择控件中获取所有值的列表 这是 html 但似乎无法验证列表 本网站的第一个重量选择框 http halls md body surface area bsa htm http halls m
  • 在 Python 中定义异常的“正确”方法,PyLint 不会抱怨

    我试图在 Python 2 6 中定义我自己的 非常简单的 异常类 但无论我怎么做 我都会收到一些警告 首先 最简单的方法 class MyException Exception pass 这有效 但在运行时打印出警告 Deprecatio
  • 如何从图像中检测机器人方向?

    我正在开发可以在玉米植株中运行并由罗盘传感器引导的机器人 但我想将相机用作机器人的眼睛 并使用图像处理来检测运动的误差角度 这是图像示例 processed image raw image segmented image 我使用以下步骤 步