前段時間做了一個車型識別的小項目,思路是利用k-means算法以及詞袋模型來做的。
近年來圖像識別的方法非常非常多,這邊只記錄一下我那個項目的思路,核心思想是k-means算法和詞匯樹。
很遺憾沒有做詳盡的開發前的思路文檔,只能按照記憶進行大致總結。
項目分為三大模塊:特征點抽取、訓練詞匯樹、識別(利用訓練好的詞匯樹)。
首先是特征點的抽取。我是用的OpenCV的框架來做的特征點抽取。這里提到兩種特征點:SURF和SIFT。
關於這兩種特征點提取算法,這里做簡要介紹(其實我真的不太care,主要是看哪個的特性適合我的項目。單純為了實現這個東西的話我覺得沒必要太深究這個,當然如果你要把這個東西做透了,那肯定得好好研究,畢竟源碼來看還是有很多可以優化的東西)。
SIFT特征是圖像的局部特征,對平移、旋轉、尺度縮放、亮度變化、遮擋和噪聲等具有良好的不變性,對視覺變化、仿射變換也保持一定程度的穩定性。SIFT算法時間復雜度的瓶頸在於描述子的建立和匹配 ,如何優化對特征點的描述方法是提升SIFT效率的關鍵。
SURF算法的優點是速度遠快於SIFT且穩定性好;在時間上,SURF運行速度大約為SIFT的3倍;在質量上,SURF的魯棒性很好,特征點識別率較SIFT高,在視角、光照、尺度變化等情形下,大體上都優於SIFT。
這里要提到的一點就是SURF是64維的特征描述子,而SIFT是128維的特征描述子,簡單點數說就是SIFT是X=(x1,x2,x3,...,x128)。而SURF是Y=(y1,y2,y3,...,y64)。從做k-means聚類的角度上來說我果斷選擇了SURF算法來提取(不過因為用的是OpenCV框架,所以幾句代碼的事。再提一句,OpenCV框架不止C有,Java也有,喜歡Java不喜歡C/C++的朋友可以嘗試,我就是用的java代碼)。
簡單點說一幅圖就是由N多個SURF特征點構成的,有點像像素點。每一個SURF特征點是一個64維的向量Y=(y1,y2,y3,...,y64),就像像素一樣,一張圖不也是由很多很多個像素點組成的嗎,每一個像素點是一個三維向量(x,y,z),其中x,y,z都在0到255之間。
首先是SURF特征點的抽取,我們采用OpenCV框架來抽取每一張圖的SURF特征點(當時大約10000張圖),將所有圖中抽出的SURF特征點都放在一起,形成一個特征點“池塘”,有大概幾十億個特征點。
第二步是詞匯樹的訓練。我們把這個詞匯樹定義為一個深層次的二叉樹,那么這個二叉樹如何生成呢,這里首先要提到k-means聚類算法:
k-means聚類算法是一類無監督機器學習的算法。至於什么是機器學習,什么是有監督學習和無監督學習,這里簡單介紹,具體可以查百度。
機器學習是一類算法的總稱,通俗點來講就是想讓機器通過學習來擁有智慧(擁有決策能力),這和大多數的基本算法其實沒啥區別,這里還要提到一點就是基於時代的背景,當下機器學習大勢是在統計學習上的,至於什么是統計學習,什么是符號學習,這個是機器學習的一個發展史相關的東西,可以百度。也就是說我們根據現有的一些數據,通過一些手段來分析,能夠得到一個決策的方法,來了一個新的數據我就知道該干什么(這跟我們人類的思維過程是一樣的,我們也是小的時候學到了很多東西,后來遇到一件新的事情之后呢我們就能根據以往的經驗來做出決策)。
有監督學習和無監督學習是機器學習中的兩個大類,有監督學習是說我給定的一大堆數據,是人為標注好哪些數據屬於哪一類的。而無監督學習是指我事先不加以人工干預,單純憑借這一大批數據來進行一個分類或者說預測。
k-means算法的大致步驟,這里我用二維坐標系中的點的聚類來形象地表示:

(不好意思自己弄得公示有點丑),(x0,y0)即質心。
從網上貼張圖來表示k-means聚類的過程:
這是一種比較形象的聚類。可以看到圖中三類點被聚到了3類中。
那么對於我們項目中的SURF特征點的聚類,實際上和上面提到的二維點聚類是一樣的,只不過我們現在是一個64維的坐標系,每一個點是一個64維向量X=(x1,x2,x3,...,x64)
而計算距離就是變成了。而質心的坐標就是(x10,x20,x30,...,x640)。
接下來就是建立二叉樹的過程了,首先我們有一個根結點。我們對於那么多的特征點利用k-means算法分成兩類。那么根結點的左右子樹分別是我們分好類的A類和B類。緊接着,對於左右子樹,我們分別對A類再分成A1,A2兩類作為左子樹的左右子樹;對B類在分成B1,B2兩類作為右子樹的左右子樹,一直迭代,直到迭代到我們需要的層數。我當時定了32層,於是在葉子節點處就有2^31個葉子節點,即有2^31類。然后對每一個葉子節點給一個符號。現在詞匯樹就建立完成了。
接下來就是圖像識別。
現在我的詞匯樹已經訓練完成,那么我要對我數據庫里面的10000張圖片做一個調整,畢竟圖片是沒啥東西可以抽取的,我們要轉換成別的形式。於是我們數據庫里的每一張圖,現在我們來走一遍這個二叉樹,就能將一張圖片轉化為一段文本。具體過程:遍歷每一個SURF特征點,然后比較和A1,A2類哪個接近(用歐氏距離),假設離A1近,然后再到A1子樹里,比較和A1的左子樹,A1的右子樹哪個近……就這樣一直比較到最終的葉子節點,然后就把這個SURF特征點轉化為一個符號(也就是一個文字)。接着我么把所有的數據庫中的圖片全部轉化為文字。
然后我們再進一步轉化,可以把一張圖轉化成一個多維向量!這個利用到文本挖掘里面的一點知識,大家可以自行百度,簡單說就是由好多好多文本(每一段文本由不同的符號組成,相當於普通文本,每一段文本由不同的詞組成嘛),然后選出一系列具有代表性的詞(我們不需要選,因為我們只有2^31個詞!那么就能轉化成2^31維的向量)。
緊接着我們拍到了一張新的汽車的圖片,我們利用相似的過程將一個新的汽車的圖片轉化成一個2^31維向量,然后根據已有的數據來匹配,最接近哪些車型?這個過程其實有很多辦法,這里不詳細展開了,最簡單的一種辦法就是像剛剛一樣計算歐氏距離。
那么整個識別過程到這里就介紹完了,當中其實還有很多坑,比如說拍的汽車圖片,周圍是有很多背景的,在提取SURF特征點過程中,如何防止這些無用的背景的干擾,還有可否拍攝視頻來識別?等等有很多問題需要解決,也有很多方法,就不在本文中描述了。