實(shí)驗(yàn)環(huán)境:python 3.6 + opencv-python 3.4.14.51 建議使用 anaconda配置相同環(huán)境
背景
人臉識別步驟
圖1:人臉識別流程圖
人臉采集
采集人臉圖片的方法多種多樣,可以直接從網(wǎng)上下載數(shù)據(jù)集,可以從視頻中提取圖片,還可以從攝像頭實(shí)時(shí)的采集圖片。
人臉檢測方法
人臉檢測在實(shí)際中主要用于人臉識別的預(yù)處理,即在圖像中準(zhǔn)確標(biāo)定出人臉的位置和大小。人臉圖像中包含的模式特征十分豐富,如直方圖特征、顏色特征、模板特征、結(jié)構(gòu)特征及Haar特征等。人臉檢測就是把這其中有用的信息挑出來,并利用這些特征實(shí)現(xiàn)人臉檢測。
人臉圖像預(yù)處理
對于人臉的圖像預(yù)處理是基于人臉檢測結(jié)果,對圖像進(jìn)行處理并最終服務(wù)于特征提取的過程。系統(tǒng)獲取的原始圖像由于受到各種條件的限制和隨機(jī) 干擾,往往不能直接使用,必須在圖像處理的早期階段對它進(jìn)行灰度校正、噪聲過濾等圖像預(yù)處理。對于人臉圖像而言,其預(yù)處理過程主要包括人臉圖像的光線補(bǔ) 償、灰度變換、直方圖均衡化、歸一化、幾何校正、濾波以及銳化等。
人臉特征提取
人臉識別系統(tǒng)可使用的特征通常分為視覺特征、像素統(tǒng)計(jì)特征、人臉圖像變換系數(shù)特征、人臉圖像代數(shù) 特征等。人臉特征提取就是針對人臉的某些特征進(jìn)行的。人臉特征提取,也稱人臉表征,它是對人臉進(jìn)行特征建模的過程。人臉特征提取的方法歸納起來分為兩大 類:一種是基于知識的表征方法;另外一種是基于代數(shù)特征或統(tǒng)計(jì)學(xué)習(xí)的表征方法。
匹配與識別
提取的人臉圖像的特征數(shù)據(jù)與數(shù)據(jù)庫中存儲的特征模板進(jìn)行搜索匹配,通過設(shè)定一個(gè)閾值,當(dāng)相似度超過這一閾值,則把匹配得到的結(jié)果輸 出。人臉識別就是將待識別的人臉特征與已得到的人臉特征模板進(jìn)行比較,根據(jù)相似程度對人臉的身份信息進(jìn)行判斷。這一過程又分為兩類:一類是確認(rèn),是一對一 進(jìn)行圖像比較的過程,另一類是辨認(rèn),是一對多進(jìn)行圖像匹配對比的過程。
關(guān)于OpenCv
Opencv是一個(gè)開源的的跨平臺計(jì)算機(jī)視覺庫,內(nèi)部實(shí)現(xiàn)了圖像處理和計(jì)算機(jī)視覺方面的很多通用算法,對于python而言,在引用opencv庫的時(shí)候需要寫為import cv2。其中,cv2是opencv的C++命名空間名稱,使用它來表示調(diào)用的是C++開發(fā)的opencv的接口
目前人臉識別有很多較為成熟的方法,這里調(diào)用OpenCv庫,而OpenCV又提供了三種人臉識別方法,分別是LBPH方法、EigenFishfaces方法、Fisherfaces方法。本文采用的是LBPH(Local Binary Patterns Histogram,局部二值模式直方圖)方法。在OpenCV中,可以用函數(shù)cv2.face.LBPHFaceRecognizer_create()生成LBPH識別器實(shí)例模型,然后應(yīng)用cv2.face_FaceRecognizer.train()函數(shù)完成訓(xùn)練,最后用cv2.face_FaceRecognizer.predict()函數(shù)完成人臉識別。
CascadeClassifier,是Opencv中做人臉檢測的時(shí)候的一個(gè)級聯(lián)分類器。并且既可以使用Haar,也可以使用LBP特征。其中Haar特征是一種反映圖像的灰度變化的,像素分模塊求差值的一種特征。它分為三類:邊緣特征、線性特征、中心特征和對角線特征。
程序設(shè)計(jì)
人臉識別算法:
圖2:人臉識別模塊圖
1.準(zhǔn)備工作
圖3:準(zhǔn)備階段
首先讀取config文件,文件中第一行代表當(dāng)前已經(jīng)儲存的人名個(gè)數(shù),接下來每一行是二元組(id,name)即標(biāo)簽和對應(yīng)的人名 讀取結(jié)果存到以下兩個(gè)全局變量中。
id_dict = {}# 字典里存的是id——name鍵值對Total_face_num = 999# 已經(jīng)被識別有用戶名的人臉個(gè)數(shù),復(fù)制代碼
def init(): # 將config文件內(nèi)的信息讀入到字典中
加載人臉檢測分類器Haar,并準(zhǔn)備好識別方法LBPH方法
# 加載OpenCV人臉檢測分類器Haarface_cascade = cv2.CascadeClassifier(“haarcascade_frontalface_default.xml”)# 準(zhǔn)備好識別方法LBPH方法recognizer = cv2.face.LBPHFaceRecognizer_create()復(fù)制代碼
然后打開標(biāo)號為0的攝像頭
camera = cv2.VideoCapture(0)# 攝像頭success, img = camera.read()# 從攝像頭讀取照片復(fù)制代碼
2.錄入新面容
圖4:錄入人臉
2.1采集面容
創(chuàng)建文件夾data用于儲存本次從攝像頭采集到的照片,每次調(diào)用前先清空這個(gè)目錄。
然后是一個(gè)循環(huán),循環(huán)次數(shù)為需要采集的樣本數(shù),攝像頭拍攝取樣的數(shù)量,越多效果越好,但獲取以及訓(xùn)練的越慢。
循環(huán)內(nèi)調(diào)用camera.read()返回值賦給全局變量success,和img 用于在GUI中實(shí)時(shí)顯示。
然后調(diào)用cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)用于將采集到的圖片轉(zhuǎn)為灰度圖片減少計(jì)算量。
然后利用加載好的人臉分類器將每一幀攝像頭記錄的數(shù)據(jù)帶入OpenCv中,讓Classifier判斷人臉。
# 其中g(shù)ray為要檢測的灰度圖像,1.3為每次圖像尺寸減小的比例,5為minNeighborsfaces = face_cascade.detectMultiScale(gray, 1.3, 5)復(fù)制代碼
faces為在img圖像中檢測到的人臉,然后利用cv2.rectangle在人臉一圈畫個(gè)矩形。并把含有人臉的區(qū)域儲存進(jìn)入data文件夾 注意這里寫入時(shí),每個(gè)圖片的標(biāo)簽時(shí)Total_face_num即當(dāng)前共有多少個(gè)可識別用戶(在錄入之前加一),亦即當(dāng)前用戶的編號
cv2.rectangle(img, (x, y), (x + w, y + w), (255, 0, 0)) cv2.imwrite(“./data/User.” + str(T) + ‘.’ + str(sample_num) + ‘.jpg’, gray[y:y + h, x:x + w])復(fù)制代碼
然后在循環(huán)末尾最后打印一個(gè)進(jìn)度條,用于提示采集圖像的進(jìn)度 主要原理就是每次輸出不換行并且將光標(biāo)移動到當(dāng)前行的開頭,輸出內(nèi)容根據(jù)進(jìn)度不斷變化即可,同時(shí)在控件的提示框也輸出進(jìn)度信息
print(“r” + “%{:.1f}”.format(sample_num / pictur_num * 100) + “=” * l + “->” + “_” * r, end=””)var.set(“%{:.1f}”.format(sample_num / pictur_num * 100))# 控件可視化進(jìn)度信息window.update()# 刷新控件以實(shí)時(shí)顯示進(jìn)度復(fù)制代碼
2.2訓(xùn)練識別器
讀取data文件夾,讀取照片內(nèi)的信息,得到兩個(gè)數(shù)組,一個(gè)faces存的是所有臉部信息、一個(gè)ids存的是faces內(nèi)每一個(gè)臉部對應(yīng)的標(biāo)簽,然后將這兩個(gè)數(shù)組傳給 recog.train用于訓(xùn)練
# 訓(xùn)練模型#將輸入的所有圖片轉(zhuǎn)成四維數(shù)組recog.train(faces, np.array(ids))復(fù)制代碼
訓(xùn)練完畢后保存訓(xùn)練得到的識別器到.yml文件中,文件名為人臉編號+.yml
recog.save(str(Total_face_num) + “.yml”)復(fù)制代碼
2.3修改配置文件
每一次訓(xùn)練結(jié)束都要修改配置文件,具體要修改的地方是第一行和最后一行。 第一行有一個(gè)整數(shù)代表當(dāng)前系統(tǒng)已經(jīng)錄入的人臉的總數(shù),每次修改都加一。這里修改文件的方式是先讀入內(nèi)存,然后修改內(nèi)存中的數(shù)據(jù),最后寫回文件。
f = open(‘config.txt’, ‘r+’)flist = f.readlines()flist[0] = str(int(flist[0]) + 1) + ” “f.close()f = open(‘config.txt’, ‘w+’)f.writelines(flist)f.close()復(fù)制代碼
還要在最后一行加入一個(gè)二元組用以標(biāo)識用戶。 格式為:標(biāo)簽+空格+用戶名+空格,用戶名默認(rèn)為Userx(其中x標(biāo)識用戶編號)
f.write(str(T) + ” User” + str(T) + ” “)復(fù)制代碼
3.人臉識別(刷臉)
圖5:刷臉流程圖
由于這里采用多個(gè).yml文件來儲存識別器(實(shí)際操作時(shí)儲存在一個(gè)文件中識別出錯(cuò)所以采用這種方式),所以在識別時(shí)需要遍歷所有的.yml文件,如果每一個(gè)都不能識別才得出無法識別的結(jié)果,相反只要有一個(gè)可以識別當(dāng)前對象就返回可以識別的結(jié)果。而對于每一個(gè)文件都識別十次人臉,若成功五次以上則表示最終結(jié)果為可以識別,否則表示當(dāng)前文件無法識別這個(gè)人臉。
識別過程中在GUI的控件中實(shí)時(shí)顯示拍攝到的內(nèi)容,并在人臉周圍畫一個(gè)矩形框,并根據(jù)識別器返回的結(jié)果實(shí)時(shí)顯示在矩形框附近。
idnum, confidence = recognizer.predict(gray[y:y + h, x:x + w])# 加載一個(gè)字體用于輸出識別對象的信息font = cv2.FONT_HERSHEY_SIMPLEX# 輸出檢驗(yàn)結(jié)果以及用戶名cv2.putText(img, str(user_name), (x + 5, y – 5), font, 1, (0, 0, 255), 1)cv2.putText(img, str(confidence), (x + 5, y + h – 5), font, 1, (0, 0, 0), 1)復(fù)制代碼
多線程:
程序的兩個(gè)功能之間可以獨(dú)立運(yùn)行,就需要采用多線程的方法,但當(dāng)遇到臨界資源的使用時(shí),多個(gè)進(jìn)程/線程之間就要互斥的訪問以免出錯(cuò),本程序中具體的設(shè)計(jì)方法: 本程序采用多線程的方法實(shí)現(xiàn)并行。 程序的三個(gè)按鈕對應(yīng)著三個(gè)功能,分別是錄入人臉、人臉檢測、退出程序。 由于程序中的用戶界面是利用python中的tkinter庫做的,其按鈕的響應(yīng)函數(shù)用command指出,所以這里在每個(gè)command跳轉(zhuǎn)到的函數(shù)中設(shè)置多線程,每敲擊一次就用threading.Thread創(chuàng)建一個(gè)新的線程,然后在新的線程的處理函數(shù)target中實(shí)現(xiàn)按鈕原本對應(yīng)的功能。
p = threading.Thread(target=f_scan_face_thread)復(fù)制代碼
在涉及到攝像頭的訪問時(shí),線程之間需要互斥的訪問,所以設(shè)置了一個(gè)全局的變量system_state_lock 來表示當(dāng)前系統(tǒng)的狀態(tài),用以實(shí)現(xiàn)帶有優(yōu)先級的互斥鎖的功能。 鎖狀態(tài)為0表示攝像頭未被使用,1表示正在刷臉,2表示正在錄入新面容。 程序在實(shí)際執(zhí)行的過程中如果狀態(tài)為0,則無論是刷臉還是錄入都能順利執(zhí)行,如果狀態(tài)為1表示正在刷臉,如果此時(shí)敲擊刷臉按鈕則,系統(tǒng)會提示正在刷臉并拒絕新的請求,如果此時(shí)敲擊錄入面容按鈕,由于錄入面容優(yōu)先級比刷臉高,所以原刷臉線程會被阻塞,
global system_state_lockwhile system_state_lock == 2:# 如果正在錄入新面孔就阻塞pass復(fù)制代碼
新的錄入面容進(jìn)程開始執(zhí)行并修改系統(tǒng)狀態(tài)為2,錄入完成后狀態(tài)變?yōu)樵瓲顟B(tài),被阻塞的刷臉進(jìn)程繼續(xù)執(zhí)行,錄入人臉線程剛執(zhí)行完錄入階段現(xiàn)在正在訓(xùn)練,此時(shí)有兩個(gè)線程并行,以此來保證訓(xùn)練數(shù)據(jù)的同時(shí)不影響系統(tǒng)的使用。
對于退出的功能,直接在函數(shù)內(nèi)調(diào)用exit(),但是python的線程會默認(rèn)等待子線程全部結(jié)束再退出,所以用p.setDaemon(True)將線程設(shè)置為守護(hù)線程,這樣在主線程退出之后其它線程也都退出從而實(shí)現(xiàn)退出整個(gè)程序的功能。
GUI設(shè)計(jì):
程序采用python中的tkinter庫做可視化,優(yōu)點(diǎn)是占用資源小、輕量化、方便。
- 首先創(chuàng)建一個(gè)窗口命名為window然后設(shè)置其大小和標(biāo)題等屬性。
- 然后在界面上設(shè)定一個(gè)綠底的標(biāo)簽,類似于一個(gè)提示窗口的作用
- 然后分別創(chuàng)建三個(gè)按鈕,并設(shè)置響應(yīng)函數(shù)和提示字符,放置在window內(nèi)部。
- 然后設(shè)置一個(gè)label類型的控件用于動態(tài)的展示攝像頭的內(nèi)容(將攝像頭顯示嵌入到控件中)。具體方法:創(chuàng)建video_loop()函數(shù),在函數(shù)內(nèi)訪問全局的變量img,img是從攝像頭讀取到的圖像數(shù)據(jù)。然后把img顯示在label內(nèi)。 使用window.after方法,在給定時(shí)間后調(diào)用函數(shù)一次,實(shí)現(xiàn)固定時(shí)間刷新控件,從而達(dá)到實(shí)時(shí)顯示攝像頭畫面在GUI中的效果。
window.after(1, video_loop)# 這句的意思是一秒以后執(zhí)行video_loop函數(shù)# 因?yàn)檫@一句是寫在video_loop函數(shù)中的所以每過一秒函數(shù)執(zhí)行一次。復(fù)制代碼
運(yùn)行測試
說明
測試環(huán)境:python 3.6 + opencv-python 3.4.14.51 需要的包:
圖6:需要的包
錄入人臉
從數(shù)據(jù)集錄入
從攝像頭錄入
人臉識別
代碼實(shí)現(xiàn):
# 實(shí)驗(yàn)環(huán)境:python 3.6 + opencv-python 3.4.14.51import cv2import numpy as npimport osimport shutilimport threadingimport tkinter as tkfrom PIL import Image, ImageTk# 首先讀取config文件,第一行代表當(dāng)前已經(jīng)儲存的人名個(gè)數(shù),接下來每一行是(id,name)標(biāo)簽和對應(yīng)的人名id_dict = {}# 字典里存的是id——name鍵值對Total_face_num = 999# 已經(jīng)被識別有用戶名的人臉個(gè)數(shù),def init():# 將config文件內(nèi)的信息讀入到字典中f = open(‘config.txt’)global Total_face_numTotal_face_num = int(f.readline())for i in range(int(Total_face_num)):line = f.readline()id_name = line.split(‘ ‘)id_dict[int(id_name[0])] = id_name[1]f.close()init()# 加載OpenCV人臉檢測分類器Haarface_cascade = cv2.CascadeClassifier(“haarcascade_frontalface_default.xml”)# 準(zhǔn)備好識別方法LBPH方法recognizer = cv2.face.LBPHFaceRecognizer_create()# 打開標(biāo)號為0的攝像頭camera = cv2.VideoCapture(0)# 攝像頭success, img = camera.read()# 從攝像頭讀取照片W_size = 0.1 * camera.get(3)H_size = 0.1 * camera.get(4)system_state_lock = 0# 標(biāo)志系統(tǒng)狀態(tài)的量 0表示無子線程在運(yùn)行 1表示正在刷臉 2表示正在錄入新面孔。# 相當(dāng)于mutex鎖,用于線程同步”’============================================================================================以上是初始化============================================================================================”’def Get_new_face():print(“正在從攝像頭錄入新人臉信息 “)# 存在目錄data就清空,不存在就創(chuàng)建,確保最后存在空的data目錄filepath = “data”if not os.path.exists(filepath):os.mkdir(filepath)else:shutil.rmtree(filepath)os.mkdir(filepath)sample_num = 0# 已經(jīng)獲得的樣本數(shù)while True:# 從攝像頭讀取圖片global successglobal img# 因?yàn)橐@示在可視化的控件內(nèi),所以要用全局的success, img = camera.read()# 轉(zhuǎn)為灰度圖片if success is True:gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)else:break# 檢測人臉,將每一幀攝像頭記錄的數(shù)據(jù)帶入OpenCv中,讓Classifier判斷人臉# 其中g(shù)ray為要檢測的灰度圖像,1.3為每次圖像尺寸減小的比例,5為minNeighborsface_detector = face_cascadefaces = face_detector.detectMultiScale(gray, 1.3, 5)# 框選人臉,for循環(huán)保證一個(gè)能檢測的實(shí)時(shí)動態(tài)視頻流for (x, y, w, h) in faces:# xy為左上角的坐標(biāo),w為寬,h為高,用rectangle為人臉標(biāo)記畫框cv2.rectangle(img, (x, y), (x + w, y + w), (255, 0, 0))# 樣本數(shù)加1sample_num += 1# 保存圖像,把灰度圖片看成二維數(shù)組來檢測人臉區(qū)域,這里是保存在data緩沖文件夾內(nèi)T = Total_face_numcv2.imwrite(“./data/User.” + str(T) + ‘.’ + str(sample_num) + ‘.jpg’, gray[y:y + h, x:x + w])pictur_num = 30# 表示攝像頭拍攝取樣的數(shù)量,越多效果越好,但獲取以及訓(xùn)練的越慢cv2.waitKey(1)if sample_num > pictur_num:breakelse:# 控制臺內(nèi)輸出進(jìn)度條l = int(sample_num / pictur_num * 50)r = int((pictur_num – sample_num) / pictur_num * 50)print(“r” + “%{:.1f}”.format(sample_num / pictur_num * 100) + “=” * l + “->” + “_” * r, end=””)var.set(“%{:.1f}”.format(sample_num / pictur_num * 100))# 控件可視化進(jìn)度信息# tk.Tk().update()window.update()# 刷新控件以實(shí)時(shí)顯示進(jìn)度def Train_new_face():print(“正在訓(xùn)練”)# cv2.destroyAllWindows()path = ‘data’# 初始化識別的方法recog = cv2.face.LBPHFaceRecognizer_create()# 調(diào)用函數(shù)并將數(shù)據(jù)喂給識別器訓(xùn)練faces, ids = get_images_and_labels(path)print(‘本次用于訓(xùn)練的識別碼為:’)# 調(diào)試信息print(ids)# 輸出識別碼# 訓(xùn)練模型#將輸入的所有圖片轉(zhuǎn)成四維數(shù)組recog.train(faces, np.array(ids))# 保存模型yml = str(Total_face_num) + “.yml”rec_f = open(yml, “w+”)rec_f.close()recog.save(yml)# recog.save(‘aaa.yml’)# 創(chuàng)建一個(gè)函數(shù),用于從數(shù)據(jù)集文件夾中獲取訓(xùn)練圖片,并獲取id# 注意圖片的命名格式為User.id.sampleNumdef get_images_and_labels(path):image_paths = [os.path.join(path, f) for f in os.listdir(path)]# 新建連個(gè)list用于存放face_samples = []ids = []# 遍歷圖片路徑,導(dǎo)入圖片和id添加到list中for image_path in image_paths:# 通過圖片路徑將其轉(zhuǎn)換為灰度圖片img = Image.open(image_path).convert(‘L’)# 將圖片轉(zhuǎn)化為數(shù)組img_np = np.array(img, ‘uint8’)if os.path.split(image_path)[-1].split(“.”)[-1] != ‘jpg’:continue# 為了獲取id,將圖片和路徑分裂并獲取id = int(os.path.split(image_path)[-1].split(“.”)[1])# 調(diào)用熟悉的人臉分類器detector = cv2.CascadeClassifier(‘haarcascade_frontalface_default.xml’)faces = detector.detectMultiScale(img_np)# 將獲取的圖片和id添加到list中for (x, y, w, h) in faces:face_samples.append(img_np[y:y + h, x:x + w])ids.append(id)return face_samples, idsdef write_config():print(“新人臉訓(xùn)練結(jié)束”)f = open(‘config.txt’, “a”)T = Total_face_numf.write(str(T) + ” User” + str(T) + ” “)f.close()id_dict[T] = “User” + str(T)# 這里修改文件的方式是先讀入內(nèi)存,然后修改內(nèi)存中的數(shù)據(jù),最后寫回文件f = open(‘config.txt’, ‘r+’)flist = f.readlines()flist[0] = str(int(flist[0]) + 1) + ” “f.close()f = open(‘config.txt’, ‘w+’)f.writelines(flist)f.close()”’============================================================================================以上是錄入新人臉信息功能的實(shí)現(xiàn)============================================================================================”’def scan_face():# 使用之前訓(xùn)練好的模型for i in range(Total_face_num):# 每個(gè)識別器都要用i += 1yml = str(i) + “.yml”print(“本次:” + yml)# 調(diào)試信息recognizer.read(yml)ave_poss = 0for times in range(10):# 每個(gè)識別器掃描十遍times += 1cur_poss = 0global successglobal imgglobal system_state_lockwhile system_state_lock == 2:# 如果正在錄入新面孔就阻塞print(“r刷臉被錄入面容阻塞”, end=””)passsuccess, img = camera.read()gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 識別人臉faces = face_cascade.detectMultiScale(gray,scaleFactor=1.2,minNeighbors=5,minSize=(int(W_size), int(H_size)))# 進(jìn)行校驗(yàn)for (x, y, w, h) in faces:# global system_state_lockwhile system_state_lock == 2:# 如果正在錄入新面孔就阻塞print(“r刷臉被錄入面容阻塞”, end=””)pass# 這里調(diào)用Cv2中的rectangle函數(shù) 在人臉周圍畫一個(gè)矩形cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)# 調(diào)用分類器的預(yù)測函數(shù),接收返回值標(biāo)簽和置信度idnum, confidence = recognizer.predict(gray[y:y + h, x:x + w])conf = confidence# 計(jì)算出一個(gè)檢驗(yàn)結(jié)果if confidenceconf > 0:cur_poss = 1# 表示可以識別elif 60 > conf > 35:cur_poss = 1# 表示可以識別else:cur_poss = 0# 表示不可以識別k = cv2.waitKey(1)if k == 27:# cam.release()# 釋放資源cv2.destroyAllWindows()breakave_poss += cur_possif ave_poss >= 5:# 有一半以上識別說明可行則返回return ireturn 0# 全部過一遍還沒識別出說明無法識別”’============================================================================================以上是關(guān)于刷臉功能的設(shè)計(jì)============================================================================================”’def f_scan_face_thread():# 使用之前訓(xùn)練好的模型# recognizer.read(‘aaa.yml’)var.set(‘刷臉’)ans = scan_face()if ans == 0:print(“最終結(jié)果:無法識別”)var.set(“最終結(jié)果:無法識別”)else:ans_name = “最終結(jié)果:” + str(ans) + id_dict[ans]print(ans_name)var.set(ans_name)global system_state_lockprint(“鎖被釋放0”)system_state_lock = 0# 修改system_state_lock,釋放資源def f_scan_face():global system_state_lockprint(“當(dāng)前鎖的值為:” + str(system_state_lock))if system_state_lock == 1:print(“阻塞,因?yàn)檎谒⒛?#8221;)return 0elif system_state_lock == 2:# 如果正在錄入新面孔就阻塞print(“刷臉被錄入面容阻塞”””)return 0system_state_lock = 1p = threading.Thread(target=f_scan_face_thread)p.setDaemon(True)# 把線程P設(shè)置為守護(hù)線程 若主線程退出 P也跟著退出p.start()def f_rec_face_thread():var.set(‘錄入’)cv2.destroyAllWindows()global Total_face_numTotal_face_num += 1Get_new_face()# 采集新人臉print(“采集完畢,開始訓(xùn)練”)global system_state_lock# 采集完就可以解開鎖print(“鎖被釋放0”)system_state_lock = 0Train_new_face()# 訓(xùn)練采集到的新人臉write_config()# 修改配置文件#recognizer.read(‘aaa.yml’)# 讀取新識別器# global system_state_lock# print(“鎖被釋放0”)# system_state_lock = 0# 修改system_state_lock,釋放資源def f_rec_face():global system_state_lockprint(“當(dāng)前鎖的值為:” + str(system_state_lock))if system_state_lock == 2:print(“阻塞,因?yàn)檎阡浫朊嫒?#8221;)return 0else:system_state_lock = 2# 修改system_state_lockprint(“改為2″, end=””)print(“當(dāng)前鎖的值為:” + str(system_state_lock))p = threading.Thread(target=f_rec_face_thread)p.setDaemon(True)# 把線程P設(shè)置為守護(hù)線程 若主線程退出 P也跟著退出p.start()# tk.Tk().update()#system_state_lock = 0# 修改system_state_lock,釋放資源def f_exit():# 退出按鈕exit()”’============================================================================================以上是關(guān)于多線程的設(shè)計(jì)============================================================================================”’window = tk.Tk()window.title(‘Cheney’ Face_rec 3.0′) # 窗口標(biāo)題window.geometry(‘1000×500′)# 這里的乘是小x# 在圖形界面上設(shè)定標(biāo)簽,類似于一個(gè)提示窗口的作用var = tk.StringVar()l = tk.Label(window, textvariable=var, bg=’green’, fg=’white’, font=(‘Arial’, 12), width=50, height=4)# 說明: bg為背景,fg為字體顏色,font為字體,width為長,height為高,這里的長和高是字符的長和高,比如height=2,就是標(biāo)簽有2個(gè)字符這么高l.pack()# 放置l控件# 在窗口界面設(shè)置放置Button按鍵并綁定處理函數(shù)button_a = tk.Button(window, text=’開始刷臉’, font=(‘Arial’, 12), width=10, height=2, command=f_scan_face)button_a.place(x=800, y=120)button_b = tk.Button(window, text=’錄入人臉’, font=(‘Arial’, 12), width=10, height=2, command=f_rec_face)button_b.place(x=800, y=220)button_b = tk.Button(window, text=’退出’, font=(‘Arial’, 12), width=10, height=2, command=f_exit)button_b.place(x=800, y=320)panel = tk.Label(window, width=500, height=350)# 攝像頭模塊大小panel.place(x=10, y=100)# 攝像頭模塊的位置window.config(cursor=”arrow”)def video_loop():# 用于在label內(nèi)動態(tài)展示攝像頭內(nèi)容(攝像頭嵌入控件)# success, img = camera.read()# 從攝像頭讀取照片global successglobal imgif success:cv2.waitKey(1)cv2image = cv2.cvtColor(img, cv2.COLOR_BGR2RGBA)# 轉(zhuǎn)換顏色從BGR到RGBAcurrent_image = Image.fromarray(cv2image)# 將圖像轉(zhuǎn)換成Image對象imgtk = ImageTk.PhotoImage(image=current_image)panel.imgtk = imgtkpanel.config(image=imgtk)window.after(1, video_loop)video_loop()#窗口循環(huán),用于顯示window.mainloop()”’============================================================================================以上是關(guān)于界面的設(shè)計(jì)============================================================================================”’復(fù)制代碼