VisionPro高级案例——液面查找

图片:

要求:

高级【F01】

1.检测每个药水瓶的液位,用红线标识出来;

2.同一套程序适用于素材目录所有图片;

答题要点及评分标准:

根据图片内容判定

试题解析:

1.每张图片至少标示出8个药瓶的液位,每个药瓶正确标示+2.5分;

2.共5个素材图片,根据每个图片运行结果,累计以上分数;

3.总分100分;

VisionPro方案:

工具参数:

CogPMAlignTool工具

CogIPOneImageTool工具

CogFindLineTool工具

高级脚本完整代码:

cs 复制代码
#region namespace imports
using System;
using System.Collections;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using Cognex.VisionPro;
using Cognex.VisionPro.ToolBlock;
using Cognex.VisionPro3D;
using Cognex.VisionPro.PMAlign;
using Cognex.VisionPro.ImageProcessing;
using Cognex.VisionPro.Caliper;
#endregion

public class CogToolBlockAdvancedScript : CogToolBlockAdvancedScriptBase
{
  #region Private Member Variables
  private Cognex.VisionPro.ToolBlock.CogToolBlock mToolBlock;
  CogGraphicCollection gc = new CogGraphicCollection();
  #endregion

  /// <summary>
  /// Called when the parent tool is run.
  /// Add code here to customize or replace the normal run behavior.
  /// </summary>
  /// <param name="message">Sets the Message in the tool's RunStatus.</param>
  /// <param name="result">Sets the Result in the tool's RunStatus</param>
  /// <returns>True if the tool should run normally,
  ///          False if GroupRun customizes run behavior</returns>
  public override bool GroupRun(ref string message, ref CogToolResultConstants result)
  {
    // To let the execution stop in this script when a debugger is attached, uncomment the following lines.
    // #if DEBUG
    // if (System.Diagnostics.Debugger.IsAttached) System.Diagnostics.Debugger.Break();
    // #endif


    // Run each tool using the RunTool function
    foreach(ICogTool tool in mToolBlock.Tools)
      mToolBlock.RunTool(tool, ref message, ref result);
    
   
    gc.Clear();
    CogPMAlignTool pma = mToolBlock.Tools[0] as  CogPMAlignTool;
    CogFindLineTool Line1 = mToolBlock.Tools[4] as CogFindLineTool;
    CogImage8Grey image1 = mToolBlock.Inputs["yy"].Value as CogImage8Grey;
    //CogDistancePointLineTool distance1 =  mToolBlock.Tools[3] as CogDistancePointLineTool;
    //CogMath math1 = new CogMath();
    /*
    foreach(CogPMAlignResult p in pma.Results)
    {
      //Line1.RunParams.ExpectedLineSegment.StartX = 
      Line1.RunParams.ExpectedLineSegment.StartX = p.GetPose().TranslationX - 8;
      Line1.RunParams.ExpectedLineSegment.StartY = p.GetPose().TranslationY;
      Line1.RunParams.ExpectedLineSegment.EndX = p.GetPose().TranslationX +8;
      Line1.RunParams.ExpectedLineSegment.EndY = p.GetPose().TranslationY;
      Line1.RunParams.ExpectedLineSegment.SelectedColor = CogColorConstants.Green;
      Line1.Run();
      
      CogFindLineResults lineResults = Line1.Results;
      CogLine foundLine = lineResults.GetLine();
      
      //CogDistancePointLineTool distance1 = new  CogDistancePointLineTool();
      double pX = p.GetPose().TranslationX;
      double pY = p.GetPose().TranslationY;
      
      double footX, footY;
      double distance1 = CogMath.DistancePointLine(
        pX,
        pY,
        foundLine,
        image1, //传入工具块输入图像
        out footX,
        out footY
        );
      
      CogLineSegment line2 = new CogLineSegment();
      line2.Color = CogColorConstants.Red;
      line2.StartX = Line1.Results.GetLineSegment().MidpointX - 28;
      line2.StartY = Line1.Results.GetLineSegment().MidpointY;
      line2.EndX = Line1.Results.GetLineSegment().MidpointX + 28;
      line2.EndY = Line1.Results.GetLineSegment().MidpointY;
      
     
      
      gc.Add(line2);
      
    }
    
    */
    // 设置距离公差,按需修改!
    double tolerance = 30;

    foreach(CogPMAlignResult p in pma.Results)
    {
      double pX = p.GetPose().TranslationX;
      double pY = p.GetPose().TranslationY;

      // 动态更新找线ROI
      Line1.RunParams.ExpectedLineSegment.StartX = pX - 18;
      Line1.RunParams.ExpectedLineSegment.StartY = pY;
      Line1.RunParams.ExpectedLineSegment.EndX = pX + 18;
      Line1.RunParams.ExpectedLineSegment.EndY = pY;
      Line1.RunParams.ExpectedLineSegment.SelectedColor = CogColorConstants.Green;
      Line1.Run();

      CogFindLineResults lineResults = Line1.Results;
      // ✅判空,防止找不到线崩溃
      if (lineResults != null && lineResults.Count > 0)
      {
        //CogFindLineResult lineResult = lineResults;
        CogLine foundLine = lineResults.GetLine();
        CogLineSegment foundSeg = lineResults.GetLineSegment();

        double footX, footY;
        double distance1 = CogMath.DistancePointLine(
          pX,
          pY,
          foundLine,
          image1,
          out footX,
          out footY
          );

        CogLineSegment line2 = new CogLineSegment();
        // 根据距离设置颜色
        if (distance1 < tolerance)
        {
          line2.Color = CogColorConstants.Red; // NG 红色
        }
        else
        {
          line2.Color = CogColorConstants.Green; // OK 绿色
        }

        line2.StartX = foundSeg.MidpointX - 28;
        line2.StartY = foundSeg.MidpointY;
        line2.EndX = foundSeg.MidpointX + 28;
        line2.EndY = foundSeg.MidpointY;
        gc.Add(line2);

        //【可选】绘制蓝色垂线(PM点→垂足,方便调试)
        
        CogLineSegment perpendicular = new CogLineSegment();
        perpendicular.Color = CogColorConstants.Blue;
        perpendicular.StartX = pX;
        perpendicular.StartY = pY;
        perpendicular.EndX = footX;
        perpendicular.EndY = footY;
        gc.Add(perpendicular);
        

        //【可选】绘制距离文本
        
        CogGraphicLabel txt = new CogGraphicLabel();
        txt.Text = "Dist:" +distance1.ToString("F3");
        txt.X = foundSeg.MidpointX;
        txt.Y = foundSeg.MidpointY - 10;
        txt.Color = line2.Color;
        gc.Add(txt);
        
      }
      else
      {
        // 本次找线失败,可以画黄色提示标记
        
        CogGraphicLabel txtFail = new CogGraphicLabel();
        txtFail.Text = "LineNotFound";
        txtFail.X = pX;
        txtFail.Y = pY;
        txtFail.Color = CogColorConstants.Yellow;
        gc.Add(txtFail);
       
      }
    }
    
    
    

    return false;
  }

  #region When the Current Run Record is Created
  /// <summary>
  /// Called when the current record may have changed and is being reconstructed
  /// </summary>
  /// <param name="currentRecord">
  /// The new currentRecord is available to be initialized or customized.</param>
  public override void ModifyCurrentRunRecord(Cognex.VisionPro.ICogRecord currentRecord)
  {
  }
  #endregion

  #region When the Last Run Record is Created
  /// <summary>
  /// Called when the last run record may have changed and is being reconstructed
  /// </summary>
  /// <param name="lastRecord">
  /// The new last run record is available to be initialized or customized.</param>
  public override void ModifyLastRunRecord(Cognex.VisionPro.ICogRecord lastRecord)
  {
    foreach(ICogGraphic g in gc)
    {
      mToolBlock.AddGraphicToRunRecord(g,lastRecord,"CogIPOneImageTool2.OutputImage","");
    }
  }
  #endregion

  #region When the Script is Initialized
  /// <summary>
  /// Perform any initialization required by your script here
  /// </summary>
  /// <param name="host">The host tool</param>
  public override void Initialize(Cognex.VisionPro.ToolGroup.CogToolGroup host)
  {
    // DO NOT REMOVE - Call the base class implementation first - DO NOT REMOVE
    base.Initialize(host);


    // Store a local copy of the script host
    this.mToolBlock = ((Cognex.VisionPro.ToolBlock.CogToolBlock)(host));
  }
  #endregion

}

