jjzjj

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

coder 2024-04-01 原文

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

这是图像示例。

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

我使用以下步骤

  1. 第 1 步:我目前使用的技术是将颜色值转换为从 this code 修改而来的 HSV

  2. 第 2 步:因此它将检测选定的颜色,即棕色或污垢颜色,然后我收集两个阵列中每个图像行的最左边和右边的棕色或选定颜色(一个红点)。

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

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

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

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

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

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

  • 第 3 步:绘制线性回归

More Information

Full Code Here

LinearRegression Class

10 example images with the same process

最佳答案

对于您的源图像

我会:

  1. 制作棕色面具

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

  2. 模糊蒙版

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

  3. 水平线扫描蒙版

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

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

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

    我用我的approximation search为此,您可以使用任何您想要的回归。回归线标有红色

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

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

在哪里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;

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

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

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

[edit1] 平滑

这是我平滑的样子:

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次,仅此而已。您可以忽略 clamp 和 pixel-format 选项,在这种情况下它是 pf==_pf_rgba但无论如何它只使用单 channel ... dec_color,enc_color只需将颜色 channel 解包、打包到变量数组中或从变量数组中打包,以避免 8 位 channel 上的截断和溢出问题,并更好地格式化/简化代码(对于不同的像素格式支持)

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

0.00 0.25 0.00
0.25 0.50 0.00
0.00 0.00 0.00

0.00 0.00 0.00
0.00 0.50 0.25
0.00 0.25 0.00

关于java - 如何从图像中检测机器人方向?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37155580/

有关java - 如何从图像中检测机器人方向?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  4. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  5. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  6. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  7. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  8. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  9. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  10. ruby - 如何使用文字标量样式在 YAML 中转储字符串? - 2

    我有一大串格式化数据(例如JSON),我想使用Psychinruby​​同时保留格式转储到YAML。基本上,我希望JSON使用literalstyle出现在YAML中:---json:|{"page":1,"results":["item","another"],"total_pages":0}但是,当我使用YAML.dump时,它不使用文字样式。我得到这样的东西:---json:!"{\n\"page\":1,\n\"results\":[\n\"item\",\"another\"\n],\n\"total_pages\":0\n}\n"我如何告诉Psych以想要的样式转储标量?解

随机推荐