1. 程式人生 > >基於SVM的貓咪圖片識別器

基於SVM的貓咪圖片識別器

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

基於SVM的貓咪圖片識別器

一、實驗介紹

1.1 實驗內容

SVM(支援向量機)是一種常用的機器學習分類演算法。本課程使用HOG+SVM演算法和OpenCV實現一個圖片分類器,通過自己訓練分類器,達到可以判斷任意圖片是否是貓咪的效果。

1.2 實驗知識點

  • HOG+SVM分類器的基本原理
  • OpenCV處理圖片
  • 訓練分類器,以得到適合自己專案的分類器
  • Python檔案操作

1.3 實驗環境

  • python2.7
  • Xfce終端

1.4 適合人群

本課程難度為中等,適合掌握Python基礎的使用者,建立對SVM分類器的基礎知識。

1.5 程式碼獲取

你可以通過下面命令將程式碼下載到實驗樓環境中,作為參照對比進行學習。

$ wget http://labfile.oss.aliyuncs.com/courses/794/train.py$ wget http://labfile.oss.aliyuncs.com/courses/794/predict.py

二、實驗原理

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內計算XY方向的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

完整的程式碼如下所示,程式首先掃描catother資料夾內的圖片,然後用灰度方式讀入,計算每個圖片的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,這個錯誤,是因為沒有載入攝像頭驅動。實驗樓使用的雲伺服器,很可能沒有攝像頭,所以報錯。但這個錯誤並不影響我們的實驗,所以忽略即可。在自己的筆記本上或者桌上型電腦上,只要正確安裝了驅動,不會有這個錯誤。

4.2 使用訓練好的SVM分類器進行分類

機器學習是一個不斷迭代的過程。訓練的資料集越大越好,訓練時間當然也是越長效果越好。當機器認錯了圖片的時候,我們要把這個圖片拿出來,標記正確,輸入機器再訓練一遍,如此迭代下去。本實驗只訓練了一次以演示原理。

我們得到了訓練好的資料svm_cat_data.dat後,可以用它來分類測試圖片。

建立程式如下:

#file name:predict.pyimport numpy as npimport cv2#from matplotlib import pyplot as pltfrom os.path import dirname, join, basenameimport sysfrom glob import glob#my_svm=cv2.SVM()#my_svm#bin_n = 16 # Number of binsbin_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 )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.0#print responses[40:80]print responses.shape,' responses'print len(trainData)print len(responses)print type(trainData)svm = cv2.SVM()svm.load('svm_cat_data.dat')img = cv2.imread('/home/shiyanlou/predict/01.jpg',0)#print img.shapes,' img_test0'hogdata = hog(img)testData = np.float32(hogdata).reshape(-1,bin_n*4)print testData.shape,' testData'result = svm.predict(testData)print resultif result > 0:    print 'this pic is a cat!'test_temp=[]for fn in glob(join(dirname(__file__)+'/predict', '*.jpg')):    img=cv2.imread(fn,0)#引數加0,只讀取黑白資料,去掉0,就是彩色讀取。    test_temp.append(img)print len(test_temp),' len(test_temp)'hogdata = map(hog,test_temp)testData = np.float32(hogdata).reshape(-1,bin_n*4)print testData.shape,' testData'result = [svm.predict(eachone) for eachone in testData]print result

執行該程式,同樣,需要提供完整的路徑:

$ python /home/shiyanlou/predict.py

程式輸出結果如下:

此處輸入圖片的描述

注意SVM分類器認為1是貓,而-1不是貓。我們提供的測試資料集裡,沒有一張圖片是貓,而分類器認為第3張和第11張是貓,其餘不是貓。這明顯是誤判了。一次訓練的結果肯定達不到100%分類正確,所以才需要迭代重複訓練。把分類器認錯的圖片,再丟進other資料夾下,再訓練一次,下次再遇到,就認識了。

五、實驗總結

使用SVM機器學習演算法和OpenCV實現了一個判斷一張圖片是否是貓的分類器。通過本課程的學習,學員應理解SVM分類器的原理,可以建立自己的圖片分類器,訓練分類器達到合適的分類精度。

六、課後習題

在本實驗中,分類器誤判了兩張圖片,請重複訓練讓分類器正確分類它們;增加圖片數量,訓練一個更加精確的分類器;使用HOG+SVM進行行人檢測。

           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述