本文概述
利用SIFT特徵進行簡單的花朵識別
SIFT演算法的特點有:
- SIFT特徵是影象的區域性特徵,其對旋轉、尺度縮放、亮度變化保持不變性,對視角變化、仿射變換、噪聲也保持一定程度的穩定性;
- SIFT演算法提取的影象特徵點數不是固定值,維度是統一的128維。
- 獨特性(Distinctiveness)好,資訊量豐富,適用於在海量特徵資料庫中進行快速、準確的匹配;
- 多量性,即使少數的幾個物體也可以產生大量的SIFT特徵向量;
- 高速性,經優化的SIFT匹配演算法甚至可以達到實時的要求;
- 可擴充套件性,可以很方便的與其他形式的特徵向量進行聯合。
KMeans聚類獲得視覺單詞,構建視覺單詞詞典
現在得到的是所有影象的128維特徵,每個影象的特徵點數目還不一定相同(大多有差異)。現在要做的是構建一個描述影象的特徵向量,也就是將每一張影象的特徵點轉換為特徵向量。這兒用到了詞袋模型,詞袋模型源自文字處理,在這兒用在影象上,本質上是一樣的。詞袋的本質就是用一個袋子將所有維度的特徵裝起來,在這兒,詞袋模型的維度需要我們手動指定,這個維度也就確定了視覺單詞的聚類中心數。
將影象特徵點對映到視覺單詞上,得到影象的特徵向量:
熟悉聚類演算法的同學已經明白了,上面講的簇就是通過聚類演算法得到的,聚類演算法將類別相近,屬性相似的樣本框起來,是一種無監督學習演算法。在本文中,我使用了Kmeans演算法來聚類得到視覺單詞,通過聚類得到了聚類中心,通過聚類得到了表徵詞袋的特徵點。
我們得到k個聚類中心(一個聚類中心表徵了一個維度特徵,k由自己手動設定)和先前SIFT得到的所有圖片的特徵點,現在就是要通過這兩項來構造每一張影象的特徵向量。
在本文中,構造的思路跟簡單,就是比對特徵點與所有聚類中心的距離,將特徵點分配到距離最近的特徵項,比如經計算某特徵點距離leg這個聚類中心最近,那麼這個影象中leg這個特徵項+1。以此類推,每一張影象特徵向量也就構造完畢。
pickle讀取檔案
pickle可以儲存什麼型別的資料呢?
- 所有python支援的原生型別:布林值,整數,浮點數,複數,字串,位元組,None。
- 由任何原生型別組成的列表,元組,字典和集合。
- 函式,類,類的例項
pickle模組中常用的方法有:
1.pickle.dump(obj, file, protocol=None,)
必填引數obj表示將要封裝的物件
必填引數file表示obj要寫入的檔案物件,file必須以二進位制可寫模式開啟,即“wb”
可選引數protocol表示告知pickler使用的協議,支援的協議有0,1,2,3,預設的協議是新增在Python 3中的協議3
2.pickle.load(file,*,fix_imports=True,encoding="ASCII", errors="strict")
必填引數file必須以二進位制可讀模式開啟,即“rb”,其他都為可選引數
支援向量機
支援向量機的兩個引數(調參增大準確率)
SVM模型有兩個非常重要的引數C與gamma。其中 C是懲罰係數,即對誤差的寬容度。c越高,說明越不能容忍出現誤差,容易過擬合。C越小,容易欠擬合。C過大或過小,泛化能力變差
gamma是選擇RBF函式作為kernel後,該函式自帶的一個引數。隱含地決定了資料對映到新的特徵空間後的分佈,gamma越大,支援向量越少,gamma值越小,支援向量越多。支援向量的個數影響訓練與預測的速度。
此外大家注意RBF公式裡面的sigma和gamma的關係如下:
這裡面大家需要注意的就是gamma的物理意義,大家提到很多的RBF的幅寬,它會影響每個支援向量對應的高斯的作用範圍,從而影響泛化效能。我的理解:如果gamma設的太大,會很小,很小的高斯分佈長得又高又瘦, 會造成只會作用於支援向量樣本附近,對於未知樣本分類效果很差,存在訓練準確率可以很高,(如果讓無窮小,則理論上,高斯核的SVM可以擬合任何非線性資料,但容易過擬合)而測試準確率不高的可能,就是通常說的過訓練;而如果設的過小,則會造成平滑效應太大,無法在訓練集上得到特別高的準確率,也會影響測試集的準確率。
演算法流程:
SIFT提取每幅影象的特徵點:
import csv import os from sklearn.cluster import KMeans import cv2 import numpy as np def knn_detect(file_list,cluster_nums, randomState = None): content = [] input_x = [] labels=[] features = [] count = 0 csv_file = csv.reader(open(file_list,'r')) #讀取csv檔案 for line in csv_file: if(line[2] !='label'): subroot = 'F:\\train\\g' + str(line[2]) #路徑拼接成絕對路徑 filename =os.path.join(subroot, line[1]) sift = cv2.xfeatures2d.SIFT_create(200) img = cv2.imdecode(np.fromfile(filename,dtype=np.uint8),-1) #用np讀影象,避免了opencv讀取影象失敗的問題 if img is not None: gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) kpG, desG = sift.detectAndCompute(gray, None) #關鍵點描述符 if desG is None: #關鍵點不存在就結束迴圈 continue print(count) count+=1 ll = np.array(line[2]).flatten() labels.append(ll) #得到標籤 features.append(desG) #得到影象的特徵矩陣 input_x.extend(desG) print(len(input_x)) return features,labels,input_x #訓練集圖片處理 des_list = [] labelG = [] input_x1 = [] des_list,labelG,input_x1 = knn_detect('E:\\py\\train.csv',50) #測試機圖片處理 festures_test = [] labels_test = [] input_x2 = [] features_test,labels_test,input_x2 = knn_detect('E:\\py\\test.csv',50)
結果截圖:
儲存中間變數:
#利用pickle儲存中間變數 import numpy as np import matplotlib.pyplot as plt import pandas as pd import pickle output = open('input_x2.pkl', 'wb') pickle.dump(input_x2, output) output.close() output = open('features_test.pkl', 'wb') pickle.dump(features_test, output) output.close() output = open('labels_test.pkl', 'wb') pickle.dump(labels_test, output) output.close() output = open('input_x22.pkl', 'wb') pickle.dump(input_x2, output,protocol=2) output.close()
將影象特徵點對映到視覺單詞上,得到影象特徵:
df=open('input_x2.pkl','rb')#注意此處是rb #此處使用的是load(目標檔案) input_x1=pickle.load(df) df.close() kmeans = KMeans(n_clusters = 50, n_jobs =4,random_state = None).fit(input_x1) #開四個執行緒加快計算 centers = kmeans.cluster_centers_
將影象特徵點對映到視覺單詞上,得到影象的特徵向量:
def des2feature(des,num_words,centures): ''' des:單幅影象的SIFT特徵描述 num_words:視覺單詞數/聚類中心數 centures:聚類中心座標 計算特徵矩陣中特徵點離聚類中心最近的加1 return: feature vector 1*num_words ''' img_feature_vec=np.zeros([num_words]) for fi in des: diffMat = np.tile(fi, (50, 1)) - centers sqSum = (diffMat**2).sum(axis=1) dist = sqSum**0.5 sortedIndices = dist.argsort() idx = sortedIndices[0] # index of the nearest center #排序得到距離最近的聚類中心座標的索引 img_feature_vec[idx] += 1 return img_feature_vec def get_all_features(des_list,num_words): # 獲取所有圖片的特徵向量 allvec=np.zeros((len(des_list),num_words),'float32') for i in range(len(des_list)): if des_list[i].any()!=None: allvec[i]=des2feature(centures=centers,des=des_list[i],num_words=num_words) return allvec df=open('des_list.pkl','rb')#注意此處是rb #此處使用的是load(目標檔案) des_list=pickle.load(df) df.close() a =get_all_features(des_list,50) print(len(a)) output = open('特徵向量.pkl', 'wb') pickle.dump(a, output) output.close()
svm訓練,檢視準確率:
import numpy as np import matplotlib.pyplot as plt import pandas as pd import pickle from sklearn.preprocessing import StandardScaler df=open('label_train.pkl','rb')#注意此處是rb #此處使用的是load(目標檔案) label_train=pickle.load(df) df.close() df=open('label_test.pkl','rb')#注意此處是rb #此處使用的是load(目標檔案) label_test =pickle.load(df) df.close() sc = StandardScaler() X_train = sc.fit_transform(a) X_test = sc.fit_transform(b) from sklearn.svm import SVC classifier = SVC(C=15) #適當調整引數,不是一個固定值 classifier.fit(X_train,label_train) y_pred = classifier.predict(X_test) print(classifier.score(X_train,label_train)) from sklearn.metrics import classification_report print(classification_report(label_test,y_pred))
結果截圖: