前言C#目前在国内的工业场景中还是有一定的应用的使用的框架有Winform、WPF很多会结合Halcon、OpenCV做一些偏视觉类的应用。目前据我所经历的在半导体封装设备、AOI设备中使用都挺多的。今天跟大家简单分享一下C#/Halcon在AOI设备软件中的应用。AOIAutomated Optical Inspection自动光学检测是一种利用计算机视觉技术和光学设备对产品进行自动化检测的技术。看起来AOI算是一种技术那AOI设备就是就是使用这种技术的一套设备看起来就像下面这样在这个设备上有很多硬件比如相机、光源等等AOI设备软件就是用来控制这些硬件执行检测操作等等部署在这个设备上的软件。现在我们大概了解了什么是AOI/AOI设备/AOI设备软件了。现在就需要知道我们拿AOI这个技术用来干嘛AOI一般可以用来检测产品的缺陷。这个技术又是如何检测的呢接下来我们通过一个简单的例子了解这个过程。简单的检测过程比如我们有一个标准的东西比如说在设计时就是这样设计的还有一个实际的东西也就是最终生产出来的东西。比如现在这个标准的东西长这样然后实际的东西长这样我们可以很直观的看到矩形的一角与设计图纸不一样不一样的地方大概率就是出问题的地方。那么使用AOI进行检测的第一个步骤就是先把设计图纸与实物图对齐因为实际上它们大概率坐标系不同。现在在读取了这两张图片之后先让它们的坐标系变得不一样。* 1. 读取图像 read_image (Test, F:/Users/user/Desktop/Halcon脚本/333.png) read_image (Test2, F:/Users/user/Desktop/Halcon脚本/444.png) * 2. 将设计图纸缩小2倍模拟图纸与实物尺寸不一致 zoom_image_factor (Test, TestScaled, 0.5, 0.5, constant)然后现在我们就要想怎么样让这两张图对齐这时候就需要用到仿射变换矩阵了它是个什么东西呢先来过一下它的定义仿射变换矩阵是一种线性代数工具用于描述二维/三维空间中的仿射变换。仿射变换是保持直线平行性和直线比例的几何变换——即变换后直线仍为直线平行线仍平行但长度和角度可能改变。可以理解为就是平面对平面的变换也就是在这张图上的某个点使用这个矩阵之后就会变换到另一张图上的对应点。那么现在的问题来到了怎么计算得到这个仿射变换矩阵呢对AOI熟悉的朋友可能会经常听到定位点这个词我们只要找到这两张图片上3个对应点就可以计算了。而具体如何计算要根据实际情况在这个例子中通过对图像进行网格拆分然后寻找封闭轮廓选择圆度比较高的来当定位点。* 3. 对设计图纸查找封闭轮廓 edges_sub_pix (TestScaled, Edges1, canny, 1.5, 10, 20) select_shape_xld (Edges1, ClosedEdges1, contlength, and, 50, 99999) * 4. 对实物图查找封闭轮廓 edges_sub_pix (Test2, Edges2, canny, 1.5, 10, 20) select_shape_xld (Edges2, ClosedEdges2, contlength, and, 50, 99999)edges_sub_pix从图像中提取亚像素级精度的边缘轮廓。select_shape_xld根据轮廓的形状特征对 XLD 轮廓进行筛选只保留满足条件的轮廓。现在设计图纸变成了这样实物图变成了这样接下来选点这一步简单了解一下就行实际上要根据实际情况进行判断很难找到全部适用的情况。* * 5 6. 从两幅图中选定位点2×3网格设计图优先实物图就近匹配 * get_image_size (TestScaled, Width1, Height1) get_image_size (Test2, Width2, Height2) count_obj (ClosedEdges1, NumEdges1) count_obj (ClosedEdges2, NumEdges2) GridRows : 2 GridCols : 3 CellHeight1 : Height1 / GridRows CellWidth1 : Width1 / GridCols CellHeight2 : Height2 / GridRows CellWidth2 : Width2 / GridCols MinCircularity : 0.6 RowDesign : [] ColDesign : [] RowReal : [] ColReal : [] for gr : 0 to GridRows - 1 by 1 for gc : 0 to GridCols - 1 by 1 * * 第一步在设计图该网格中找圆度最高的特征点 * RowMin1 : gr * CellHeight1 ColMin1 : gc * CellWidth1 RowMax1 : (gr 1) * CellHeight1 ColMax1 : (gc 1) * CellWidth1 BestCircularity1 : 0 BestRow1 : -1 BestCol1 : -1 for i : 1 to NumEdges1 by 1 select_obj (ClosedEdges1, EdgeSelected, i) area_center_xld (EdgeSelected, Area, Row, Column, PointOrder) if (Row RowMin1 and Row RowMax1 and Column ColMin1 and Column ColMax1) gen_region_contour_xld (EdgeSelected, Region, filled) circularity (Region, Circularity) if (Circularity BestCircularity1 and Circularity MinCircularity) BestCircularity1 : Circularity BestRow1 : Row BestCol1 : Column endif endif endfor * 设计图该网格没找到跳过 if (BestRow1 0) continue endif * * 第二步推算实物图中的预期位置 * 设计图点在网格内的相对比例 → 映射到实物图网格 * RelRow : (BestRow1 - RowMin1) / CellHeight1 RelCol : (BestCol1 - ColMin1) / CellWidth1 RowMin2 : gr * CellHeight2 ColMin2 : gc * CellWidth2 RowMax2 : (gr 1) * CellHeight2 ColMax2 : (gc 1) * CellWidth2 ExpectedRow2 : RowMin2 RelRow * CellHeight2 ExpectedCol2 : ColMin2 RelCol * CellWidth2 * * 第三步在实物图该网格中找圆度0.6且距离预期位置最近的特征点 * BestDist2 : 999999 BestRow2 : -1 BestCol2 : -1 for i : 1 to NumEdges2 by 1 select_obj (ClosedEdges2, EdgeSelected, i) area_center_xld (EdgeSelected, Area, Row, Column, PointOrder) if (Row RowMin2 and Row RowMax2 and Column ColMin2 and Column ColMax2) gen_region_contour_xld (EdgeSelected, Region, filled) circularity (Region, Circularity) if (Circularity MinCircularity) Dist : sqrt((Row - ExpectedRow2) * (Row - ExpectedRow2) (Column - ExpectedCol2) * (Column - ExpectedCol2)) if (Dist BestDist2) BestDist2 : Dist BestRow2 : Row BestCol2 : Column endif endif endif endfor * * 第四步两幅图都找到才添加点对 * if (BestRow1 0 and BestRow2 0) RowDesign : [RowDesign, BestRow1] ColDesign : [ColDesign, BestCol1] RowReal : [RowReal, BestRow2] ColReal : [ColReal, BestCol2] endif endfor endfor反正通过这一步我们获取到了设计图与实物图上对应的一组点现在可以通过这些点来计算仿射变换矩阵了。* 7. 检查定位点数量是否足够仿射变换至少需要3对点 NumPoints : |RowDesign| if (NumPoints 3) disp_message (3600, 定位点不足需要至少3对当前 NumPoints 对无法计算变换矩阵, window, 12, 12, red, true) stop () endif * 8. 计算仿射变换矩阵从设计图纸坐标 → 实物图坐标 vector_to_hom_mat2d (RowDesign, ColDesign, RowReal, ColReal, HomMat2D)然后就可以使用仿射变换矩阵将设计图纸与实物图对齐或者使用逆矩阵实物图与设计图对齐反正现在可以实现两者的对齐了。* 9. 将设计图纸通过仿射变换对齐到实物图 affine_trans_image (TestScaled, TestAligned, HomMat2D, constant, true)可以看下我们的定位点选的情况怎么样。* 10. 在设计图上绘制定位点十字标记 gen_cross_contour_xld (Cross1, RowDesign, ColDesign, 15, 0.785398) dev_display (TestScaled) dev_display (Cross1) * 11. 在实物图上绘制定位点十字标记 gen_cross_contour_xld (Cross2, RowReal, ColReal, 15, 0.785398) dev_display (Test2) dev_display (Cross2)设计图定位点坐标实物图定位点坐标只要我们定位点选的好仿射变换矩阵一般效果就不错。现在得到了仿射变换矩阵现在就可以开始检测了今天介绍一下最简单的检测实际的检测肯定没有这么简单。* * 13. 缺陷检测对齐后的设计图与实物图做差 轮廓掩膜排除 * * 13.1 裁剪对齐后的设计图使其与实物图尺寸一致 affine_trans_point_2d (HomMat2D, 0, 0, OffsetRow, OffsetCol) get_image_size (Test2, W2, H2) crop_part (TestAligned, TestAlignedCrop, OffsetRow, OffsetCol, W2, H2) * 13.2 将两幅图转为灰度 rgb1_to_gray (TestAlignedCrop, GrayDesign) rgb1_to_gray (Test2, GrayReal) * 13.3 计算绝对差值 abs_diff_image (GrayReal, GrayDesign, DiffImage, 1) * * 13.4 提取设计图中的正常轮廓区域作为掩膜 * * 对设计图做边缘检测 膨胀生成轮廓掩膜 edges_sub_pix (TestAlignedCrop, DesignEdges, canny, 1.5, 10, 20) gen_region_contour_xld (DesignEdges, DesignEdgeRegion, margin) * 膨胀轮廓区域覆盖轮廓线宽 配准误差范围 dilation_circle (DesignEdgeRegion, DesignEdgeDilated, 5.0) * 合并所有轮廓为一个掩膜 union1 (DesignEdgeDilated, ContourMask) * * 13.5 差分结果中排除正常轮廓区域 * * 阈值分割提取差异 threshold (DiffImage, DiffRegion, 30, 255) * 从差异区域中减去轮廓掩膜 difference (DiffRegion, ContourMask, DefectCandidates) * * 13.6 形态学处理 面积过滤 * * 填充小孔洞 fill_up (DefectCandidates, DefectFilled) * 连通域分割 connection (DefectFilled, DefectConnected) * 去除小噪声 面积过滤 select_shape (DefectConnected, DefectRegions, area, and, 200, 5000) * * 13.7 显示结果用圆标记缺陷位置 * * 显示实物图 dev_display (Test2) * 显示被排除的轮廓区域绿色半透明 dev_set_draw (margin) dev_set_line_width (1) dev_set_color (green) dev_display (DesignEdgeDilated) * 用圆标记缺陷位置 dev_set_draw (margin) dev_set_line_width (2) dev_set_color (red) * 计算每个缺陷的等效圆半径用于绘制 smallest_circle (DefectRegions, DefectCenterRows, DefectCenterCols, DefectRadii) * 绘制圆标记 gen_circle_contour_xld (DefectCircles, DefectCenterRows, DefectCenterCols, DefectRadii*1.5, 0, 6.28318, positive, 1) dev_display (DefectCircles) * 在圆心处画十字 gen_cross_contour_xld (DefectCrosses, DefectCenterRows, DefectCenterCols, 15, 0.785398) dev_set_color (yellow) dev_display (DefectCrosses)我们来看这一个过程首先是灰度化之后做差。然后我们发现直接做差并不全是缺陷区域也有一些是正常的轮廓然后就把轮廓当掩膜。减去掩膜区域之后就会得到真正的缺陷位置。然后我们再标注出这个缺陷位置。这只是简化的例子实际上可能还要再做一些操作找到缺陷之后还需要判断是什么缺陷也要结合深度学习去训练模型去做一些过滤之类的。当我们有Halcon脚本之后再转化为C#问题就不大了现在有AI了一般交给AI就可以了篇幅有限转化为WPF应用后面再进行分享。以上是AOI检测流程的一个简单分享与自己的学习记录希望对你有所帮助。
C#/Halcon:简单介绍在AOI设备软件中的应用
发布时间:2026/5/24 6:00:00
前言C#目前在国内的工业场景中还是有一定的应用的使用的框架有Winform、WPF很多会结合Halcon、OpenCV做一些偏视觉类的应用。目前据我所经历的在半导体封装设备、AOI设备中使用都挺多的。今天跟大家简单分享一下C#/Halcon在AOI设备软件中的应用。AOIAutomated Optical Inspection自动光学检测是一种利用计算机视觉技术和光学设备对产品进行自动化检测的技术。看起来AOI算是一种技术那AOI设备就是就是使用这种技术的一套设备看起来就像下面这样在这个设备上有很多硬件比如相机、光源等等AOI设备软件就是用来控制这些硬件执行检测操作等等部署在这个设备上的软件。现在我们大概了解了什么是AOI/AOI设备/AOI设备软件了。现在就需要知道我们拿AOI这个技术用来干嘛AOI一般可以用来检测产品的缺陷。这个技术又是如何检测的呢接下来我们通过一个简单的例子了解这个过程。简单的检测过程比如我们有一个标准的东西比如说在设计时就是这样设计的还有一个实际的东西也就是最终生产出来的东西。比如现在这个标准的东西长这样然后实际的东西长这样我们可以很直观的看到矩形的一角与设计图纸不一样不一样的地方大概率就是出问题的地方。那么使用AOI进行检测的第一个步骤就是先把设计图纸与实物图对齐因为实际上它们大概率坐标系不同。现在在读取了这两张图片之后先让它们的坐标系变得不一样。* 1. 读取图像 read_image (Test, F:/Users/user/Desktop/Halcon脚本/333.png) read_image (Test2, F:/Users/user/Desktop/Halcon脚本/444.png) * 2. 将设计图纸缩小2倍模拟图纸与实物尺寸不一致 zoom_image_factor (Test, TestScaled, 0.5, 0.5, constant)然后现在我们就要想怎么样让这两张图对齐这时候就需要用到仿射变换矩阵了它是个什么东西呢先来过一下它的定义仿射变换矩阵是一种线性代数工具用于描述二维/三维空间中的仿射变换。仿射变换是保持直线平行性和直线比例的几何变换——即变换后直线仍为直线平行线仍平行但长度和角度可能改变。可以理解为就是平面对平面的变换也就是在这张图上的某个点使用这个矩阵之后就会变换到另一张图上的对应点。那么现在的问题来到了怎么计算得到这个仿射变换矩阵呢对AOI熟悉的朋友可能会经常听到定位点这个词我们只要找到这两张图片上3个对应点就可以计算了。而具体如何计算要根据实际情况在这个例子中通过对图像进行网格拆分然后寻找封闭轮廓选择圆度比较高的来当定位点。* 3. 对设计图纸查找封闭轮廓 edges_sub_pix (TestScaled, Edges1, canny, 1.5, 10, 20) select_shape_xld (Edges1, ClosedEdges1, contlength, and, 50, 99999) * 4. 对实物图查找封闭轮廓 edges_sub_pix (Test2, Edges2, canny, 1.5, 10, 20) select_shape_xld (Edges2, ClosedEdges2, contlength, and, 50, 99999)edges_sub_pix从图像中提取亚像素级精度的边缘轮廓。select_shape_xld根据轮廓的形状特征对 XLD 轮廓进行筛选只保留满足条件的轮廓。现在设计图纸变成了这样实物图变成了这样接下来选点这一步简单了解一下就行实际上要根据实际情况进行判断很难找到全部适用的情况。* * 5 6. 从两幅图中选定位点2×3网格设计图优先实物图就近匹配 * get_image_size (TestScaled, Width1, Height1) get_image_size (Test2, Width2, Height2) count_obj (ClosedEdges1, NumEdges1) count_obj (ClosedEdges2, NumEdges2) GridRows : 2 GridCols : 3 CellHeight1 : Height1 / GridRows CellWidth1 : Width1 / GridCols CellHeight2 : Height2 / GridRows CellWidth2 : Width2 / GridCols MinCircularity : 0.6 RowDesign : [] ColDesign : [] RowReal : [] ColReal : [] for gr : 0 to GridRows - 1 by 1 for gc : 0 to GridCols - 1 by 1 * * 第一步在设计图该网格中找圆度最高的特征点 * RowMin1 : gr * CellHeight1 ColMin1 : gc * CellWidth1 RowMax1 : (gr 1) * CellHeight1 ColMax1 : (gc 1) * CellWidth1 BestCircularity1 : 0 BestRow1 : -1 BestCol1 : -1 for i : 1 to NumEdges1 by 1 select_obj (ClosedEdges1, EdgeSelected, i) area_center_xld (EdgeSelected, Area, Row, Column, PointOrder) if (Row RowMin1 and Row RowMax1 and Column ColMin1 and Column ColMax1) gen_region_contour_xld (EdgeSelected, Region, filled) circularity (Region, Circularity) if (Circularity BestCircularity1 and Circularity MinCircularity) BestCircularity1 : Circularity BestRow1 : Row BestCol1 : Column endif endif endfor * 设计图该网格没找到跳过 if (BestRow1 0) continue endif * * 第二步推算实物图中的预期位置 * 设计图点在网格内的相对比例 → 映射到实物图网格 * RelRow : (BestRow1 - RowMin1) / CellHeight1 RelCol : (BestCol1 - ColMin1) / CellWidth1 RowMin2 : gr * CellHeight2 ColMin2 : gc * CellWidth2 RowMax2 : (gr 1) * CellHeight2 ColMax2 : (gc 1) * CellWidth2 ExpectedRow2 : RowMin2 RelRow * CellHeight2 ExpectedCol2 : ColMin2 RelCol * CellWidth2 * * 第三步在实物图该网格中找圆度0.6且距离预期位置最近的特征点 * BestDist2 : 999999 BestRow2 : -1 BestCol2 : -1 for i : 1 to NumEdges2 by 1 select_obj (ClosedEdges2, EdgeSelected, i) area_center_xld (EdgeSelected, Area, Row, Column, PointOrder) if (Row RowMin2 and Row RowMax2 and Column ColMin2 and Column ColMax2) gen_region_contour_xld (EdgeSelected, Region, filled) circularity (Region, Circularity) if (Circularity MinCircularity) Dist : sqrt((Row - ExpectedRow2) * (Row - ExpectedRow2) (Column - ExpectedCol2) * (Column - ExpectedCol2)) if (Dist BestDist2) BestDist2 : Dist BestRow2 : Row BestCol2 : Column endif endif endif endfor * * 第四步两幅图都找到才添加点对 * if (BestRow1 0 and BestRow2 0) RowDesign : [RowDesign, BestRow1] ColDesign : [ColDesign, BestCol1] RowReal : [RowReal, BestRow2] ColReal : [ColReal, BestCol2] endif endfor endfor反正通过这一步我们获取到了设计图与实物图上对应的一组点现在可以通过这些点来计算仿射变换矩阵了。* 7. 检查定位点数量是否足够仿射变换至少需要3对点 NumPoints : |RowDesign| if (NumPoints 3) disp_message (3600, 定位点不足需要至少3对当前 NumPoints 对无法计算变换矩阵, window, 12, 12, red, true) stop () endif * 8. 计算仿射变换矩阵从设计图纸坐标 → 实物图坐标 vector_to_hom_mat2d (RowDesign, ColDesign, RowReal, ColReal, HomMat2D)然后就可以使用仿射变换矩阵将设计图纸与实物图对齐或者使用逆矩阵实物图与设计图对齐反正现在可以实现两者的对齐了。* 9. 将设计图纸通过仿射变换对齐到实物图 affine_trans_image (TestScaled, TestAligned, HomMat2D, constant, true)可以看下我们的定位点选的情况怎么样。* 10. 在设计图上绘制定位点十字标记 gen_cross_contour_xld (Cross1, RowDesign, ColDesign, 15, 0.785398) dev_display (TestScaled) dev_display (Cross1) * 11. 在实物图上绘制定位点十字标记 gen_cross_contour_xld (Cross2, RowReal, ColReal, 15, 0.785398) dev_display (Test2) dev_display (Cross2)设计图定位点坐标实物图定位点坐标只要我们定位点选的好仿射变换矩阵一般效果就不错。现在得到了仿射变换矩阵现在就可以开始检测了今天介绍一下最简单的检测实际的检测肯定没有这么简单。* * 13. 缺陷检测对齐后的设计图与实物图做差 轮廓掩膜排除 * * 13.1 裁剪对齐后的设计图使其与实物图尺寸一致 affine_trans_point_2d (HomMat2D, 0, 0, OffsetRow, OffsetCol) get_image_size (Test2, W2, H2) crop_part (TestAligned, TestAlignedCrop, OffsetRow, OffsetCol, W2, H2) * 13.2 将两幅图转为灰度 rgb1_to_gray (TestAlignedCrop, GrayDesign) rgb1_to_gray (Test2, GrayReal) * 13.3 计算绝对差值 abs_diff_image (GrayReal, GrayDesign, DiffImage, 1) * * 13.4 提取设计图中的正常轮廓区域作为掩膜 * * 对设计图做边缘检测 膨胀生成轮廓掩膜 edges_sub_pix (TestAlignedCrop, DesignEdges, canny, 1.5, 10, 20) gen_region_contour_xld (DesignEdges, DesignEdgeRegion, margin) * 膨胀轮廓区域覆盖轮廓线宽 配准误差范围 dilation_circle (DesignEdgeRegion, DesignEdgeDilated, 5.0) * 合并所有轮廓为一个掩膜 union1 (DesignEdgeDilated, ContourMask) * * 13.5 差分结果中排除正常轮廓区域 * * 阈值分割提取差异 threshold (DiffImage, DiffRegion, 30, 255) * 从差异区域中减去轮廓掩膜 difference (DiffRegion, ContourMask, DefectCandidates) * * 13.6 形态学处理 面积过滤 * * 填充小孔洞 fill_up (DefectCandidates, DefectFilled) * 连通域分割 connection (DefectFilled, DefectConnected) * 去除小噪声 面积过滤 select_shape (DefectConnected, DefectRegions, area, and, 200, 5000) * * 13.7 显示结果用圆标记缺陷位置 * * 显示实物图 dev_display (Test2) * 显示被排除的轮廓区域绿色半透明 dev_set_draw (margin) dev_set_line_width (1) dev_set_color (green) dev_display (DesignEdgeDilated) * 用圆标记缺陷位置 dev_set_draw (margin) dev_set_line_width (2) dev_set_color (red) * 计算每个缺陷的等效圆半径用于绘制 smallest_circle (DefectRegions, DefectCenterRows, DefectCenterCols, DefectRadii) * 绘制圆标记 gen_circle_contour_xld (DefectCircles, DefectCenterRows, DefectCenterCols, DefectRadii*1.5, 0, 6.28318, positive, 1) dev_display (DefectCircles) * 在圆心处画十字 gen_cross_contour_xld (DefectCrosses, DefectCenterRows, DefectCenterCols, 15, 0.785398) dev_set_color (yellow) dev_display (DefectCrosses)我们来看这一个过程首先是灰度化之后做差。然后我们发现直接做差并不全是缺陷区域也有一些是正常的轮廓然后就把轮廓当掩膜。减去掩膜区域之后就会得到真正的缺陷位置。然后我们再标注出这个缺陷位置。这只是简化的例子实际上可能还要再做一些操作找到缺陷之后还需要判断是什么缺陷也要结合深度学习去训练模型去做一些过滤之类的。当我们有Halcon脚本之后再转化为C#问题就不大了现在有AI了一般交给AI就可以了篇幅有限转化为WPF应用后面再进行分享。以上是AOI检测流程的一个简单分享与自己的学习记录希望对你有所帮助。