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

基於HOG+SVM的貓咪識別器

版權宣告:本文為博主原創文章,未經博主允許不得轉載。    https://blog.csdn.net/Co_zy/article/details/78308316
目的
使用HOG+SVM演算法和OpenCV實現一個圖片分類器,通過自己訓練分類器,達到可以判斷任意圖片是否是貓咪的效果

實驗環境
python2.7 
win10

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

下面會首先介紹一下OpenCV,HOG,SVM的相關知識 
- ##Opencv

1.讀取圖片
import cv2

img_path = 'test.jpg'
image = cv2.imread(filename=img_path)

#視窗預設一直處於彈出窗狀態
cv2.waitKey()
#按任意鍵盤,銷燬視窗
cv2.destroyAllWindows()
1
2
3
4
5
6
7
8
9
2.圖片資料型別
影象是由畫素組成的,而畫素實際上就是帶有座標位置和顏色資訊的點。我們把圖片想象成由若干行,若干列的點組成的, 現實中有RGB顏色系統,我們可以把圖中任意一點(位置在第m行,第n列)的點A表示為

A[m,n] = [blue,green,red]
1
我們可以看下image的shape 
800行,480列的點組成的圖片,RGB三色,圖片為彩色

>>>print(image.shape)
>>>(800,480,3)  
1
2
3.裁剪圖片大小
這裡介紹裁剪圖片大小的方法,由於輸入到分類器的圖片都是固定畫素的,所以我們需要對下載的圖片資料進行處理,使其符合我們程式的要求。將大圖片裁減成固定畫素的小圖片的程式如下:

# -*- coding: utf-8 -*-
'''
這段程式碼會掃描Python指令碼所在的資料夾的子資料夾other資料夾下的所有.jpg檔案,
然後使用OpenCV讀取圖片資料,
並按照指定的大小進行縮放,
將縮放後的結果寫入到指定目錄下的指定圖片中。
'''
import numpy as np 
import cv2
from os.path import dirname,join,basename
from glob import glob
'''
glob參考http://python.jobbole.com/81552/
os.path.join參考http://www.cnblogs.com/jsplyy/p/5634640.html
'''
num = 0
#dirname(__file__)參考http://blog.csdn.net/lxjames833539/article/details/5251608
for i in glob(join(dirname(__file__)+'\dog','*.jpg')):
    img = cv2.imread(i)
    '''
    interpolation即插值法
    INTER_AREA  - 使用畫素區域關係進行重取樣。
    它可能是影象抽取的首選方法,因為它會產生無雲紋理的結果。
    但是當影象縮放時,它類似於INTER_NEAREST方法
    '''
    res = cv2.resize(img,(194,259),interpolation = cv2.INTER_AREA)
    cv2.imwrite('E:/test/'+str(num)+'.jpg',res)
    num +=1
print('all done')
##視窗預設一直處於彈出窗狀態
cv2.waitKey(0)
##按任意鍵盤,銷燬視窗
cv2.destroyAllWindows()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
## HOG 
https://www.leiphone.com/news/201708/ZKsGd2JRKr766wEd.html
## SVM


實驗過程
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特徵是特別適合於做影象中的人體檢測的。

步驟
首先,我們根據已經分類好的資料集來對分類器進行訓練。我們的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個值。這就是我們用來訓練資料的特徵向量。這部分的程式碼如下所示: 
(注:這段使用了Python3.6)

bin_n = 16*16 # Number of bins
def 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)
    #cartToPolar函式參照 http://www.hahack.com/wiki/opencv-image.html
    mag, ang = cv2.cartToPolar(gx, gy)
    bins = np.int32(bin_n*ang/(2*np.pi))    # quantizing binvalues in (0...16)
    bin_cells = bins[:(int)(x_pixel/2),:(int)(y_pixel/2)], bins[(int)(x_pixel/2):,:(int)(y_pixel/2)], bins[:(int)(x_pixel/2),(int)(y_pixel/2):], bins[(int)(x_pixel/2):,(int)(y_pixel/2):]
    mag_cells = mag[:(int)(x_pixel/2),:(int)(y_pixel/2)], mag[(int)(x_pixel/2):,:(int)(y_pixel/2)], mag[:(int)(x_pixel/2),(int)(y_pixel/2):], mag[(int)(x_pixel/2):,(int)(y_pixel/2):]
    #bincount()統計出現的次數 參照http://blog.csdn.net/lanchunhui/article/details/50491632
    #ravel()參照http://blog.csdn.net/lanchunhui/article/details/50354978
    hists = [np.bincount(b.ravel(), m.ravel(), bin_n) for b, m in zip(bin_cells, mag_cells)]
    #hstack()返回結果為numpy的陣列
    hist = np.hstack(hists)     # hist is a 64 bit vector
#    print hist.shape
#    print type(hist)
    return hist
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
完整的程式碼如下所示,程式首先掃描cat和other資料夾內的圖片,然後用灰度方式讀入,計算每個圖片的hog值,然後建立SVM分類器,使用輸入的資料進行訓練,將訓練結果保存於svm_cat_data.dat檔案中。

# -*- coding: utf-8 -*-
import numpy as np
import cv2
#from matplotlib import pyplot as plt
from os.path import dirname, join, basename
import sys
from glob import glob


bin_n = 16*16 # Number of bins

def 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=0
for fn in glob(join(dirname(__file__)+'/cat', '*.jpg')):
    img[num] = cv2.imread(fn,0)#引數加0,只讀取黑白資料,去掉0,就是彩色讀取。
#    print img[num].shape
    num=num+1
print num,' num'
print 'the file path is ', dirname(__file__)
positive=num
for fn in glob(join(dirname(__file__)+'/other', '*.jpg')):
    img[num] = cv2.imread(fn,0)#引數加0,只讀取黑白資料,去掉0,就是彩色讀取。
#    print img[num].shape
    num=num+1
print 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.0
print 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')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
機器學習是一個不斷迭代的過程。訓練的資料集越大越好,訓練時間當然也是越長效果越好。當機器認錯了圖片的時候,我們要把這個圖片拿出來,標記正確,輸入機器再訓練一遍,如此迭代下去。本實驗只訓練了一次以演示原理。

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

建立程式如下:

# -*- coding: utf-8 -*-

import numpy as np
import cv2
#from matplotlib import pyplot as plt
from os.path import dirname, join, basename
import sys
from glob import glob

bin_n = 16*16 # Number of bins

def 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=0
for fn in glob(join(dirname(__file__)+'/cat', '*.jpg')):
    img[num] = cv2.imread(fn,0)#引數加0,只讀取黑白資料,去掉0,就是彩色讀取。
#    print img[num].shape
    num=num+1
print num,' num'
positive=num
for fn in glob(join(dirname(__file__)+'/other', '*.jpg')):
    img[num] = cv2.imread(fn,0)#引數加0,只讀取黑白資料,去掉0,就是彩色讀取。
#    print img[num].shape
    num=num+1
print 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'
#trainData.shape[0] = 89
#重新定義一個89行,轉置為89列,向量轉置np.newaxis,每個都賦值為1.0
responses = np.float32(np.repeat(1.0,trainData.shape[0])[:,np.newaxis])
# print responses
responses[positive:trainData.shape[0]]=-1.0
# print trainData

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('E:/Python/cat/predict/05.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 result
if 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
--------------------- 
作者:Co_zy 
來源:CSDN 
原文:https://blog.csdn.net/Co_zy/article/details/78308316 
版權宣告:本文為博主原創文章,轉載請附上博文連結!