工业视觉_63:通过Susan角点进行旋转无关性匹配

/* 
      工业视觉_63:通过Susan角点进行旋转无关性匹配
      未来20年里,最火的行业之一:机器换人,智能使用机器人,改造传统工厂成智慧工厂!
      工业视觉,目标很明确:"快,准,稳"三个字. 快:开发快,运行速度快;准:高精度;稳:稳健可靠
      所以,目前"机器换人"项目大多采用工控电脑,搭建 Windows7+VS2019+EMGU(或AForge+Accord),这一方案见效最快.
      Halcon,Emgu的视觉库都很强大,而AForge+Accord库更全面,更丰富(如:数学,人工智能,机器学习).
      散点集合,可来自角点,或称关键点,特征点,机器视觉中有丰富的类型.本文本取Susan角点进行旋转无关性匹配.
      因为大多机床,或机器人在视觉下工作,每一次工件流动过来位置与角度常常有变,机器人要抓住它,并装配到另一处,数学计算很重多.
      这就要提取工件的一些几何特征,以保证在旋转后继续能正确识别型号,确定位置与方位角.以便使机器人工作.
      AI训练也是很多人热衷的.但是本文提出的思路是,找到中心(质心),计算惯性矩(中心二阶矩)找到方位角,依各扇形中的点数来进行工件的匹配与定位(与姿态).
      也就是对每一工件图,生成与旋转无关的一系列值,构成特征向量,再分析这些向量之间的关系(如欧氏距离,相关系数等).
      在主轴明显且角点比较稳定时,本方案比训练N次方便些.若打光方案正确,则提取的角点也更稳定.
      在使用机器人时,应该对相机进行纠偏(消除畸变),标定,打光等处理,以进一步提高精度.

         ---------     编撰:    项道德(微信:daode1212),2021-07-13
  */

  //载入照片:
  pictureBox1.Image = Image.FromFile("工业视觉_8(旋转无关性匹配).png");//四区块(四象限)各一工件图
  Bitmap bmp = (Bitmap)(pictureBox1.Image);
  Graphics g = Graphics.FromImage(bmp);
  Pen pen0 = new Pen(Color.FromArgb(180, 0, 0), 4);
  Pen pen1 = new Pen(Color.FromArgb(120, 0, 120), 4);
  int ww = bmp.Width;
  int hh = bmp.Height;
  //自定: MSG("")为内置函数MessageBox.Show("")的简写方式;
  MSG("图片宽度与高度:\r\n" + string.Format("w={0},h={1}", ww, hh) + "\r\n 接着,呈现处理结果");
  SolidBrush bh = new SolidBrush(Color.FromArgb(155, 80, 122));
  //Susan角点检测:
  var scd = new Accord.Imaging.SusanCornersDetector();
  List<IntPoint> LP = scd.ProcessImage(bmp);

  //绘制各角点:
  int k = 0;
  foreach (var p in LP)
  {
      int u = 255 * k / LP.Count;
      bh = new SolidBrush(Color.FromArgb(255 - u, 0, u));//从红色到蓝色
      g.FillEllipse(bh, p.X - 1, p.Y - 1, 2, 2);
      k++;
  }

  MSG("绿色:Susan角点检测:" + LP.Count + "个. ");

  //将所有角点划分为四个组,并找到各组的中心:
  List<PointF> LP0 = new List<PointF>();
  List<PointF> LP1 = new List<PointF>();
  List<PointF> LP2 = new List<PointF>();
  List<PointF> LP3 = new List<PointF>();
  foreach (var p in LP)
  {
      PointF p2 = new PointF(p.X, p.Y);
      if (p.X < ww / 2 && p.Y < hh / 2)
      {
          LP0.Add(p2);
      }
      if (p.X > ww / 2 && p.Y < hh / 2)
      {
          LP1.Add(p2);
      }
      if (p.X < ww / 2 && p.Y > hh / 2)
      {
          LP2.Add(p2);
      }
      if (p.X > ww / 2 && p.Y > hh / 2)
      {
          LP3.Add(p2);
      }
  }
  float X0 = LP0.Average(o => o.X); float Y0 = LP0.Average(o => o.Y);
  float X1 = LP1.Average(o => o.X); float Y1 = LP1.Average(o => o.Y);
  float X2 = LP2.Average(o => o.X); float Y2 = LP2.Average(o => o.Y);
  float X3 = LP3.Average(o => o.X); float Y3 = LP3.Average(o => o.Y);

  //各组平移变换:
  List<PointF> LP0b = new List<PointF>();
  List<PointF> LP1b = new List<PointF>();
  List<PointF> LP2b = new List<PointF>();
  List<PointF> LP3b = new List<PointF>();
  for (int i = 0; i < LP0.Count; i++)
  {
      PointF p = LP0[i];
      p.X -= X0; p.Y -= Y0;
      LP0b.Add(p);
  }
  for (int i = 0; i < LP1.Count; i++)
  {
      PointF p = LP1[i];
      p.X -= X1; p.Y -= Y1;
      LP1b.Add(p);
  }
  for (int i = 0; i < LP2.Count; i++)
  {
      PointF p = LP2[i];
      p.X -= X2; p.Y -= Y2;
      LP2b.Add(p);
  }
  for (int i = 0; i < LP3.Count; i++)
  {
      PointF p = LP3[i];
      p.X -= X3; p.Y -= Y3;
      LP3b.Add(p);
  }
  //============计算产品3二阶矩,以获取方位角=============
  bh = new SolidBrush(Color.FromArgb(155, 0, 155));
  //计算各组的二阶矩,以确定方位角:
  double xM3 = 0, yM3 = 0;
  for (int i = 0; i < LP3b.Count; i++)
  {
      PointF p = LP3b[i];
      xM3 += Math.Sign(p.X) * (p.X * p.X);//
      yM3 += Math.Sign(p.Y) * (p.Y * p.Y);//
  }
  double ori3 = Math.Atan2(yM3, xM3);
  g.DrawLine(pen0, X3, Y3, (float)(X3 + 50 * Math.Cos(ori3)), (float)(Y3 + 50 * Math.Sin(ori3)));
  g.FillEllipse(bh, X3 - 8, Y3 - 8, 16, 16);

  //============计算产品2二阶矩,以获取方位角=============
  double xM2 = 0, yM2 = 0;
  for (int i = 0; i < LP2b.Count; i++)
  {
      PointF p = LP2b[i];
      xM2 += Math.Sign(p.X) * (p.X * p.X);//
      yM2 += Math.Sign(p.Y) * (p.Y * p.Y);//
  }
  double ori2 = Math.Atan2(yM2, xM2);
  g.DrawLine(pen0, X2, Y2, (float)(X2 + 50 * Math.Cos(ori2)), (float)(Y2 + 50 * Math.Sin(ori2)));
  g.FillEllipse(bh, X2 - 8, Y2 - 8, 16, 16);

  //============计算产品1二阶矩,以获取方位角=============
  double xM1 = 0, yM1 = 0;
  for (int i = 0; i < LP1b.Count; i++)
  {
      PointF p = LP1b[i];
      xM1 += Math.Sign(p.X) * (p.X * p.X);//
      yM1 += Math.Sign(p.Y) * (p.Y * p.Y);//
  }
  double ori1 = Math.Atan2(yM1, xM1);
  g.DrawLine(pen0, X1, Y1, (float)(X1 + 50 * Math.Cos(ori1)), (float)(Y1 + 50 * Math.Sin(ori1)));
  g.FillEllipse(bh, X1 - 8, Y1 - 8, 16, 16);

  //============计算产品0二阶矩,以获取方位角=============
  double xM0 = 0, yM0 = 0;
  for (int i = 0; i < LP0b.Count; i++)
  {
      PointF p = LP0b[i];
      xM0 += Math.Sign(p.X) * (p.X * p.X);//
      yM0 += Math.Sign(p.Y) * (p.Y * p.Y);//
  }
  double ori0 = Math.Atan2(yM0, xM0);
  g.DrawLine(pen0, X0, Y0, (float)(X0 + 50 * Math.Cos(ori0)), (float)(Y0 + 50 * Math.Sin(ori0)));
  g.FillEllipse(bh, X0 - 8, Y0 - 8, 16, 16);

  //----------------产品0------------------
  //依方位角进行统计,计算误差或相似性:
  float[] s0 = new float[360];//记录样品特征
  for (int i = 0; i < LP0b.Count; i++)
  {
      int q0 = (int)(toD * (-ori0 + Math.Atan2(LP0b[i].Y, LP0b[i].X) + 2 * PI) % 360);
      int q1 = (q0 / 30);
      s0[q1]++;
  }
  //绘制条形统计图:
  for (int h = 0; h < 12; h++)
  {
      g.DrawLine(pen1, 0, 10 + 8 * h, 4 * s0[h], 10 + 8 * h);
  }


  //----------------产品1,计算各扇形内点的数目------------------
  float[] s1 = new float[360];//记录样品特征
  for (int i = 0; i < LP1b.Count; i++)
  {
      int q0 = (int)(toD * (-ori1 + Math.Atan2(LP1b[i].Y, LP1b[i].X) + 2 * PI) % 360);
      int q1 = (q0 / 30);
      s1[q1]++;
  }
  //绘制条形统计图:
  for (int h = 0; h < 12; h++)
  {
      g.DrawLine(pen1, ww / 2, 10 + 8 * h, ww / 2 + 4 * s1[h], 10 + 8 * h);
  }
  //----------------产品2,计算各扇形内点的数目------------------
  float[] s2 = new float[360];//记录样品特征
  for (int i = 0; i < LP2b.Count; i++)
  {
      int q0 = (int)(toD * (-ori2 + Math.Atan2(LP2b[i].Y, LP2b[i].X) + 2 * PI) % 360);
      int q1 = (q0 / 30);
      s2[q1]++;
  }
  //绘制条形统计图:
  for (int h = 0; h < 12; h++)
  {
      g.DrawLine(pen1, 0, hh / 2 + 10 + 8 * h, 4 * s2[h], hh / 2 + 10 + 8 * h);
  }
  //----------------产品3,计算各扇形内点的数目------------------
  float[] s3 = new float[360];//记录样品特征
  for (int i = 0; i < LP3b.Count; i++)
  {
      int q0 = (int)(toD * (-ori3 + Math.Atan2(LP3b[i].Y, LP3b[i].X) + 2 * PI) % 360);
      int q1 = (q0 / 30);
      s3[q1]++;
  }
  //绘制条形统计图:
  for (int h = 0; h < 12; h++)
  {
      g.DrawLine(pen1, ww / 2, hh / 2 + 10 + 8 * h, ww / 2 + 4 * s3[h], hh / 2 + 10 + 8 * h);
  }

  //计算向量的曼哈顿距离(也可计算相关系数或欧氏距离的):====================================
  float std1 = 0, std2 = 0, std3 = 0, std23 = 0;
  for (int h = 0; h < 12; h++)
  {
      std1 += Math.Abs(s1[h] - s0[h]);
      std2 += Math.Abs(s2[h] - s0[h]);
      std3 += Math.Abs(s3[h] - s0[h]);
      std23 += Math.Abs(s3[h] - s2[h]);
  }
  std1 /= 12; std2 /= 12; std3 /= 12; std23 /= 12;


  //计算向量的相关系数:====================================
  float sov1 = 0, sov2 = 0, sov3 = 0, sov23 = 0;
  float ds0 = 0, ds1 = 0, ds2 = 0, ds3 = 0;
  for (int h = 0; h < 12; h++)
  {
      sov1 += (s1[h] * s0[h]); ds0 += (s0[h] * s0[h]);
      sov2 += (s2[h] * s0[h]); ds1 += (s1[h] * s1[h]);
      sov3 += (s3[h] * s0[h]); ds2 += (s2[h] * s2[h]);
      sov23 += (s3[h] * s2[h]); ds3 += (s3[h] * s3[h]);
  }
  sov1 = sov1 / (float)Math.Sqrt(ds0 * ds1);
  sov2 = sov2 / (float)Math.Sqrt(ds0 * ds2);
  sov3 = sov3 / (float)Math.Sqrt(ds0 * ds3);
  sov23 = sov23 / (float)Math.Sqrt(ds2 * ds3);

  //显示结果:==============================
  textBox1.Text = string.Format("后三图与第一图的误差为:\r\n{0:0.##},{1:0.##},{2:0.##}", std1, std2, std3);
  textBox1.Text += string.Format("\r\n三,四两图的误差为:\r\n{0:0.##}", std23);
  textBox1.Text += string.Format("\r\n后三图与第一图的相关系数为:\r\n{0:0.##},{1:0.##},{2:0.##}", sov1, sov2, sov3);
  textBox1.Text += string.Format("\r\n三,四两图的相关系数为:\r\n{0:0.##}", sov23);
  /* 文本框中将显示:
  后三图与第一图的误差为:
  10.83,8.42,3.5
  三,四两图的误差为:
  10.58
  后三图与第一图的相关系数为:
  0.9,0.95,0.99
  三,四两图的相关系数为:
  0.92
   */
  g.DrawString("通过Susan角点进行旋转无关性匹配", new Font("", 10), bh, bmp.Width / 3,hh/2-30);
  pictureBox1.Image = bmp;

上图模拟各方向的工件.

下图是视觉处理后的图:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值