ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于HSV颜色空间与LBP纹理特征的水果图像识别系统Matlab实现

基于HSV颜色空间与LBP纹理特征的水果图像识别系统Matlab实现 简介这是一套面向图像处理与计算机视觉初学者及课程设计者的水果识别MATLAB实践系统聚焦纹理特征建模与无监督分类任务适用于数字图像处理、模式识别等课程实验或本科毕设参考。资源包含LBP与MBLBP纹理算子实现、HSV色彩空间非均匀量化、K均值聚类核心算法以及灰度共生矩阵等进阶特征提取模块完整覆盖从预处理、特征融合到聚类判别的全流程。压缩包共5468个文件主体为5004张BMP格式水果样本图像含多角度、光照与遮挡变化辅以229个PNG标注图、95个MATLAB源码.m、13个预训练数据.mat及可视化结果.fig整体容量88.29MB结构清晰、模块解耦便于逐层调试与功能替换。已有147人学习下载提供可直接运行的主流程脚本、详细注释的子函数、典型水果如苹果、橙子、香蕉的聚类效果对比图及参数调优说明显著降低复现门槛。1. 项目概述从“看”到“识”的水果图像识别系统最近在整理硬盘翻出来一个几年前做的Matlab项目一个基于多种纹理和颜色特征融合的水果识别系统。当时做这个主要是为了解决一个朋友在小型自动化分拣线上的需求——如何用摄像头快速、准确地识别不同种类的水果。市面上成熟的工业视觉方案太贵而简单的颜色识别又容易受光照和背景干扰。于是我就琢磨着把几种经典的图像特征提取方法组合起来用Matlab快速搭建一个原型验证系统。这个系统的核心思路很直接模仿人眼和大脑的识别过程。我们看一个苹果会注意到它的颜色红色、绿色、表面的纹理光滑或有斑点、以及大概的形状。这个系统就是把“颜色”交给HSV颜色空间去量化把“纹理”交给LBP局部二值模式和MB-LBP多块局部二值模式去描述最后用K均值聚类把这些特征“打包”成不同的类别从而实现识别。整个项目源码打包成了一个.zip文件包含了从图像预处理、特征提取、聚类训练到识别测试的全套Matlab脚本和函数。如果你正在学习数字图像处理、模式识别或者想用Matlab做一个完整的、有实际应用场景的课程设计或毕业设计这个项目会是一个很好的切入点。它不涉及过于深奥的神经网络而是立足于扎实的传统图像处理方法能让你透彻理解特征工程在识别任务中的核心地位。接下来我就把这个系统的设计思路、关键技术的实现细节、以及我踩过的一些坑毫无保留地分享给你。2. 系统核心设计思路与方案选型做一个识别系统首要问题是用什么特征来区分不同水果水果之间的差异无外乎颜色、纹理、形状、大小。在静态图像识别中形状和大小容易受拍摄角度、距离影响鲁棒性较差。因此我选择了以颜色和纹理作为核心特征这两种特征相对稳定且计算效率高。2.1 为什么选择HSV而非RGB我们最常接触的是RGB颜色模型但它有个致命缺点对光照变化极其敏感。同一个红苹果在强光下和阴影下其RGB值会相差很大。这会给识别带来巨大干扰。HSV颜色模型将颜色信息分解为H色相颜色的种类如红、黄、绿。这是颜色最本质的属性受光照影响小。S饱和度颜色的纯度或鲜艳程度。V明度颜色的明亮程度。在水果识别中色相H通道最能稳定地表征水果的固有颜色。一个成熟的草莓无论是放在亮处还是暗处它的“红色”属性H值是相对恒定的。因此提取HSV空间的H分量直方图作为颜色特征是比RGB直方图稳健得多的选择。注意Matlab的rgb2hsv函数转换后H、S、V的取值范围是[0, 1]。在计算直方图时通常将H分量量化到若干个区间例如0-179模仿OpenCV的0-180以减少计算量并增强鲁棒性。2.2 纹理特征LBP与MB-LBP的互补组合颜色能区分橙子和香蕉但区分蛇果和富士苹果都是红色就力不从心了。这时就需要纹理特征。LBP局部二值模式是纹理分析的“瑞士军刀”。它的原理非常巧妙对于一个像素点比较它和周围3x3邻域内8个像素的灰度值比中心亮的记为1否则记为0。这样就得到一个8位的二进制数再转换为十进制作为该中心点的纹理编码。统计整幅图像所有LBP编码的直方图就得到了图像的纹理特征。LBP计算简单对灰度变化不敏感。但基础LBP也有局限它只关注微小的局部纹理对更大尺度、更具结构性的纹理模式捕捉能力不足。例如橙子表面的毛孔状纹理和香蕉皮的纵向条纹在宏观上差异明显。于是我引入了MB-LBP多块局部二值模式。MB-LBP不再比较单个像素而是比较图像中两个小矩形区域Block的平均灰度值。通过改变矩形块的大小和位置MB-LBP能够捕捉从细微到宏观不同尺度的纹理信息。它就像是LBP的“升级版”视野更广对纹理结构的描述能力更强。将LBP和MB-LBP的特征直方图拼接起来就形成了一组既能描述微观纹理细节又能刻画宏观纹理结构的复合纹理特征向量。这大大提升了系统对纹理相似但尺度不同的水果的区分能力。2.3 分类器为什么用K均值聚类而非SVM或神经网络有了特征下一步就是分类。常见的分类器有SVM支持向量机、决策树、神经网络等。但我这里选择了K均值聚类。主要基于以下几点考虑项目阶段与目标这是一个原型验证系统。K均值聚类是一种无监督学习方法不需要大量精确标注的数据来训练一个复杂的分类模型。我只需要准备一些水果图片系统能自动将它们聚成几类我再去检查每一类对应哪种水果即可。这非常适合快速验证特征的有效性。直观与可解释性K均值聚类的过程非常直观——物以类聚。我可以清晰地看到在特征空间里苹果的特征向量彼此靠近与香蕉的特征向量距离较远。这有助于我直观地分析和调试特征提取的效果。计算效率相对于训练一个SVM或小型神经网络K均值聚类的计算开销更小在Matlab中实现和迭代速度很快。灵活性如果我想增加一种新的水果只需要将其图片加入数据集重新运行一次K均值聚类即可。而监督学习模型则需要重新标注数据并训练。当然K均值聚类的缺点是要求预先指定类别数K且对初始聚类中心敏感对非球形分布的数据效果不佳。但在当前这个颜色和纹理特征分布相对明确的水果识别场景中它完全够用且优势明显。整个系统的流程可以概括为输入图像 - 预处理缩放、去噪- 分别提取HSV颜色直方图特征、LBP纹理直方图特征、MB-LBP纹理直方图特征 - 将三个特征向量拼接成一个高维特征向量 - 将所有训练图片的特征向量集合输入K均值算法进行聚类 - 保存聚类中心即“模板”- 识别新图片时提取其特征向量计算与各个聚类中心的距离距离最近的类别即为识别结果。3. 关键技术细节与Matlab实现解析理论说清楚了我们来看看在Matlab里具体怎么实现。这里会涉及一些关键函数和参数设置我会解释为什么这么选。3.1 图像预处理与HSV特征提取预处理的第一步通常是统一尺寸和去噪。我用imresize函数将所有输入图像缩放到一个固定大小比如256x256这保证了后续提取的特征维度一致。对于简单的椒盐噪声可以使用中值滤波medfilt2。HSV特征提取的核心代码如下function colorHist extractHSVFeature(imgRGB) % 转换为HSV颜色空间 imgHSV rgb2hsv(imgRGB); % 提取H分量 H imgHSV(:,:,1); % 将H分量从[0,1]量化到0-179共180个bin % 乘以179并取整1是为了让索引从1开始 H_quantized floor(H * 179) 1; % 计算直方图180个bin colorHist histcounts(H_quantized(:), 1:181); % 可选进行L1或L2归一化消除图片大小的影响 colorHist colorHist / norm(colorHist, 1); % L1归一化总和为1 end实操心得histcounts比旧的imhist函数更灵活高效。量化到180个bin是一个经验值在计算复杂度和特征区分度之间取得了平衡。一定要做归一化否则一张大图和一张小图的特征向量会因像素总数不同而失去可比性严重影响聚类效果。3.2 LBP纹理特征提取Matlab图像处理工具箱中没有现成的LBP函数需要自己实现。function lbpHist extractLBPFeature(imgGray) % imgGray 是灰度图像 [rows, cols] size(imgGray); lbpCode zeros(rows-2, cols-2, uint8); % 边界不处理 % 3x3邻域LBP计算 for i 2:rows-1 for j 2:cols-1 center imgGray(i, j); code 0; % 定义8个邻域位置从左上角开始顺时针 neighbors [imgGray(i-1, j-1), imgGray(i-1, j), imgGray(i-1, j1), ... imgGray(i, j1), imgGray(i1, j1), imgGray(i1, j), ... imgGray(i1, j-1), imgGray(i, j-1)]; for k 1:8 if neighbors(k) center code code 2^(k-1); % 注意索引也可以按bit位赋值 end end lbpCode(i-1, j-1) code; end end % 计算LBP直方图经典LBP有256种可能值(2^8) lbpHist histcounts(lbpCode(:), 0:256); lbpHist lbpHist / norm(lbpHist, 1); % 归一化 end注意事项上述是最基础的LBP实际中更常用的是“均匀模式”(Uniform LBP)它能将256维特征降至59维58个均匀模式1个非均匀模式混合既能降维又能提升鲁棒性。在项目中我强烈建议你实现均匀LBP。判断一个LBP编码是否为均匀模式的标准是其二进制串中0/1的变化次数是否不超过2次。例如00111100变化2次是均匀的而01010101变化7次则不是。3.3 MB-LBP纹理特征提取MB-LBP的实现比LBP稍复杂关键在于计算图像积分图以快速计算矩形区域的平均灰度。function mblbpHist extractMBLBPFeature(imgGray, blockSize) % blockSize: 用于比较的矩形块大小例如3或5 [rows, cols] size(imgGray); % 1. 计算积分图 intImage integralImage(imgGray); % Matlab自带函数 % 2. 定义MB-LBP算子这里以3x3的块为例比较中心块与周围8个块 % 每个块的大小是 blockSize x blockSize radius blockSize; mblbpCode zeros(floor(rows/(3*radius)), floor(cols/(3*radius)), uint8); idx 1; for i 1radius: 3*radius: rows - 3*radius for j 1radius: 3*radius: cols - 3*radius % 计算中心块的平均灰度 centerBlockMean computeBlockMean(intImage, i, j, radius); code 0; % 定义周围8个块的位置相对位置 positions [-radius, -radius; -radius, 0; -radius, radius; 0, radius; radius, radius; radius, 0; radius, -radius; 0, -radius]; for k 1:8 r i positions(k,1); c j positions(k,2); neighborBlockMean computeBlockMean(intImage, r, c, radius); if neighborBlockMean centerBlockMean code code 2^(k-1); end end mblbpCode(idx) code; idx idx 1; end end % 3. 计算直方图并归一化 mblbpHist histcounts(mblbpCode(:), 0:256); mblbpHist mblbpHist / norm(mblbpHist, 1); end function blockMean computeBlockMean(intImage, startRow, startCol, size) % 利用积分图快速计算矩形区域和 % 积分图坐标是1-based且区域和公式为Sum D A - B - C % 其中A、B、C、D是积分图在矩形四个角的像素值 r1 startRow - 1; c1 startCol - 1; r2 startRow size - 1; c2 startCol size - 1; A intImage(r1, c1); B intImage(r1, c2); C intImage(r2, c1); D intImage(r2, c2); blockSum D A - B - C; blockMean blockSum / (size * size); end关键参数解析blockSize是MB-LBP最重要的参数。blockSize较小如3捕捉的是中等尺度的纹理blockSize较大如7或9则捕捉更大尺度、更全局的纹理结构。在实际项目中我通常会提取多个不同blockSize的MB-LBP特征然后拼接形成多尺度纹理描述子效果更好但计算量也会增加。3.4 特征融合与K均值聚类将上述三个特征向量HSV直方图、LBP直方图、MB-LBP直方图拼接起来function featureVector fuseFeatures(hsvHist, lbpHist, mblbpHist) % 简单拼接 featureVector [hsvHist, lbpHist, mblbpHist]; % 更优的做法加权拼接 % 因为不同特征的数值范围和重要性可能不同 % weight_color 0.4; weight_texture 0.6; % featureVector [weight_color * hsvHist, weight_texture * lbpHist, weight_texture * mblbpHist]; end然后对训练集所有图片提取特征形成一个N x D的矩阵N是图片数D是特征维度。使用Matlab的kmeans函数进行聚类。% 假设 allFeatures 是 N x D 的特征矩阵 k 5; % 假设我们有5种水果 [clusterIdx, clusterCenters] kmeans(allFeatures, k, Distance, sqeuclidean, Replicates, 10);Distance, sqeuclidean使用平方欧氏距离这是最常用的计算量小。Replicates, 10这个参数至关重要。K均值聚类的结果受初始随机聚类中心影响很大。设置Replicates为10意味着Matlab会用不同的初始中心运行聚类算法10次然后选择其中总距离和最小的那次结果作为最终输出。这能有效避免因坏运气而得到次优解。聚类完成后clusterCenters就是一个k x D的矩阵每一行代表一种水果的“平均特征模板”。保存这个矩阵和对应的水果标签识别阶段就是计算新图片特征与所有模板的距离找最小值。4. 完整系统搭建与核心流程实现有了各个模块我们需要把它们串成一个完整的、可用的系统。这个系统通常分为两个主要阶段训练阶段和识别阶段。4.1 训练阶段流程与代码组织训练阶段的目标是生成聚类中心模板和标签映射。我的项目文件结构通常如下fruit_recognition_system/ ├── data/ │ ├── train/ │ │ ├── apple/ │ │ ├── banana/ │ │ └── orange/ │ └── test/ ├── src/ │ ├── extractHSVFeature.m │ ├── extractLBPFeature.m │ ├── extractMBLBPFeature.m │ ├── fuseFeatures.m │ ├── trainSystem.m │ └── predictFruit.m └── main.mtrainSystem.m脚本的核心逻辑% 1. 设置路径和参数 trainDataPath data/train/; categories {apple, banana, orange, strawberry, lemon}; k length(categories); % 类别数 allFeatures []; trueLabels {}; % 2. 遍历所有训练图片 for catIdx 1:length(categories) category categories{catIdx}; folderPath fullfile(trainDataPath, category); imgFiles dir(fullfile(folderPath, *.jpg)); % 假设是jpg格式 for i 1:length(imgFiles) % 读取并转换为灰度图用于纹理和RGB用于颜色 imgPath fullfile(folderPath, imgFiles(i).name); imgRGB imread(imgPath); imgGray rgb2gray(imgRGB); % 3. 提取特征 hsvFeat extractHSVFeature(imgRGB); lbpFeat extractLBPFeature(imgGray); mblbpFeat extractMBLBPFeature(imgGray, 3); % 使用3x3的块 % 4. 特征融合 fusedFeat fuseFeatures(hsvFeat, lbpFeat, mblbpFeat); % 5. 收集特征和标签 allFeatures [allFeatures; fusedFeat]; trueLabels{end1} category; end end % 6. 执行K均值聚类 [clusterIdx, clusterCenters] kmeans(allFeatures, k, Distance, sqeuclidean, Replicates, 10, MaxIter, 1000); % 7. 建立聚类中心与水果标签的映射这是关键且需要人工干预的一步 % K均值给出的clusterIdx是自动分配的我们需要知道每个簇对应哪种水果。 labelMap containers.Map(KeyType, int32, ValueType, char); % 通常采用“投票法”统计每个簇中出现次数最多的真实标签 for clu 1:k indices find(clusterIdx clu); labelsInThisCluster trueLabels(indices); % 使用mode函数找出众数 [dominantLabel, ~] mode(labelsInThisCluster); labelMap(clu) dominantLabel{1}; % 存储映射关系 end % 8. 保存模型模板和映射 save(fruit_model.mat, clusterCenters, labelMap, categories); disp(训练完成模型已保存为 fruit_model.mat);4.2 识别阶段预测流程识别阶段就是加载模型对新图片进行同样的特征提取然后与所有模板比较。predictFruit.m函数function predictedLabel predictFruit(imgPath, modelPath) % 加载模型 load(modelPath, clusterCenters, labelMap); % 假设模型文件包含这两个变量 % 读取并预处理图片 imgRGB imread(imgPath); imgGray rgb2gray(imgRGB); % 提取特征必须与训练时完全一致 hsvFeat extractHSVFeature(imgRGB); lbpFeat extractLBPFeature(imgGray); mblbpFeat extractMBLBPFeature(imgGray, 3); testFeature fuseFeatures(hsvFeat, lbpFeat, mblbpFeat); % 计算与所有聚类中心的距离 numClusters size(clusterCenters, 1); distances zeros(1, numClusters); for i 1:numClusters % 计算欧氏距离 distances(i) norm(testFeature - clusterCenters(i, :)); end % 找到距离最小的聚类中心索引 [~, minIdx] min(distances); % 根据映射表得到预测标签 predictedLabel labelMap(minIdx); end4.3 主程序与交互示例一个简单的main.m脚本可以这样写用于演示整个流程% main.m clear; clc; % 阶段一训练如果已有模型可跳过 fprintf( 水果识别系统训练阶段 \n); trainSystem; % 运行训练脚本 % 阶段二测试单张图片 fprintf(\n 水果识别系统测试阶段 \n); model fruit_model.mat; testImg data/test/test_apple_1.jpg; predicted predictFruit(testImg, model); fprintf(识别结果图片【%s】中的水果是【%s】\n, testImg, predicted); % 显示图片和结果 img imread(testImg); figure; imshow(img); title([识别结果: , predicted]);5. 实战调试、常见问题与性能优化理论跑通只是第一步让系统在实际图片上稳定工作才是挑战。下面分享几个我调试过程中遇到的典型问题和优化技巧。5.1 特征维度爆炸与降维处理HSV180维 LBP256维 MB-LBP256维 692维特征。对于只有几十张训练图片的小数据集来说这很容易陷入“维数灾难”即特征空间过于稀疏导致聚类效果不稳定。解决方案特征选择不是所有维度都有用。可以使用简单的方差过滤移除那些在所有样本上取值变化很小的特征维度例如某一直方图bin的值在所有图片中都接近0。使用均匀LBP如前所述将256维LBP降至59维。PCA主成分分析这是最有效的降维方法之一。在训练阶段对所有特征进行PCA分析保留累计贡献率例如95%以上的主成分。在识别阶段新特征也要投影到同样的PCA空间。% 在trainSystem.m的聚类之前加入PCA [coeff, score, latent, ~, explained] pca(allFeatures); % 计算累计贡献率决定保留前多少个主成分 cumulative cumsum(explained); retainDims find(cumulative 95, 1); % 保留贡献率95%以上的成分 allFeaturesReduced score(:, 1:retainDims); % 使用 allFeaturesReduced 进行聚类 % 同时必须保存 coeff 和 retainDims用于后续预测时的同样变换5.2 光照与背景干扰这是图像识别永恒的主题。我们的系统对光照有一定鲁棒性得益于HSV但复杂的背景会引入大量无关纹理和颜色噪声。解决方案前景分割至关重要在特征提取前尽可能将水果从背景中分割出来。对于放在纯色如黑色、白色 conveyor belt 上的水果可以用简单的颜色阈值法。对于复杂背景可以尝试GrabCut等交互式分割算法或者使用深度学习分割模型如U-Net但这超出了本项目的范畴。一个简单的改进是在计算直方图前先创建一个水果区域的掩膜mask只对掩膜内的像素进行统计。颜色校正如果光照条件极端可以考虑使用简单的灰度世界算法或Retinex算法进行颜色恒常性处理校正图像颜色。数据增强对训练集图片进行随机的亮度、对比度微调模拟不同光照条件可以让模型更鲁棒。5.3 K均值聚类的陷阱与调优如何确定K值我们已知水果种类数所以K是明确的。如果未知可以使用“肘部法则”绘制不同K值对应的聚类误差平方和SSE曲线选择曲线拐点肘部对应的K。空簇问题有时聚类会产出一个没有任何样本的“空簇”。kmeans函数的EmptyAction参数可以设置为singleton将最远的点作为一个新簇的中心或者error直接报错。聚类中心初始化使用Replicates参数多次运行是避免局部最优的标配。还可以使用Start, plus选项它采用K-means算法初始化中心点通常比随机初始化效果更好、收敛更快。5.4 系统性能评估训练好模型后需要用独立的测试集进行评估。% 评估脚本 evaluateSystem.m testDataPath data/test/; categories {apple, banana, orange}; confusionMatrix zeros(length(categories)); for catIdx 1:length(categories) category categories{catIdx}; folderPath fullfile(testDataPath, category); imgFiles dir(fullfile(folderPath, *.jpg)); for i 1:length(imgFiles) imgPath fullfile(folderPath, imgFiles(i).name); trueLabel category; predictedLabel predictFruit(imgPath, fruit_model.mat); % 找到真实标签和预测标签在categories中的索引 trueIdx find(strcmp(categories, trueLabel)); predIdx find(strcmp(categories, predictedLabel)); if ~isempty(predIdx) confusionMatrix(trueIdx, predIdx) confusionMatrix(trueIdx, predIdx) 1; else fprintf(警告预测标签【%s】不在类别列表中。\n, predictedLabel); end end end % 计算准确率 accuracy sum(diag(confusionMatrix)) / sum(confusionMatrix(:)); fprintf(整体识别准确率%.2f%%\n, accuracy * 100); disp(混淆矩阵); disp(array2table(confusionMatrix, RowNames, categories, VariableNames, categories));通过混淆矩阵你可以清晰地看到系统容易把哪种水果误识别成另一种从而有针对性地优化特征或增加对应类别的训练数据。5.5 从原型到实用化的思考这个Matlab原型系统证明了“颜色纹理聚类”思路的可行性。但如果要投入实际应用还需要考虑实时性Matlab在算法验证上很方便但部署到嵌入式设备或生产环境可能需要用C/Python如OpenCV重写核心算法并进行大量优化。鲁棒性升级引入更强大的特征如HOG方向梯度直方图描述形状或直接使用预训练的CNN卷积神经网络模型进行迁移学习准确率会有质的飞跃。流程集成识别系统需要与机械臂、传送带控制系统等联动涉及串口通信、触发拍照、结果反馈等工控环节。这个项目最大的价值在于它提供了一个完整、清晰、可复现的图像识别技术栈涵盖了从特征设计、融合到无监督学习的全流程。理解了它你再去看更复杂的深度学习模型就会明白那些“黑箱”到底在做什么以及为什么需要海量的数据。本文还有配套的精品资源点击获取
返回列表