ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

MATLAB水果识别实战:传统图像处理实现颜色分割与分类

MATLAB水果识别实战:传统图像处理实现颜色分割与分类

简介:这是一份面向MATLAB初学者与图像识别入门者的水果识别程序源码包,通过图像处理与机器学习技术实现对不同种类水果的自动分类,可应用于课程设计、学术研究及农业自动化、超市果蔬分类等场景。压缩包共11个文件,包含5张jpg水果样本图、4个m脚本文件、1个txt说明文档和1个fig图形文件,整体约77KB,其中m文件承载预处理、特征提取与分类识别的核心流程,jpg提供青椒、西红柿、香蕉、梨等测试样本,txt为运行指引,fig保存界面或结果图形。程序覆盖灰度化、二值化、边缘检测等预处理步骤,并涉及颜色直方图、LBP、HOG等特征提取方法,以及SVM、决策树等分类器训练与测试评估思路。目前已有2679人学习下载,适合希望理解图像识别基本流程、提升MATLAB编程技巧的读者参考借鉴。

1. 从一堆烂水果照片到能跑的识别程序:这个 MATLAB 项目到底能干什么

实验室的桌子上堆着三十多张用手机拍的水果照片,苹果、香蕉、橘子混在一起,光照忽明忽暗,有的还带着阴影。带我的师兄丢过来一句:“你试试用 MATLAB 把这些分出来,别用深度学习,就传统图像处理。”我当时的第一反应是——这能行吗?后来翻到这个 MATLAB 水果识别程序,才发现它正好就是为这种场景写的:不依赖任何深度学习工具箱,纯靠颜色空间转换、形态学操作和区域特征做分类,把一张随手拍的水果图变成带标签的识别结果。

它适合谁?如果你正在上数字图像处理、模式识别这类课,需要交一份能演示、能改参数的课程设计;或者你手头有工业分拣、农产品初筛的小需求,想先用轻量方案验证可行性,这个项目就是很好的起点。整个程序围绕 MATLAB 的 Image Processing Toolbox 展开,核心文件不多,但把“读图—预处理—分割—特征提取—分类—可视化”这条链路走得很完整,拿来当骨架改比从零写要省太多事。

2. 技术选型与整体架构:为什么用传统图像处理而不是深度学习

2.1 选型逻辑:轻量、可解释、不挑机器

这个项目最核心的决策就是放弃深度学习路线。原因很实际:水果识别在颜色和形状上的区分度本来就高,苹果偏红、香蕉偏黄、橘子偏橙,用 HSV 颜色空间做阈值分割就能拿到相当干净的区域,没必要上卷积网络。传统方法的另一个好处是可解释——每一步处理完你都能 imshow 出来看,哪一步翻车了一眼就知道,不像神经网络那样是个黑匣子。

从依赖上看,整个项目只要求 MATLAB 基础环境加 Image Processing Toolbox,不需要 Deep Learning Toolbox,也不需要 GPU。这意味着在普通办公电脑甚至老旧的实验室机器上都能跑起来,对课程设计这种交付场景非常友好。常见做法是配合 App Designer 做一个简单的 GUI,把图片加载、识别按钮和结果显示框拼在一起,演示的时候点两下就行。

2.2 模块划分:六个函数各管一段

项目按处理流程拆成了几个独立函数,主脚本负责串联。典型的目录结构长这样:

fruit_recognition/ ├── main.m % 主入口,串联整个流程 ├── load_image.m % 读图与尺寸归一化 ├── preprocess.m % 滤波、增强、去噪 ├── segment_fruit.m % 颜色空间分割 + 形态学 ├── extract_features.m % 提取颜色/形状/纹理特征 ├── classify_fruit.m % 基于规则的分类器 ├── show_result.m % 结果可视化与标注 └── dataset/ % 存放测试图片 ├── apple_01.jpg ├── banana_01.jpg └── orange_01.jpg

这种拆法的好处是每个环节可以单独调试。比如分割效果不好,你只需要改 segment_fruit.m,不用动分类逻辑;想换分类器,也只影响 classify_fruit.m 一个文件。我一般会在每个函数开头加一段参数注释,写清楚输入输出和可调阈值,后面改起来不用来回翻。

2.3 数据流:一张图从进入到出结果

