
简介面向图像处理领域的MATLAB纹理特征提取算法资源适合学习或研究GLCM、LBP等纹理特征的初学者与工程人员帮助理解像素间灰度关系与频域方向特征的实际计算过程。内容涉及统计特征、结构特征与模型化特征的基本概念其中灰度共生矩阵GLCM和局部二值模式LBP是重点示例。压缩包仅375KB共4个文件1个基于灰度统计的statxture.m源码文件3张PNG格式纹理示例图便于对照不同纹理区域的提取效果。目前已有2264人学习下载属于轻量实用型参考资料。通过研究源码实现可以快速掌握灰度级统计特征如对比度、能量、熵的编程方法并基于示例图像验证算法效果同时可作为扩展Gabor滤波器等更丰富特征提取方式的起点适合用于课程实验、算法比较或项目预研。1. 图像纹理特征提取算法入门先用MATLAB跑通一个统计特征我在上个月给一批金属表面缺陷分类数据做特征工程时发现一个很现实的问题直方图和边缘统计量对光照变化太敏感同一个样本换个拍摄角度特征向量就散得不像话。纹理特征提取算法要解决的正是这种“看花纹说话”的场景——把局部窗口里灰度变化的规律归纳成对比度、能量、方向性等数值让同类纹理聚在一起异类纹理拉得开而且对轻微光照扰动尽量稳。MATLAB 在图纹理特征提取这类任务里是理想的试验场图像处理工具箱直接提供灰度共生矩阵入口也允许用几十行脚本实现 LBP、Gabor 滤波器组改参数立即看到结果。从工程落地角度讲纹理特征提取不是跑通一个函数就结束还要回答三个问题算哪些特征、窗口取多大、如何验证特征真的有用。下面按做项目的方式走一遍先分清算法选型把 GLCM 完整跑通补上 LBP 和 Gabor最后用多维缩放可视化和交叉验证闭环来判断指标到底可不可信。2. 纹理特征提取算法的选型依据统计法、滤波器法与结构法2.1 三类主流的图像纹理特征提取算法纹理本质上描述的是图像局部区域里灰度变化的空间分布规律所以算法设计的核心就是如何把这种分布压缩成低维数值。常见做法分成三类统计法直接对灰度像素对的共现关系计数最典型的就是灰度共生矩阵GLCM滤波器法用不同尺度和方向的滤波器扫描图像Gabor 滤波器组和小波变换都归在这一类结构法关心局部二值模式或基元排列LBP 是最常被引用的代表。三类方法提取的物理意义差异非常大我在实际项目里用下面这张表快速做初筛。类别代表算法主要捕获的纹理属性计算开销适合的图像类型统计法GLCM、灰度差分统计粗糙度、对比度、灰度变化的重复模式低到中纹理均匀、有统计规律的材质图像滤波器法Gabor、小波变换方向性、周期结构、频带能量较高有明显方向性的织物、木材、遥感图像结构法LBP及变体、形态学方法局部微模式、边缘交点中人脸、细胞、噪声较多的纹理2.2 为什么工业场景里 GLCM 常被列为第一选择Mamdani 模糊分类时代的老经验到今天依然成立GLCM 给出的四个统计量——对比度、相关性、能量、同质性——每一项都有直观物理含义。对比度反映了纹理沟纹的深浅能量反映灰度分布的均匀程度相关性描述行或列方向上的灰度相似度。做缺陷检测时我可以直接对着这几个数字写规则能量突然下降说明局部纹理变得杂乱对比度异常升高多半是划痕或异物。这种可解释性是深度学习特征图给不了的。但 GLCM 也有明确边界它把整幅影像的空间位置信息压缩成了统计量丢掉“花纹长在哪”的布局信息。纯 GLCM 无法区分“左上角一块亮斑”和“随机分布几个亮斑”这两类纹理。所以工程上更常见的做法是GLCM 用作基底特征再搭配 LBP 或者 Gabor 响应方差把布局和方向信息补回来。2.3 MATLAB 内置函数地图MATLAB 的图像处理工具箱和统计工具箱覆盖面很全不需要从零实现最核心的部分。graycomatrix负责构建共生矩阵graycoprops从矩阵里导出统计量stdfilt、rangefilt、entropyfilt可以快速做局部纹理滤波做预分析Gabor 部分用gabor生成滤波器组配合imgaborfilt一次性得到多通道响应。LBP 没有现成函数需要自己写循环或者用 Image Processing Toolbox 的 blockproc 加速。如果只是验证想法这套内建函数足够跑通全流程。3. MATLAB实现GLCM纹理特征提取灰度共生矩阵的构建、参数与特征拼接3.1 graycomatrix 最小可运行示例GLCM 的定义是统计图像中相距某一偏移量的两个像素在指定灰度级上共同出现的次数。MATLAB 里调用graycomatrix需要先确认输入是灰度图采样位数最好控制在 8 bit 以内。下面这一小段是把纹理样张转灰度后直接计算默认方向的共生矩阵并导出四个经典统计量。img imread(texture_sample.png); if size(img, 3) 3 img rgb2gray(img); end glcms graycomatrix(img, Offset, [0 1], ... NumLevels, 64, ... Symmetric, true); stats graycoprops(glcms, {Contrast, Correlation, ... Energy, Homogeneity}); disp(stats);这段代码中Offset决定统计哪些相邻像素对[0 1]表示水平方向向右移动 1 个像素NumLevels指定灰度量化级别Symmetric设置为 true 会把 0 度和 180 度方向的统计合并避免方向对称信息重复计入。运行后stats是一个 1x1 struct四个字段分别对应对比度、相关性、能量和同质性。只算一个方向往往不够。纹理可能有斜向走向通常的做法是把 0 度、45 度、90 度、135 度四个方向的偏移全部算进去再对统计量取平均或者取最大值。下表给出我常用的参数区间。参数常用取值对特征的影响Offset[0 1]、[0 2]、[1 1]、[1 -1]步距越大感知的纹理尺度越粗NumLevels32、64、256级别太少丢失细节太多矩阵稀疏Symmetrictrue / falsetrue 时统计结果与方向无关GrayLimits[] 或 [min max]控制灰度动态范围光照不稳时务必手动指定3.2 用 graycoprops 得到统计量并拼成特征向量graycoprops支持的统计量其实不止四种。除了代码里写的对比度、相关性、能量和同质性还有基于 GLCM 导出的方差、熵、和平均等二阶统计量。如果要投入分类器最省事的做法是把这些标量拼到一个行向量里作为单张图像的纹理特征。props {Contrast, Correlation, Energy, Homogeneity, ... Entropy, Variance}; stats graycoprops(glcms, props); featureVector [stats.Contrast, stats.Correlation, ... stats.Energy, stats.Homogeneity, ... stats.Entropy, stats.Variance];Entropy和Variance需要较新版本的系统辨识或图像处理工具箱支持个别老版本没提供这两个属性时直接用glcms自己算熵等于-sum(sum(p .* log2(p eps)))。拼接前建议打印featureVector观察量纲对比度和方差的数值可能到几百相关性只在 0 到 1 之间后续进分类器前必须归一化。3.3 多方向和多种步距的特征融合实际纹理不总是水平或垂直分布。一张机加工表面的刀痕可能是 15 度方向也可能是 75 度方向单方向 GLCM 很可能漏掉主要纹理周期。常见的做法是构造一个偏移量矩阵分别计算后把所有特征横向拼接。offsets [0 1; 0 2; 1 1; 1 -1; 2 0; 2 2]; numOffsets size(offsets, 1); featAll []; for i 1:numOffsets glcm graycomatrix(img, Offset, offsets(i, :), ... NumLevels, 64, Symmetric, true); props graycoprops(glcm, {Contrast, Energy, ... Homogeneity, Correlation}); featBlock [props.Contrast, props.Energy, ... props.Homogeneity, props.Correlation]; featAll [featAll, featBlock]; end循环里每次计算一个偏移方向的共生矩阵最后featAll的维度是 1x24即 6 个偏移乘以 4 个统计量。这样既保留了方向信息又不会像直接堆叠四方向那么冗余。需要提醒的是偏移量为[2 2]时矩阵统计的是对角方向相隔两个像素的灰度关系对粗纹理更敏感但也更容易受噪声干扰。特征维度达到几十维时配合后续的主成分分析或者最大相关最小冗余做一次筛选比直接丢给分类器更稳。4. 补盲点LBP和Gabor纹理特征提取算法在MATLAB中的落地4.1 手动实现旋转不变的LBPLBP 的思想很简单把每个像素和邻域像素比较大于中心点记 1否则记 0再按顺序组成二进制码。MATLAB 里没有现成的 lbp 函数我一般用一个基础版本把算法流程讲清楚。radius 1; neighbors 8; [m, n] size(img); lbpImg zeros(m, n, uint8); for i radius1 : m-radius for j radius1 : n-radius center double(img(i, j)); code 0; for p 0 : neighbors-1 dx round(radius * cos(2*pi*p/neighbors)); dy round(radius * sin(2*pi*p/neighbors)); if double(img(idy, jdx)) center code code bitshift(1, p); end end lbpImg(i, j) code; end end lbpHist imhist(lbpImg);这段代码里三层循环的写法非常不高效只是为了展示 LBP 原始编码过程。真正做项目时建议把邻域像素取值改成双线性插值提升旋转不变性再统计 uniform pattern 的直方图。uniform LBP 指的是二进制编码中最多只有两次 0/1 跳变例如 11110000 是 uniform10100100 不是。总共有 58 种 uniform 模式加 1 个混合模式特征维度从 256 降到 59且纹理识别准确率通常不降反升。4.2 Gabor滤波器组的尺寸、方向、频率设置Gabor 滤波器由正弦平面波和高斯核相乘得到不同尺度和方向组合起来形成滤波器组。MATLAB 的gabor函数可以一次生成多组参数不用自己构造复数核。wavelengths [4 8 16]; orientations [0 45 90 135]; gFilterBank gabor(wavelengths, orientations); gaborMag imgaborfilt(img, gFilterBank); gaborFeatures cellfun((m) var(m(:)), gaborMag);波长设为 4、8、16 对应三种纹理粗细方向取 0 度到 135 度覆盖主要朝向。每条gaborMag通道还只是响应幅度图不能直接作为特征通常需要把一个通道的均值、方差或能量汇总成标量。代码里cellfun对每一层响应求方差方差大说明该方向和尺度下纹理波动明显。最终得到 12 维特征对应 3 个波长乘以 4 个方向。实践中需要注意波长超过图像尺寸的一半会失去意义方向间隔 45 度对大部分工业纹理够用若纹理方向很尖锐可以改成 30 度间隔。4.3 特征融合前要先归一化当 GLCM、LBP 直方图和 Gabor 响应方差被拼进同一个矩阵时单位差异会让你误判特征重要性。对比度可能上千LBP 直方图都是整数次频数相关性却只有 0.1 左右。featureMatrix [glcmFeats, lbpHist, gaborFeats]; featureMatrixNorm zscore(featureMatrix);zscore把每一列变成零均值、单位方差语法简洁但要注意它是全局归一化。如果训练集和测试集要分开归一化应该把均值和标准差只从训练集统计测试集套用同一组参数避免数据泄漏。5. 用验证闭环判断纹理特征提取质量可视化、参数调优与blockproc5.1 用t-SNE可视化特征聚合特征提取完第一件事不是急着喂分类器而是先看特征能不能把不同类别的样本分开。t-SNE 是常用的高维特征可视化工具可以直接观察聚类结构。rng(0); reducedY tsne(featureMatrixNorm, NumDimensions, 2, ... Perplexity, 25); gscatter(reducedY(:, 1), reducedY(:, 2), trueLabels);tsne在 MATLAB 统计与机器学习工具箱里直接可用。Perplexity 一般取 5 到 50 之间样本量低于 100 时不要超过 30。如果散点图上各类别边界清晰说明当前特征提取算法的可分性不错如果不同类别混叠严重优先返回去改窗口大小和 Offset而不是马上换一个更强分类器。t-SNE 只适合可视化不建议作为特征降维输入后续分类器因为它的映射不稳定。5.2 窗口大小、灰度级和步距的组合策略纹理特征提取里最常见的错误是特征提取窗口和分类目标不匹配。分块大小选 64x64而纹理单元只有 8x8 像素窗口内混合了多种纹理提取出的特征会是各模式的均值。参数失效表现调参经验窗口大小特征图和标签对不上先用不同尺寸试几个选使类间方差最大的方案NumLevels特征出现大量常数列噪声大的图优先降到 32 级Offset 步距小纹理丢失大纹理没有区分度从步距 1 开始逐步翻倍观察分类曲线块与块之间重叠不能忽略。块大小不变重叠 50% 会让特征数量翻四倍对训练时间影响很大。最稳妥的调参方式是固定验证集每次只改一个参数记录分类准确率和特征计算耗时的变化。5.3 大图性能优化blockproc分块提取当面对整张显微图像或遥感图时逐像素循环的 LBP 会慢到无法接受。MATLAB 的blockproc是常见优化手段可以把大图切成小方块每个 block 单独提取特征最后拼成特征图。blockSize [64 64]; textureFeatMap blockproc(grayImg, blockSize, ... (block) extractTexture(block.data), ... BorderSize, [8 8], ... PadPartialBlocks, true);BlockSize决定每个块的覆盖范围BorderSize在块边缘多取 8 像素避免共生矩阵统计到块边界截断。PadPartialBlocks让边缘不完整的块也能输出防止整幅图的最右和最下部分被丢弃。extractTexture可以是我们自己封装的特征提取函数内部调用 graycomatrix 或者 Gabor 滤波把它做成函数句柄后blockproc的累加效率远高于 for 循环逐块调用。6. 纹理特征提取的最后一公里特征降维与交叉验证评估把三类特征全部拼接之后特征维度往往超过 50。一个常见误区是觉得特征越多分类效果越好实际在小样本纹理数据集上维度超过样本量的一半时分类器很容易过拟合。我通常先用最大相关最小冗余mRMR选一份 20 维以内的候选特征子集再放进fitcecoc做五折交叉验证。[idx, scores] fscmrmr(featureMatrixNorm, trueLabels); selectedFeats featureMatrixNorm(:, idx(1:15)); rng(0); cv cvpartition(trueLabels, KFold, 5); svmModel fitcecoc(selectedFeats, trueLabels, ... CVPartition, cv); acc 1 - kfoldLoss(svmModel); fprintf(分类准确率: %.2f%%\n, acc * 100);fscmrmr会根据类别标签给每个特征打分排序输出的idx是特征按重要程度排序后的编号。fitcecoc把二分类 SVM 扩展成多分类器kfoldLoss 返回的是交叉验证平均损失用 1 减去它得到准确率。这一套代码把前面提取的所有特征直接纳入同一个评估闭环换图像数据时只需要换特征矩阵和标签。评估过程中值得做的最后一个技巧是记录不同特征组合的准确率对比表单独 GLCM、单独 LBP、单独 Gabor、三融合。如果融合后准确率提升幅度不到两个百分点而计算耗时增加了三四倍就可以考虑砍掉那组特征保留计算效率更高的组合。我一般会记录每次的耗时到日志最后用同类实验对比格式输出。把上述判断逻辑固化成脚本注释下次换数据时只改图像读入路径、标签向量和块大小三个参数整套纹理特征提取评估流程就能直接复用。本文还有配套的精品资源点击获取