支援向量機:尋求一個最優的超平面,實現樣本的分類
下面我們用SVM實現一個根據身高體重對男女生分類的問題
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 準備資料
rand1 = np.array([[155,48],[159,50],[164,53],[168,56],[172,60]])
rand2 = np.array([[152,53],[156,55],[160,56],[172,64],[176,65]])
# 0為女生 1為男生
label = np.array([[0],[0],[0],[0],[0],[1],[1],[1],[1],[1]])
data = np.vstack((rand1,rand2)) # 合併兩組資料
data = np.array(data, dtype = 'float32')
svm = cv2.ml.SVM_create() # 建立svm學習模型
svm.setType(cv2.ml.SVM_C_SVC) # 型別為svm分類
svm.setKernel(cv2.ml.SVM_LINEAR) # 設定svm的核心為線性分類器
svm.setC(0.01)
#訓練
svm.train(data,cv2.ml.ROW_SAMPLE,label)
# 預測
pt_data = np.array([[167,55],[162,57]])
pt_data = np.array(pt_data, dtype = 'float32')
#pt_label = [[0],[1]]
predict = svm.predict(pt_data)
predict[1]
array([[0.],
[1.]], dtype=float32)
看下面這個例子:
這裡有一個影象img(整個白色區域), win
視窗(藍色區域)為影象中Hog特徵計算的最大模板,官方模板大小為64*128
。block
(紅色區域部分)為win
視窗中的小模板,大小一般為16*16
。block
中又有很多的小模板cell
(綠色區域),大小一般為8*8
。
cell bin: 通過計算畫素的梯度得到梯度的大小和方向, 方向為0-360度, 如果以40度進行劃分,將會得到9塊,將這9塊設定為9個單元,每一個單元就是一個bin。
hot特徵維度: win視窗中block模板的個數 * cell模板個數 * bin的個數
hog特徵: 畫素都有一個梯度,win視窗中所有的畫素梯度構成了hog特徵
如何計算梯度:
我們使用倆個模板:水平梯度模板[1 0 -1]
, 豎直梯度模板[[1],[0],[-1]]
,即相鄰畫素之差。
求得幅值為:f = sqrt(x^2 + y^2)
,角度angle = arctan(a / b)
bin的劃分:如果以40度進行劃分,將會得到9個bin,則bin1的區域為(0-20度)和180-200度,即關於180度對稱的夾角
如果某個梯度的角度正好在bin角度範圍的正中心, 如d = 10,則將其規劃到bin1區域。否則將該梯度分解到相鄰的倆個bin單元中:d1 = d * d(夾角), d2 = d * (1 - d(夾角))
計算整體的hog特徵
1.首先計算每個cell
下所有bin的值,每一個bin的計算方式為所有劃分到該bin下的幅值之和sum(d)
2.得到影象特徵的維度,以上面的為例,該影象的特徵維度為win視窗中block模板的個數 * cell模板個數 * bin的個數 = 105* 4 * 9 = 3780
3.通過使用svm支援向量機對特徵進行分類,得到一個3780維的分類結果,用hog * svm得到一個值 f,讓 f 與我們的判決門限進行比較,如果大於判決門限則認為是目標。
這裡使用正樣本(PosNum) 820張圖片,負樣本(NegNum)1931張圖片進行訓練模型,最終完成訓練,最後採用帶有小獅子的圖片進行測試
這裡使用的正負樣本的資料集如下百度雲連結中:
連結:https://pan.baidu.com/s/1jNpN8ecMKhOHLiy1KlEj4w
提取碼:61hr
訓練步驟如下:
1.設定引數
2.建立Hog:我們使用cv2.HOGDescriptor(winSize,blockSize,blockStride,cellSize,Bin)
函數來建立
3.建立svm,我們使用cv2.ml.SVM_create()
函數來建立,並設定屬性
4.計算Hog,準備標籤label
5.訓練
6.預測
7.繪圖
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 1.設定引數
PosNum = 820
NegNum = 1931
winSize = (64,128)
blockSize = (16,16) # 105
blockStride = (8,8)
cellSize = (8,8)
Bin = 9 # 3780
#2.建立hog
hog = cv2.HOGDescriptor(winSize,blockSize,blockStride,cellSize,Bin)
#3.建立svm
svm = cv2.ml.SVM_create()
#svm屬性設定
svm.setType(cv2.ml.SVM_C_SVC)
svm.setKernel(cv2.ml.SVM_LINEAR)
svm.setC(0.01) # 優化
#4.計算hog
featureNum = int(((128 - 16) / 8 + 1) * ((64 - 16) / 8 + 1) * 4 * 9) # 3780
featureArray = np.zeros((PosNum + NegNum, featureNum),np.float32)
labelArray = np.zeros((PosNum + NegNum, 1),np.int32)
# 處理正樣本
for i in range(PosNum):
filename = 'pos\\' + str(i + 1) + '.jpg'
img = cv2.imread(filename)
# 計算影象的hog特徵, shape (3780,1)
hist = hog.compute(img, (8,8)) # 第二個引數: winStride Window stride
# 將該hog特徵值存到featureArray裡面
featureArray[i] = hist.reshape(-1)
labelArray[i] = 1
# 處理負樣本
for i in range(PosNum, PosNum + NegNum):
filename = 'neg\\' + str(i + 1 - PosNum) + '.jpg'
img = cv2.imread(filename)
# 計算影象的hog特徵, shape (3780,1)
hist = hog.compute(img, (8,8)) # 第二個引數: winStride Window stride
# 將該hog特徵值存到featureArray裡面
featureArray[i] = hist.reshape(-1)
labelArray[i] = -1
# 5.訓練
svm.train(featureArray,cv2.ml.ROW_SAMPLE, labelArray)
# 6.檢測
alpha = np.zeros((1), np.float32)
rho = svm.getDecisionFunction(0, alpha) # 得到分類闕值
print(rho)
print(alpha)
alphaArray = np.zeros((1,1),np.float32)
supportVArray = np.zeros((1,featureNum), np.float32)
resultArray = np.zeros((1,featureNum), np.float32)
alphaArray[0,0] = alpha
resultArray = -1 * alphaArray * supportVArray
# 7.繪圖
myDetect = np.zeros((3781), np.float32)
for i in range(3780):
myDetect[i] = resultArray[0,i]
myDetect[3780] = rho[0]
# 構建hog
myHog = cv2.HOGDescriptor()
myHog.setSVMDetector(myDetect)
# 載入待檢測圖片
imageSrc = cv2.imread('test.jpg', 1)
cv2.imshow('img', imageSrc)
# 引數:(8,8)win滑動步長,(32,32)win大小,縮放係數 目標大小
objects = myHog.detectMultiScale(imageSrc, 0, (8,8), (32,32), 1.05, 2)
x = int(objects[0][0][0])
y = int(objects[0][0][1])
w = int(objects[0][0][2])
h = int(objects[0][0][3])
cv2.rectangle(imageSrc, (x,y),(x+w,y+h),(255,0,0),2)
cv2.imshow('img', imageSrc)
print(objects)
cv2.waitKey(0)
(0.2555259476741386, array([[1.]]), array([[0]], dtype=int32))
[0.]
(array([[ 0, 0, 64, 128]], dtype=int32), array([[0.25552595]]))