主流程的数据流很清晰:读入 RGB 图像后先统一缩放到固定尺寸(常见是 256×256),避免大图拖慢处理;接着做中值滤波去噪,再转 HSV 空间;在 HSV 上用阈值把水果区域和背景分开,得到二值掩膜;对掩膜做开闭运算去掉小噪点和填补空洞;然后用掩膜抠出原图里的水果区域,提取颜色直方图、面积、圆度、长宽比等特征;最后送进规则分类器判断类别,把结果和置信度画回原图上。

提示:尺寸归一化这一步别省。不同手机拍出来的分辨率差异很大,不统一尺寸的话,后面基于像素数的面积特征会完全失去可比性。

3. 核心模块拆解:分割、特征提取与分类的代码实现

3.1 图像预处理:滤波和颜色空间转换

预处理的目标是把噪声压下去、把颜色信息整理成更容易分割的形式。代码大致是这样:

function img_hsv = preprocess(img_rgb) % 统一尺寸,兼顾速度和细节 img_rgb = imresize(img_rgb, [256, 256]); % 中值滤波去椒盐噪声,窗口别开太大否则边缘糊 img_filt = medfilt2(img_rgb(:,:,1), [3 3]); img_filt(:,:,2) = medfilt2(img_rgb(:,:,2), [3 3]); img_filt(:,:,3) = medfilt2(img_rgb(:,:,3), [3 3]); % 转 HSV,H 通道对光照变化更鲁棒 img_hsv = rgb2hsv(img_filt); end

这里有几个参数值得说。imresize 的目标尺寸 256×256 是速度和精度的折中,如果你要识别的水果在画面里占比很小,可以提到 512×512,但处理时间会明显上升。medfilt2 的窗口用 [3 3] 是常规选择,窗口越大去噪越狠但边缘也越糊,水果轮廓本来就不算复杂,3×3 够用。转 HSV 是关键一步,RGB 三个通道都和亮度耦合,光照一变阈值就失效,而 HSV 的 H 通道基本只反映颜色种类,对阴影和反光容忍度高得多。

3.2 颜色分割与形态学处理

分割是整个项目里最需要调参的环节。思路是在 H 通道上给每个水果设一个色相区间,落在区间内的判为水果:

function mask = segment_fruit(img_hsv, fruit_type) H = img_hsv(:,:,1); S = img_hsv(:,:,2); V = img_hsv(:,:,3); % 按水果类型设定色相范围(H 取值 0~1) switch fruit_type case 'apple' % 红色系,H 在 0 附近或接近 1 mask = (H < 0.05 | H > 0.95) & (S > 0.4) & (V > 0.2); case 'banana' % 黄色系 mask = (H > 0.12 & H < 0.18) & (S > 0.4) & (V > 0.4); case 'orange' % 橙色系 mask = (H > 0.05 & H < 0.12) & (S > 0.5) & (V > 0.4); end % 形态学开运算去小噪点,闭运算填内部空洞 se = strel('disk', 5); mask = imopen(mask, se); mask = imclose(mask, se); % 只保留最大连通区域,去掉背景干扰 mask = bwareafilt(mask, 1); end

色相阈值是这套方法的核心参数。苹果的红色在 H 上跨越 0 边界,所以要写成H < 0.05 | H > 0.95这种双区间;香蕉和橘子的区间相对集中。S 和 V 的下限用来排除灰白背景和过暗区域。形态学那两行的顺序不能反——先开后闭,开运算先把零星噪点清掉,闭运算再把水果内部因为反光造成的空洞补上。bwareafilt 保留最大连通域是个实用技巧,能自动丢掉画面边缘的杂物。

注意:如果你的测试图背景是纯白或纯黑,这套阈值很好用;但背景里有其他暖色物体时,分割会误判,这时候要么换背景,要么加一步基于位置或形状的二次筛选。

3.3 特征提取:颜色、形状、纹理三路并进

分割出掩膜后,就可以在水果区域上算特征了。项目里主要用了三类:

function feats = extract_features(img_rgb, mask) % 颜色特征:H 通道均值,反映主色调 img_hsv = rgb2hsv(img_rgb); feats.hue_mean = mean(img_hsv(:,:,1), 'all', 'omitnan'); % 形状特征:面积、圆度、长宽比 stats = regionprops(mask, 'Area', 'Perimeter', ... 'BoundingBox', 'Centroid'); feats.area = stats.Area; feats.circularity = 4 * pi * stats.Area / (stats.Perimeter^2); bbox = stats.BoundingBox; feats.aspect_ratio = bbox(3) / bbox(4); % 纹理特征:灰度共生矩阵的对比度 gray = rgb2gray(img_rgb); glcm = graycomatrix(gray, 'NumLevels', 8); props = graycoprops(glcm, 'Contrast'); feats.contrast = props.Contrast; end