结果:

代码整体概述

这是一段 VisionPro ToolBlock 高级脚本(C#),典型应用场景:

模板匹配(PMAlign)多点定位 → 每一个模板结果位置动态移动【找线工具 CogFindLineTool】的 ROI → 抓取直线 → 计算模板点到直线的垂直距离 → 根据距离公差 OK/NG 上色、叠加绘图(线段、垂线、文字),最后把图形挂载到图像输出记录上。 核心:在 GroupRun 里自定义串行逻辑,循环处理多个 PM 匹配结果,动态驱动子工具运行 + 自定义绘图
重要特点:return false; 代表接管运行流程,不再走 ToolBlock 默认依次执行子工具的逻辑。


一、核心框架知识点(ToolBlock 高级脚本固定结构)

1. 基类与重写函数

复制代码
public class CogToolBlockAdvancedScript : CogToolBlockAdvancedScriptBase

VisionPro 高级脚本固定继承这个基类,4 个关键重写方法:

表格

方法 作用 本代码要点
Initialize(host) 脚本初始化,只加载一次 把 host 强转为CogToolBlock mToolBlock,保存工具块引用;必须先调用 base.Initialize (host)
GroupRun(ref message, ref result) ✅ 运行主逻辑,每次工具块 Run 都会执行 自定义运行流程;return true:让工具块自动依次跑里面所有子工具;return false:自己全权控制子工具运行
ModifyCurrentRunRecord 当前记录构建时回调 本代码空实现
ModifyLastRunRecord 上次运行记录构建回调 把绘图集合 gc 里的图形添加到 RunRecord,在图像上显示

重点区分:

  • GroupRun:计算逻辑、跑子工具、算距离、生成 Graphic
  • ModifyLastRunRecord:只负责把图形挂到图像输出上,不能在这里跑工具、做计算 ❌ 大坑:不要在 ModifyLastRunRecord 里面调用 Tool.Run (),记录回调不是运行流程!

2. ToolBlock 内部对象访问两种方式

复制代码
// 方式1:按索引取子工具(代码在用)
CogPMAlignTool pma = mToolBlock.Tools[0] as CogPMAlignTool;
CogFindLineTool Line1 = mToolBlock.Tools[4] as CogFindLineTool;

// 方式2:按名称取(更稳定,推荐,避免拖动工具改变索引后代码崩)
// mToolBlock.Tools["PMAlignTool1"]

// 读取输入端子
CogImage8Grey image1 = mToolBlock.Inputs["yy"].Value as CogImage8Grey;

风险点:索引取工具,一旦在 ToolBlock 里调换工具顺序,脚本直接空引用崩溃。


二、GroupRun 运行流程知识点

1. 子工具运行:RunTool

复制代码
foreach(ICogTool tool in mToolBlock.Tools)
    mToolBlock.RunTool(tool, ref message, ref result);
  • RunTool:ToolBlock 脚本里标准调用子工具运行的 API,会填充子工具自己的 Results
  • 这段代码先把所有子工具跑一遍;然后脚本内部又单独循环多次调用 Line1.Run()

⚠️ 这里是一个容易踩坑点: Line1 被外层先 Run 一次,之后在 PM 结果循环里反复覆盖 Run,Line1 的结果会被不断覆盖,只保留最后一次找线结果。 如果后续其他工具依赖 Line1 输出,会出问题;本脚本只是在脚本内部即时拿结果绘图,所以暂时可用。

2. CogPMAlignTool 多结果遍历

复制代码
foreach(CogPMAlignResult p in pma.Results)
{
    double pX = p.GetPose().TranslationX;
    double pY = p.GetPose().TranslationY;
}
  • PMAlign.Results 是匹配到的全部模板实例集合,支持多目标
  • p.GetPose():CogTransform2D,包含 X/Y 平移、角度、缩放
  • TranslationX / TranslationY:模板原点坐标(像素坐标系)

3. 动态修改 CogFindLineTool ROI(核心业务逻辑)

复制代码
Line1.RunParams.ExpectedLineSegment.StartX = pX - 18;
Line1.RunParams.ExpectedLineSegment.StartY = pY;
Line1.RunParams.ExpectedLineSegment.EndX = pX + 18;
Line1.RunParams.ExpectedLineSegment.EndY = pY;
Line1.RunParams.ExpectedLineSegment.SelectedColor = CogColorConstants.Green;
Line1.Run();
  • ExpectedLineSegment:就是找线工具的搜索 ROI 线段框
  • 跟着每一个 PM 匹配点,实时平移找线窗口,实现跟随式找线
  • 调用 Line1.Run() 单独执行找线,不依赖工具块自动调度

区分:

  • RunParams:运行参数(ROI、阈值、边缘极性等,可改)
  • Results:运行输出结果(只读)

4. CogFindLineTool 结果判空与取值

复制代码
if (lineResults != null && lineResults.Count > 0)
{
    CogLine foundLine = lineResults.GetLine();
    CogLineSegment foundSeg = lineResults.GetLineSegment();
}
  • GetLine():无限长几何直线(用于数学距离计算)
  • GetLineSegment():找到的线段(有起止点、中点,用于绘图)
  • 判空是必须的:产品偏移、无边缘时找线会无结果,不判断直接GetLine()会抛异常崩溃

5. CogMath.DistancePointLine 点到直线距离 API

复制代码
double distance1 = CogMath.DistancePointLine(
    pX, pY,
    foundLine,
    image1,
    out footX, out footY
);

重点:

  1. 第二个参数是 CogLine(无限直线),不是 CogLineSegment!如果传线段,函数语义就变成点到线段最短距离,和业务不符;本代码写法是正确的
  2. footX,footY:垂足坐标,用来绘制垂线
  3. 返回值:垂直距离(像素单位)
  4. image 参数:图像对象,用于坐标系上下文,一般直接传入输入图即可

业务逻辑:距离 < tolerance → NG(红),否则 OK(绿)


三、VisionPro 矢量绘图 Graphic 体系知识点

复制代码
CogGraphicCollection gc = new CogGraphicCollection();
gc.Clear();
gc.Add(xxx);

常用 Graphic 类型:

表格

图形类 用途
CogLineSegment 线段(有起止点)
CogGraphicLabel 文字标签,显示数值、报错文本
CogCircle、CogCrosshair 十字、圆标记(本代码没用到)

属性:

  • Color = CogColorConstants.Red/Green/Blue/Yellow 枚举颜色
  • X/Y:标签锚点坐标

绘图是先全部放进内存集合 gc ,等到ModifyLastRunRecord统一挂载,而不是 Run 的时候直接画。

挂载图形到图像输出(ModifyLastRunRecord)

复制代码
mToolBlock.AddGraphicToRunRecord(g,lastRecord,"CogIPOneImageTool2.OutputImage","");

参数解析:

  1. graphic 对象
  2. lastRecord:运行记录
  3. 图像 Record 路径字符串 :工具名.输出端子名

这是高频踩坑点:名字写错、工具改名后字符串不会自动更新 → 图形不显示!

  1. 第四个参数:图层名称,留空默认图层

原理:VisionPro 的图像和绘图都保存在 ICogRecord 树里面,不是直接贴在图片 Bitmap 上;Record 可以保存、回放、在 Display 控件上加载显示。


四、代码里存在的缺陷 & 坑点(重点面试 / 调试知识点)

  1. 按索引取子工具、图像路径硬编码字符串 Tools[0]、Tools[4]、"CogIPOneImageTool2.OutputImage",重构工具块极易失效。✅ 推荐改成按名称访问。
  2. Line1 反复 Run,结果被覆盖 循环多次调用 Line1.Run (),Line1 自身的 Result 只保留最后一次;如果后续有其他子工具依赖 Line1 输出,会逻辑错误。 ✅ 改进方案:脚本内部不要复用同一个 CogFindLineTool 多次 Run,要么在脚本里 new 临时 CogFindLineTool,或者把找线逻辑改成脚本内 CogCaliper 实现。
  3. 没有输出距离、OK/NG 结果到 ToolBlock 输出端子 现在只绘图,没有把每一个距离、判定结果输出到 Outputs,外部程序(C# 上位机)拿不到数据,只能看画面。 ✅ 补充:mToolBlock.Outputs["Dist"].Value = distance1; 可以输出单个 / 数组结果。
  4. 没有结果计数:匹配到多少个、找线失败多少个 缺少统计量,不方便做统计监控。
  5. 没有异常捕获 try-catch 图像为空、PM 匹配异常等场景会直接让 ToolBlock Run 报错。
  6. 公差tolerance写死在脚本常量,不方便产线调试 ✅ 优化:做成 ToolBlock 输入端子,外部界面直接改公差,不用改脚本重编译。

五、业务流程图总结

复制代码
ToolBlock.Run()
    ↓
GroupRun 开始
    ↓
先批量Run所有子工具
    ↓
拿到PMAlign结果集合
    ↓
循环每一个匹配点Pose
        ├─ 动态移动FindLine的ROI窗口到当前点
        ├─ 执行FindLine.Run()
        ├─ 找到线:计算点到直线距离、判定OK/NG、绘制线段+垂线+距离文字
        └─ 找不到线:绘制黄色提示文字
    ↓
全部Graphic存入gc集合
    ↓
return false; 结束GroupRun(不再执行默认流程)
    ↓
回调 ModifyLastRunRecord
    ↓
把gc里所有图形挂载到指定图像输出的Record上
    ↓
VisionPro Display 显示叠加绘图

六、高频面试提问点(基于这份代码)

  1. GroupRun return true 和 false 的区别?
  2. ModifyLastRunRecord 和 GroupRun 的分工?能不能在 ModifyLastRunRecord 里面跑工具?
  3. CogMath.DistancePointLine 传 CogLine 和 CogLineSegment 有什么区别?
  4. 为什么图形要放到 CogGraphicCollection,在 ModifyLastRunRecord 统一添加?
  5. PM 多目标循环里复用同一个 FindLineTool 反复 Run 有什么隐患?
  6. AddGraphicToRunRecord 第三个字符串参数是什么作用,写错会怎么样?
相关推荐
yuhouxiyang5 小时前
相机镜头匹配检查清单:靶面、像圈、分辨率、接口逐项核对
机器视觉·靶面·相机镜头匹配·选型清单·像圈
朗锐智科_机器视觉2 天前
钢铁冶金视觉检测实战:从高温连铸坯到冷轧带钢的完整方案设计
视觉检测·机器视觉·工业相机·硬件选型·朗锐智科·钢铁冶金
光视界5 天前
GigE vs USB3:工业相机接口选型指南与场景推荐
经验分享·机器视觉·工业相机
双翌视觉6 天前
.机器视觉国产化方案梳理:对位算法、AI 检测平台与精密测量仪器技术架构
人工智能·计算机视觉·视觉检测·机器视觉·工业自动化·视觉对位系统·ai 视觉检测
yuhouxiyang8 天前
光源波长怎么选?红光、蓝光、红外、紫外的成像差异
机器视觉·光源波长·滤光片·打光·红外紫外
weixin_468466858 天前
让机器像视网膜一样“看”:事件相机与脉冲神经网络如何重构视觉感知
人工智能·神经网络·ai·机器视觉·1024程序员节
朗锐智科_机器视觉10 天前
纺织布匹表面疵点视觉检测实战:从验布机到成品检验的完整方案设计
视觉检测·机器视觉·工业相机·硬件选型·朗锐智科·纺织检测
LittroInno12 天前
运动平台上的巡检相机:曝光时间与快门该怎样选?
数码相机·机器视觉·曝光时间·运动模糊·滚动快门·巡检相机
yuhouxiyang12 天前
镜头接口与后截距:C 口、CS 口、M42、F 口及接圈原理
机器视觉·c口·镜头选型·镜头接口·接圈