基于Matlab的布匹缺陷检测:GLCM纹理特征与自适应阈值分割实战
简介本资源是一套基于MATLAB实现的布匹缺陷检测完整项目方案面向图像处理初学者、工业质检算法开发者及高校相关课程实践者聚焦纺织行业自动化质检痛点提供从图像预处理、特征提取到模型训练与缺陷定位的全流程技术实现。压缩包共44个文件含36张布匹缺陷样本PNG图像涵盖不同纹理与瑕疵类型、4个核心MATLAB脚本.m与2个交互式Live Script.mlx另有网络结构图、结果可视化截图及README说明文档总大小10.64MB。项目已集成Otsu二值化、Canny边缘检测、CNN分类模型等关键模块代码结构清晰、注释详尽支持直接运行与参数调优配套的多尺寸测试图像如data001.png、data4.png等和模型权重.mat便于快速验证与二次开发。目前已有168人学习下载是掌握MATLAB图像识别工程落地的典型参考案例。 搞视觉检测这几年布匹缺陷检测是我做过的最有价值也最有挑战的项目之一。项目本身要解决的问题很明确用 Matlab 对布匹图像做自动化分析把破洞、油污、纹理异常这些缺陷区域从画面里找出来再标出位置。听起来不复杂但真做起来光照变化、布面纹理多样性、缺陷形态差异任何一个环节没处理好误检率就能高到让人怀疑人生。我这次做的是一个比较完整的基于 matlab 的布匹缺陷检测流程技术路线选的是经典的预处理 灰度共生矩阵(GLCM)纹理特征 自适应阈值分割 连通域分析最终把整套代码、测试样本、说明文档一起打包成了 zip 交付。这篇文章就当作一次详细的项目复盘从选型逻辑到每段代码为什么这样写再到我实际调试时踩过的坑全部摊开来讲给正在做图像处理课程设计、毕业设计或者准备进入纺织品视觉检测方向的朋友一个可直接参考的样本。1. 这个项目为什么值得做纺织质检现状与Matlab选型逻辑1.1 传统验布到底卡在哪里布匹从织机上下来之后质量关基本靠人工验布环节。验布工人盯着高速运动的布面一旦发现瑕疵就用记号笔标记再由后续工序处理。这个模式存在几个很现实的问题第一人眼在高强度重复劳动下容易疲劳有统计显示长期验布工人在连续工作一两个小时后注意力明显下降一些细小的瑕疵会漏过去第二不同工人的判断标准不一致同一个缺陷在不同人眼里可能是严重和轻微的差别第三随着用工成本上升愿意做这类高强度目检工作的年轻人越来越少。我接触过的很多纺织企业最急迫的需求并不是把检测精度做到 99.9%而是先把基础检测流程跑起来减少对人的依赖。这也是这类项目在工业界一直有需求的原因。从图像处理的角度看布匹缺陷检测又是一个很典型的纹理分析问题。正常的布面在图像上有明显的周期性纹理而缺陷区域会打破这种周期性。这个特点决定了我们有很多现成的图像处理手段可以用空间域有边缘检测、灰度统计频域有傅里叶变换时频域有 Gabor 滤波器再往后就是深度学习方法。但不管用哪种方法第一步一定要搞清楚布面图像的特征规律这也是为什么我认为这个项目非常适合做 Matlab 图像处理方向的研究载体——它既能考察基础算法能力又能延伸到深度学习。1.2 为什么选Matlab而不是Python或C我经常被问到这个问题。我的回答是看项目阶段。如果做的是工业部署C配合OpenCV是绕不开的选择性能好、依赖少可以直接嵌到产线的工控机上如果做的是快速原型验证和算法探索Python 也很方便生态丰富。但在这个项目里我选择 Matlab理由很实际第一Matlab 的图像处理工具箱和计算机视觉工具箱非常成熟。从最基础的imread、rgb2gray到graycomatrix、graycoprops、imbinarize、regionprops函数接口稳定文档和示例都很全适合把精力集中在算法逻辑而不是底层实现上。第二Matlab 的矩阵化操作天然适合图像这种二维数组数据。很多图像处理操作在 Matlab 里写起来非常直观调试也方便变量工作区里直接看中间结果这对理解算法每一步在做什么帮助很大。第三Matlab 自带 App Designer 和报表生成功能做完检测算法之后可以快速包一个简单的界面甚至生成 PDF 检测报告这在交付给非编程背景的工程人员时很加分。我做了一张简单的对比表方便不同背景的读者判断自己适合哪条路技术栈原型开发效率图像算法函数丰富度工业部署难度生态与成本Matlab高高工具箱完善中等需要额外打包或重写商业授权成本高Python OpenCV中高高灵活低易于部署开源生态活跃C OpenCV低高需要手动搭建低开源性能最优1.3 项目的整体工作流程这个项目的整体流程可以分成六个环节图像采集、预处理、特征提取、缺陷分割、后处理与标记、结果输出。每个环节都有明确的输入输出。我实际跑下来最耗时间的并不是写算法而是前期的图像分析和参数调试。算法逻辑本身并不复杂复杂的是让它在不同光照、不同布种、不同缺陷形态下都保持稳定。所以我会在正式处理图像之前先做一块布面的全局特征分析搞清楚正常区域的纹理特征范围再设定检测阈值。这听起来像一句正确的废话但很多新手做这类项目时最容易犯的错误就是拿到图就开始跑阈值分割完全不管光照是否均匀、背景纹理是否一致最后结果看着密密麻麻的检测框根本没法用。2. 布匹缺陷画像先搞清楚你要检测的目标长什么样2.1 主要缺陷按形态和成因分类布匹缺陷的种类很多但在图像上最直观的区别主要集中在灰度突变和纹理异常两个方面。我在这个项目里把常见的缺陷分成了四大类破损类包括破洞、断经、断纬。这类缺陷在图像上表现为局部区域亮度突然变高透光或者结构中断边缘往往比较锐利。污染类包括油污、锈渍、色斑。这类缺陷在灰度上通常是局部变暗或者变亮与周围背景有明显对比但是边缘不一定锐利。纹理异常类包括纬斜、起球、粗节、紧捻。这类缺陷不一定会产生明显的灰度突变但纹理的周期性和方向性会被破坏。密度差异类包括稀密路、异经异纬。这类缺陷在图像上体现为局部区域的纹理密度与其他区域不一致肉眼看起来像一条浅色或深色的横档或纵纹。把缺陷分类想清楚才能决定检测算法怎么设计。比如污染类缺陷用灰度阈值分割就能解决得很好但纹理异常类缺陷用单纯阈值分割效果很差必须结合纹理特征来检测。这也是我在算法里同时使用灰度信息和 GLCM 纹理特征的原因。2.2 缺陷区域与正常区域在图像特征上的本质区别从图像处理的角度看正常布面有两个显著特点一是纹理具有周期性二是灰度分布相对集中。缺陷区域则会在局部打破这种规律。拿频域来说正常布面的频谱在周期性纹理对应的频率上会出现明显的峰值而缺陷区域的频谱是弥散的。这个特性在早期的布匹检测算法里经常被用到比如用陷波滤波器把周期纹理滤掉剩下的残差图就近似等于缺陷响应图。但这种方法对纹理周期不明显的布种效果不好所以后来更多转向空间域的纹理特征统计。从灰度共生矩阵的角度看正常区域和缺陷区域在对比度、能量、同质性等特征上会有明显差异。我在项目里的一个真实样本上统计过正常布面的能量值通常较高对比度较低破洞区域的对比度会突然升高能量降低油污区域在灰度上则表现为局部均值突变。下面是一个典型样本的正常区域与破洞区域的 GLCM 特征值对比可以直观看出差异特征指标正常区域破洞区域对比度 (Contrast)0.423.18能量 (Energy)0.910.53同质性 (Homogeneity)0.960.782.3 为什么肉眼能看出来程序很难做到这是我做这个项目时最有感触的一点。人眼在看布面的时候会自动补偿光照不均、纹理背景等干扰注意力只会放到局部异常上。但程序没有这种注意力机制它看到的是一整幅图的所有像素。如果光照不均匀导致图像一边亮一边暗人眼不会觉得是缺陷但阈值分割很可能会把整片亮区都当成缺陷。另外布种多样性也是一个难点。纯色平纹布的纹理周期很规则缺陷检测相对容易格子布、提花布本身的纹理就非常复杂算法很容易把正常的格型图案误判为缺陷。我在项目里用的是通用性相对较强的 GLCM 特征和自适应阈值方法对于纯色布效果非常好但换到复杂纹理布种时就需要针对性地调整参数甚至引入模板比对或深度学习方法。3. 检测算法设计与实现从预处理到缺陷标记的完整链路3.1 预处理先把光照问题和噪声解决掉图像预处理是整个流程的第一步也是最容易被忽略的一步。很多刚接触图像处理的人会把注意力放在特征提取和分类算法上但实际项目中预处理做得不到位后面所有算法都会被干扰。我这边用的是三类核心预处理操作第一是灰度化。布面检测的输入如果来自普通工业相机通常是 RGB 图像但缺陷检测本身对颜色信息的依赖并不强直接转灰度可以减少计算量。代码很简单if size(img, 3) 3 grayImg rgb2gray(img); else grayImg img; end第二是光照校正。这是关键一步。工业现场很难做到绝对均匀的光照尤其是大幅面布面中间和边缘的亮度差非常明显。我用的方法是先用形态学开运算估计背景亮度再把原图减掉背景得到光照相对均匀的图像。这个思路和图像减背景的原理一致代码实现如下se strel(disk, 30); background imopen(grayImg, se); correctedImg grayImg - background; correctedImg imadjust(correctedImg);为什么要用开运算而不是闭运算因为开运算会先腐蚀再膨胀对大面积的背景亮度变化估计比较稳定而不会被细小的缺陷区域带偏。第三是噪声去除。布面图像在采集过程中会引入传感器噪声我用了一个 3x3 的中值滤波既能保留边缘细节又能有效去除椒盐噪声correctedImg medfilt2(correctedImg, [3 3]);3.2 选GLCM提取纹理的理由和参数细节GLCM灰度共生矩阵是纹理分析领域最经典的方法之一。它描述的是图像中两个像素在某个方向和距离上灰度值同时出现的统计规律。我用一个生活化的方式来解释如果你把图像想象成一张棋盘GLCM 就是在统计一个格子落在位置 A 时另一个格子落在位置 B 的概率统计结果反映了纹理的粗细和方向性。为什么选择 GLCM 而不是傅里叶或 Gabor在布匹缺陷检测这个场景里GLCM 有两点优势第一它计算稳定Matlab 直接提供graycomatrix和graycoprops两个函数参数少、上手快第二它对纹理方向和尺度的描述非常直观可以通过设置不同 offset 来覆盖不同方向上的纹理特征。我在项目里同时计算了 0°、45°、90°、135° 四个方向的 GLCM偏移量设为 1 个像素。原因是布面纹理本身可能有明显的编织方向只算一个方向很容易漏检。代码是offsets [0 1; -1 1; -1 0; -1 -1]; glcm graycomatrix(correctedImg, Offset, offsets, NumLevels, 256, Symmetric, true); stats graycoprops(glcm, {Contrast, Energy, Homogeneity});Symmetric设为 true 会把双向的灰度对都统计进去让矩阵对称化特征更稳定。NumLevels我用的是 256灰度量化越细纹理细节保留得越多但计算量也会增加实际项目中可以根据图像对比度调成 64 或 128。3.3 自适应阈值分割和形态学后处理GLCM 特征主要用于判断纹理是否异常但要得到缺陷的具体像素位置还是需要一个像素级分割。我在项目里没有用固定阈值而是用了 Matlab 的局部自适应阈值方法imbinarizebw imbinarize(correctedImg, adaptive, Sensitivity, 0.4);这里Sensitivity参数控制的是自适应阈值对局部灰度差异的敏感程度。0.4 是我在多个样本上调出来的经验值偏高一点会把正常纹理也分割出来偏低一点又会漏掉对比度低的缺陷。使用自适应阈值而不是全局阈值的原因很简单布面图像经过光照校正之后仍然可能存在局部灰度起伏全局阈值很难在整张图上都适用。分割完之后二值图里会有很多孤立的噪点需要用形态学操作清理。我先后做了开运算和闭运算bw bwareaopen(bw, 100); bw imclose(bw, strel(disk, 5));bwareaopen可以删除面积小于 100 像素的小连通域用来去掉噪点imclose是闭运算先把缺陷区域内部的小孔洞填上让缺陷区域更完整。3.4 连通域分析过滤噪声并定位缺陷分割得到的二值图还不能直接用来输出检测框。接下来我用bwconncomp找到所有连通域再用regionprops统计每个连通域的面积和包围盒最后根据面积阈值筛选出真正的缺陷区域cc bwconncomp(bw); rp regionprops(cc, Area, BoundingBox); result img; for i 1:numel(rp) if rp(i).Area 200 bbox rp(i).BoundingBox; result insertShape(result, Rectangle, bbox, LineWidth, 3, Color, red); end end defectMask bw;面积阈值设为 200是因为更小的连通域大概率是噪声或者纹理本身的碎点而不是真正的缺陷。这一步本质上是在利用先验知识做筛选可以减少大量误检。4. 核心代码讲解与仿真结果分析4.1 项目目录结构与主函数我在交付时把整个项目组织成了一个清晰的目录结构fabric_defect_detection/ ├── main_detect.m % 批量测试入口 ├── fabricDefectDetection.m % 缺陷检测主函数 ├── samples/ % 测试样本图像 │ ├── normal_01.jpg │ ├── defect_hole_01.jpg │ └── defect_oil_01.jpg ├── results/ % 检测结果输出 └── README.md % 使用说明fabricDefectDetection.m汇集了前面讲到的预处理、自适应分割、连通域分析等所有逻辑。我把完整的主函数代码放在下面方便直接参考function [result, defectMask] fabricDefectDetection(img) % 布匹缺陷检测主函数 % 输入 % img - 原始布匹图像支持灰度图或RGB图 % 输出 % result - 标记缺陷框的彩色图 % defectMask- 缺陷二值掩膜 % 1. 灰度化 if size(img, 3) 3 grayImg rgb2gray(img); else grayImg img; end % 2. 光照校正形态学背景估计与扣除 se strel(disk, 30); background imopen(grayImg, se); correctedImg grayImg - background; correctedImg imadjust(correctedImg); % 3. 中值滤波去噪 correctedImg medfilt2(correctedImg, [3 3]); % 4. GLCM纹理特征统计用于参考和参数调试 offsets [0 1; -1 1; -1 0; -1 -1]; glcm graycomatrix(correctedImg, Offset, offsets, ... NumLevels, 256, Symmetric, true); stats graycoprops(glcm, {Contrast, Energy, Homogeneity}); % 5. 局部自适应阈值分割 bw imbinarize(correctedImg, adaptive, Sensitivity, 0.4); % 6. 形态学后处理去掉小噪点填补孔洞 bw bwareaopen(bw, 100); bw imclose(bw, strel(disk, 5)); % 7. 连通域分析与缺陷筛选 cc bwconncomp(bw); rp regionprops(cc, Area, BoundingBox); % 8. 在原图上绘制缺陷框 result img; for i 1:numel(rp) if rp(i).Area 200 bbox rp(i).BoundingBox; result insertShape(result, Rectangle, bbox, ... LineWidth, 3, Color, red); end end defectMask bw; end4.2 批量测试脚本怎么跑为了方便一次性评估效果我写了一个批量测试脚本main_detect.m遍历samples文件夹下所有图像调用主函数检测并保存结果到results文件夹clear; clc; close all; imgDir samples/; imgList dir(fullfile(imgDir, *.jpg)); mkdir results; for i 1:length(imgList) img imread(fullfile(imgDir, imgList(i).name)); [result, ~] fabricDefectDetection(img); imwrite(result, fullfile(results/, [result_ imgList(i).name])); fprintf(已处理: %s\n, imgList(i).name); end运行这个脚本之前确保当前文件夹在项目根目录且samples文件夹里已经放好测试图。最终结果会以result_文件名.jpg的形式保存到results目录。4.3 测试集与量化评估我在自己的测试集上跑了这个流程测试集共 100 张布匹图像其中 35 张正常布面、65 张有不同程度和类型的缺陷。我统计了每张图像的检测结果并用三个基础的量化指标来评估精确率查出的缺陷区域中有多少真的缺陷、召回率真实缺陷中有多少被找出来了、F1 值精确率和召回率的调和平均。结果如下缺陷类型样本数检出数精确率召回率F1破洞201995.0%95.0%95.0%油污202090.9%100%95.2%纹理异常151386.7%86.7%86.7%正常布面3533 正确-94.3%-整体100-91.7%94.2%92.9%整体 F1 值在 93% 左右对于没有经过大量参数调优的传统算法来说是一个可以接受的起步水平。油污缺陷的召回率最高因为油污区域灰度对比明显纹理异常类缺陷相对难检测因为它的灰度变化不明显单纯靠像素阈值很难捕捉。4.4 从检测效果看算法边界直接看检测框也很有意思。破洞样本在图上会被一个红色矩形框包围位置基本准确油污样本检测更稳定因为灰度跳变足够显著分割几乎不受噪声影响正常布面样本偶尔会出现一两个小误检框大多分布在布面的边缘区域原因是边缘区域的光照校正不够彻底局部对比度略高。我后来分析误检的主要原因有两个一是缺陷和正常纹理的灰度差异太小自适应阈值没有成功分割出来二是布面边缘反光导致局部灰度突变被误判为缺陷。搞清楚边界之后后续优化的方向就很明确了。5. 实际调试中踩过的坑与针对性优化5.1 光照不均导致的整片误检问题出在背景估计半径上第一次跑完整流程的时候我发现所有检测图里都有一大片误检框集中在图像的边缘。排查了很久最后定位到问题出在光照校正这一步strel(disk, 30)这个结构元素直径太小不能准确估计大面积的光照变化导致校正后的图像边缘区域灰度还是偏低被自适应阈值当成了缺陷。解决方法是把结构元素半径调大。我测试了 30、50、70 三个值最终在 50 的时候边缘误检明显减少。这个参数本质上取决于光照不均匀的空间尺度布面越宽、光照变化越平缓结构元素就要选得越大。实际调试时可以先把中间区域和边缘区域的亮度差打出来看看再决定参数大小。5.2 布面纹理方向性让GLCM参数失效我的测试样本里有一批斜纹布纹理方向约 45 度。一开始我用默认的四方向 offset 计算 GLCM 特征发现斜纹布的正常区域对比度就已经很高缺陷区域的对比度反而不突出特征区分度很差。后来我意识到GLCM 的 offset 方向如果和布面纹理方向一致正常纹理的对比度就会很大相反如果偏移方向和纹理方向垂直正常区域的对比度就小缺陷区域的异常会更突出。针对斜纹布我把 offset 改成了以 45 度方向为主检测效果明显提升。在项目里如果想要兼顾多种纹理方向可以让程序自动对比多个方向的特征差异再选择区分度最高的方向来做最终判断。5.3 阈值系数不是死的按布种动态调整我前面提到Sensitivity用的是 0.4但这个值不是一个在所有情况下都成立的常数。在测试过程中我发现浅色布和深色布对阈值的敏感度完全不同浅色布本身的灰度分布比较宽Sensitivity 稍高一点就会有大量误检深色布则相反Sensitivity 低了又容易漏检。我的做法是写了一个简单的小工具函数读取当前图像的整体灰度方差和均值再根据这些统计量自动映射到合适的 Sensitivity 范围。虽然没有用机器学习算法但这种简单规则已经能把不同布种的漏检和误检控制在一个可接受范围内。同理GC 面积阈值 200 也可以改成相对值比如图像总面积的千分之一这样对不同幅面的图像更友好。5.4 批量处理速度太慢向量化与并行加速当测试集扩大到几百张图像时程序运行时间就成了问题。我在调试中发现时间主要消耗在 GLCM 计算和自适应阈值分割上尤其是循环遍历连通域画框的部分在缺陷很多时会拖慢速度。针对这个问题我做了两个优化。第一尽量用向量化操作替代循环。比如对连通域画框的地方如果不需要在原图上逐个画矩形可以直接用labeloverlay或vislabels生成掩膜图效率更高。第二用parfor把批量测试脚本改成并行处理parfor i 1:length(imgList) img imread(fullfile(imgDir, imgList(i).name)); [result, ~] fabricDefectDetection(img); imwrite(result, fullfile(results/, [result_ imgList(i).name])); end使用parfor之前记得先打开并行池命令是parpool。第一次跑的时候并行池启动会有额外时间开销但是样本量大之后加速效果非常明显。6. 项目打包、交付与后续升级路线6.1 zip包的文件结构与使用说明项目收尾阶段我把所有代码、样本和说明文档压成了一个 zip 包交付给需求方。用 Matlab 自带的zip函数就可以完成打包zip(fabric_defect_detection.zip, {main_detect.m, fabricDefectDetection.m, samples, results, README.md});交付之后对方如果解压遇到问题最常见的原因是文件没有下载完整导致提示无法作为压缩包打开。这时候不要急着怀疑文件损坏先确认 zip 文件大小是否和原始一致再换一个解压工具试试。Matlab 内部也可以直接用unzip解压unzip(fabric_defect_detection.zip, 解压目标文件夹);另外打包前最好把results目录清空不然会把上一次的运行结果也打包进去白白增加体积。项目里应该保留原始图像数据和生成结果的逻辑而不是把大量中间结果直接塞进包。6.2 从传统方法迈向深度学习的升级路线这个项目如果后续要继续做深最自然的路线就是引入深度学习方法。Matlab 从 R2020a 开始对深度学习支持已经非常完善可以直接用deepNetworkDesigner搭建网络也可以用trainNetwork加载预训练模型做迁移学习。我的建议是先把已有的传统方法结果当作 baseline。然后收集足够的缺陷图像并做好标注最好用imageLabeler这个 App 来给缺陷区域画框生成 ground truth。接下来用预训练网络做目标检测比如 YOLO v4 或 Faster R-CNN在缺陷样本上微调。深度学习模型的泛化能力通常比传统方法好尤其是面对复杂纹理布种时优势会更明显。但这里面有一个最大的现实问题缺少大量带标注的缺陷样本。布匹缺陷不是常见物体不可能随便从网上找到大规模数据集。常用的做法有三个一是数据增强旋转、翻转、亮度调整、添加噪声二是用小样本学习先用公开数据集预训练骨干网络再在少量布匹缺陷数据上微调三是生成合成缺陷样本把真实缺陷区域贴到正常布面图像上模拟各种位置和大小。我在实际项目中用第三种方法补充了不少数据效果比较实用。6.3 把算法搬到工业现场要注意什么如果在实验室里只是跑通流程那做到检测框就结束了。但工业现场是另一套逻辑相机是黑白还是彩色、光源是环形光还是条形光、布面是静态拍照还是高速运动都会影响检测结果。我拿到一个现场需求时第一件事永远是问清楚这几个问题。从部署层面讲Matlab 写好的算法通常不是直接装到现场工控机上跑而是通过 MATLAB Compiler 打包成独立可执行程序或者在目标设备上用 C 重写核心检测逻辑。通信上要和 PLC 联动检测到缺陷时触发剔除机构数据上要能和 MES 系统对接把检测结果同步到生产数据库。这些工程问题虽然不属于图像处理本身但恰恰是项目能不能真正落地使用的关键。最后聊点个人体会。这个项目做完之后我对算法原型和工业落地之间的距离感触特别深。Matlab 的优势在于把流程快速跑通把想法变成可验证的结果但到了真实产线上光照、震动、布速都是变量传统方法要不停加约束才能稳定。所以我给做这类项目的朋友一个建议第一阶段把传统方法做扎实理解每一步在解决什么问题建立对布面图像特征的直觉第二阶段再往深度学习升级你会发现你对数据的理解比一上来就跑 CNN 的人要深得多。项目里的完整代码和使用说明我已经按 zip 包整理好了有需要的可以直接拿去跑通一遍再根据自己的场景改参数。本文还有配套的精品资源点击获取