颜色特征用 H 均值最直接,苹果、香蕉、橘子的 H 均值天然分得开。形状特征里,圆度4πA/P²对苹果和橘子这种接近圆形的物体取值高,香蕉因为细长会明显偏低;长宽比进一步强化这个区分。纹理特征用灰度共生矩阵的对比度,香蕉表面的纹理比苹果光滑表皮要丰富一些,可以作为辅助判据。regionprops 返回的 stats 是结构体,字段名要和调用时写的一致,写错了 MATLAB 会直接报错,这点新手容易踩。

3.4 规则分类器:用 if-else 也能分得准

分类部分没有用任何机器学习工具箱,就是基于阈值的规则判断:

function [label, score] = classify_fruit(feats) % 按优先级依次判断,命中即返回 if feats.circularity > 0.75 && feats.hue_mean < 0.1 label = 'apple'; score = feats.circularity; elseif feats.aspect_ratio > 1.8 label = 'banana'; score = feats.aspect_ratio / 3; elseif feats.hue_mean >= 0.05 && feats.hue_mean < 0.15 label = 'orange'; score = 1 - abs(feats.hue_mean - 0.1) * 10; else label = 'unknown'; score = 0; end end

规则顺序有讲究。圆度加色相先判苹果,因为苹果的圆形特征最稳定;长宽比判香蕉,细长形状几乎不会误判;剩下的橙色系归橘子。score 是个粗略的置信度,用特征值离阈值的距离估算,方便在界面上显示“像不像”。这套规则在背景干净、光照均匀的图片上准确率不错,但阈值是硬编码的,换一批数据可能就要重新调。

4. 跑起来:环境准备、运行步骤与参数调试

4.1 环境要求和启动方式

跑这个项目需要 MATLAB R2018b 或更高版本,以及 Image Processing Toolbox。检查工具箱是否安装,在命令行敲ver看列表里有没有 Image Processing Toolbox 就行。没有的话通过附加功能管理器装一下。

启动方式有两种。命令行方式最直接:

% 在 MATLAB 命令行里执行 cd('fruit_recognition'); % 进入项目目录 img = imread('dataset/apple_01.jpg'); result = main(img); % 主函数返回识别结果 disp(result.label);

如果项目带了 GUI,直接运行main.m或者对应的 App 文件,界面上会有“加载图片”和“开始识别”两个按钮,点完结果会标注在原图上。第一次跑建议先用 dataset 里自带的图,确认流程通了再换自己的照片。

4.2 关键参数怎么调

调参是这个项目绕不开的环节,几个最常动的参数和它们的调整方向:

参数位置作用调大效果调小效果
目标尺寸preprocess.m归一化分辨率细节多、速度慢速度快、细节少
滤波窗口preprocess.m去噪强度更平滑、边缘糊保边缘、噪点多
色相区间segment_fruit.m分割范围纳入更多像素分割更严格
形态学半径segment_fruit.m开闭运算核去噪强、可能吞细节保留细节、噪点多
圆度阈值classify_fruit.m苹果判据更严格更宽松

调参的顺序建议从分割开始。先把 mask 用 imshow 显示出来,看水果区域是不是完整、背景有没有残留,分割对了再去看分类结果。分割不对的情况下调分类阈值是白费功夫。

4.3 用自带数据验证流程

验证流程是否正常,可以写个小脚本批量跑一遍 dataset:

% 批量测试,统计识别情况 files = dir('dataset/*.jpg'); for i = 1:length(files) img = imread(fullfile('dataset', files(i).name)); result = main(img); fprintf('%s -> %s (score: %.2f)\n', ... files(i).name, result.label, result.score); end

跑完看输出,如果大部分图片都能给出合理标签,说明流程没问题。有个别识别错的先别急着改代码,把那张图的中间结果(mask、特征值)打出来看看是哪一步出的问题,定位清楚再动手。

5. 避坑与常见问题:那些让我重跑好几遍的坑

5.1 分割结果全是噪点

