提示文章写完后目录可以自动生成如何生成可参考右边的帮助文档文章目录一、Blob的基本原理是什么二、Blob分析的基本流程1.Halcon实现2.Opencv实现总结一、Blob的基本原理是什么Blob分析是对图像中相同像素的连通阈进行分析。其过程其实就是将图像进行二值化分割得到前景和背景然后进行联通区域检测从而得到Blob块的过程。也可以讲Blob则指图像中具有相似灰度、颜色或纹理特征的连通像素区域。Blob分析则通过对这些区域进行检测、分割、特征提取与筛选实现目标定位的功能。二、Blob分析的基本流程采集图像—预处理—分割图像(用于区分前景先像素和背景像素)—特征提取(面积、圆度、角度等。)需要注意在实际的工程应用中Blob分割会很复杂需要处理更多的步骤其原因有很多种比如杂乱和或不均匀的照明或者图像背景复杂等。1.Halcon实现* * * 基本流程,Blob 分析流程预处理 → 二值化 → 连通区域 → 特征提取 → 筛选 → 可视化 *定位芯片Die区域-提取焊线与球键合区域-通过形态学处理分离球键合-筛选、排序球键合并测量直径 * dev_update_window (off)//初始化环境关闭窗口自动更新提高执行速度;作用关闭窗口的自动刷新避免后续每步操作都重绘窗口提升处理效率 dev_close_window ()//关闭当前可能存在的所有窗口避免干扰 dev_open_window (0, 0, 728, 512, black, WindowID)//打开一个新窗口,(0,0)窗口左上角坐标728,512窗口宽高与图像尺寸匹配;black窗口背景色WindowID输出参数窗口句柄后续操作窗口需用到 read_image (Bond, die/die_03)//读取并显示原始图像 dev_display (Bond)//在当前窗口显示原始图像 set_display_font (WindowID, 14, mono, true, false)// 设置显示字体 disp_continue_message (WindowID, black, true)//在窗口显示“按 F5 继续”的提示文字 stop ()//暂停程序执行 *第一步定位芯片区域 threshold (Bond, Bright, 100, 255)//全局阈值二值化提取“亮区域”芯片本身比背景亮 shape_trans (Bright, Die, rectangle2)//将亮区域转换为“最小外接斜矩形”rectangle2精确定位芯片轮廓 *可视化芯片定位结果 dev_set_color (green)//设置后续绘制的颜色为绿色 dev_set_line_width (3)//设置绘制线条宽度为3 dev_set_draw (margin)//设置绘制模式为“边缘绘制”不填充区域只画轮廓 dev_display (Die)//在原图上叠加显示芯片的绿色矩形轮廓 disp_continue_message (WindowID, black, true) stop () *第二步裁剪芯片内部区域提取焊线与球键合 reduce_domain (Bond, Die, DieGrey)//抠图用芯片矩形区域Die裁剪原图Bond只保留芯片内部的灰度图 threshold (DieGrey, Wires, 0, 50)//在芯片内部二值化提取“暗区域”焊线和球键合比芯片背景暗 fill_up_shape (Wires, WiresFilled, area, 1, 100)//按“面积”填充二值区域内的小孔洞焊线或球内部可能有小空洞 dev_display (Bond)//重新显示原图清除之前的绿色矩形 dev_set_draw (fill)//设置绘制模式为“填充” dev_set_color (red)//设置绘制颜色为红色 dev_display (WiresFilled)//在原图上叠加显示填充后的红色焊线与球区域 disp_continue_message (WindowID, black, true) stop () * 第三步形态学开运算分离球键合去掉焊线 opening_circle (WiresFilled, Balls, 15.5)//用“圆形结构元素”做开运算先腐蚀后膨胀 dev_set_color (green) dev_display (Balls)// 用绿色显示分离后的球键合 disp_continue_message (WindowID, black, true) stop () *第四步筛选和排序 connection (Balls, SingleBalls)//连通区域标记将连在一起的球区域拆分为独立的单个球区域 select_shape (SingleBalls, IntermediateBalls, circularity, and, 0.85, 1.0)//按“圆度”筛选区域只保留接近圆形的球排除可能的杂质或未分离干净的焊线 sort_region (IntermediateBalls, FinalBalls, first_point, true, column)//对筛选后的球按“列坐标”排序从左到右排列 dev_display (Bond) dev_set_colored (12) dev_display (FinalBalls)//显示排序后的彩色球区域 disp_continue_message (WindowID, black, true) stop () * 第五步测量球键合的直径并显示结果 smallest_circle (FinalBalls, Row, Column, Radius)//计算每个球的“最小外接圆”得到圆心坐标和半径 NumBalls : |Radius|//统计球的数量 Diameter : 2 * Radius//计算每个球的直径直径2*半径数组运算 meanDiameter : mean(Diameter)//计算所有球的平均直径 minDiameter : min(Diameter)//计算所有球的最小直径可用于质量判断如直径过小则不合格 dev_display (Bond) disp_circle (WindowID, Row, Column, Radius) dev_set_color (white)//设置文字颜色为白色 disp_message (WindowID, D: Diameter$.4, image, Row - 2 * Radius, Column, white, false) //在每个球的上方标注直径 dev_update_window (on)//恢复窗口的自动更新2.Opencv实现using OpenCvSharp; using System; using System.Collections.Generic; using System.Linq; namespace BallBondingInspection { class Program { static void Main(string[] args) { // // 1. 读取图像请替换为实际图像路径原Halcon示例为die/die_03 // string imagePath die_03.png; Mat bond Cv2.ImRead(imagePath, ImreadModes.Grayscale); if (bond.Empty()) { Console.WriteLine(图像读取失败请检查路径); return; } // 转彩色图用于后续绘制OpenCV默认灰度图无法画彩色 Mat bondColor new Mat(); Cv2.CvtColor(bond, bondColor, ColorConversionCodes.GRAY2BGR); // 显示原始图像按任意键继续 Cv2.ImShow(1. 原始图像, bondColor); Cv2.WaitKey(0); // // 2. 定位芯片Die区域对应Halcon: threshold shape_trans // // 2.1 全局阈值二值化提取亮区域像素值100-255 Mat bright new Mat(); Cv2.Threshold(bond, bright, 99, 255, ThresholdTypes.Binary); // 99的设为255 // 2.2 找轮廓并按面积降序排序取最大轮廓作为芯片 Cv2.FindContours(bright, out Point[][] contours, out _, RetrievalModes.External, ContourApproximationModes.ApproxSimple); var sortedContours contours.OrderByDescending(c Cv2.ContourArea(c)).ToList(); // 2.3 计算最小外接斜矩形对应Halcon rectangle2 RotatedRect dieRect Cv2.MinAreaRect(sortedContours[0]); // 2.4 可视化芯片定位绿色3px宽矩形 Mat dieDisplay bondColor.Clone(); Point2f[] diePoints dieRect.Points(); for (int i 0; i 4; i) { Cv2.Line(dieDisplay, (Point)diePoints[i], (Point)diePoints[(i 1) % 4], Scalar.Green, 3); } Cv2.ImShow(2. 芯片定位, dieDisplay); Cv2.WaitKey(0); // // 3. 裁剪芯片内部区域对应Halcon: reduce_domain // // 3.1 创建芯片区域的掩码白色为有效区域 Mat dieMask Mat.Zeros(bond.Size(), MatType.CV_8UC1); Point[] diePointsInt Array.ConvertAll(diePoints, p (Point)p); Cv2.FillConvexPoly(dieMask, diePointsInt, Scalar.White); // 3.2 用掩码裁剪原图 Mat dieGrey new Mat(); bond.CopyTo(dieGrey, dieMask); // // 4. 提取暗区域焊线球对应Halcon: threshold 0-50 // Mat wires new Mat(); Cv2.Threshold(dieGrey, wires, 50, 255, ThresholdTypes.BinaryInv); // ≤50的设为255暗区域 // // 5. 填充小面积孔洞对应Halcon: fill_up_shape area 1-100 // Mat wiresFilled FillSmallHoles(wires, 1, 100); // 5.1 可视化焊线与球红色填充 Mat wiresDisplay bondColor.Clone(); Cv2.FindContours(wiresFilled, out Point[][] wiresContours, out _, RetrievalModes.External, ContourApproximationModes.ApproxSimple); Cv2.DrawContours(wiresDisplay, wiresContours, -1, Scalar.Red, -1); Cv2.ImShow(3. 焊线与球提取, wiresDisplay); Cv2.WaitKey(0); // // 6. 形态学开运算分离球键合对应Halcon: opening_circle 15.5 // Mat balls new Mat(); int radius 16; // 15.5取整结构元素大小为33x33 Mat element Cv2.GetStructuringElement(MorphShapes.Ellipse, new Size(2 * radius 1, 2 * radius 1)); Cv2.MorphologyEx(wiresFilled, balls, MorphTypes.Open, element); // 6.1 可视化分离后的球绿色填充 Mat ballsDisplay bondColor.Clone(); Cv2.FindContours(balls, out Point[][] ballsContours, out _, RetrievalModes.External, ContourApproximationModes.ApproxSimple); Cv2.DrawContours(ballsDisplay, ballsContours, -1, Scalar.Green, -1); Cv2.ImShow(4. 球键合分离, ballsDisplay); Cv2.WaitKey(0); // // 7. 筛选圆度排序对应Halcon: connection select_shape sort_region // // 7.1 找球的轮廓 Cv2.FindContours(balls, out Point[][] singleBallsContours, out _, RetrievalModes.External, ContourApproximationModes.ApproxSimple); // 7.2 筛选圆度0.85-1.0的轮廓圆度公式4π*Area/Perimeter² ListPoint[] validBalls new ListPoint[](); foreach (var contour in singleBallsContours) { double area Cv2.ContourArea(contour); double perimeter Cv2.ArcLength(contour, true); if (perimeter 0) continue; double circularity (4 * Math.PI * area) / (perimeter * perimeter); if (circularity 0.85 circularity 1.0) { validBalls.Add(contour); } } // 7.3 按质心X坐标升序排序对应Halcon column排序 ListTuplePoint[], double ballsWithCentroidX validBalls.Select(contour { Moments m Cv2.Moments(contour); double cx m.M10 / m.M00; // 质心X坐标 return Tuple.Create(contour, cx); }).OrderBy(t t.Item2).ToList(); ListPoint[] finalBalls ballsWithCentroidX.Select(t t.Item1).ToList(); // 7.4 可视化筛选排序后的球12种彩色循环 Mat finalDisplay bondColor.Clone(); Scalar[] colors { Scalar.Red, Scalar.Green, Scalar.Blue, Scalar.Yellow, Scalar.Cyan, Scalar.Magenta, Scalar.Orange, Scalar.Purple, Scalar.Pink, Scalar.Lime, Scalar.Teal, Scalar.Indigo }; for (int i 0; i finalBalls.Count; i) { Cv2.DrawContours(finalDisplay, new Point[][] { finalBalls[i] }, -1, colors[i % colors.Length], -1); } Cv2.ImShow(5. 最终球键合检测, finalDisplay); Cv2.WaitKey(0); // // 8. 测量直径并显示结果对应Halcon: smallest_circle disp_circle // Mat resultDisplay bondColor.Clone(); Listfloat diameters new Listfloat(); for (int i 0; i finalBalls.Count; i) { // 计算最小外接圆 Cv2.MinEnclosingCircle(finalBalls[i], out Point2f center, out float ballRadius); float diameter 2 * ballRadius; diameters.Add(diameter); // 画圆红色2px宽 Cv2.Circle(resultDisplay, (Point)center, (int)ballRadius, Scalar.Red, 2); // 标注直径白色文字保留4位小数 string text $D: {diameter:F4}; int textY (int)(center.Y - 2 * ballRadius); // 球上方2倍半径处 Cv2.PutText(resultDisplay, text, new Point((int)center.X - 30, textY), HersheyFonts.HersheySimplex, 0.5, Scalar.White, 1); } // 输出统计信息 if (diameters.Count 0) { Console.WriteLine($检测到 {diameters.Count} 个球键合); Console.WriteLine($平均直径: {diameters.Average():F4}); Console.WriteLine($最小直径: {diameters.Min():F4}); } // 显示最终结果 Cv2.ImShow(6. 直径测量结果, resultDisplay); Cv2.WaitKey(0); // 释放资源 Cv2.DestroyAllWindows(); } // // 辅助函数填充小面积孔洞 // /// summary /// 填充二值图像中面积在[minArea, maxArea]之间的孔洞 /// /summary static Mat FillSmallHoles(Mat src, int minArea, int maxArea) { Mat dst src.Clone(); // 找所有轮廓包含内部孔洞用RetrievalModes.CComp Cv2.FindContours(dst, out Point[][] contours, out HierarchyIndex[] hierarchy, RetrievalModes.CComp, ContourApproximationModes.ApproxSimple); for (int i 0; i contours.Length; i) { // 内部孔洞的特征hierarchy[i].Parent ! -1有父轮廓 if (hierarchy[i].Parent ! -1) { double area Cv2.ContourArea(contours[i]); if (area minArea area maxArea) { // 填充孔洞为白色 Cv2.DrawContours(dst, contours, i, Scalar.White, -1); } } } return dst; } } }总结以上就是今天要讲的内容本文仅仅简单介绍了blob的基本原理和使用包括了通过halcon的方式以及用opencv的方式实现相同的效果。