MATLAB多特征融合稻叶病识别:颜色纹理形状融合与SVM分类实战

简介:本资源为基于Matlab开发的多特征融合稻叶病图像识别系统完整源码,面向农业信息化、图像识别方向的本科生与研究生,以及参加科创竞赛的开发者。系统以颜色特征分割提取病斑区域,融合颜色、形状、纹理等多维特征,采用MCCA多视图典型相关分析生成集成特征,并基于CNN与SVM进行分类对比实验,可帮助读者理解从特征提取到分类评估的完整流程。压缩包共58个文件,约8.55MB,包含30张jpg测试图像、11个m脚本、9个mat数据文件,以及xlsx特征数据表、mlapp与mlappinstall应用安装包、pdf说明文档和md说明等,覆盖算法实现、数据与界面部署各环节。目前已有80人学习下载。源码结构清晰,适合作为课程设计、毕业设计或科创项目的参考模板,便于在此基础上复现实验、替换数据集或进一步优化分类器性能。

1. 稻叶病识别为什么不能只靠单一特征:从一张误判照片说起

去年帮一个做水稻育种的客户看他们拍的稻叶照片,同一片叶子,肉眼能看出是稻瘟病的典型梭形斑,但用他们之前那套只提取颜色特征的脚本跑出来,被判成了健康叶。原因很简单:那张照片是阴天拍的,整体色温偏冷,病斑的灰绿色和健康叶的深绿色在 RGB 空间里几乎重叠。这件事让我彻底放弃了「单特征打天下」的幻想,也促成了后来这套基于 MATLAB 多特征融合的稻叶病图像识别系统的搭建。

这个标题讲的核心,是把稻叶图像的颜色、纹理、形状三类特征提取出来,做归一化和加权融合,再送进分类器完成病害判别。它解决的是单一特征在光照变化、拍摄角度、病斑早期等场景下识别率骤降的问题。适合谁?适合手里有稻叶图像数据集、想用 MATLAB 快速搭一套可复现识别流程的农业信息化方向的学生和工程师,也适合做图像识别大作业、需要一套完整源码参考的人。整套流程不依赖深度学习框架,纯 MATLAB 图像处理工具箱加统计工具箱就能跑通,对硬件要求低,这是它最大的落地优势。

2. 多特征融合的底层逻辑与 MATLAB 选型理由

2.1 颜色、纹理、形状三类特征各自管什么

颜色特征负责区分病斑和健康组织的色调差异,常用 HSV 空间的 H 和 S 分量做颜色矩,因为 HSV 对光照强度变化比 RGB 更鲁棒。纹理特征负责捕捉病斑区域的粗糙度和方向性,稻瘟病的梭形斑边缘有放射状纹理,而健康叶面纹理均匀,用灰度共生矩阵(GLCM)提取对比度、相关性、能量、同质性四个量就能拉开差距。形状特征负责描述病斑的几何形态,比如圆度、长宽比、面积占比,这对区分稻瘟病(梭形)和胡麻斑病(椭圆形小斑)特别关键。

三类特征单独用都有短板:颜色特征在色温偏移时失效,纹理特征在病斑面积过小时噪声大,形状特征在病斑粘连时算不准。融合的思路就是让它们互相补位,用加权方式把三类特征拼成一个高维向量,再降维后分类。

2.2 为什么选 MATLAB 而不是 Python

MATLAB 在图像处理这块的优势是工具箱函数成熟且接口统一,rgb2hsv、graycomatrix、regionprops这些函数开箱即用,不需要像 Python 那样在 OpenCV、scikit-image、mahotas 之间来回切换数据格式。对于需要快速验证融合策略、又不想在环境配置上耗时间的场景,MATLAB 的Image Processing Toolbox加Statistics and Machine Learning Toolbox两个工具箱就够覆盖全流程。另外 MATLAB 的矩阵运算语法天然适合做特征向量的归一化和加权拼接,代码量比 Python 少三成左右。

提示:如果手头只有 MATLAB 基础版,graycomatrix和regionprops可能不可用,需要确认工具箱授权。没有工具箱的话,GLCM 可以用双重循环手写,但速度会慢很多。

2.3 融合策略:串行拼接还是加权求和

常见做法有两种。串行拼接是把三类特征直接首尾相接成一个长向量,优点是实现简单,缺点是不同特征量纲差异大会导致分类器偏向数值大的特征。加权求和是先对每类特征做归一化,再按权重系数相加,权重可以按类间可分性(比如 Fisher 判别比)来定。

