:傳統圖像處理實現顏色分割與分類)
簡介這是一份面向MATLAB初學者與圖像識別入門者的水果識別程序源碼包通過圖像處理與機器學習技術實現對不同種類水果的自動分類可應用于課程設計、學術研究及農業(yè)自動化、超市果蔬分類等場景。壓縮包共11個文件包含5張jpg水果樣本圖、4個m腳本文件、1個txt說明文檔和1個fig圖形文件整體約77KB其中m文件承載預處理、特征提取與分類識別的核心流程jpg提供青椒、西紅柿、香蕉、梨等測試樣本txt為運行指引fig保存界面或結果圖形。程序覆蓋灰度化、二值化、邊緣檢測等預處理步驟并涉及顏色直方圖、LBP、HOG等特征提取方法以及SVM、決策樹等分類器訓練與測試評估思路。目前已有2679人學習下載適合希望理解圖像識別基本流程、提升MATLAB編程技巧的讀者參考借鑒。1. 從一堆爛水果照片到能跑的識別程序這個 MATLAB 項目到底能干什么實驗室的桌子上堆著三十多張用手機拍的水果照片蘋果、香蕉、橘子混在一起光照忽明忽暗有的還帶著陰影。帶我的師兄丟過來一句“你試試用 MATLAB 把這些分出來別用深度學習就傳統圖像處理。”我當時的第一反應是——這能行嗎后來翻到這個 MATLAB 水果識別程序才發(fā)現它正好就是為這種場景寫的不依賴任何深度學習工具箱純靠顏色空間轉換、形態(tài)學操作和區(qū)域特征做分類把一張隨手拍的水果圖變成帶標簽的識別結果。它適合誰如果你正在上數字圖像處理、模式識別這類課需要交一份能演示、能改參數的課程設計或者你手頭有工業(yè)分揀、農產品初篩的小需求想先用輕量方案驗證可行性這個項目就是很好的起點。整個程序圍繞 MATLAB 的 Image Processing Toolbox 展開核心文件不多但把“讀圖—預處理—分割—特征提取—分類—可視化”這條鏈路走得很完整拿來當骨架改比從零寫要省太多事。2. 技術選型與整體架構為什么用傳統圖像處理而不是深度學習2.1 選型邏輯輕量、可解釋、不挑機器這個項目最核心的決策就是放棄深度學習路線。原因很實際水果識別在顏色和形狀上的區(qū)分度本來就高蘋果偏紅、香蕉偏黃、橘子偏橙用 HSV 顏色空間做閾值分割就能拿到相當干凈的區(qū)域沒必要上卷積網絡。傳統方法的另一個好處是可解釋——每一步處理完你都能 imshow 出來看哪一步翻車了一眼就知道不像神經網絡那樣是個黑匣子。從依賴上看整個項目只要求 MATLAB 基礎環(huán)境加 Image Processing Toolbox不需要 Deep Learning Toolbox也不需要 GPU。這意味著在普通辦公電腦甚至老舊的實驗室機器上都能跑起來對課程設計這種交付場景非常友好。常見做法是配合 App Designer 做一個簡單的 GUI把圖片加載、識別按鈕和結果顯示框拼在一起演示的時候點兩下就行。2.2 模塊劃分六個函數各管一段項目按處理流程拆成了幾個獨立函數主腳本負責串聯。典型的目錄結構長這樣fruit_recognition/ ├── main.m % 主入口串聯整個流程 ├── load_image.m % 讀圖與尺寸歸一化 ├── preprocess.m % 濾波、增強、去噪 ├── segment_fruit.m % 顏色空間分割 形態(tài)學 ├── extract_features.m % 提取顏色/形狀/紋理特征 ├── classify_fruit.m % 基于規(guī)則的分類器 ├── show_result.m % 結果可視化與標注 └── dataset/ % 存放測試圖片 ├── apple_01.jpg ├── banana_01.jpg └── orange_01.jpg這種拆法的好處是每個環(huán)節(jié)可以單獨調試。比如分割效果不好你只需要改 segment_fruit.m不用動分類邏輯想換分類器也只影響 classify_fruit.m 一個文件。我一般會在每個函數開頭加一段參數注釋寫清楚輸入輸出和可調閾值后面改起來不用來回翻。2.3 數據流一張圖從進入到出結果主流程的數據流很清晰讀入 RGB 圖像后先統一縮放到固定尺寸常見是 256×256避免大圖拖慢處理接著做中值濾波去噪再轉 HSV 空間在 HSV 上用閾值把水果區(qū)域和背景分開得到二值掩膜對掩膜做開閉運算去掉小噪點和填補空洞然后用掩膜摳出原圖里的水果區(qū)域提取顏色直方圖、面積、圓度、長寬比等特征最后送進規(guī)則分類器判斷類別把結果和置信度畫回原圖上。提示尺寸歸一化這一步別省。不同手機拍出來的分辨率差異很大不統一尺寸的話后面基于像素數的面積特征會完全失去可比性。3. 核心模塊拆解分割、特征提取與分類的代碼實現3.1 圖像預處理濾波和顏色空間轉換預處理的目標是把噪聲壓下去、把顏色信息整理成更容易分割的形式。代碼大致是這樣function img_hsv preprocess(img_rgb) % 統一尺寸兼顧速度和細節(jié) 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]); % 轉 HSVH 通道對光照變化更魯棒 img_hsv rgb2hsv(img_filt); end這里有幾個參數值得說。imresize 的目標尺寸 256×256 是速度和精度的折中如果你要識別的水果在畫面里占比很小可以提到 512×512但處理時間會明顯上升。medfilt2 的窗口用 [3 3] 是常規(guī)選擇窗口越大去噪越狠但邊緣也越糊水果輪廓本來就不算復雜3×3 夠用。轉 HSV 是關鍵一步RGB 三個通道都和亮度耦合光照一變閾值就失效而 HSV 的 H 通道基本只反映顏色種類對陰影和反光容忍度高得多。3.2 顏色分割與形態(tài)學處理分割是整個項目里最需要調參的環(huán)節(jié)。思路是在 H 通道上給每個水果設一個色相區(qū)間落在區(qū)間內的判為水果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 % 形態(tài)學開運算去小噪點閉運算填內部空洞 se strel(disk, 5); mask imopen(mask, se); mask imclose(mask, se); % 只保留最大連通區(qū)域去掉背景干擾 mask bwareafilt(mask, 1); end色相閾值是這套方法的核心參數。蘋果的紅色在 H 上跨越 0 邊界所以要寫成H 0.05 | H 0.95這種雙區(qū)間香蕉和橘子的區(qū)間相對集中。S 和 V 的下限用來排除灰白背景和過暗區(qū)域。形態(tài)學那兩行的順序不能反——先開后閉開運算先把零星噪點清掉閉運算再把水果內部因為反光造成的空洞補上。bwareafilt 保留最大連通域是個實用技巧能自動丟掉畫面邊緣的雜物。注意如果你的測試圖背景是純白或純黑這套閾值很好用但背景里有其他暖色物體時分割會誤判這時候要么換背景要么加一步基于位置或形狀的二次篩選。3.3 特征提取顏色、形狀、紋理三路并進分割出掩膜后就可以在水果區(qū)域上算特征了。項目里主要用了三類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/P2對蘋果和橘子這種接近圓形的物體取值高香蕉因為細長會明顯偏低長寬比進一步強化這個區(qū)分。紋理特征用灰度共生矩陣的對比度香蕉表面的紋理比蘋果光滑表皮要豐富一些可以作為輔助判據。regionprops 返回的 stats 是結構體字段名要和調用時寫的一致寫錯了 MATLAB 會直接報錯這點新手容易踩。3.4 規(guī)則分類器用 if-else 也能分得準分類部分沒有用任何機器學習工具箱就是基于閾值的規(guī)則判斷function [label, score] classify_fruit(feats) % 按優(yōu)先級依次判斷命中即返回 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規(guī)則順序有講究。圓度加色相先判蘋果因為蘋果的圓形特征最穩(wěn)定長寬比判香蕉細長形狀幾乎不會誤判剩下的橙色系歸橘子。score 是個粗略的置信度用特征值離閾值的距離估算方便在界面上顯示“像不像”。這套規(guī)則在背景干凈、光照均勻的圖片上準確率不錯但閾值是硬編碼的換一批數據可能就要重新調。4. 跑起來環(huán)境準備、運行步驟與參數調試4.1 環(huán)境要求和啟動方式跑這個項目需要 MATLAB R2018b 或更高版本以及 Image Processing Toolbox。檢查工具箱是否安裝在命令行敲ver看列表里有沒有 Image Processing Toolbox 就行。沒有的話通過附加功能管理器裝一下。啟動方式有兩種。命令行方式最直接% 在 MATLAB 命令行里執(zhí)行 cd(fruit_recognition); % 進入項目目錄 img imread(dataset/apple_01.jpg); result main(img); % 主函數返回識別結果 disp(result.label);如果項目帶了 GUI直接運行main.m或者對應的 App 文件界面上會有“加載圖片”和“開始識別”兩個按鈕點完結果會標注在原圖上。第一次跑建議先用 dataset 里自帶的圖確認流程通了再換自己的照片。4.2 關鍵參數怎么調調參是這個項目繞不開的環(huán)節(jié)幾個最常動的參數和它們的調整方向參數位置作用調大效果調小效果目標尺寸preprocess.m歸一化分辨率細節(jié)多、速度慢速度快、細節(jié)少濾波窗口preprocess.m去噪強度更平滑、邊緣糊保邊緣、噪點多色相區(qū)間segment_fruit.m分割范圍納入更多像素分割更嚴格形態(tài)學半徑segment_fruit.m開閉運算核去噪強、可能吞細節(jié)保留細節(jié)、噪點多圓度閾值classify_fruit.m蘋果判據更嚴格更寬松調參的順序建議從分割開始。先把 mask 用 imshow 顯示出來看水果區(qū)域是不是完整、背景有沒有殘留分割對了再去看分類結果。分割不對的情況下調分類閾值是白費功夫。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 值分別是多少據此收窄區(qū)間。另外 S 和 V 的下限也要檢查背景如果是淺色S 值低加個 S 下限就能濾掉。5.2 蘋果被識別成橘子現象紅色蘋果的識別結果偶爾跳到 orange。原因是蘋果的 H 值在 0 附近而橘子的區(qū)間從 0.05 開始邊界處有重疊。解決辦法是在分類規(guī)則里把蘋果的判斷放在前面并且給蘋果的色相條件加上H 0.95這個分支確???0 邊界的紅色被蘋果先接住。規(guī)則分類器的順序本質上就是優(yōu)先級把最不容易混淆的判據放前面。5.3 換一批圖片后準確率暴跌現象自帶數據跑得好好的換成自己拍的照片就大面積識別錯誤。這幾乎都是光照和背景差異導致的。傳統閾值方法對成像條件敏感這是它的固有短板。應對辦法有兩個方向一是拍攝時盡量用純色背景、均勻光照從源頭減少變量二是在預處理里加一步白平衡或直方圖均衡把不同圖片的亮度和對比度拉到相近水平減小域偏移。5.4 處理大圖時卡到懷疑人生現象用幾千萬像素的手機原圖跑每一步都要等好幾秒。原因是所有操作都是逐像素的圖越大越慢。解決很簡單在 preprocess 第一步就 imresize 到 256×256后面所有計算都在小圖上做。如果確實需要高分辨率輸出可以只在小圖上算 mask 和特征最后把 mask 放大回原尺寸再疊加顯示。5.5 regionprops 報字段錯誤現象調用 regionprops 后取 stats.Circularity 報錯說字段不存在。原因是 regionprops 的屬性名要用它支持的字符串圓度對應的屬性名是 Circularity 沒錯但如果你在調用時沒把它寫進參數列表返回的結構體里就不會有這個字段。檢查regionprops(mask, Area, Perimeter)這行需要哪些屬性就顯式列哪些別指望它全返回。6. 二次開發(fā)從規(guī)則分類器升級到可訓練的分類流程規(guī)則分類器跑通之后最自然的改造方向就是把它換成可訓練的分類器讓閾值從數據里學出來而不是手調。這一步不需要動前面的分割和特征提取只替換 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{end1} 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 矩這類形狀不變量。驗證方法上交叉驗證的準確率比單次劃分更可靠樣本少的時候尤其明顯。我自己的習慣是每次改完分類器都先把混淆矩陣打出來看哪個類別容易被誤判一目了然。有一次蘋果和橘子互相誤判得厲害查了半天發(fā)現是特征里 hue_mean 的區(qū)分度不夠加了一維 H 直方圖特征之后就分開了。從那以后我每次動特征或分類器都強制走一遍混淆矩陣加交叉驗證不再只看總準確率那個數。希望這個項目能幫到你把傳統圖像處理這條鏈路真正跑通、改順。本文還有配套的精品資源點擊獲取