Opencv和機器學習
隨着人工智能的相關研究不斷發展 深度學習與圖像處理相結合 彌補了傳統圖像處理在分類 識別領域的不足
人臉識別 一鍵換臉 風格遷移等應用 受到了廣大人的喜歡
Opencv與機器學習相關的函數已經日漸豐富 本章將會介紹Opencv4中傳統機器學習相關的函數和識別方法
並且會介紹Opencv4中和深度學習有關的內容
Opencv中和傳統機器學習相關的函數和使用方法
Opencv中提供了兩個有關機器學習的模塊 分別是 Machine Learning(ml) 和 Deep Neural Network(dnn)
前者主要是傳統機器學習的相關函數 后者則是集成了深度神經網絡的相關函數
k均值聚類算法:
非常經典的一個聚類算法 一種無監督學習
根據指定的種類數目來進行聚類
k均值的步驟有以下四點:
1. 將數據分為k類 生成k個中心點
2. 遍歷所有的數據 根據數據和中心的位置關系歸到不同的中心
3. 計算每個聚類的均值 作為新的中心點
4. 重復2 3 直到聚類中心的坐標收斂 輸出結果
函數:
retval,bestLabels,centers = cv.kmeans(data,k,bestLabels,criteria,attempts,flags,centers)
data: 需要聚類的輸入數據 該參數 按行排列 每行都是一個單獨的數據 圖像可以先轉為N*1代表每個有一個特征 共N個數據
K: 給定的聚類數目 必須是正整數
bestLabels: 存儲每個數據聚類結果 中索引的矩陣或向量 和輸入有相同的大小 存儲的就是標簽的索引
cireria: 迭代停止條件
attempts: 表示嘗試采樣不同初始化標簽的次數
flags: 每類中心坐標初始化的方法
cv.KMEANS_RANDOM_CENTERS 隨機位置
cv.KMEANS_USE_INITIAL_LABELS 第一次嘗試時 使用用戶提供的標簽 后續隨機
cv.KMEANS_PP_CENTERS 使用Arthur和Vassilvitskii提出的kmeans++方法初始化
centers: 最終聚類后每個類的中心坐標 如果不要輸入None
import cv2 as cv
import numpy as np
# 展示kmeans聚類算法
from matplotlib import pyplot as plt
if __name__ == '__main__':
# 產生兩組點 以x y 為特征
pts1 = np.random.randint(100,200,(25,2))
pts2 = np.random.randint(300,400,(25,2))
pts = np.vstack((pts1,pts2))
# vstack(()) 垂直把數組堆疊 [[],[],...[]],[[],[],...,[]] == [[],[],.....[]]
data = np.float32(pts)
#如果精度滿足就停止迭代 如果達到最大迭代數就停止迭代
#cv.TERM_CRITERIA_COUNT 迭代次數到達后 停止
#criteria = (type,count,eps)
criteria = (cv.TERM_CRITERIA_EPS+cv.TERM_CRITERIA_MAX_ITER,10,1.0)
ret,label,center = cv.kmeans(data,2,None,criteria,2,cv.KMEANS_RANDOM_CENTERS)
ap = data[label.ravel() == 0]
bp = data[label.ravel() == 1]
# A[:,0] a類點的x
plt.scatter(ap[:,0],ap[:,1],s=10,c='r')
plt.scatter(bp[:,0],bp[:,1],s=10,c='b')
plt.scatter(center[:,0],center[:,1],s=20,c='g',marker='*')
plt.title('kmeans')
plt.xlabel('x')
plt.ylabel('y')
plt.show()
利用k均值聚類可以實現圖像的像素分割
在分割時 特征是每個像素的值
1. 先將每個像素整理為 N*1
2. 聚類
3. 將不同的類表示為不同的顏色
import cv2 as cv
import numpy as np
import sys
if __name__ == '__main__':
cap = cv.VideoCapture(0)
if not cap.isOpened():
print('視頻開啟失敗')
sys.exit()
_,img = cap.read()
data = img.reshape((-1,3))
data = np.float32(data)
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER,10,1)
ret,labels,centers = cv.kmeans(data,3,None,criteria,3,cv.KMEANS_RANDOM_CENTERS)
for i in range(3):
data[labels.ravel() == 0] = (255,0,0)
data[labels.ravel() == 1] = (0,255,0)
data[labels.ravel() == 2] = (0,0,255)
res = data.reshape(img.shape)
cv.imshow('res',res)
cv.waitKey(0)
cap.release()
cv.destroyAllWindows()
k近鄰算法
一個監督學習算法 常用於對象的分類 根據近鄰的個數K 來找和物體最相近的K個目標 其中最多的類型及為物體當前類型
在 傳統的機器學習算法在Opencv4中實現類似於特征點檢測算法的實現 先定義一個base的機器學習類
每個方法的具體實現繼承這個base類實現
k近鄰算法是使用的是KNearest類 繼承了StatModel類(base類)
StatModel類 方法:
訓練函數
ret = cv.ml_StatModel.train(samples,layout,responses)
samples: 訓練的樣本矩陣
layout: 排列方式
responses: 標簽矩陣
返還一個bool類型變量來作為是否完成了模型訓練
samples 必須為float32類型
layout cv.ml.ROW_SAMPLE 樣本按行排列
cv.ml.COL_SAMPLE 按列排列
responses: 單行或者單列的矩陣 類型為int 或者 float
檢測函數
retval, res = cv.ml_StatModel.predict(samples)
flags模型標志
res 結果矩陣
samples 輸入矩陣
retval: 第一個值得標簽
下面提供了一個有5000個手寫數字的圖片
每個手寫數字占20 * 20
大小為 2000 * 1000 100 * 50 一行50張 一共100行
首先將圖片轉為
5000 * 20的矩陣
再創建一個
5000 * 1 的矩陣存儲數值標簽
將訓練的模型保存為 一個 .yml文件
# np.hsplit 按列切分每n列變為新的一行 每列對應為新的元素
# np.vsplit 按行切份每n行變成新的一行
模型的訓練
import cv2 as cv
# 導入數據分割函數 引入這個包 為了分割出數據集和測試集
from sklearn.model_selection import train_test_split
# 1000 * 2000
import numpy as np
img = cv.imread('digit.png')
# 1000 // 50
print(img.shape)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# gray先 分為 50 行 一行有100個數字 將100個數字分開 [
# [數字1的第一列][數字1的第二列][。。]..[。。]
# [數字1的第一列][..][..]..[..]
#
# [數字2的第一列][][]。。
# ]
cells = [np.hsplit(row, 100) for row in np.vsplit(gray, 50)]
x = np.array(cells).astype(np.float32)
# 將每一行的圖片合並 鋪平
all_data = x.reshape(-1, 400)
cv.imwrite('all_data.jpg', all_data)
print(all_data.shape)
# np.repeat()用於將numpy數組重復。
# numpy.repeat(a, repeats, axis=None);
# 參數:
# axis=0,沿着y軸復制,實際上增加了行數
# axis=1,沿着x軸復制,實際上增加了列數
k = np.arange(10)
all_label = np.repeat(k, 500).reshape(5000, 1)
cv.imwrite('all_label.jpg', all_label)
# 打散並分為測試集 數據集 保存
train_data, test_data, train_label, test_label = train_test_split(all_data, all_label, random_state=0)
# print(test_data)
cv.imwrite('train_data.jpg',train_data)
cv.imwrite('test_data.jpg',test_data)
cv.imwrite('train_label.jpg',train_label)
cv.imwrite('test_label.jpg',test_label)
# 生成k近鄰算法的對象
knn = cv.ml.KNearest_create()
# 設置近鄰數
knn.setDefaultK(5)
# 設置是否分類
knn.setIsClassifier(True)
model = knn.train(train_data, cv.ml.ROW_SAMPLE, train_label)
res = knn.save('knn_model.yml')
print('im ok')
# cv.waitKey(0)
模型保存函數:
None = cv.ml_StatModel.save(path)
將模型保存為 .yml文件
模型加載函數:
retval = cv.ml.KNearest_load(filepath)
加載knn的模型
返還的是KNearest對象
預測:
除了使用
StatModel提供的通用的預測方法
KNearest類也提供了預測方法
retval,results,neighborResponses,dist = cv.ml_KNearest.findNearest(
sample
k
)
sample: 待預測數據
k: 近鄰數
results: 預測結果
neighborResponses: 可以選擇輸出的每個數據的k個最近鄰
dist: 輸出k個最近鄰的距離
import cv2 as cv
import numpy as np
if __name__ == '__main__':
knn = cv.ml.KNearest_load('knn_model.yml')
test_data = cv.imread('test_data.jpg',cv.COLOR_BGR2GRAY).astype(np.float32)
test_label = cv.imread('test_label.jpg',cv.COLOR_BGR2GRAY)
print(test_data.shape)
_,res = knn.predict(test_data)
match = (test_label == res)
# 統計非0個數
match_num = np.count_nonzero(match)
accur = match_num / len(res)
print(accur)
識別率為 82%
決策樹算法
決策數算法也是一種對數據進行分類的算法
主要思想是通過構建樹狀結構來對數據進行分類
Opencv中的DTrees類
retval = cv.ml.DTrees_create()
retval = cv.ml.DTrees_load()
模型參數:
1. setMaxDepth() 必須 樹最大深度 輸入參數為正整數
2. setCVFolds() 必須 交叉驗證 一般為0
3. setUseSurrogates() 非必須 是否建立代替分裂點 輸入 bool
4. setMinSampleCount() 非必須 節點最小樣本數 當樣本數量過小 則不細分
5. setUselSERule() 非必須 表示是否嚴格剪枝
6. setTruncatePrunedTree() 非必須 分支是否完全移除
import cv2 as cv
import numpy as np
# 讀入訓練集
train_data = cv.imread('train_data.jpg',cv.COLOR_BGR2GRAY).astype(np.float32)
train_label = cv.imread('train_label.jpg',cv.COLOR_BGR2GRAY).astype(np.int32)
# 讀入測試集
test_data = cv.imread('test_data.jpg',cv.COLOR_BGR2GRAY).astype(np.float32)
test_label = cv.imread('test_label.jpg',cv.COLOR_BGR2GRAY).astype(np.int32)
# print(test_label)
dt = cv.ml.DTrees_create()
dt.setMaxDepth(10)
dt.setCVFolds(0)
# 訓練保存模型
dt.train(train_data,cv.ml.ROW_SAMPLE,train_label)
dt.save('dt_model.yml')
# 得到結果
_,res = dt.predict(test_data)
print(res)
# 計算成功率
match = res == test_label
match_num = np.count_nonzero(match)
print('識別成功率為:{}'.format(match_num / len(test_label)))
# print(test_label)
隨機森林算法
決策樹在使用時僅僅構建一棵樹 這樣容易出現過擬合現象 可以通過構建多個決策樹來避免過擬合現象
當構建多個決策樹時 就出現了隨機森林
這種方法通過多個決策樹的投票來得到在最終的結果
RTrees類:
setRegressionAccuracy() 非必須 回歸算法的精度
setPriors() 非必須 數據類型
setCalculateVarImportance() 非必須 是否要計算var
setActiveVarCount() 非必須 設置var的數目
import cv2 as cv
import numpy as np
train_data = cv.imread('train_data.jpg',cv.COLOR_BGR2GRAY).astype(np.float32)
train_label = cv.imread('train_label.jpg',cv.COLOR_BGR2GRAY).astype(np.int32)
test_data = cv.imread('test_data.jpg',cv.COLOR_BGR2GRAY).astype(np.float32)
test_label = cv.imread('test_label.jpg',cv.COLOR_BGR2GRAY).astype(np.int32)
# print(test_label)
rt = cv.ml.RTrees_create()
rt.setMaxDepth(10)
rt.setCVFolds(0)
rt.train(train_data,cv.ml.ROW_SAMPLE,train_label)
rt.save('dt_model.yml')
_,res = rt.predict(test_data)
print(res)
match = res == test_label
match_num = np.count_nonzero(match)
print('識別成功率為:{}'.format(match_num / len(test_label)))
# print(test_label)
支持向量機
也是一種分類器 可以將不同的樣本通過超平面分割在不同的區域
在直線分割樣本時 如果直線距離樣本太近 則會使直線容易受到噪聲的影響
具有較差的泛化性 因此 尋找最佳分割直線就是尋找一條與所有點最遠的直線
在支持向量機中 兩條直線的間距稱為間隔
retval = cv.ml.SVM_create()
全部非必須
setKernel() 設置核函數模型
setType() 設置SVM類型
setTermCriteria() 迭代停止條件
setGamma() 設置算法中的γ參數
setC() 設置算法中的c變量
setP() 設置e變量
setNu 設置v變量
setDegree 設置核函數度數
核函數的可選參數
cv.ml.SVM_LINEAR 0 線性核函數
cv.ml.SVM_POLY 1 多項式核函數
cv.ml.SVM_LINEAR 2 徑向基函數
cv.ml.SVM_LINEAR 3 sigmodi核函數
cv.ml.SVM_LINEAR 4 指數卡方核函數
cv.ml.SVM_LINEAR 5 直方圖交叉核函數
import cv2 as cv
import numpy as np
train_data = cv.imread('train_data.jpg',cv.COLOR_BGR2GRAY).astype(np.float32)
train_label = cv.imread('train_label.jpg',cv.COLOR_BGR2GRAY).astype(np.int32)
test_data = cv.imread('test_data.jpg',cv.COLOR_BGR2GRAY).astype(np.float32)
test_label = cv.imread('test_label.jpg',cv.COLOR_BGR2GRAY).astype(np.int32)
# print(test_label)
svm = cv.ml.SVM_create()
svm.setKernel(1)
svm.setType(cv.ml.SVM_C_SVC)
svm.setDegree(3)
svm.train(train_data,cv.ml.ROW_SAMPLE,train_label)
svm.save('svm_model.yml')
_,res = svm.predict(test_data)
print(res)
match = res == test_label
match_num = np.count_nonzero(match)
print('識別成功率為:{}'.format(match_num / len(test_label)))
# print(test_label)