我一般用串行拼接加 Z-score 归一化的方案,因为实现直接、可解释性强,归一化之后量纲问题基本消除。权重这块如果要做精细调优,可以在拼接前给每类特征乘一个系数,系数用网格搜索在验证集上定。下面这张表是三类特征的维度分配和归一化方式对照:

特征类别具体特征维度归一化方式
颜色H/S/V 一阶矩、二阶矩6Z-score
纹理GLCM 对比度、相关性、能量、同质性4Min-Max
形状面积占比、圆度、长宽比、偏心率4Min-Max

3. 从稻叶照片到特征向量:完整预处理与提取流程

3.1 图像预处理:去噪、分割、病斑定位

预处理的目标是把病斑区域从整张叶片图像里干净地切出来。第一步用中值滤波去噪,窗口大小取 3×3 或 5×5,稻叶图像噪声主要来自传感器和压缩伪影,中值滤波对椒盐噪声效果好且不模糊边缘。第二步做颜色空间转换,从 RGB 转到 HSV,在 H 通道上用阈值分割把绿色背景去掉,保留病斑和枯黄区域。第三步用形态学闭运算填补病斑内部空洞,再用bwlabel连通域标记,取面积最大的前三个连通域作为候选病斑。

% 读取稻叶图像并预处理 img = imread('rice_leaf.jpg'); img = imresize(img, [256, 256]); % 统一尺寸,减少后续计算量 img_denoise = medfilt2(img(:,:,2), [5 5]); % 对绿通道做中值滤波 % 转 HSV 空间做病斑分割 hsv = rgb2hsv(img); h_channel = hsv(:,:,1); s_channel = hsv(:,:,2); % 病斑在 H 通道偏黄褐,健康叶偏绿,用阈值分离 lesion_mask = (h_channel > 0.05 & h_channel < 0.15) & (s_channel > 0.3); lesion_mask = imclose(lesion_mask, strel('disk', 3)); % 闭运算填洞 lesion_mask = bwareaopen(lesion_mask, 50); % 去掉小于50像素的噪点 % 连通域标记,取前三个最大区域 [labeled, num] = bwlabel(lesion_mask); stats = regionprops(labeled, 'Area', 'BoundingBox'); [~, idx] = sort([stats.Area], 'descend'); top_n = min(3, num); lesion_regions = false(size(lesion_mask)); for i = 1:top_n lesion_regions = lesion_regions | (labeled == idx(i)); end

这段代码的逻辑是先把图像统一到 256×256,减少后续 GLCM 计算量。中值滤波只对绿通道做,因为绿通道对叶片纹理最敏感。HSV 阈值范围 0.05 到 0.15 对应黄褐色调,是稻瘟病斑的典型色相区间,饱和度阈值 0.3 用来排除灰白色反光。闭运算的 disk 半径取 3 是经验值,太小填不满空洞,太大会把相邻病斑粘连。bwareaopen的 50 像素阈值根据图像分辨率调整,如果原图是 1024×1024,这个值要相应放大。

3.2 三类特征提取的 MATLAB 实现

颜色特征用颜色矩,一阶矩是均值,二阶矩是标准差,在 H、S、V 三个通道上各算两个,共 6 维。纹理特征用 GLCM,先转灰度图,再指定四个方向(0°、45°、90°、135°)和距离 1 计算共生矩阵,然后对四个方向的对比度、相关性、能量、同质性取平均,得到 4 维。形状特征用regionprops直接拿面积、周长、偏心率,再算圆度和长宽比,共 4 维。

% 颜色特征:HSV 三通道的一阶矩和二阶矩 hsv_lesion = hsv .* repmat(double(lesion_regions), [1 1 3]); h_vals = hsv_lesion(:,:,1); h_vals = h_vals(lesion_regions); s_vals = hsv_lesion(:,:,2); s_vals = s_vals(lesion_regions); v_vals = hsv_lesion(:,:,3); v_vals = v_vals(lesion_regions); color_feat = [mean(h_vals), std(h_vals), ... mean(s_vals), std(s_vals), ... mean(v_vals), std(v_vals)]; % 纹理特征:GLCM 四方向平均 gray = rgb2gray(img); gray_lesion = gray; gray_lesion(~lesion_regions) = 0; glcm = graycomatrix(gray_lesion, 'NumLevels', 16, ... 'Offset', [0 1; -1 1; -1 0; -1 -1], 'Symmetric', true); glcm_props = graycoprops(glcm, {'Contrast','Correlation','Energy','Homogeneity'}); texture_feat = [mean(glcm_props.Contrast), mean(glcm_props.Correlation), ... mean(glcm_props.Energy), mean(glcm_props.Homogeneity)]; % 形状特征 shape_stats = regionprops(lesion_regions, 'Area', 'Perimeter', 'Eccentricity', 'MajorAxisLength', 'MinorAxisLength'); area_ratio = shape_stats.Area / numel(lesion_regions); circularity = 4 * pi * shape_stats.Area / (shape_stats.Perimeter^2); aspect_ratio = shape_stats.MajorAxisLength / shape_stats.MinorAxisLength; shape_feat = [area_ratio, circularity, aspect_ratio, shape_stats.Eccentricity];

