這是我的本科畢設(shè)題目,剛開始接觸機器學(xué)習(xí)這方面,感謝CSDN和GitHub上的大佬,網(wǎng)上類似項目很多,方法也有很多,自己順帶進行了整理,邊做畢設(shè)邊分享一下自己學(xué)習(xí)心得吧,也算是梳理一下所學(xué)知識,各大佬有什么好的建議還請指出,不吝賜教。
項目簡介:基于Win10 + Python3.7的環(huán)境,利用Python的OpenCV、Sklearn和PyQt5等庫搭建了一個較為完整的手勢識別系統(tǒng),用于識別日常生活中1-10的靜態(tài)手勢。
整個項目的資源:https://download.csdn.net/download/qq_41562704/11471042(包含手勢庫和已訓(xùn)練的模型,可以直接運行使用)
環(huán)境:Win10 + Python3.7 + OpenCV3.4.5,各個庫的安裝就不多說了
最終的效果圖如圖所示:
?
整個項目分為四個部分,即預(yù)處理,特征提取,模型訓(xùn)練,界面設(shè)計
預(yù)處理
1.獲取手勢
2.圖像預(yù)處理
2.1去噪
2.2 膚色檢測 + 二值化處理
2.3 形態(tài)學(xué)處理
2.4 輪廓提取
特征提取
3 傅里葉算子提取
4 建立特征庫
4.1 數(shù)據(jù)增強
4.2 計算手勢庫的特征
模型訓(xùn)練
5 訓(xùn)練SVM模型
界面設(shè)計
6 PyQt設(shè)計界面
預(yù)處理
這部分需要完成攝像頭錄制手勢后,提取出手的輪廓線
這部分參考資料:
https://blog.csdn.net/ifruoxi/article/details/78091954(獲取手勢,基于Python)
https://blog.csdn.net/qq_22527639/article/details/81501565(膚色檢測:方法全,理論介紹的也很全面,基于C++)
https://blog.csdn.net/shadow_guo/article/details/43602051(基于RGB空間膚色檢測,基于Python)
https://blog.csdn.net/weixin_40893939/article/details/84527037(基于HSV空間和YCrCb空間膚色檢測,基于Python)
https://blog.csdn.net/Eastmount/article/details/83581277(腐蝕膨脹理論介紹,基于Python)
https://blog.csdn.net/dz4543/article/details/80655067(輪廓提取,基于Python)
1.獲取手勢
主要是調(diào)用OpenCV,創(chuàng)建main.py和 picture.py
main.py 當前負責錄像,picture負責處理圖像
main.py
import cv2import picture as pic font = cv2.FONT_HERSHEY_SIMPLEX size = 0.5 width, height = 300, 300 x0,y0 = 300, 100 cap = cv2.VideoCapture(0) if __name__ == "__main__": while(1):ret, frame = cap.read() frame = cv2.flip(frame, 2)roi = pic.binaryMask(frame, x0, y0, width, height) key = cv2.waitKey(1) & 0xFF if key == ord('i'):y0 += 5 elif key == ord('k'):y0 -= 5 elif key == ord('l'):x0 += 5 elif key == ord('j'):x0 -= 5 if key == ord('q'): breakcv2.imshow('frame', frame) cap.release()cv2.destroyAllWindows()
2.圖像預(yù)處理
預(yù)處理在picture.py中完成。
預(yù)處理的主要步驟為:去噪 -> 膚色檢測 -> 二值化 -> 形態(tài)學(xué)處理?-> 輪廓提取,其中最麻煩的兩項為膚色檢測和輪廓提取。
2.1去噪
即濾波,主要是為了實現(xiàn)對圖像噪聲的消除,增強圖像的效果,其實個人感覺這里濾波的作用不是很明顯,也可以選擇不濾波,在膚色檢測后會有二次濾波。
blur = cv2.blur(roi, (3,3))blur = cv2.GaussianBlur(roi, (3,3), 0)blur = cv2.medianBlur(roi,5)blur = cv2.bilateralFilter(img,9,75,75)
均值濾波器、高斯濾波器、中值濾波器、雙邊濾波器都可以進行使用。推薦使用雙邊濾波器,該濾波器考慮了圖像的空間關(guān)系,也考慮圖像的灰度關(guān)系。雙邊濾波同時使用了空間高斯權(quán)重和灰度相似性高斯權(quán)重,確保了邊界不會被模糊掉。不過我在處理中直接省去了去噪這個過程。
2.2 膚色檢測 + 二值化處理
picture.py
方法一:基于RGB顏色空間
判斷條件:
在均勻光照下,R>95 AND G>40 B>20 AND MAX(R,G,B)-MIN(R,G,B)>15 AND ABS(R-G)>15 AND R>G AND R>B;
在側(cè)光拍攝環(huán)境下,R>220 AND G>210 AND B>170 AND ABS(R-G)<=15 AND R>B AND G>B
import cv2import numpy as np def binaryMask(frame, x0, y0, width, height):cv2.rectangle(frame,(x0,y0),(x0+width, y0+height),(0,255,0)) roi = frame[y0:y0+height, x0:x0+width] cv2.imshow("roi", roi) res = skinMask(roi) cv2.imshow("res", res) return res def skinMask(roi):rgb = cv2.cvtColor(roi, cv2.COLOR_BGR2RGB) (R,G,B) = cv2.split(rgb) skin = np.zeros(R.shape, dtype = np.uint8) (x,y) = R.shape for i in range(0, x): for j in range(0, y): if (abs(R[i][j] - G[i][j]) > 15) and (R[i][j] > G[i][j]) and (R[i][j] > B[i][j]): if (R[i][j] > 95) and (G[i][j] > 40) and (B[i][j] > 20) \ and (max(R[i][j],G[i][j],B[i][j]) - min(R[i][j],G[i][j],B[i][j]) > 15):skin[i][j] = 255 elif (R[i][j] > 220) and (G[i][j] > 210) and (B[i][j] > 170):skin[i][j] = 255res = cv2.bitwise_and(roi,roi, mask = skin) return res
效果圖:
方法二:基于HSV顏色空間
判斷條件:0<=H<=20,S>=48,V>=50
膚色檢測的方式不同影響的是skinMask,之后的代碼只是修改skinMask函數(shù),picture.py中其他代碼不需要改動。
def skinMask(roi):low = np.array([0, 48, 50]) high = np.array([20, 255, 255]) hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV) mask = cv2.inRange(hsv,low,high) res = cv2.bitwise_and(roi,roi, mask = mask) return res
效果圖:
方法三:橢圓膚色檢測模型
在YCrCb空間,膚色像素點會聚集到一個橢圓區(qū)域。先定義一個橢圓模型,然后將每個RGB像素點轉(zhuǎn)換到Y(jié)CrCb空間比對是否在橢圓區(qū)域,是的話判斷為皮膚。
def skinMask(roi):skinCrCbHist = np.zeros((256,256), dtype= np.uint8)cv2.ellipse(skinCrCbHist, (113,155),(23,25), 43, 0, 360, (255,255,255), -1) YCrCb = cv2.cvtColor(roi, cv2.COLOR_BGR2YCR_CB) (y,Cr,Cb) = cv2.split(YCrCb) skin = np.zeros(Cr.shape, dtype = np.uint8) (x,y) = Cr.shape for i in range(0, x): for j in range(0, y): if skinCrCbHist [Cr[i][j], Cb[i][j]] > 0: skin[i][j] = 255res = cv2.bitwise_and(roi,roi, mask = skin) return res
效果圖:
?
方法四:YCrCb顏色空間的Cr分量+Otsu法閾值分割算法
針對YCrCb中Cr分量的處理,對CR通道單獨進行Otsu處理,Otsu方法opencv里用threshold,Otsu算法是對圖像的灰度級進行聚類。
def skinMask(roi):YCrCb = cv2.cvtColor(roi, cv2.COLOR_BGR2YCR_CB) (y,cr,cb) = cv2.split(YCrCb) cr1 = cv2.GaussianBlur(cr, (5,5), 0)_, skin = cv2.threshold(cr1, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) res = cv2.bitwise_and(roi,roi, mask = skin) return res
效果圖:
方法五:Cr,Cb范圍篩選法
該方法與方法一、二類似,不同的只是顏色空間不相同
判斷條件:133<=Cr<=173 77<=Cb<=127
def skinMask(roi):YCrCb = cv2.cvtColor(roi, cv2.COLOR_BGR2YCR_CB) (y,cr,cb) = cv2.split(YCrCb) skin = np.zeros(cr.shape, dtype = np.uint8)(x,y) = cr.shape for i in range(0, x): for j in range(0, y): if(cr[i][j] > 130) and (cr[i][j] < 175) and (cb[i][j] > 77) and (cb[i][j] < 127):skin[i][j] = 255res = cv2.bitwise_and(roi,roi, mask = skin) return res
效果圖:
方法六:OpenCV自帶AdaptiveSkinDetector
關(guān)于該函數(shù)的使用可以參考http://www.cnblogs.com/tornadomeet/archive/2012/11/20/2778740.html
最終方案選擇:在幾種方式中選擇效果比較好的,RGB和HSV的效果一般,而且曝光的話,效果更差,YCrCb是一個單獨把亮度分離開來的顏色模型,使用這個顏色模型的話,像膚色不會受到光線亮度而發(fā)生改變,方法三和四均可。
2.3 形態(tài)學(xué)處理
即便是比較好的膚色檢測算法,分割出來的手勢,也難免有黑點,或者背景有白點,這時候需要對分割出來的手勢圖進行進一步處理,主要是腐蝕膨脹兩個操作。
腐蝕和膨脹是針對白色部分(高亮部分而言)。從數(shù)學(xué)角度來說,膨脹或者腐蝕操作就是將圖像(或圖像的一部分區(qū)域,稱之為A)與核(稱之為B)進行卷積。?
膨脹就是求局部最大值操作,即計算核B覆蓋的區(qū)域的像素點的最大值,并把這個最大值賦值給參考點指定的像素,這樣就會使圖像中的高亮區(qū)域逐漸增長。?
腐蝕就是求局部最小值操作,即計算核B覆蓋的區(qū)域的像素點的最小值,并把這個最小值賦值給參考點指定的像素,這樣就會使圖像中的高亮區(qū)域逐漸減少。?
開運算:先腐蝕后膨脹,去除孤立的小點,毛刺
閉運算:先膨脹后腐蝕,填平小孔,彌合小裂縫
在binaryMask函數(shù)中return前面添加以下代碼,進行開運算
kernel = np.ones((3,3), np.uint8) erosion = cv2.erode(res, kernel) cv2.imshow("erosion",erosion)dilation = cv2.dilate(erosion, kernel)cv2.imshow("dilation",dilation)
效果如圖:
可以看到背景雜質(zhì)點去掉了
2.4 輪廓提取
在binaryMask函數(shù)中return前面添加以下代碼,對膚色檢測后的圖像提取手勢區(qū)域
binaryimg = cv2.Canny(res, 50, 200) h = cv2.findContours(binaryimg,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE) contours = h[1] ret = np.ones(res.shape, np.uint8) cv2.drawContours(ret,contours,-1,(255,255,255),1) cv2.imshow("ret", ret)
特征提取
這部分主要任務(wù)是對第一部分提取的輪廓點坐標提取出他們的傅里葉描述子,建立手勢特征庫
參考資料:
https://github.com/timfeirg/Fourier-Descriptors(提取特征代碼比較完整)
https://github.com/alessandroferrari/elliptic-fourier-descriptors(橢圓傅里葉描述子的提取)
https://www.cnblogs.com/edie0902/p/3658174.html(傅里葉算子的數(shù)學(xué)思想)
3 傅里葉算子提取
將picture.py中的提取輪廓點部分刪去,添加
import fourierDescriptor as fdret, fourier_result = fd.fourierDesciptor(res)
創(chuàng)建fourierDescriptor.py
在這個文件中完成對輪廓點坐標的傅里葉描述子的提取,具體代碼如下:
import cv2import numpy as np MIN_DESCRIPTOR = 32 def fourierDesciptor(res): gray = cv2.cvtColor(res, cv2.COLOR_BGR2GRAY)dst = cv2.Laplacian(gray, cv2.CV_16S, ksize = 3)Laplacian = cv2.convertScaleAbs(dst)contour = find_contours(Laplacian)contour_array = contour[0][:, 0, :]ret_np = np.ones(dst.shape, np.uint8) ret = cv2.drawContours(ret_np,contour[0],-1,(255,255,255),1) contours_complex = np.empty(contour_array.shape[:-1], dtype=complex)contours_complex.real = contour_array[:,0]contours_complex.imag = contour_array[:,1]fourier_result = np.fft.fft(contours_complex) descirptor_in_use = truncate_descriptor(fourier_result) return ret, descirptor_in_use def find_contours(Laplacian): h = cv2.findContours(Laplacian,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE) contour = h[1]contour = sorted(contour, key = cv2.contourArea, reverse=True) return contour def truncate_descriptor(fourier_result):descriptors_in_use = np.fft.fftshift(fourier_result) center_index = int(len(descriptors_in_use) / 2)low, high = center_index - int(MIN_DESCRIPTOR / 2), center_index + int(MIN_DESCRIPTOR / 2)descriptors_in_use = descriptors_in_use[low:high] descriptors_in_use = np.fft.ifftshift(descriptors_in_use) return descriptors_in_use def reconstruct(img, descirptor_in_use): contour_reconstruct = np.fft.ifft(descirptor_in_use)contour_reconstruct = np.array([contour_reconstruct.real,contour_reconstruct.imag])contour_reconstruct = np.transpose(contour_reconstruct)contour_reconstruct = np.expand_dims(contour_reconstruct, axis = 1) if contour_reconstruct.min() < 0:contour_reconstruct -= contour_reconstruct.min()contour_reconstruct *= img.shape[0] / contour_reconstruct.max()contour_reconstruct = contour_reconstruct.astype(np.int32, copy = False) black_np = np.ones(img.shape, np.uint8) black = cv2.drawContours(black_np,contour_reconstruct,-1,(255,255,255),1) cv2.imshow("contour_reconstruct", black) return black
這里需要注意:
輪廓提取后進行了二次去噪,即只保留區(qū)域面積最大的曲線,效果如圖
其次關(guān)于利用傅里葉算子重建輪廓圖,在實際使用過程中并不需要,僅僅為了在測試階段檢驗效果
取32項傅里葉算子的時候,重建效果如下,基本可以還原手勢形狀。
4 建立特征庫
這個部分的任務(wù)是采集手勢1-10,同時利用旋轉(zhuǎn)平移等操作對得到的手勢庫進行擴充。然后對整個手勢庫中的每張照片中的手勢輪廓線計算傅里葉描述子并保存。我采取的方案是每個手勢采集20份,然后擴充為200份,這里的數(shù)目可以自己調(diào)節(jié)。保存格式為"x_i",表示手勢_x的第i張圖片
4.1 數(shù)據(jù)增強
在數(shù)據(jù)增強前,先采集手勢,在項目文件夾中創(chuàng)建一個“image”文件夾保存樣本庫,"test_image"保存測試庫(看需求,可以不用)
創(chuàng)建data_augmention.py
對測試庫進行操作的時候僅僅需要修改一下path及相關(guān)的數(shù)字
import randomimport cv2path = './' + 'image' + '/' def rotate(image, scale=0.9):angle = random.randrange(-90, 90)w = image.shape[1]h = image.shape[0] M = cv2.getRotationMatrix2D((w/2,h/2), angle, scale) image = cv2.warpAffine(image,M,(w,h)) return image if __name__ == "__main__": for i in range(5, 6): cnt = 21 for j in range(1, 21):roi = cv2.imread(path + str(i) + '_' + str(j)+'.png') for k in range(12):img_rotation = rotate(roi)cv2.imwrite(path + str(i) + '_' + str(cnt)+ '.png',img_rotation)cnt += 1img_flip = cv2.flip(img_rotation,1)cv2.imwrite(path + str(i) + '_' + str(cnt)+ '.png',img_flip)cnt += 1print(i,'_',j,'完成')
4.2 計算手勢庫的特征
創(chuàng)建loadData.py文件
import fourierDescriptor as fdimport cv2import numpy as np path = './' + 'feature' + '/'path_img = './' + 'image' + '/' if __name__ == "__main__": for i in range(1, 11): for j in range(1, 201):roi = cv2.imread(path_img + str(i) + '_' + str(j) + '.png') descirptor_in_use = abs(fd.fourierDesciptor(roi)) fd_name = path + str(i) + '_' + str(j) + '.txt' with open(fd_name, 'w', encoding='utf-8') as f:temp = descirptor_in_use[1] for k in range(1, len(descirptor_in_use)):x_record = int(100 * descirptor_in_use[k] / temp)f.write(str(x_record))f.write(' ')f.write('\n')print(i, '_', j, '完成')
對手勢庫中10個手勢的200份圖片一一進行操作,保存的特征的格式與圖片格式一致,用txt文件進行保存
模型訓(xùn)練
這個部分的主要任務(wù)是利用已有的樣本庫訓(xùn)練SVM模型并保存
這部分參考資料:
https://cuijiahua.com/blog/2017/11/ml_8_svm_1.html(SVM的原理)
https://cuijiahua.com/blog/2017/11/ml_9_svm_2.html(sklearn的使用)
https://blog.csdn.net/qysh123/article/details/80063447(SVM調(diào)參)
5 訓(xùn)練SVM模型
使用網(wǎng)格搜索法進行調(diào)參,利用joblib模塊保存模型
import numpy as npfrom os import listdirfrom sklearn.externals import joblibfrom functools import reducefrom sklearn.svm import SVCfrom sklearn.model_selection import GridSearchCVimport matplotlib.pyplot as plt path = './' + 'feature' + '/'model_path = "./model/"test_path = "./test_feature/" test_accuracy = [] def txtToVector(filename, N):returnVec = np.zeros((1,N))fr = open(filename)lineStr = fr.readline()lineStr = lineStr.split(' ') for i in range(N):returnVec[0, i] = int(lineStr[i]) return returnVec def tran_SVM(N):svc = SVC()parameters = {'kernel':('linear', 'rbf'), 'C':[1, 3, 5, 7, 9, 11, 13, 15, 17, 19], 'gamma':[0.00001, 0.0001, 0.001, 0.1, 1, 10, 100, 1000]}hwLabels = []trainingFileList = listdir(path)m = len(trainingFileList)trainingMat = np.zeros((m,N)) for i in range(m):fileNameStr = trainingFileList[i]classNumber = int(fileNameStr.split('_')[0])hwLabels.append(classNumber)trainingMat[i,:] = txtToVector(path+fileNameStr,N)print("數(shù)據(jù)加載完成")clf = GridSearchCV(svc, parameters, cv=5, n_jobs=8)clf.fit(trainingMat,hwLabels)print(clf.return_train_score)print(clf.best_params_)best_model = clf.best_estimator_print("SVM Model save...")save_path = model_path + "svm_efd_" + "train_model.m"joblib.dump(best_model,save_path) def test_SVM(clf,N):testFileList = listdir(test_path)errorCount = 0mTest = len(testFileList) for i in range(mTest):fileNameStr = testFileList[i]classNum = int(fileNameStr.split('_')[0])vectorTest = txtToVector(test_path+fileNameStr,N)valTest = clf.predict(vectorTest) if valTest != classNum:errorCount += 1print("總共錯了%d個數(shù)據(jù)\n錯誤率為%f%%" % (errorCount, errorCount/mTest * 100)) if __name__ == "__main__":tran_SVM(31)clf = joblib.load(model_path + "svm_efd_" + "train_model.m")test_SVM(clf,31)
訓(xùn)練結(jié)果如圖:
界面設(shè)計
字面意思,這部分的任務(wù)就是設(shè)計一個界面可以實時調(diào)用已經(jīng)訓(xùn)練好的模型預(yù)測手勢
小聲嘀咕一句:原來Python有這么好用的寫界面的庫呀(。-ω-)zzz
這個部分也不是必須的,只不過為了稍微好看那么一點,可以直接修改main.py,使得按p的時候就進行預(yù)測
import classfier as cf...... elif key == ord('p'):descirptor_in_use = abs(fourier_result)fd_test = np.zeros((1,31))temp = descirptor_in_use[1] for k in range(1,len(descirptor_in_use)):fd_test[0,k-1] = int(100 * descirptor_in_use[k] / temp)test_svm = cf.test_fd(fd_test)print("test_svm =",test_svm)test_svm_efd = cf.test_efd(efd_test)print("test_svm_efd =",test_svm_efd)cv2.imshow('frame', frame)
這部分參考資料:
https://blog.csdn.net/niuyongjie/article/details/81161559(PyQt安裝,我安裝的是5.11.3版本)
http://code.py40.com/pyqt5/16.html(PyQt教程)
6 PyQt設(shè)計界面
創(chuàng)建myGUI.py文件
第一次用PyQt這個庫,所以尺寸方面的控制用來最古老的方式(數(shù)字控制)還請大家見諒(╥╯^╰╥)
import sysfrom PyQt5.QtWidgets import QApplication, QWidget, QToolTip, \QPushButton,QMessageBox,QDesktopWidget, QLabelfrom PyQt5.QtGui import QFont,QIcon,QPixmap,QImagefrom PyQt5.QtCore import QTimerimport cv2import picture as picimport classify as cfimport numpy as np class myWindow(QWidget): def __init__(self,parent = None):super(myWindow,self).__init__(parent) self.timer_camera = QTimer()self.cap = cv2.VideoCapture()self.initUI()self.slot_init() def initUI(self): self.mylabel()self.myButton()self.myLabelPic() self.setFixedSize(670,520)self.center()self.setWindowIcon(QIcon('icon.jpg'))self.setWindowTitle('gesture recognition') def mylabel(self): label_roi = QLabel('原圖',self)label_roi.setStyleSheet("QLabel{font-size:18px;}")label_roi.resize(60,30)label_roi.move(120,15) label_res = QLabel('輪廓線', self)label_res.setStyleSheet("QLabel{font-size:18px;}")label_res.resize(60, 30)label_res.move(480, 15) label_pre = QLabel('預(yù)測', self)label_pre.setStyleSheet("QLabel{font-size:20px;}")label_pre.resize(50,30)label_pre.move(400,400) label_result = QLabel('結(jié)果', self)label_result.setStyleSheet("QLabel{font-size:20px;}")label_result.resize(50, 30)label_result.move(400,430) def myLabelPic(self):self.label_show_roi = QLabel(self)self.label_show_roi.setFixedSize(301,301)self.label_show_roi.move(20,50)self.label_show_roi.setStyleSheet("QLabel{background:white;}")self.label_show_roi.setAutoFillBackground(True) self.label_show_ret = QLabel(self)self.label_show_ret.setFixedSize(301, 301)self.label_show_ret.move(350, 50)self.label_show_ret.setStyleSheet("QLabel{background:white;}")self.label_show_ret.setAutoFillBackground(True) self.label_show_recognition = QLabel('0',self)self.label_show_recognition.setStyleSheet("QLabel{background:white;}")self.label_show_recognition.setStyleSheet("QLabel{font-size:50px;}")self.label_show_recognition.setFixedSize(100,100)self.label_show_recognition.move(500, 380)self.label_show_recognition.setAutoFillBackground(True) def myButton(self):QToolTip.setFont(QFont('SansSerif', 10)) self.button_open_camera = QPushButton('打開相機', self)self.button_open_camera.setToolTip('按i,k,j,l可以進行上下左右調(diào)整')self.button_open_camera.resize(100,30)self.button_open_camera.move(100, 400) self.butoon_recognition = QPushButton('開始預(yù)測', self)self.butoon_recognition.setFixedSize(100, 30)self.butoon_recognition.move(100, 450) def slot_init(self):self.button_open_camera.clicked.connect(self.button_open_camera_click)self.butoon_recognition.clicked.connect(self.button_recognition_click)self.timer_camera.timeout.connect(self.show_camera) def button_open_camera_click(self): if self.timer_camera.isActive() == False:self.cap.open(0)self.timer_camera.start(30)self.button_open_camera.setText(u'關(guān)閉相機') else:self.timer_camera.stop()self.cap.release()self.label_show_roi.clear()self.label_show_ret.clear()self.label_show_recognition.setText('0')self.button_open_camera.setText(u'打開相機') def button_recognition_click(self):descirptor_in_use = abs(self.fourier_result)fd_test = np.zeros((1, 31))temp = descirptor_in_use[1] for k in range(1, len(descirptor_in_use)):fd_test[0, k - 1] = int(100 * descirptor_in_use[k] / temp)efd_test = np.zeros((1, 15)) for k in range(1, len(self.efd_result)):temp = np.sqrt(self.efd_result[k][0] ** 2 + self.efd_result[k][1] ** 2) + np.sqrt(self.efd_result[k][2] ** 2 + self.efd_result[k][3] ** 2)efd_test[0, k - 1] = (int(1000 * temp))test_knn, test_svm = cf.test_fd(fd_test)print("test_knn =", test_knn)print("test_svm =", test_svm)test_knn_efd, test_svm_efd = cf.test_efd(efd_test)print("test_knn_efd =", test_knn_efd)print("test_svm_efd =", test_svm_efd)num = [0]*11num[test_knn[0]] += 1num[test_svm[0]] += 1num[test_knn_efd[0]] += 1num[test_svm_efd[0]] += 1res = 0 for i in range(1, 11): if num[i] >= 2:res = i breakprint(res)self.label_show_recognition.setText(str(res)) def show_camera(self):width, height = 300, 300 x0, y0 = 300, 100 flag, frame = self.cap.read()roi, res, ret, self.fourier_result, self.efd_result = pic.binaryMask(frame, x0, y0, width, height)roi = cv2.cvtColor(roi, cv2.COLOR_BGR2RGB)show_roi = QImage(roi.data, roi.shape[1], roi.shape[0], QImage.Format_RGB888)show_ret = QImage(ret.data, ret.shape[1], ret.shape[0], QImage.Format_Grayscale8)self.label_show_roi.setPixmap(QPixmap.fromImage(show_roi))self.label_show_ret.setPixmap(QPixmap.fromImage(show_ret)) def closeEvent(self, QCloseEvent):reply = QMessageBox.question(self, 'Message',"Are you sure to quit?",QMessageBox.Yes |QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: if self.cap.isOpened():self.cap.release() if self.timer_camera.isActive():self.timer_camera.stop()QCloseEvent.accept() else:QCloseEvent.ignore() def center(self):qr = self.frameGeometry()cp = QDesktopWidget().availableGeometry().center()qr.moveCenter(cp)self.move(qr.topLeft()) if __name__ == "__main__":app = QApplication(sys.argv)win = myWindow()win.show()sys.exit(app.exec())
后面幾個部分由于時間關(guān)系,講得不如前面的詳細,向大家表示歉意,一些細節(jié)部分建議大家下載我的源碼來看。有問題的地方歡迎討論~源碼中另外也用了橢圓傅里葉描述子作為特征。
總結(jié)
以上是生活随笔為你收集整理的基于傅里叶算子的手势识别的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。