:從圖像分割到SVM分類的完整流程)
簡介基于MATLAB實現(xiàn)的水果自動識別程序面向圖像處理初學者、高校學生以及農(nóng)業(yè)分揀相關開發(fā)者解決不同種類水果的快速分類問題。壓縮包共11個文件包含5張水果樣本圖片青椒、西紅柿、香蕉、梨等、4個M腳本、1個fig界面文件和1個說明文檔整體僅77KB輕量易用。代碼完整覆蓋圖像預處理、特征提取和分類器訓練測試環(huán)節(jié)如灰度化、二值化、邊緣檢測、顏色直方圖、LBP特征以及SVM/隨機森林分類核心腳本shuiguoshibie串聯(lián)流程RGB2bw.m等函數(shù)便于模塊化復用。同時附有運行指南說明幫助快速在MATLAB中配置環(huán)境并復現(xiàn)識別效果也可替換數(shù)據(jù)集遷移到其他果蔬分類任務。該資源已有2673人學習適合希望通過實例掌握MATLAB圖像識別與機器學習流程的讀者。1. 項目思路拆解為什么選MATLAB做水果識別第一次被問到能不能用MATLAB做一個水果識別程序時我下意識覺得這又是一個典型的“課程設計”題目。但真做下來才發(fā)現(xiàn)它把數(shù)字圖像處理里最核心的幾塊內(nèi)容——圖像分割、特征提取、模式識別——全串起來了當成入門計算機視覺的第一個完整項目來練手其實非常合適。先說清楚這個程序能干什么。你給它一張水果圖片它能判斷出圖片里是什么水果比如蘋果、香蕉、橙子、梨這些常見種類??雌饋砗唵蔚澈笊婕耙惶淄暾膱D像處理流程先把水果從背景里摳出來再提取能代表這類水果的特征最后交給分類器做判斷。這個流程放大了看和工業(yè)質(zhì)檢、農(nóng)產(chǎn)品分揀線上用的視覺系統(tǒng)原理一模一樣只是規(guī)模小得多。為什么用MATLAB而不是Python我個人的看法是MATLAB在圖像處理這個場景下有三個不可替代的優(yōu)勢。第一它的矩陣思維和圖像數(shù)據(jù)的存儲方式天然契合讀進來一張圖就是一個三維數(shù)組做像素級操作非常直觀不像Python要反復考慮numpy的維度問題。第二工具箱太全了Image Processing Toolbox里幾十個現(xiàn)成函數(shù)imread、imshow、rgb2hsv這些基礎操作幾乎不用查文檔極大縮短了開發(fā)周期。第三調(diào)試體驗好變量區(qū)直接雙擊就能看數(shù)據(jù)長什么樣圖像每一步處理結(jié)果可以即時可視化和對比這對理解算法到底發(fā)生了什么特別有幫助。當然如果你是做深度學習方向想上CNN做端到端識別那MATLAB的Deep Learning Toolbox也能用但從開發(fā)效率和生態(tài)豐富度來說不如Python順手。這個項目我用的是傳統(tǒng)圖像處理和機器學習路線這也是目前MATLAB做水果識別最主流、最穩(wěn)定的方案——訓練速度快邏輯透明每個環(huán)節(jié)都能控制。2. 圖像預處理與背景分割這一步直接決定識別上限2.1 讀圖與顏色空間轉(zhuǎn)換的邏輯拿到圖像的第一步不是急著識別而是把“水果”和“背景”分開。這一步做不好后面所有特征提取都是在垃圾數(shù)據(jù)上做分析識別率天花板就被鎖死了。讀取圖像用imread顯示用imshow這個沒什么好說的。關鍵在顏色空間的選擇。MATLAB默認讀進來的是RGB圖像但RGB空間有三個致命問題三個通道之間相關性很高光照變化會讓三個分量同時大幅波動而且RGB并不能很好地表達“顏色”這個人類感知概念。所以實際做顏色分析時我一般會轉(zhuǎn)到HSV空間。HSV把顏色拆成色調(diào)Hue、飽和度Saturation、明度Value三個獨立維度。色調(diào)分量基本不受光照強度影響——這太重要了因為實際拍攝時角度、光源、陰影都會造成明暗變化如果直接用RGB分量做閾值分割同一個蘋果在亮處和暗處的像素值可能差好幾倍。而不管光照怎么變蘋果的“紅”在色調(diào)軸上始終落在0到30度這個區(qū)間附近。這就是用HSV做顏色分割的核心優(yōu)勢。MATLAB里rgb2hsv一行代碼就完成了轉(zhuǎn)換返回的還是和原圖同尺寸的三通道矩陣H、S、V各占一個通道。拿到H通道后就可以開始設計分割規(guī)則了。2.2 基于顏色閾值的分割方案及代碼實現(xiàn)分割的思路是每種水果都有相對穩(wěn)定的色調(diào)區(qū)間。比如橙子的主色調(diào)在HSV的H通道大約在10到25度香蕉在25到45度附近蘋果的紅色在0到10度青色蘋果則在60到90度。但只用H通道還不夠因為背景里可能有顏色相近的區(qū)域。比如拍攝時桌面是木色的那它的色調(diào)和香蕉就很容易混淆。這時候需要把S通道和V通道也納入判斷。S通道低說明顏色發(fā)灰、不鮮艷V通道極端低說明是黑色區(qū)域這些都不可能是我們要的水果。所以完整的分割條件是H、S、V三個條件同時滿足才算目標區(qū)域。下面是我實際在用的分割代碼骨架直接在MATLAB命令行或腳本里跑就行img imread(apple.jpg); hsvImg rgb2hsv(img); H hsvImg(:,:,1); S hsvImg(:,:,2); V hsvImg(:,:,3); % 蘋果紅色區(qū)域H在0~0.08對應0~30度S0.3V0.15 mask (H 0 H 0.08) (S 0.3) (V 0.15); % 形態(tài)學清理開運算去噪閉運算填洞 mask imopen(mask, strel(disk, 5)); mask imclose(mask, strel(disk, 10)); figure; subplot(1,3,1); imshow(img); title(原圖); subplot(1,3,2); imshow(mask); title(分割掩碼); subplot(1,3,3); imshow(bsxfun(times, img, cast(mask,like,img))); title(摳圖結(jié)果);這里strel(disk, radius)是創(chuàng)建圓形結(jié)構(gòu)元素半徑選5到10之間比較合適。開運算可以去掉背景里零散的噪點閉運算能填補水果表面反光造成的孔洞。其實反光問題是水果識別的老大難蘋果、橙子這類表面光滑的水果特別容易出現(xiàn)高光點高光區(qū)域在HSV里表現(xiàn)為S值很低、V值很高如果不做閉運算這些點會變成分割掩碼上的黑洞直接影響后續(xù)面積、周長等形狀特征的計算精度。提示如果目標水果和背景顏色接近單純的顏色閾值就失效了。這時可以試試K-means聚類分割把像素按顏色聚成2到3類取包含圖像中心點的那一類作為前景。MATLAB里用imsegkmeans(img, k)一句就能搞定。這個函數(shù)在工具箱里不需要額外裝東西。3. 特征提取的設計顏色、形狀、紋理缺一不可分割完成之后我們就得到了一張只含水果區(qū)域的二值掩碼以及一張被摳出來的彩色水果圖。接下來的核心問題是用什么特征來“描述”這個水果讓計算機能夠區(qū)分不同種類3.1 顏色特征均值、方差、偏度一起上顏色特征是最直觀的。但注意不能用所有像素的平均顏色因為分割后的區(qū)域可能還有一些邊緣毛刺、反光殘留均值會被這些噪點污染一點。更穩(wěn)的做法是只取掩碼內(nèi)的像素做統(tǒng)計這是用掩碼的一個額外好處。每個顏色通道R、G、B或者H、S、V計算三個統(tǒng)計量均值反映整體色調(diào)、標準差反映顏色分布的集中程度、偏度反映分布是否對稱。這樣三個通道一共9維特征。偏度這個指標很多新手會忽略但它在區(qū)分青蘋果和紅蘋果時很管用——紅蘋果的紅色像素在低色調(diào)區(qū)集中偏度較高青蘋果的綠色分布更均勻偏度自然就低。核心代碼長這樣% 用掩碼提取目標區(qū)域像素 mask double(mask); R img(:,:,1) .* mask; G img(:,:,2) .* mask; B img(:,:,3) .* mask; % 找出掩碼內(nèi)的像素索引 idx find(mask 0); if isempty(idx) error(分割結(jié)果為空請檢查閾值參數(shù)); end % 計算顏色矩 feature []; for channel {R, G, B} pix channel{1}(idx); feature [feature, mean(pix), std(pix), skewness(pix)]; end % feature現(xiàn)在是9維向量R均值/R標準差/R偏度/G均值/...3.2 形狀特征從二值掩碼里榨取信息形狀特征是區(qū)分“長條形的香蕉”和“圓滾滾的蘋果”的關鍵。只需要把之前分割得到的二值掩碼送進regionprops函數(shù)就能一次性提取出幾十個形狀屬性stats regionprops(mask, Area, Perimeter, MajorAxisLength, ... MinorAxisLength, Eccentricity, Circularity, ... Extent); area stats.Area; perimeter stats.Perimeter; eccentricity stats.Eccentricity; % 離心率0是圓1是直線 circularity 4 * pi * area / (perimeter^2); % 圓度1是標準圓挑幾個重點解釋一下。Eccentricity離心率是區(qū)分香蕉的利器——香蕉的細長形狀對應離心率接近1而蘋果、橙子這種近圓形果實離心率基本在0.3以下。Circularity圓度的定義是4π乘以面積除以周長的平方標準圓的圓度是1形狀越復雜、邊緣越崎嶇圓度值越小。Extent是面積和最小外接矩形面積之比這個特征在區(qū)分“帶葉子的草莓”和“光滑的番茄”時表現(xiàn)很好。這里有個細節(jié)regionprops默認是按8連通域計算如果分割掩碼有多個不連通區(qū)域它返回的就是一個結(jié)構(gòu)體數(shù)組每個元素對應一個區(qū)域。使用時建議先按面積排序只保留面積最大的區(qū)域——這通常就是目標水果其他小區(qū)域都是噪聲。3.3 紋理特征用灰度共生矩陣描述表面質(zhì)地顏色和形狀已經(jīng)能解決大部分區(qū)分問題但遇到顏色相近、形狀也相近的水果比如青蘋果和香梨就不夠了。這時候紋理特征派得上用場。香梨表面有粗糙的果點青蘋果表面光潔兩者的紋理差異肉眼可辨計算機則可以通過灰度共生矩陣GLCM來量化這種差異。MATLAB里用graycomatrix和graycorprops兩個函數(shù)就能實現(xiàn)。灰度共生矩陣統(tǒng)計的是“相隔某個距離和角度的兩個像素點灰度值組合出現(xiàn)的頻率”它的四個統(tǒng)計屬性對比度、相關性、能量、同質(zhì)性分別刻畫了紋理的不同側(cè)面。對比度大說明紋理溝紋深、視覺上粗糙能量大說明紋理均勻、變化平緩。% 先把圖像轉(zhuǎn)灰度 grayImg rgb2gray(img); % 只取目標區(qū)域生成ROI灰度圖 roiGray grayImg .* uint8(mask); % 計算GLCM距離取1方向取0度、45度、90度、135度并求平均 glcms graycomatrix(roiGray, Offset, [0 1; -1 1; -1 0; -1 -1], ... NumLevels, 64, Symmetric, true); statsTexture graycoprops(glcms, {Contrast, Correlation, ... Energy, Homogeneity}); % 四個方向取平均避免方向性偏差 contrast mean([statsTexture.Contrast]); energy mean([statsTexture.Energy]);實際用下來我把顏色矩9維、形狀特征5維面積、周長、離心率、圓度、Extent、紋理特征4維對比度、相關性、能量、同質(zhì)性合起來一共18維特征向量。這個維度對水果識別來說已經(jīng)足夠特征太多反而容易過擬合而且訓練速度會變慢。4. 分類器選型與訓練KNN、SVM、BP神經(jīng)網(wǎng)絡的取舍特征提取完成后識別問題變成了標準的分類問題輸入18維特征向量輸出水果類別標簽。我試過三種常見分類器各有適用場景下面是我實操下來的對比結(jié)論。4.1 三種分類器的對比與選擇分類器適用場景優(yōu)點缺點我的實測表現(xiàn)KNN小數(shù)據(jù)集、類別差異明顯原理簡單無需訓練調(diào)參少預測時逐個計算距離速度慢18維特征下準確率約92%SVM中等數(shù)據(jù)集、非線性可分泛化能力強適合高維特征核函數(shù)和參數(shù)需要調(diào)用RBF核準確率能到96%BP神經(jīng)網(wǎng)絡大樣本、特征與類別關系復雜能自動學習非線性關系訓練時間長需要調(diào)網(wǎng)絡結(jié)構(gòu)樣本量不足時容易過擬合KNN是個很好的起點。它的思路特別樸素“物以類聚”待識別樣本的特征向量和哪個類別的訓練樣本最接近就屬于哪個類。MATLAB里有個麻煩是新版把knnclassify函數(shù)移到Statistics and Machine Learning Toolbox里了舊腳本直接會報錯需要改用fitcknn和predict這對新接口。第一次用的時候被這個坑卡了半天特地說一下。SVM是我最終選擇的方案因為它在樣本量不大我只有每類30張圖左右的情況下表現(xiàn)最穩(wěn)。SVM的核心思想是尋找一個超平面讓不同類別的樣本點到這個平面的間隔最大化。對線性不可分的數(shù)據(jù)通過核函數(shù)把特征映射到高維空間再找超平面。我用的是RBF高斯核參數(shù)方面關注兩個值盒約束BoxConstraint控制錯誤分類的懲罰力度和核尺度KernelScale控制高斯函數(shù)的寬度。4.2 MATLAB完整訓練流程訓練階段的完整流程如下% 假設已經(jīng)提取了所有訓練樣本的特征存為矩陣features每行一個樣本 % labels是對應的類別標簽用1、2、3、4分別代表蘋果、香蕉、橙子、梨 % 歸一化這一步極其重要否則大數(shù)值特征會主導距離計算 % 比如面積是幾千量級而圓度是0到1不歸一化的話面積完全淹沒其他特征 features normalize(features); % 新版MATLAB直接用normalize函數(shù) % 劃分訓練集和測試集70%訓練30%測試 cv cvpartition(size(features, 1), HoldOut, 0.3); trainIdx training(cv); testIdx test(cv); % 訓練SVM分類器 svmModel fitcecoc(features(trainIdx,:), labels(trainIdx), ... Learners, templateSVM(KernelFunction, rbf, ... BoxConstraint, 1, ... KernelScale, auto)); % 在測試集上評估 predLabels predict(svmModel, features(testIdx,:)); accuracy sum(predLabels labels(testIdx)) / numel(testIdx); fprintf(SVM識別準確率%.2f%%\n, accuracy * 100); % 保存模型這個模型后面可以直接用于新圖片的預測 save(fruitSVM.mat, svmModel);這里用fitcecoc而不是fitcsvm是因為我們需要做多分類4種水果fitcsvm只支持二分類。fitcecoc是“一對一”策略對4類問題自動訓練6個二分類器最后投票決定結(jié)果省去了手寫多分類策略的麻煩。注意normalize函數(shù)是R2018a引入的老版本會提示找不到函數(shù)。如果用的是2018a之前的版本需要用zscore替代。另外如果用fitcknnK值默認是1對噪聲敏感建議用交叉驗證選一個合適的K值一般3到5比較合適。5. 完整識別流程與GUI封裝從訓練好的模型到真正“識別”5.1 單張圖片識別的完整代碼模型訓練好、保存成mat文件后識別階段就非常輕量了。對新來的一張圖片走一遍“預處理—分割—特征提取—預測”的流水線。我把這條流水線封裝成了一個函數(shù)function label classifyFruit(imgPath, svmModel) % 步驟1讀圖、分割 img imread(imgPath); hsvImg rgb2hsv(img); H hsvImg(:,:,1); S hsvImg(:,:,2); V hsvImg(:,:,3); % 這里用的是多類別合并分割策略 % 檢測幾種常見水果的主色調(diào)區(qū)間合并成一個大掩碼 maskRed (H 0.08) (S 0.3) (V 0.15); maskOrange (H 0.08 H 0.15) (S 0.3) (V 0.1); maskYellow (H 0.15 H 0.22) (S 0.25) (V 0.1); maskGreen (H 0.22 H 0.45) (S 0.2) (V 0.1); mask maskRed | maskOrange | maskYellow | maskGreen; % 取最大連通域 mask imopen(mask, strel(disk, 5)); mask imclose(mask, strel(disk, 10)); mask bwareafilt(mask, 1); % 只保留面積最大的連通域 % 步驟2特征提取顏色矩 形狀 紋理18維 feat extractFruitFeatures(img, mask); feat normalize(feat); % 步驟3分類 label predict(svmModel, feat); endbwareafilt函數(shù)是個好工具一行就實現(xiàn)了“只保留最大連通域”省掉了regionprops加sort的繁瑣寫法。這個函數(shù)支持舊版通過bwareaopen加取反的方式實現(xiàn)但直接bwareafilt就沒有兼容性問題。5.2 用App Designer做個簡單的識別界面如果只是腳本運行每次都要改圖片路徑不太方便。我習慣用App Designer把程序包成一個帶界面的工具即使不懂MATLAB的人也能直接用。這個其實不難核心布局就三個要素一個坐標區(qū)用來顯示圖片一個按鈕用來加載圖片一個文本框用來顯示識別結(jié)果。操作步驟在MATLAB命令行輸入appdesigner調(diào)出設計工具從左側(cè)控件庫拖一個Button、一個Axes、一個Label或Edit Field到畫布上。雙擊按鈕在回調(diào)函數(shù)里寫代碼邏輯。比如“打開圖片”按鈕的回調(diào)函數(shù)大概長這樣% 按鈕回調(diào)函數(shù) function OpenImageButtonPushed(app, event) % 彈出文件選擇對話框 [file, path] uigetfile({*.jpg;*.png;*.bmp, 圖片文件 (*.jpg, *.png, *.bmp)}); if isequal(file, 0) return; % 用戶取消 end % 讀取并顯示圖片 imgPath fullfile(path, file); app.ImageAxes.imshow imread(imgPath); % 顯示原圖 app.ImageAxes.imshow(imgPath); % 修正應該用image或imshow函數(shù) % 調(diào)用識別函數(shù) label classifyFruit(imgPath, app.svmModel); app.ResultLabel.Text [識別結(jié)果 label]; end一個小提醒App Designer里主軸的顯示方式跟傳統(tǒng)figure的imshow不太一樣正確的方式是用imshow函數(shù)并把父容器指定為坐標區(qū)或者直接用image函數(shù)避免顯示出的圖片變形或只顯示一角。這個GUI封裝起來以后整個程序的可用性就完全不同了從“一個只能改代碼才能跑的腳本”變成了“一個能交給別人點兩下就出結(jié)果的工具”。6. 常見問題與排查技巧實錄水果識別這個項目看著簡單實際跑起來坑還不少。我把自己踩過和一些朋友問過的問題整理一下基本覆蓋了大多數(shù)新手的痛點。6.1 分割結(jié)果不干凈的三種情況最典型的異常是掩碼里包含大片背景區(qū)域。八成原因是閾值范圍設得太寬。解決辦法是把分割后的掩碼顯示出來檢查微調(diào)HSV的上下界。另一個經(jīng)常出現(xiàn)的問題是水果區(qū)域內(nèi)有大量空洞這是高光反光導致的用imclose閉運算能解決大部分如果還不夠可以試試imfill函數(shù)加holes參數(shù)專門填洞。如果背景里恰好有和水果顏色相近的物體比如紅桌布上的紅蘋果那顏色閾值就怎么調(diào)都有問題。這時要么換背景要么上K-means聚類分割。畢竟成熟的分割方案從來不是單一方法的天下組合拳才是工程上的常態(tài)。還有一類分割結(jié)果“切多了”把陰影也切進來了。HSV分割時V通道下限設低一點或者分割前先做一次光照校正。簡單做法是對灰度圖做imtophat濾波能去掉不均勻照明的影響。6.2 識別準確率上不去從哪里入手如果分割看上去沒什么問題但識別率就是不高我一般按這個順序排查先懷疑特征提取代碼。把提取的特征向量print出來逐維檢查數(shù)值范圍是否合理。比如圓度算出來大于1肯定是代碼寫錯了面積是負數(shù)那肯定不對。再看訓練集和測試集是否同分布。比如訓練集全是白色背景拍的圖片測試時拿了一張復雜背景的識別率斷崖式下跌非常正常。確認是否做了特征歸一化。這個太容易被忽略我一開始忘了歸一化準確率直接從94%掉到60%教訓慘痛。檢查樣本數(shù)量。每類少于20張訓練圖再好的分類器也難有穩(wěn)定表現(xiàn)。可以考慮對圖片做平移、旋轉(zhuǎn)、縮放、加噪等數(shù)據(jù)增強把樣本擴到50張以上。6.3 分類器報錯的經(jīng)典問題MATLAB版本導致函數(shù)調(diào)用方式變化是我遇到最多的坑。knnclassify被移除改用fitcknnsvmtrain不建議用了改成fitcsvm/fitcecoc。看到“Undefined function or variable”先別慌大概率是函數(shù)在新版本被替換了查一下文檔就能解決。SVM訓練報內(nèi)存不足多半是特征維度太高或者樣本量太大。水果識別特征最多幾十維樣本量幾百個理論上是不會爆內(nèi)存的。如果真遇到了先檢查是不是訓練數(shù)據(jù)里混入了異常值比如某個樣本的特征全是NaN——這通常是分割結(jié)果為空導致的特征向量是空值。7. 寫在最后的幾個經(jīng)驗做水果識別這個項目帶給我最直接的價值不是這個程序本身而是讓我把圖像處理和模式識別里最容易忽略的幾個知識點串起來了。以前單獨學直方圖均衡、邊緣檢測、形態(tài)學操作的時候都是“學過就忘”但一旦它們成了某個目標鏈路上的必要環(huán)節(jié)印象就非常深刻。如果你是自己學習做這個項目我建議從最簡單的開始先拿兩類顏色差異大的水果比如紅蘋果和香蕉打通全流程準確率穩(wěn)定了再逐步加類別。類和類之間差異越小對特征設計和數(shù)據(jù)質(zhì)量的要求就翻著倍提高這個過程中踩的坑才是最值錢的。最后分享一個小技巧。訓練好模型后把整個識別流程封裝成函數(shù)以后不管拿到什么水果圖片一行代碼就能出結(jié)果。這種“工具箱化”的思路在項目越做越大的時候特別重要。我現(xiàn)在電腦里這個水果識別的工程文件還留著偶爾換一批新圖片測試一下既是復習舊知識也為以后做更復雜的視覺項目留了個可擴展的底子。本文還有配套的精品資源點擊獲取