颜色特征里 H 通道的均值反映病斑整体色调,标准差反映色调均匀度,病斑越均匀标准差越小。GLCM 的NumLevels设 16 而不是默认的 256,是为了减少计算量同时保留足够纹理区分度,这个参数在稻叶图像上 16 和 32 的效果差异不到 2%。Symmetric设为 true 让共生矩阵对称,避免方向偏差。形状特征里圆度越接近 1 说明病斑越接近圆形,稻瘟病的梭形斑圆度通常在 0.3 到 0.5 之间,胡麻斑病在 0.6 以上。

3.3 特征归一化与融合向量拼接

三类特征量纲不同,颜色矩的数值范围在 0 到 1 之间,GLCM 对比度可能到几十,形状特征里面积占比是小数但偏心率也在 0 到 1 之间。直接拼接会让分类器被 GLCM 对比度主导。归一化用 Z-score,对每个特征维度减均值除标准差,在训练集上算均值和标准差,测试集用同样的参数变换。

% 假设 train_feat 是 N×14 的训练特征矩阵,test_feat 是 M×14 测试矩阵 mu = mean(train_feat, 1); sigma = std(train_feat, 0, 1); sigma(sigma == 0) = 1; % 防止除零 train_norm = (train_feat - mu) ./ sigma; test_norm = (test_feat - mu) ./ sigma; % 融合向量就是归一化后的 14 维拼接向量 fusion_train = train_norm; fusion_test = test_norm;

归一化的关键点是均值和标准差只能从训练集算,测试集必须用训练集的参数,否则会造成数据泄露,验证集准确率虚高。这个坑我在早期项目里踩过,当时测试集准确率比实际部署高了 8 个百分点,排查半天才发现是归一化参数用错了。

4. 分类器训练与识别效果验证:避坑与排查

4.1 用 SVM 做基线分类器的最小命令

MATLAB 的fitcsvm做多分类需要 one-vs-one 策略,直接传标签向量就行。核函数先用线性核跑基线,如果准确率不够再换 RBF 核。特征维度只有 14,样本量如果不超过几千,线性核通常够用。

% 训练 SVM 分类器 labels_train = categorical(label_vector); % label_vector 是训练集标签 svm_model = fitcsvm(fusion_train, labels_train, ... 'KernelFunction', 'linear', ... 'Standardize', false, ... % 已经手动归一化过,这里关掉 'ClassNames', categories(labels_train)); % 预测 pred = predict(svm_model, fusion_test); accuracy = sum(pred == categorical(label_test)) / numel(label_test); fprintf('识别准确率: %.2f%%\n', accuracy * 100);

Standardize设为 false 是因为前面已经做了 Z-score,重复标准化会让特征方差被二次压缩。ClassNames显式指定类别顺序,避免预测结果和标签对不齐。如果类别不平衡,比如健康叶样本远多于病叶,要加'Prior', 'uniform'让先验均匀,否则分类器会偏向多数类。

4.2 交叉验证与混淆矩阵怎么看

单次划分训练测试集的结果波动大,用 5 折交叉验证看平均准确率和标准差。混淆矩阵重点看对角线以外的数值,如果稻瘟病被大量判成胡麻斑病,说明形状特征权重不够,需要调高形状特征的归一化后系数。

% 5 折交叉验证 cv_model = crossval(svm_model, 'KFold', 5); cv_accuracy = 1 - kfoldLoss(cv_model); fprintf('5折交叉验证准确率: %.2f%%\n', cv_accuracy * 100); % 混淆矩阵 cm = confusionmat(label_test, pred); disp(cm);

交叉验证准确率比单次测试低 3 到 5 个百分点是正常的,如果低太多说明模型过拟合。混淆矩阵里如果某一类的召回率低于 70%,优先检查那类样本的特征提取是否有问题,而不是急着换分类器。

4.3 避坑与常见问题排查

