SVM(支持向量機)是一種常用的機器學習分類算法。本課程使用HOG+SVM算法和OpenCV實現一個圖片分類器,通過自己訓練分類器,達到可以判斷任意圖片是否是貓咪的效果。
一、課程介紹
1. 內容簡介SVM(支持向量機)是一種常用的機器學習分類算法。本課程使用HOG+SVM算法和OpenCV實現一個圖片分類器,通過自己訓練分類器,達到可以判斷任意圖片是否是貓咪的效果。
2. 課程知識點HOG+SVM分類器的基本原理
OpenCV處理圖片
訓練分類器,以得到適合自己項目的分類器
Python文件操作
3. 適合人群
本課程難度為中等,適合掌握Python基礎的用戶,建立對SVM分類器的基礎知識。
4. 實驗環境
二、實驗原理
SVM(支持向量機)分類器的原理是利用「分類超平面」來實現數據分類。在利用「分類超平面」對數據進行劃分時,遵循「間距最大」原則。例如,將二維平面內的兩組數據分類,可以確定很多個「分類超平面」,在二維維度下,超平面退化為一條直線:
上圖中使用綠線將藍色圓圈和紅色方塊進行分類,可以有多種方式。那麼根據SVM原理,哪一條線是最佳分類線呢?答案是,最佳分類線因該是距離藍色圓圈和紅色方框的距離都是最大的那一條,即找到兩組數據的最大間距,在最大間距中點畫一條線,如下:
如果分類3維數據,我們就使用一個平面來分割數據。如果分類4維數據,我們將會使用一個體來分割數據。以此類推,如果分類1024維數據,我們將使用1023維平面來分割數據。1023維的平面是什麼樣子?天知道。所以這個時候,將1023維度的平面命名為「分類超平面」。
SVM是一個由分類超平面定義的判別分類器。也就是說給定一組帶標籤的訓練樣本,算法將會輸出一個最優超平面對新樣本(測試樣本)進行分類。
這也是監督類型機器學習的特點,即,把一堆帶有標籤的數據輸入到機器中,讓機器根據給定的數據計算出規則,再利用這個規則,去對未知數據進行分類。說白了,就是先積累幾年工作經驗,然後去工作。
本實驗是讀入輸入圖片的灰度圖,即黑白的。然後計算該圖片的hog值,將計算得到的結果作為向量來代表該圖片。對由很多張圖片組成的向量集進行計算,找到最大間距的分類超平面,進而分類數據。
hog的全稱是Histogram of Oriented Gradient, HOG,即方向梯度直方圖。它是一種在計算機視覺和圖像處理中用來進行物體檢測的特徵描述子。它通過計算和統計圖像局部區域的梯度方向直方圖來構成特徵。Hog特徵結合SVM分類器已經被廣泛應用於圖像識別中,尤其在行人檢測中獲得了極大的成功。HOG+SVM進行行人檢測的方法是法國研究人員Dalal在2005的CVPR上提出的,今天的很多行人檢測算法基本都是以HOG+SVM的思路。
主要思想:在一副圖像中,局部目標的表象和形狀(appearance and shape)能夠被梯度或邊緣的方向密度分布很好地描述。(本質:梯度的統計信息,而梯度主要存在於邊緣的地方)。
具體的實現方法是:首先將圖像分成小的連通區域,我們把它叫細胞單元。然後採集細胞單元中各像素點的梯度的或邊緣的方向直方圖。最後把這些直方圖組合起來就可以構成特徵描述器。
提高性能:把這些局部直方圖在圖像的更大的範圍內(我們把它叫區間或block)進行對比度歸一化(contrast-normalized),所採用的方法是:先計算各直方圖在這個區間(block)中的密度,然後根據這個密度對區間中的各個細胞單元做歸一化。通過這個歸一化後,能對光照變化和陰影獲得更好的效果。
優點:與其他的特徵描述方法相比,HOG有很多優點。首先,由於HOG是在圖像的局部方格單元上操作,所以它對圖像幾何的和光學的形變都能保持很好的不變性,這兩種形變只會出現在更大的空間領域上。其次,在粗的空域抽樣、精細的方向抽樣以及較強的局部光學歸一化等條件下,只要行人大體上能夠保持直立的姿勢,可以容許行人有一些細微的肢體動作,這些細微的動作可以被忽略而不影響檢測效果。因此HOG特徵是特別適合於做圖像中的人體檢測的。
三、開發準備
打開Xfce終端,下載並安裝 OpenCV的相關依賴。
$ sudo pip install numpy$ sudo apt-get install python-opencv
遇到是否安裝的詢問時,輸入y,按回車鍵繼續安裝。安裝時間較長,並且視網絡狀態而定。
下載實驗所需的圖片數據:
$ wget http://labfile.oss.aliyuncs.com/courses/794/cat.zip$ wget http://labfile.oss.aliyuncs.com/courses/794/other.zip$ wget http://labfile.oss.aliyuncs.com/courses/794/predict.zip
這三組數據分別是含有貓的圖片,沒有貓的圖片,以及用於測試SVM分類器的數據集。
下載後,解壓得到圖片:
$ unzip cat.zip$ unzip other.zip$ unzip predict.zip
這些圖片都是從網上下載的。如果想使用自己下載的圖片,也沒有問題。需要注意爹是,輸入到分類器的圖片都是固定像素的。我們需要對下載的圖片數據進行處理,使其符合我們程序的要求。將大圖片裁減成固定像素的小圖片的程序如下:
# -*- coding: utf-8 -*-import numpy as npimport cv2from os.path import dirname, join, basenamefrom glob import globnum=0for fn in glob(join(dirname(__file__)+'\other', '*.jpg')): img = cv2.imread(fn) res=cv2.resize(img,(64,128),interpolation=cv2.INTER_AREA) cv2.imwrite(r'D:\ECLIPSE-PROJECT\Python\my_opencv\other_64_128\test'+str(num)+'.jpg',res) num=num+1print 'all done!' cv2.waitKey(0)cv2.destroyAllWindows()
使用程序時,請替換輸出路徑為一個已存在的路徑,即替換這一句中的路徑:
cv2.imwrite(r'D:\ECLIPSE-PROJECT\Python\my_opencv\other_64_128\test'+str(num)+'.jpg',res)
這段代碼會掃描Python腳本所在的文件夾的子文件夾other文件夾下的所有.jpg文件,然後使用OpenCV讀取圖片數據,並按照指定的大小進行縮放,將縮放後的結果寫入到指定目錄下的指定圖片中。
四、實驗步驟
4.1 訓練數據集首先,我們根據已經分類好的數據集來對分類器進行訓練。我們的cat文件夾下全是貓的照片,而other文件夾下全不是貓,已經完成了貼標籤這個過程了。讓程序從這兩組數據裡學習,計算分類的方法。
使用HOG+SVM算法進行訓練前,需要先計算每張圖片的HOG值以得到供SVM分類器使用的輸入向量。計算該值的算法實現的一般過程為:
灰度化(OpenCV處理圖像時,一般都處理為灰度圖像,忽略顏色幹擾)
採用Gamma校正法對輸入圖像進行顏色空間的標準化(歸一化);目的是調節圖像的對比度,降低圖像局部的陰影和光照變化所造成的影響,同時可以抑制噪音的幹擾;
計算圖像每個像素的梯度(包括大小和方向);主要是為了捕獲輪廓信息,同時進一步弱化光照的幹擾。
將圖像劃分成小cells(例如6*6像素/cell);
統計每個cell的梯度直方圖(不同梯度的個數),即可形成每個cell的descriptor;
將每幾個cell組成一個block(例如3*3個cell/block),一個block內所有cell的特徵descriptor串聯起來便得到該block的HOG特徵descriptor。
將圖像image內的所有block的HOG特徵descriptor串聯起來就可以得到該image(你要檢測的目標)的HOG特徵descriptor了。這個就是最終的可供分類使用的特徵向量了。
在本實驗中,沒有嚴格按照上述的過程實現,我們採用了下述方法:我們在每個cell內計算X和Y方向的Sobel導數。然後找到每個像素的梯度和方向。此梯度被量化為16*16個整數值。把每個圖像分成四個子圖方塊。對於每個子正方形,計算加權其幅度的方向(16*16bins)的直方圖。因此,每個子圖給我們一個包含16*16個值的向量。四個這樣的向量(分別代表四個子圖的16*16向量)一起給我們一個特徵向量包含1024個值。這就是我們用來訓練數據的特徵向量。這部分的代碼如下所示:
bin_n = 16*16 # Number of binsdef hog(img): x_pixel,y_pixel=194,259 gx = cv2.Sobel(img, cv2.CV_32F, 1, 0) gy = cv2.Sobel(img, cv2.CV_32F, 0, 1) mag, ang = cv2.cartToPolar(gx, gy) bins = np.int32(bin_n*ang/(2*np.pi)) # quantizing binvalues in (0...16) bin_cells = bins[:x_pixel/2,:y_pixel/2], bins[x_pixel/2:,:y_pixel/2], bins[:x_pixel/2,y_pixel/2:], bins[x_pixel/2:,y_pixel/2:] mag_cells = mag[:x_pixel/2,:y_pixel/2], mag[x_pixel/2:,:y_pixel/2], mag[:x_pixel/2,y_pixel/2:], mag[x_pixel/2:,y_pixel/2:] hists = [np.bincount(b.ravel(), m.ravel(), bin_n) for b, m in zip(bin_cells, mag_cells)] hist = np.hstack(hists) # hist is a 64 bit vector# print hist.shape# print type(hist) return hist
完整的代碼如下所示,程序首先掃描cat和other文件夾內的圖片,然後用灰度方式讀入,計算每個圖片的hog值,然後建立SVM分類器,使用輸入的數據進行訓練,將訓練結果保存於svm_cat_data.dat文件中。
#file name:train.pyimport numpy as npimport cv2#from matplotlib import pyplot as pltfrom os.path import dirname, join, basenameimport sysfrom glob import globbin_n = 16*16 # Number of binsdef hog(img): x_pixel,y_pixel=194,259 gx = cv2.Sobel(img, cv2.CV_32F, 1, 0) gy = cv2.Sobel(img, cv2.CV_32F, 0, 1) mag, ang = cv2.cartToPolar(gx, gy) bins = np.int32(bin_n*ang/(2*np.pi)) # quantizing binvalues in (0...16) bin_cells = bins[:x_pixel/2,:y_pixel/2], bins[x_pixel/2:,:y_pixel/2], bins[:x_pixel/2,y_pixel/2:], bins[x_pixel/2:,y_pixel/2:] mag_cells = mag[:x_pixel/2,:y_pixel/2], mag[x_pixel/2:,:y_pixel/2], mag[:x_pixel/2,y_pixel/2:], mag[x_pixel/2:,y_pixel/2:] hists = [np.bincount(b.ravel(), m.ravel(), bin_n) for b, m in zip(bin_cells, mag_cells)] hist = np.hstack(hists) # hist is a 64 bit vector# print hist.shape# print type(hist) return hist#print glob(join(dirname(__file__)+'/cat','*.jpg'))img={}num=0for fn in glob(join(dirname(__file__)+'/cat', '*.jpg')): img[num] = cv2.imread(fn,0)#參數加0,只讀取黑白數據,去掉0,就是彩色讀取。# print img[num].shape num=num+1print num,' num'positive=numfor fn in glob(join(dirname(__file__)+'/other', '*.jpg')): img[num] = cv2.imread(fn,0)#參數加0,只讀取黑白數據,去掉0,就是彩色讀取。# print img[num].shape num=num+1print num,' num'print positive,' positive'trainpic=[]for i in img:# print type(i) trainpic.append(img[i])svm_params = dict( kernel_type = cv2.SVM_LINEAR, svm_type = cv2.SVM_C_SVC, C=2.67, gamma=5.383 )#img = cv2.imread('02.jpg',0)#hist_full = cv2.calcHist([img],[0],None,[256],[0,256])#print hist_full#plt.plot(hist_full)#plt.show()#img1 = cv2.imread('02.jpg',0)#temp=img[0].ravel()#print temp#print len(temp)temp=hog(img[0])print temp.shape#hogdata = [map(hog,img[i]) for i in img]hogdata = map(hog,trainpic)print np.float32(hogdata).shape,' hogdata'trainData = np.float32(hogdata).reshape(-1,bin_n*4)print trainData.shape,' trainData'responses = np.float32(np.repeat(1.0,trainData.shape[0])[:,np.newaxis])responses[positive:trainData.shape[0]]=-1.0print responses.shape,' responses'print len(trainData)print len(responses)print type(trainData)svm = cv2.SVM()svm.train(trainData,responses, params=svm_params)svm.save('svm_cat_data.dat')
注意,如果想要運行此程序並得到正確的結果,需要在控制臺輸入:
$ python /home/shiyanlou/train.py
如果只輸入:
$ python train.py
也能看到輸出,只不過執行到一半會報錯。
這其中的原因,主要是程序中使用了glob包來枚舉文件夾下的某個類型的文件。
正常運行後,可以見到文件夾下生成的數據:
同時也會看到,當導入OpenCV包的時候,程序報錯libdc1394 error: Failed to initiallize libdc1394,這個錯誤,是因為沒有加載攝像頭驅動。實驗樓使用的雲伺服器,很可能沒有攝像頭,所以報錯。但這個錯誤並不影響我們的實驗,所以忽略即可。在自己的筆記本上或者臺式機上,只要正確安裝了驅動,不會有這個錯誤。
做到這裡,項目只完成了一半,後續步驟及詳細代碼請點擊閱讀原文觀看。學編程,當然是要一遍敲一遍看啦!
猛戳「閱讀原文」
查看完整《基於SVM的貓咪圖片識別器》