前言前面十二篇我们学了分割、模板匹配、几何变换、测量拟合、特征提取。现在把它们串起来做工业视觉中最核心的任务——缺陷检测。缺陷检测的本质是找出和标准不一样的东西。方法取决于不一样体现在哪里是亮度不同形状不同尺寸偏差还是纹理异常本文讲解五种主流缺陷检测方法每种附带完整可运行代码。一、缺陷检测概述1.1 五大检测方法方法核心思路适用场景涉及算子Blob 分析分割 → 连通域 → 特征筛选颗粒计数、异物检测、尺寸分选threshold/connection/select_shape尺寸偏差提取目标 → 测量 → 对比公差焊盘缺失、孔径偏差、间距异常smallest_circle/distance_pp形态学差分灰度开运算建参考 → 差分提取缺陷表面划痕、凹坑、凸点gray_opening_rect/abs_diff_image动态阈值局部均值做参考 → 偏离即为缺陷非均匀光照、纹理表面mean_image/dyn_threshold多特征联合同时用面积形状灰度分类复杂缺陷分类OK/NG/噪声circularity/intensity/select_shape1.2 标准检测流程读图 → 预处理滤波/增强 → 定位ROI → 分割 → 特征提取 → 分类判定 → 结果输出关键原则先定位再检测。大多数缺陷检测的第一步不是找缺陷而是找目标——先定位零件/区域再在该区域内找缺陷。这样可以大幅减少误检。二、Blob 分析缺陷检测 — 颗粒分选2.1 原理BlobBinary Large Object分析是最基础的缺陷检测方法将图像二值化提取连通域用面积/形状等特征筛选目标。适用于目标与背景对比明显、缺陷表现为面积或形状异常的场景。检测对象pellets图像中的颗粒。正常颗粒面积应在某个范围内过大或过小的都是异常。2.2 完整示例* Blob 分析缺陷检测颗粒分选 * 场景检测 pellets 图像中的颗粒按面积分类正常/异常 dev_update_off() dev_close_window() read_image(Image, pellets) get_image_size(Image, Width, Height) dev_open_window(0, 0, Width, Height, black, WindowHandle) set_display_font(WindowHandle, 14, mono, true, false) dev_set_draw(margin) dev_set_line_width(2) * 第1步自动阈值分割Otsu 自动找最佳阈值 binary_threshold(Image, LightRegion, max_separability, light, UsedThreshold) opening_circle(LightRegion, Region, 2.5) * 第2步分离粘连颗粒 * 直接 connection 会导致粘连颗粒分不开先腐蚀再连通再膨胀 erosion_circle(Region, RegionErosion, 7.5) connection(RegionErosion, ConnectedRegions) dilation_circle(ConnectedRegions, FinalRegions, 7.5) * 第3步提取面积特征 area_center(FinalRegions, Area, Row, Column) count_obj(FinalRegions, NumPellets) * 第4步按面积分类 * 用平均面积作为基准±40% 范围内为正常 MeanArea : mean(Area) MinNormal : MeanArea * 0.6 MaxNormal : MeanArea * 1.4 dev_display(Image) CountOK : 0 CountNG : 0 for I : 0 to NumPellets - 1 by 1 select_obj(FinalRegions, SinglePellet, I 1) if (Area[I] MinNormal or Area[I] MaxNormal) * 异常颗粒 CountNG : CountNG 1 dev_set_color(red) disp_message(WindowHandle, NG A Area[I]$.0f, image, Row[I] - 15, Column[I] - 20, red, false) else * 正常颗粒 CountOK : CountOK 1 dev_set_color(green) endif dev_display(SinglePellet) disp_cross(WindowHandle, Row[I], Column[I], 8, 0) endfor * 汇总 dev_set_color(white) disp_message(WindowHandle, Total: NumPellets OK: CountOK NG: CountNG MeanArea MeanArea$.0f, window, 10, 10, white, false) dev_update_on()代码要点步骤算子作用自动阈值binary_threshold(max_separability)Otsu 自动计算最佳阈值无需手动调开运算opening_circle(2.5)去除边缘毛刺和小噪点腐蚀→连通→膨胀erosion(7.5)→connection→dilation(7.5)分离粘连颗粒恢复原始大小面积分类mean(Area) * 0.6 / 1.4以均值为基准±40% 为正常范围为什么先腐蚀再膨胀颗粒可能互相粘连直接connection会把两个颗粒当成一个。先腐蚀缩小使粘连处断开连通后再膨胀放大恢复原始大小。这是 Blob 分析中处理粘连目标的标准手法。三、尺寸偏差检测 — 焊盘检测3.1 原理尺寸偏差检测不找缺陷区域而是测量目标的几何尺寸与标准值对比。如果某个尺寸超出公差则判定为缺陷。检测对象die/die_03图像中 IC 芯片的焊盘Bond Pads。先定位芯片区域提取焊盘测量每个焊盘直径与均值对比找出异常焊盘。3.2 完整示例* 尺寸偏差检测焊盘检测 * 场景检测 die_03 图像中 IC 芯片焊盘找出尺寸异常的焊盘 dev_update_off() dev_close_window() read_image(Image, die/die_03) get_image_size(Image, Width, Height) dev_open_window(0, 0, Width, Height, black, WindowHandle) set_display_font(WindowHandle, 14, mono, true, false) dev_set_draw(margin) dev_set_line_width(2) * 第1步定位芯片区域 * 亮区 芯片本体 焊盘用 shape_trans 提取外接矩形 threshold(Image, Bright, 100, 255) shape_trans(Bright, Die, rectangle2) reduce_domain(Image, Die, ImageDie) * 第2步在芯片区域内提取焊盘 * 焊盘是暗区域灰度 50填充小孔后开运算变圆 threshold(ImageDie, Wires, 0, 50) fill_up_shape(Wires, WiresFilled, area, 1, 100) opening_circle(WiresFilled, Balls, 15.5) connection(Balls, SingleBalls) * 第3步筛选圆形焊盘 select_shape(SingleBalls, RoundBalls, circularity, and, 0.85, 1.0) sort_region(RoundBalls, FinalBalls, first_point, true, column) * 第4步测量每个焊盘直径 smallest_circle(FinalBalls, Row, Column, Radius) Diameter : 2 * Radius MeanDiameter : mean(Diameter) MinD : MeanDiameter * 0.8 MaxD : MeanDiameter * 1.2 * 第5步显示并标注异常 dev_display(Image) count_obj(FinalBalls, NumBalls) CountOK : 0 CountNG : 0 for I : 0 to NumBalls - 1 by 1 if (Diameter[I] MinD or Diameter[I] MaxD) CountNG : CountNG 1 dev_set_color(red) disp_circle(WindowHandle, Row[I], Column[I], Radius[I]) disp_message(WindowHandle, NG D Diameter[I]$.1f, image, Row[I] - 25, Column[I] - 20, red, false) else CountOK : CountOK 1 dev_set_color(green) disp_circle(WindowHandle, Row[I], Column[I], Radius[I]) endif endfor dev_set_color(white) disp_message(WindowHandle, Balls: NumBalls OK: CountOK NG: CountNG MeanD MeanDiameter$.1f, window, 10, 10, white, false) dev_update_on()代码要点步骤算子作用芯片定位threshold(100,255)→shape_trans(rectangle2)提取芯片外接矩形区域限定reduce_domain后续操作只在芯片内进行避免外部干扰焊盘提取threshold(0,50)→fill_up_shape→opening_circle(15.5)暗区→填充→圆形化圆度筛选select_shape(circularity, 0.85, 1.0)只保留圆形焊盘排除不规则噪点直径测量smallest_circle获取每个焊盘的最小外接圆半径公差判定均值 ±20%超出范围标记为 NG为什么要reduce_domain原始图像中芯片外部也有暗像素如果不限定区域threshold(0, 50)会把芯片外的暗背景也选进来。reduce_domain把操作范围限制在芯片矩形内确保只提取芯片上的焊盘。四、形态学差分缺陷检测 — 表面缺陷4.1 原理形态学差分的核心思想用灰度开运算生成一张无缺陷参考图再与原图做差差值大的位置就是缺陷。灰度开运算 先腐蚀后膨胀会消除比结构元素小的亮特征原图 - 开运算结果 被消除的亮特征即小亮点、凸起闭运算 - 原图 被消除的暗特征即小暗点、凹坑用abs_diff_image取绝对差同时捕获亮缺陷和暗缺陷检测对象photometric_stereo/embossed_01浮雕金属表面。表面有真实划痕缺陷划痕灰度 80~120、正常表面 200、浮雕凸起 220对比度极强最适合演示形态学差分。4.2 完整示例* 形态学差分缺陷检测表面缺陷 * 场景检测 photometric_stereo/embossed_01 浮雕金属表面的划痕缺陷 * 关键调优灰度窗口 15 / 差分阈值 30 / 面积下限 30 dev_update_off() dev_close_window() read_image(Image, photometric_stereo/embossed_01) get_image_size(Image, Width, Height) dev_open_window(0, 0, Width, Height, black, WindowHandle) set_display_font(WindowHandle, 12, mono, true, false) dev_set_draw(margin) dev_set_line_width(2) * 第1步定位金属表面区域亮区 threshold(Image, MetalRegion, 128, 255) connection(MetalRegion, ConnectedParts) select_shape(ConnectedParts, Parts, area, and, 500, 999999) * 第2步在表面区域内做形态学差分 reduce_domain(Image, Parts, ImageReduced) * 灰度开运算消除比 15×15 小的特征生成无缺陷参考图 * 窗口 15 能消除划痕和噪声但保留浮雕凸起 gray_opening_rect(ImageReduced, ImageOpening, 15, 15) * 灰度闭运算消除比 15×15 小的暗特征 gray_closing_rect(ImageReduced, ImageClosing, 15, 15) * 第3步差分提取缺陷 abs_diff_image(ImageReduced, ImageOpening, DiffBright, 1) abs_diff_image(ImageClosing, ImageReduced, DiffDark, 1) add_image(DiffBright, DiffDark, DiffSum, 1, 0) * 第4步阈值化 形状筛选 * 阈值 30本图差分值 5~30 是浮雕渐变不算缺陷30 是划痕 threshold(DiffSum, DefectRegion, 30, 255) connection(DefectRegion, ConnectedDefects) * 联合筛选面积 30 以上去掉小噪点circularity 0.05保留任意有形状区域 select_shape(ConnectedDefects, ShapeFiltered, [area, circularity], and, [30, 0.05], [9999, 1.0]) * 第5步按面积降序排序只取前 20 个最显著缺陷 * Top-N 限制避免标签乱象 area_center(ShapeFiltered, Areas, _, _) Indices : sort_index(-Areas) count_obj(ShapeFiltered, NumAll) MaxShow : min([20, NumAll]) gen_empty_obj(Defects) for I : 0 to MaxShow - 1 by 1 select_obj(ShapeFiltered, SingleDefect, Indices[I] 1) concat_obj(Defects, SingleDefect, Defects) endfor * 第6步显示结果 dev_display(Image) dev_set_color(red) count_obj(Defects, NumDefects) if (NumDefects 0) area_center(Defects, DefArea, DefRow, DefCol) for I : 0 to NumDefects - 1 by 1 select_obj(Defects, SingleDefect, I 1) dev_display(SingleDefect) * 标注显示在窗口底部而非图上避免遮挡 disp_message(WindowHandle, D I 1 A DefArea[I]$.0f, window, 40 I * 18, 10, yellow, false) endfor endif dev_set_color(white) disp_message(WindowHandle, Top 20 defects (of NumAll total), window, 10, 10, white, false) dev_update_on()代码要点步骤算子作用区域定位threshold(128,255)→connection→select_shape提取金属表面亮区灰度开/闭运算gray_opening_rect(15, 15)/gray_closing_rect(15, 15)窗口 15 消除小特征保留浮雕主体差分abs_diff_image→add_image提取亮暗特征含凸起投影阴影差分阈值threshold(30, 255)30 以下过滤掉浮雕渐变30 包含划痕和凸起阴影见已知限制形状筛选area30~9999 circularity 0.05去小噪点保留任意形状特征排序取前 Nsort_index(-Areas) 循环拼接避免满屏标签标签位置window模式 底部列表不再叠在金属件上已知限制实测现象本图实际运行会检出 ~40 个区域其中大部分集中在每个圆点凸起的右侧阴影区——这是单方向光照下凸起物的投影阴影并非真实划痕。形态学差分无法区分凸起投影和真划痕两者差分值都在 30~80 区间。这不影响本节演示算法原理的目标差分法能稳定检出暗特征但若工程上需要只检划痕需要① 改用photometric_stereo多方向光照合成消除阴影② 或用lines_gauss等线检测算子专门提取细长划痕。五、动态阈值缺陷检测 — 非均匀光照5.1 原理全局阈值如threshold(128, 255)在光照不均匀时会失效亮的区域阈值偏高暗的区域阈值偏低。动态阈值用每个像素周围的局部均值作为参考偏离局部均值超过一定值的就是异常——不需要全局阈值自适应光照变化。检测对象photometric_stereo/embossed_01浮雕金属表面。参数参考已有的5-3 局部阈值分割算子dyn_threshold.hdevmean_image(59, 59)dyn_threshold(15, not_equal)已经过验证能稳定检出表面暗特征。与 4.2 节的关系4.2 用gray_opening形态学差分5.2 用mean_image动态阈值。两种方法对凸起阴影的敏感度不同动态阈值法用大窗口59×59能抑制小范围灰度变化如凸起投影只保留跨越大范围的异常。5.2 完整示例* 动态阈值缺陷检测非均匀光照 * 场景检测 photometric_stereo/embossed_01 浮雕金属表面异常 * 参数参考 5-3 局部阈值分割算子dyn_threshold.hdev dev_update_off() dev_close_window() read_image(Image, photometric_stereo/embossed_01) get_image_size(Image, Width, Height) dev_open_window(0, 0, Width, Height, black, WindowHandle) set_display_font(WindowHandle, 12, mono, true, false) dev_set_draw(margin) dev_set_line_width(2) * 第1步定位金属表面区域 threshold(Image, MetalRegion, 128, 255) connection(MetalRegion, ConnectedParts) select_shape(ConnectedParts, Parts, area, and, 500, 999999) reduce_domain(Image, Parts, ImageReduced) * 第2步大窗口均值滤波作为局部参考图 * 关键窗口 59×59 远大于划痕/凸起阴影能抑制小范围灰度变化 mean_image(ImageReduced, ImageMean, 59, 59) * 第3步动态阈值 * not_equal 找偏离局部均值的区域无论偏暗还是偏亮 * 阈值 15只有灰度偏差超过 15 的才是异常 dyn_threshold(ImageReduced, ImageMean, DefectRegion, 15, not_equal) * 第4步连通域 形状筛选 connection(DefectRegion, ConnectedDefects) * 面积 30 以上去掉小噪点circularity 0.05保留任意形状 select_shape(ConnectedDefects, ShapeFiltered, [area, circularity], and, [30, 0.05], [9999, 1.0]) * 第5步按面积降序排序只取前 20 个最显著异常 * Top-N 限制避免标签乱象 area_center(ShapeFiltered, Areas, _, _) Indices : sort_index(-Areas) count_obj(ShapeFiltered, NumAll) MaxShow : min([20, NumAll]) gen_empty_obj(Defects) for I : 0 to MaxShow - 1 by 1 select_obj(ShapeFiltered, SingleDefect, Indices[I] 1) concat_obj(Defects, SingleDefect, Defects) endfor * 第6步显示结果 dev_display(Image) dev_set_color(red) count_obj(Defects, NumDefects) if (NumDefects 0) area_center(Defects, DefArea, DefRow, DefCol) for I : 0 to NumDefects - 1 by 1 select_obj(Defects, SingleDefect, I 1) dev_display(SingleDefect) * 标签显示在窗口底部避免遮挡 disp_message(WindowHandle, D I 1 A DefArea[I]$.0f, window, 40 I * 18, 10, yellow, false) endfor endif dev_set_color(white) disp_message(WindowHandle, Top 20 anomalies (of NumAll total), window, 10, 10, white, false) dev_update_on()动态阈值 vs 全局阈值对比特性全局阈值threshold动态阈值dyn_threshold参考值固定数值如 128每个像素的局部均值光照适应性差光照不均时失效好自适应局部亮度参数Min, Max窗口大小 偏差阈值速度快较慢需计算均值图适用对比度高、光照均匀对比度低、光照不均mean_image窗口大小选择窗口应大于待检测缺陷、小于目标本身。如果窗口太小如 3×3均值图和原图几乎一样什么都检不出来如果窗口太大如 51×51计算量大且可能把整个零件当缺陷。15×15 是一个常用的起点。六、综合实战 — 多特征联合缺陷分类6.1 场景检测对象rings_and_nuts图像中的所有金属件。分类规则5 特征联合决策齿轮GearOKconvexity 0.95—— 浅齿也能识别凸性 ≈ 0.90~0.97圆环RingOKarea_holes 30且circularity 0.80螺母NutOKarea_holes 30但circularity 0.80六角形实心件NGarea_holes 30且convexity 0.95完整凸形反光光斑GlareDeviation 15—— 灰度方差小是反光/污渍碎屑噪声area 5006.2 完整示例* 综合实战多特征联合缺陷分类 * 完整流水线定位 → 分割 → 多特征提取 → 分类判定 → 结果汇总 dev_update_off() dev_close_window() read_image(Image, rings_and_nuts) get_image_size(Image, Width, Height) dev_open_window(0, 0, Width, Height, black, WindowHandle) set_display_font(WindowHandle, 14, mono, true, false) dev_set_draw(margin) dev_set_line_width(2) * 第1步分割 threshold(Image, Region, 128, 255) connection(Region, ConnectedRegions) select_shape(ConnectedRegions, Parts, area, and, 500, 999999) * 第2步批量提取多维度特征 area_center(Parts, Area, Row, Column) circularity(Parts, Circularity) convexity(Parts, Convexity) intensity(Parts, Image, MeanGray, Deviation) * 第3步逐个检查孔洞面积 count_obj(Parts, NumParts) dev_display(Image) CountRing : 0 CountNut : 0 CountSolid : 0 CountNoise : 0 CountGlare : 0 for I : 0 to NumParts - 1 by 1 select_obj(Parts, SinglePart, I 1) * 检查孔洞 area_holes(SinglePart, HoleArea) * 分类规则关键修正增加 convexity 区分齿轮加灰度方差过滤光斑 * 噪声面积 500 * 光斑灰度方差 15均匀亮无纹理是反光/污渍 * 环/齿轮凸性 0.95齿与凸包差异大 * 环圆有孔洞 圆度高 * 螺母有孔洞但圆度低六角形 * 实心件NG无孔洞且凸性接近 1完整凸形 if (Area[I] 500) CountNoise : CountNoise 1 dev_set_color(gray) Label : Noise elseif (Deviation[I] 15) * 灰度方差小 → 均匀亮斑反光/污渍不是金属件 CountGlare : CountGlare 1 dev_set_color(magenta) Label : Glare D Deviation[I]$.1f elseif (Convexity[I] 0.95) * 凸性 0.95 → 齿轮类齿与凸包差异大 CountRing : CountRing 1 dev_set_color(green) Label : Gear Cvx Convexity[I]$.2f elseif (HoleArea 30 and Circularity[I] 0.80) * 圆环 CountRing : CountRing 1 dev_set_color(green) Label : Ring H HoleArea$.0f elseif (HoleArea 30) * 六角螺母 CountNut : CountNut 1 dev_set_color(cyan) Label : Nut H HoleArea$.0f else * 完整凸形 无孔洞 → 真正实心 CountSolid : CountSolid 1 dev_set_color(red) Label : Solid NG endif dev_display(SinglePart) disp_message(WindowHandle, Label, image, Row[I] - 15, Column[I] - 25, yellow, false) endfor * 第4步汇总 dev_set_color(white) Summary : Ring: CountRing Nut: CountNut Solid(NG): CountSolid Glare: CountGlare Noise: CountNoise disp_message(WindowHandle, Summary, window, 10, 10, red, false) dev_update_on()分类决策树面积 500 ──是──→ 噪声Noise │否 ▼ 灰度方差 15 ──是──→ 光斑Glare反光/污渍 │否 ▼ 凸性 0.95 ──是──→ 齿轮GearOK齿与凸包差异大 │否 ▼ 孔洞面积 30 ──否──→ 实心件Solid NG凸形无孔 │是 ▼ 圆度 0.80 ──是──→ 圆环RingOK │否 ▼ 螺母NutOK六角形为什么用area_holes而不是fill_updifferencearea_holes直接返回区域内所有孔洞的总面积一步到位。fill_updifference需要三步填充→做差→再计算面积。功能等价但area_holes更简洁。为什么必须加convexity齿轮的齿间空隙在拓扑上与背景连通是开口的不是封闭孔洞所以area_holes对齿轮返回 0。convexity凸性 区域面积 / 凸包面积是专门用来识别有凸出/凹入特征的形状的浅齿齿轮 ≈ 0.90~0.97六角螺母 ≈ 0.97圆环 ≈ 0.99圆盘 ≈ 1.00。单靠area_holes一定漏检齿轮。凸性阈值经验不要凭直觉设 0.95。对浅齿形状如本图齿轮凸性实际值可能就在 0.95 附近0.95 阈值会全部漏掉。建议先用 0.85 或 0.90必要时打印Convexity数组看实际分布再调。七、常用算子速查表7.1 分割与定位算子功能关键参数threshold全局阈值分割Min, Maxbinary_threshold自动阈值Otsu‘max_separability’, ‘light’/‘dark’dyn_threshold动态阈值局部参考Threshold, ‘light’/‘dark’mean_image局部均值滤波Width, Heightreduce_domain限定操作区域Regionshape_trans区域形状变换‘rectangle2’, ‘convex’, ‘circle’7.2 形态学算子功能灰度/二值opening_circle圆形开运算去毛刺二值closing_circle圆形闭运算填缝隙二值erosion_circle腐蚀缩小/分离粘连二值dilation_circle膨胀扩大/恢复二值gray_opening_rect灰度开运算去亮缺陷灰度gray_closing_rect灰度闭运算去暗缺陷灰度fill_up_shape按形状特征填充孔洞二值7.3 差分与比较算子功能abs_diff_image两张灰度图绝对差分add_image两张灰度图相加difference两个区域做差集union2两个区域并集area_holes计算区域内孔洞总面积7.4 测量与特征算子输出用途smallest_circleRow, Col, Radius最小外接圆 → 直径测量area_centerArea, Row, Col面积 重心circularityCircularity (0~1)圆度intensityMean, Deviation灰度均值 标准差select_shapeSelectedRegions按特征筛选区域八、常见问题与避坑指南8.1 Blob 分析中粘连目标怎么处理* ❌ 直接 connection粘连颗粒被当成一个 connection(Region, WrongResult) * ✅ 先腐蚀→连通→膨胀分离粘连 erosion_circle(Region, Eroded, 7.5) connection(Eroded, Separated) dilation_circle(Separated, Final, 7.5)腐蚀半径要根据粘连程度调粘连轻微用小半径35粘连严重用大半径710。8.2 形态学差分的结构元素选多大结构元素检测缺陷大小噪声风险5×51~3 像素高噪点被当缺陷9×93~7 像素中推荐起点15×157~13 像素低可能漏检小缺陷25×2513 像素很低只检测大缺陷8.3 dyn_threshold 找不到缺陷* 检查1mean_image 窗口是否太小 mean_image(Image, ImageMean, 3, 3) * 太小和原图几乎一样 mean_image(Image, ImageMean, 15, 15) * 合适 * 检查2阈值是否太大 dyn_threshold(Image, ImageMean, Region, 30, dark) * 太大什么都检不到 dyn_threshold(Image, ImageMean, Region, 5, dark) * 合适 * 检查3方向是否选错 * dark 找暗缺陷比周围暗 * light 找亮缺陷比周围亮 * equal 找和周围相同的区域8.4 reduce_domain 的必要性* ❌ 不限定区域全图搜索外部噪点干扰 threshold(Image, DarkSpots, 0, 50) * ✅ 先定位再搜索只找零件上的暗点 threshold(Image, PartRegion, 128, 255) reduce_domain(Image, PartRegion, ImageReduced) threshold(ImageReduced, DarkSpots, 0, 50)8.5 area_holes 对非连通区域的处理* area_holes 对整个区域集合返回总孔洞面积 area_holes(ConnectedRegions, TotalHoleArea) * 所有区域孔洞之和 * 要逐个检查必须 select_obj 后再调用 select_obj(ConnectedRegions, SingleRegion, I 1) area_holes(SingleRegion, SingleHoleArea)总结本文系统讲解了五种缺陷检测方法Blob 分析binary_thresholdconnectionselect_shape——最基础适合目标与背景对比明显的场景关键是处理粘连目标尺寸偏差检测shape_transreduce_domainsmallest_circle——先定位再测量用公差带判定 OK/NG形态学差分gray_opening_rectabs_diff_image——用开运算做参考图差分提取表面缺陷动态阈值mean_imagedyn_threshold——局部自适应适合非均匀光照场景多特征联合分类areacircularityarea_holesintensity——综合多个维度做复杂分类决策方法选择原则对比明显 → Blob 分析尺寸要求 → 尺寸偏差检测表面缺陷 → 形态学差分光照不均 → 动态阈值复杂分类 → 多特征联合