现象一:所有测试样本都被判成同一类。原因通常是归一化参数用错,或者特征矩阵里有 NaN。解决方法是检查train_feat里有没有 NaN,用sum(isnan(train_feat(:)))查,有的话回溯到 GLCM 计算,病斑区域为空时graycoprops会返回 NaN。在特征提取后加一行feat(isnan(feat)) = 0兜底。

现象二:训练集准确率 99%,测试集只有 60%。典型过拟合,原因可能是样本量太少而特征维度相对高,或者训练测试划分时同一片叶子的不同照片被分到了两边。解决办法是确保划分按叶片个体分组,同一片叶子的所有照片只出现在训练集或测试集一边。另外可以把 SVM 的BoxConstraint调小,默认是 1,调到 0.1 能增强正则化。

现象三:GLCM 计算报错「Image must be 2-D」。原因是把 RGB 三通道图像直接传给了graycomatrix。必须先rgb2gray转灰度,而且灰度图要转成uint8类型,double类型在某些 MATLAB 版本里会报错。加一行gray = im2uint8(gray)就能解决。

现象四:病斑分割结果全是噪点。阈值范围设错了。H 通道的 0.05 到 0.15 是针对稻瘟病的,如果识别的是胡麻斑病,病斑偏深褐色,H 值在 0.02 到 0.08 之间。解决办法是先用imtool打开几张典型样本,手动看 H 通道的像素值分布,再定阈值。不要照搬网上的固定阈值。

现象五:识别速度太慢,单张图要好几秒。瓶颈在 GLCM 的NumLevels设太高,或者图像没降采样。把NumLevels从 256 降到 16,图像从 1024×1024 降到 256×256,单张处理时间能从 3 秒降到 0.2 秒以内。精度损失通常在 1% 到 2% 之间,完全可接受。

5. 把融合权重调出效果:一个可复现的网格搜索技巧

融合权重的调优是这套系统里最容易被忽略但收益最明显的环节。默认三类特征等权拼接,实际上颜色特征在光照稳定的数据集上贡献最大,纹理特征在病斑早期贡献更大。我一般用网格搜索在验证集上找最优权重组合,权重范围设 0.5 到 2.0,步长 0.25,三类特征各一个权重系数,搜索空间是 7 的三次方共 343 种组合。每种组合下重新训练 SVM 并算验证集准确率,取最高的那组。

% 网格搜索融合权重 w_range = 0.5:0.25:2.0; best_acc = 0; best_w = [1, 1, 1]; for w1 = w_range for w2 = w_range for w3 = w_range % 按权重缩放三类特征 feat_weighted = [fusion_train(:,1:6) * w1, ... fusion_train(:,7:10) * w2, ... fusion_train(:,11:14) * w3]; feat_test_w = [fusion_test(:,1:6) * w1, ... fusion_test(:,7:10) * w2, ... fusion_test(:,11:14) * w3]; mdl = fitcsvm(feat_weighted, labels_train, ... 'KernelFunction', 'linear', 'Standardize', false); pred_w = predict(mdl, feat_test_w); acc = sum(pred_w == categorical(label_test)) / numel(label_test); if acc > best_acc best_acc = acc; best_w = [w1, w2, w3]; end end end end fprintf('最优权重: 颜色=%.2f, 纹理=%.2f, 形状=%.2f\n', best_w(1), best_w(2), best_w(3)); fprintf('最优验证准确率: %.2f%%\n', best_acc * 100);

这段代码的逻辑是固定分类器参数,只变权重,避免权重和分类器超参数耦合导致搜索空间爆炸。343 次训练在 14 维特征、几百个样本的规模下,MATLAB 跑完大概两三分钟,完全可以接受。如果样本量上万,可以把步长放宽到 0.5,搜索空间降到 64 种。

权重搜索有个容易翻车的地方:如果验证集和测试集划分不固定,每次搜索的最优权重会漂移。解决办法是在搜索前固定随机种子,rng(42)放在循环外面,保证每次划分一致。另外权重搜索的结果要拿到独立测试集上复核,如果测试集准确率比验证集低超过 5 个百分点,说明权重过拟合了验证集,这时候要退回等权方案或者减小权重搜索范围。

我自己的习惯是先把等权基线跑出来,记下准确率,再跑权重搜索,如果提升不到 3 个百分点就不折腾了,直接用等权,因为权重调优带来的复杂度在后续维护时不划算。这套系统最终在客户的数据集上,等权基线是 87.3%,权重搜索后到 91.6%,提升 4.3 个百分点,主要来自纹理特征权重的上调,因为他们的照片很多是阴天拍的,颜色特征本身就不太可靠。希望这套流程能帮你在自己的稻叶数据集上少走点弯路。

本文还有配套的精品资源,点击获取