现象:mask 显示出来是一片散点,完全看不出水果轮廓。原因通常是色相阈值设得太宽,把背景里的杂色也纳入了。解决方法是先把原图转 HSV,用impixelinfo在图上点几个水果像素和背景像素,看它们的 H 值分别是多少,据此收窄区间。另外 S 和 V 的下限也要检查,背景如果是浅色,S 值低,加个 S 下限就能滤掉。

5.2 苹果被识别成橘子

现象:红色苹果的识别结果偶尔跳到 orange。原因是苹果的 H 值在 0 附近,而橘子的区间从 0.05 开始,边界处有重叠。解决办法是在分类规则里把苹果的判断放在前面,并且给苹果的色相条件加上H > 0.95这个分支,确保跨 0 边界的红色被苹果先接住。规则分类器的顺序本质上就是优先级,把最不容易混淆的判据放前面。

5.3 换一批图片后准确率暴跌

现象:自带数据跑得好好的,换成自己拍的照片就大面积识别错误。这几乎都是光照和背景差异导致的。传统阈值方法对成像条件敏感,这是它的固有短板。应对办法有两个方向:一是拍摄时尽量用纯色背景、均匀光照,从源头减少变量;二是在预处理里加一步白平衡或直方图均衡,把不同图片的亮度和对比度拉到相近水平,减小域偏移。

5.4 处理大图时卡到怀疑人生

现象:用几千万像素的手机原图跑,每一步都要等好几秒。原因是所有操作都是逐像素的,图越大越慢。解决很简单,在 preprocess 第一步就 imresize 到 256×256,后面所有计算都在小图上做。如果确实需要高分辨率输出,可以只在小图上算 mask 和特征,最后把 mask 放大回原尺寸再叠加显示。

5.5 regionprops 报字段错误

现象:调用 regionprops 后取 stats.Circularity 报错说字段不存在。原因是 regionprops 的属性名要用它支持的字符串,圆度对应的属性名是 'Circularity' 没错,但如果你在调用时没把它写进参数列表,返回的结构体里就不会有这个字段。检查regionprops(mask, 'Area', 'Perimeter')这行,需要哪些属性就显式列哪些,别指望它全返回。

6. 二次开发:从规则分类器升级到可训练的分类流程

规则分类器跑通之后,最自然的改造方向就是把它换成可训练的分类器,让阈值从数据里学出来而不是手调。这一步不需要动前面的分割和特征提取,只替换 classify_fruit.m 就行,接口保持不变——输入特征结构体,输出标签和分数。

具体做法是先用 extract_features 把 dataset 里所有图片的特征批量算出来,存成一个特征矩阵,标签单独存一列。然后用 MATLAB 自带的分类器应用或者 fitcknn、fitcsvm 这类函数训练一个模型:

% 批量提取特征,构建训练集 files = dir('dataset/*.jpg'); X = []; Y = {}; for i = 1:length(files) img = imread(fullfile('dataset', files(i).name)); mask = segment_fruit(preprocess(img), 'apple'); % 分割逻辑复用 feats = extract_features(img, mask); X = [X; feats.hue_mean, feats.circularity, ... feats.aspect_ratio, feats.contrast]; Y{end+1} = extract_label(files(i).name); % 从文件名解析标签 end % 训练 KNN 分类器并做交叉验证 mdl = fitcknn(X, Y, 'NumNeighbors', 3); cvmdl = crossval(mdl, 'KFold', 5); fprintf('交叉验证准确率: %.2f%%\n', ... (1 - kfoldLoss(cvmdl)) * 100);

这样改的好处是分类边界由数据决定,换一批图片时只要重新标注、重新训练,不用再手动调那一堆 if-else 阈值。特征维度也可以扩展,比如把 H 通道的直方图分箱后拼进去,或者加上 Hu 矩这类形状不变量。验证方法上,交叉验证的准确率比单次划分更可靠,样本少的时候尤其明显。

我自己的习惯是,每次改完分类器都先把混淆矩阵打出来看,哪个类别容易被误判一目了然。有一次苹果和橘子互相误判得厉害,查了半天发现是特征里 hue_mean 的区分度不够,加了一维 H 直方图特征之后就分开了。从那以后我每次动特征或分类器,都强制走一遍混淆矩阵加交叉验证,不再只看总准确率那个数。希望这个项目能帮到你,把传统图像处理这条链路真正跑通、改顺。

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进