Opencv實現人臉識別(三)加入mysql和GUI設計

設計語言:python
Python庫:numpy、PIL、pyttsx3、mysql.connector、cv2、os

改進:加入了數據庫功能,不是從文本讀取數據,並對其中一些邏輯做了改進。

具備功能:
能夠實現人臉識別並保存、人臉數據訓練、人臉鑑別。將信息保存在數據庫中,並查詢簽到信息(可以查詢全部,也可以查詢某個人,也可以查詢某天的信息),並進行語音播報。

數據庫中會定義兩個表
Personal和Check_table表

create table Personal(
   id int primary key auto_increment,
   name varchar(40) not null)charset utf8;

create table Check_table(
    id int primary key auto_increment,
    name varchar(45) not null,
    check_time datetime)charset utf8;

對這兩個表的操作有查看是否存在,不存在則創建;對其進行查詢、插入操作。

mysql函數定義

import mysql.connector


#  錯誤   unread result found 連接時加上 buffered=True
def Mysql_Init():
    conn = mysql.connector.connect(user='root', password='yangzhi', database='face', charset='utf8', buffered=True)
    # 使用cursor()方法獲取操作遊標
    cursor = conn.cursor()
    return conn, cursor


# 檢測表是否存在
def Check_Tables(conn, cursor):
    # 首先檢測是否存在 Personal   和  check_in 表
    cursor.execute("show tables like 'people'")
    result = cursor.fetchone()
    if result is None:
        Create_table_Personal(conn, cursor)
    cursor.execute("show tables like 'Check_table'")
    result = cursor.fetchone()
    if result is None:
        Create_table_Check(conn, cursor)


# 創建個人信息表
def Create_table_Personal(conn, cursor):
    sql = """create table Personal(
   id int primary key auto_increment,
   name varchar(40) not null)charset utf8;
   """
    try:
        # 執行sql語句
        cursor.execute(sql)
        # 提交到數據庫執行
        conn.commit()
    except:
        conn.rollback()


# 創建簽到表
def Create_table_Check(conn, cursor):
    sql = """create table Check_table(
    id int primary key auto_increment,
    name varchar(45) not null,
    check_time datetime)charset utf8;
   """

    try:
        # 執行sql語句
        cursor.execute(sql)
        # 提交到數據庫執行
        conn.commit()
    except:
        conn.rollback()


def Insert_Data_Personal(conn, cursor, name):
    sql = "insert into Personal values (null, '%s')" % name
    try:
        # 執行sql語句
        cursor.execute(sql)
        # 提交到數據庫執行
        conn.commit()
    except:
        # 發生錯誤時回滾
        conn.rollback()


def Insert_Data_Check(conn, cursor, name):
    sql = "insert into check_table values (null,'%s', now())" % name
    try:
        # 執行sql語句
        cursor.execute(sql)
        # 提交到數據庫執行
        conn.commit()
    except:
        # 發生錯誤時回滾
        conn.rollback()

def Query_Data_Personal(conn, cursor):
    sql = "select name from personal"
    cursor.execute(sql)
    conn.commit()
    results = cursor.fetchall()
    list_name = []
    for x in results:
       x = list(x)
       list_name += x
    return list_name

def Query_Data_Check(conn, cursor, name='', *dates):
    if dates is ():
        if name == '':
            sql = "select * from check_table;"
        else:
            sql = "select * from check_table where name = '%s';" % name
        # print(sql)
        cursor.execute(sql)

    else:
        if name == '':
            for date in dates:
                sql = """select * from check_table where  check_time = any (
                    select check_time from check_table where DATEDIFF(date(check_time),'%s') < 1)
                    """ % date
                # print(sql)
                # 執行sql語句
                cursor.execute(sql)
        else:
            for date in dates:
                print(date)
                sql = """select * from check_table where name = '%s' and check_time = any (
                       select check_time from check_table where  DATEDIFF(date(check_time),'%s') < 1);
                       """ % name, date
                # print(sql)
                # 執行sql語句
                cursor.execute(sql)

    # 提交到數據庫執行
    conn.commit()
    results = cursor.fetchall()
    for x in results:
        print("("+ str(x[0])+",  "+x[1] +",  "+ str(x[2]) +")")



# 關閉數據庫
def dataBase_Close(conn):
    conn.close()
    print('database close')


if __name__ == '__main__':
    conn, cursor = Mysql_Init()
    # Check_Tables(conn, cursor)
    # Insert_Data_Personal(conn, cursor, 'lisi')
    # Insert_Data_Check(conn, cursor, "lisi")
    # Query_Data_Check(conn, cursor, '', '2019-07-04')
    # dataBase_Close(conn)
    print(Query_Data_Personal(conn,cursor))
    # Query_Data_Personal(conn, cursor)

說明:
首先在mysql數據庫中先創建好face數據庫。

def Mysql_Init():
	# 連接mysql數據庫  root賬戶 密碼yangzhi  使用數據庫 face
    conn = mysql.connector.connect(user='root', password='yangzhi', database='face', charset='utf8', buffered=True)
    # 使用cursor()方法獲取操作遊標
    cursor = conn.cursor()
    return conn, cursor

這個是你自己的人臉分類器所在地址

 face_detector = cv2.CascadeClassifier('./lib/haarcascade_frontalface_default.xml')

如:
在這裏插入圖片描述
初始實現代碼:

import cv2
import os
import numpy as np
from PIL import Image
import pyttsx3
import  sys
import json
import time
import mysql.connector

def Mysql_Init():
    conn = mysql.connector.connect(user='root', password='yangzhi', database='face', charset='utf8', buffered=True)
    # 使用cursor()方法獲取操作遊標
    cursor = conn.cursor()
    return conn, cursor
def Check_Tables(conn, cursor):
    # 首先檢測是否存在 Personal   和  check_in 表
    cursor.execute("show tables like 'people'")
    result = cursor.fetchone()
    if result is None:
        Create_table_Personal(conn, cursor)
    cursor.execute("show tables like 'check'")
    result = cursor.fetchone()
    if result is None:
        Create_table_Check(conn, cursor)
def Create_table_Personal(conn, cursor):
    sql = """create table Personal(
   id int primary key auto_increment,
   name varchar(40) not null)charset utf8;
   """
    try:
        # 執行sql語句
        cursor.execute(sql)
        # 提交到數據庫執行
        conn.commit()
    except:
        conn.rollback()
def Create_table_Check(conn, cursor):
    sql = """create table Check_table(
    id int primary key auto_increment,
    name varchar(45) not null,
    check_time datetime)charset utf8;
   """
    try:
        # 執行sql語句
        cursor.execute(sql)
        # 提交到數據庫執行
        conn.commit()
    except:
        conn.rollback()
def Insert_Data_Personal(conn, cursor, name):
    sql = "insert into Personal values (null, '%s')" % name
    try:
        # 執行sql語句
        cursor.execute(sql)
        # 提交到數據庫執行
        conn.commit()
    except:
        # 發生錯誤時回滾
        conn.rollback()
def Insert_Data_Check(conn, cursor, name):
    sql = "insert into check_table values (null,'%s', now())" % name
    try:
        # 執行sql語句
        cursor.execute(sql)
        # 提交到數據庫執行
        conn.commit()
    except:
        # 發生錯誤時回滾
        conn.rollback()
def Query_Data_Personal(conn, cursor):
    sql = "select name from personal"
    cursor.execute(sql)
    conn.commit()
    results = cursor.fetchall()
    list_name = []
    for x in results:
       x = list(x)
       list_name += x
    return list_name
def Query_Data_Check(conn, cursor, name='', *dates):
    if dates is ():
        if name == '':
            sql = "select * from check_table;"
        else:
            sql = "select * from check_table where name = '%s';" % name
        print(sql)
        cursor.execute(sql)

    else:
        if name == '':
            for date in dates:
                sql = """select * from check_table where  check_time = any (
                    select check_time from check_table where DATEDIFF(date(check_time),'%s') < 1)
                    """ % date
                print(sql)
                # 執行sql語句
                cursor.execute(sql)
        else:
            for date in dates:
                print(date)
                sql = """select * from check_table where name = '%s' and check_time = any (
                       select check_time from check_table where  DATEDIFF(date(check_time),'%s') < 1);
                       """ % name, date
                print(sql)
                # 執行sql語句
                cursor.execute(sql)
    # 提交到數據庫執行
    conn.commit()
    results = cursor.fetchall()
    for x in results:
        print("("+ str(x[0])+",  "+x[1] +",  "+ str(x[2]) +")")
def dataBase_Close(conn):
    conn.close()
    print('database close')

def makeDir(engine,x):
    if not os.path.exists("face_trainer"):
        print("創建預訓練環境")
        engine.say('檢測到第一次啓動,正在創建預訓練環境')
        os.mkdir("face_trainer")
        engine.say('創建成功')
        engine.runAndWait()
        x=2
    if not os.path.exists("Facedata"):
        print("創建訓練環境")
        engine.say('正在創建訓練環境')
        os.mkdir("Facedata")
        engine.say('創建成功')
        engine.runAndWait()
        x=2
    return x
def getFace(cap,face_id):
    face_detector = cv2.CascadeClassifier('./lib/haarcascade_frontalface_default.xml')
    print('\n Initializing face capture. Look at the camera and wait ...')
    count = 0
    while True:
        sucess, img = cap.read()
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        faces = face_detector.detectMultiScale(gray, 1.3, 5)
        for (x, y, w, h) in faces:
            cv2.rectangle(img, (x, y), (x+w, y+w), (255, 0, 0))
            count += 1
            cv2.imwrite("Facedata/User." + str(face_id) + '.' + str(count) + '.jpg', gray[y: y + h, x: x + w])
            cv2.imshow('image', img)
        # 保持畫面的持續。
        k = cv2.waitKey(1)
        if k == 27:   # 通過esc鍵退出攝像
            break
        elif count >= 100:  # 得到1000個樣本後退出攝像
            break
    cv2.destroyAllWindows()
def getImagesAndLabels(path,detector):
        imagePaths = [os.path.join(path, f) for f in os.listdir(path)]
        faceSamples = []
        ids = []
        for imagePath in imagePaths:
            PIL_img = Image.open(imagePath).convert('L')
            img_numpy = np.array(PIL_img, 'uint8')
            id = int(os.path.split(imagePath)[-1].split(".")[1])
            faces = detector.detectMultiScale(img_numpy)
            for (x, y, w, h) in faces:
                faceSamples.append(img_numpy[y:y + h, x: x + w])
                ids.append(id)
        return faceSamples, ids
def trainFace():
    # 人臉數據路徑
    path = 'Facedata'
    recognizer = cv2.face.LBPHFaceRecognizer_create()
    detector = cv2.CascadeClassifier("./lib/haarcascade_frontalface_default.xml")
    print('Training faces. It will take a few seconds. Wait ...')
    faces, ids = getImagesAndLabels(path,detector)
    recognizer.train(faces, np.array(ids))
    recognizer.write(r'face_trainer\trainer.yml')
    print("{0} faces trained. Exiting Program".format(len(np.unique(ids))))
def checkFace(cam,names,engine):
    recognizer = cv2.face.LBPHFaceRecognizer_create()
    recognizer.read('face_trainer/trainer.yml')
    cascadePath = "./lib/haarcascade_frontalface_default.xml"
    faceCascade = cv2.CascadeClassifier(cascadePath)
    font = cv2.FONT_HERSHEY_SIMPLEX
    minW = 0.1 * cam.get(3)
    minH = 0.1 * cam.get(4)
    while True:
        ret, img = cam.read()
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        faces = faceCascade.detectMultiScale(
            gray,
            scaleFactor=1.2,
            minNeighbors=5,
            minSize=(int(minW), int(minH))
        )
        for (x, y, w, h) in faces:
            cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
            idnum, confidence = recognizer.predict(gray[y:y + h, x:x + w])

            if confidence < 100:
                idnum = names[idnum]
                confidence = "{0}%".format(round(100 - confidence))
                say(engine, "歡迎      "+idnum+"簽到成功!")
                cv2.putText(img, str(idnum), (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)
                cv2.imshow('camera', img)
                time.sleep(2)
                Insert_Data_Check(conn, cursor, idnum)#簽到信息插入數據庫
                return
            else:
                idnum = "unknown"
                confidence = "{0}%".format(round(100 - confidence))
                cv2.putText(img, str(idnum), (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)

        cv2.imshow('camera', img)
        k = cv2.waitKey(10)
        if k == 27:
            break
    cam.release()
    cv2.destroyAllWindows()
def say(engine,str):
    engine.say(str)
    engine.runAndWait()

if __name__ == '__main__':
    conn, cursor = Mysql_Init()
    Check_Tables(conn, cursor)

    names = []
    names=Query_Data_Personal(conn, cursor)
    print(names)
    """
    txt讀取名字列表
    if os.path.exists("name.txt"):
        with open("name.txt") as f:
            names = json.loads(f.read())
            print(names)
    """
    engine = pyttsx3.init()
    rate = engine.getProperty('rate')
    engine.setProperty('rate', rate - 20)
    say(engine, "歡迎使用人臉識別簽到系統")

    simply_0=1#標誌變量 象徵是否第一次使用系統
    simply_0=makeDir(engine,simply_0)

    while True:
        if simply_0 == '1':  # 不是第一次使用
            say(engine, "輸入0錄入新的人臉信息 輸入其他跳轉至人臉簽到 ")
            value = input("0-錄入 or other-簽到")
            if value == '0':
                say(engine, "請輸入您的姓名")
                name = input("請輸入姓名:")
                names.append(name)
                # 新成員信息已保存到數據庫中
                Insert_Data_Personal(conn, cursor, name)
                say(engine, "新的人員信息已保存到數據庫中")
                say(engine,"正在打開攝像頭")
                cam = cv2.VideoCapture(0)
                say(engine, "注視攝像頭,開始採集人臉數據")
                getFace(cam,len(names)-1)
                say(engine, "採集完畢,開始訓練")
                trainFace()
                say(engine, "訓練完畢,開始人臉簽到")
            else:
                say(engine, "開始人臉識別")
                say(engine, "正在打開攝像頭")
                cam = cv2.VideoCapture(0)

        else:#是第一次使用系統
            say(engine, "這是首次錄入人臉信息,請輸入您的姓名 ")
            name = input("請輸入姓名:")
            names.append(name)
            #新成員信息保存到數據庫中
            Insert_Data_Personal(conn, cursor, name)
            say(engine, "新的人員信息已保存到數據庫中")
            say(engine, "正在打開攝像頭")
            cam = cv2.VideoCapture(0)
            say(engine, "注視攝像頭,開始採集人臉數據")
            getFace(cam, len(names) - 1)
            say(engine, "採集完畢,開始訓練")
            trainFace()
            say(engine, "訓練完畢,跳轉至人臉簽到")

        say(engine, "輸入0進行人臉簽到,輸入其他跳轉至主菜單")
        simply_1 = input("輸入0進行人臉簽到,輸入其他跳轉至主菜單")
        if simply_1=='0':
            checkFace(cam, names, engine)
            say(engine, "簽到記錄信息已保存到數據庫中")
        else:
            cam.release()
            cv2.destroyAllWindows()
        """保存
        with open("name.txt", 'w') as f:
            f.write(json.dumps(names))
        say(engine, "信息已保存")
        """
        say(engine, "輸入 0 退出系統 ,輸入1 查詢簽到記錄 其他任意鍵 錄入人臉模塊")
        key = input("輸入key:(0 - 退出系統 ,1-查詢簽到記錄 other - 重新啓動系統)")
        if key=='1':
            say(engine, "輸入要查詢記錄的姓名或日期")
            name_0 = input("輸入要查詢記錄的名字")
            date_0=input("輸入要查詢記錄的日期")
            Query_Data_Check(conn, cursor, name_0, *date_0)
            say(engine, "簽到記錄查詢完畢")
            say(engine, "系統即將跳轉至錄入人臉模塊")
        if key == '0':
            say(engine, "系統將退出,歡迎下次使用")
            dataBase_Close(conn)
            sys.exit(0)

改進:可以加以個界面、C/S架構保證數據庫安全
先設計一個界面,之後再說吧
界面設計初始:(無功能)

from tkinter import *
import cv2
import os
from PIL import Image, ImageTk
from tkinter import ttk
from multiprocessing import Process
import facial_recognition
import database

class APP:
	def __init__(self):
		self.camera = None   # 攝像頭
		self.root = Tk()
		self.root.title('FACE')
		self.root.geometry('%dx%d' % (800, 600))
		self.createFirstPage()
		mainloop()

	def createFirstPage(self):
		self.page1 = Frame(self.root)
		self.page1.pack()
		Label(self.page1, text='歡迎使用人臉識別系統', font=('粗體', 20)).pack()
		image = Image.open("1.jpg")
		photo = ImageTk.PhotoImage(image = image)
		self.data1 = Label(self.page1,  width=780,image = photo)
		self.data1.image = photo
		self.data1.pack(padx=5, pady=5)

		self.button11 = Button(self.page1, width=18, height=2, text="簽到打卡", bg='red', font=("宋", 12),
							   relief='raise',command = self.createSecondPage)
		self.button11.pack(side=LEFT, padx=25, pady = 10)
		self.button12 = Button(self.page1, width=18, height=2, text="錄入新的人臉", bg='green', font=("宋", 12),
		                       relief='raise', command = self.createSecondPage)
		self.button12.pack(side=LEFT, padx=25, pady = 10)
		self.button13 = Button(self.page1, width=18, height=2, text="查詢簽到信息", bg='white', font=("宋", 12), relief='raise',
							   command = self.checkDataView)
		self.button13.pack(side=LEFT, padx=25, pady = 10)
		self.button14 = Button(self.page1, width=18, height=2, text="退出系統", bg='gray', font=("宋", 12),
							   relief='raise',command = self.quitMain)
		self.button14.pack(side=LEFT, padx=25, pady = 10)

	def createSecondPage(self):
		self.camera = cv2.VideoCapture(0)
		self.page1.pack_forget()
		self.page2 = Frame(self.root)
		self.page2.pack()
		Label(self.page2, text='歡迎使用人臉識別系統', font=('粗體', 20)).pack()
		self.data2 = Label(self.page2)
		self.data2.pack(padx=5, pady=5)

		self.button21 = Button(self.page2, width=18, height=2, text="返回", bg='gray', font=("宋", 12),
							   relief='raise',command = self.backFirst)
		self.button21.pack(padx=25,pady = 10)
		self.video_loop(self.data2)

	def video_loop(self, panela):

		success, img = self.camera.read()  # 從攝像頭讀取照片
		if success:
			cv2image = cv2.cvtColor(img, cv2.COLOR_BGR2RGBA)  # 轉換顏色從BGR到RGBA
			current_image = Image.fromarray(cv2image)  # 將圖像轉換成Image對象
			imgtk = ImageTk.PhotoImage(image=current_image)
			panela.imgtk = imgtk
			panela.config(image=imgtk)
			self.root.after(1, lambda: self.video_loop(panela))

	#  簽到信息展示
	# noinspection PyAttributeOutsideInit
	def checkDataView(self):
		self.page3 = Frame(self.root)
		self.page1.pack_forget()
		self.root.geometry('700x360')
		Label(self.page3, text='今日簽到信息', bg='white', fg='red', font=('宋體', 25)).pack(side=TOP, fill='x')
		self.checkDate = ttk.Treeview(self.page3, show='headings', column=('sid', 'name', 'check_time' ))

		self.checkDate.column('sid', width=100, anchor="center")
		self.checkDate.column('name', width=200, anchor="center")
		self.checkDate.column('check_time', width=300, anchor="center")


		self.checkDate.heading('sid', text='簽到序號')
		self.checkDate.heading('name', text='名字')
		self.checkDate.heading('check_time', text='簽到時間')

		# 例子
		# data = {"item0": ["1a", "2a", "4a"],
		# 		"item1": ["1c", "2c", "3c"]}
		# self.checkDate.insert('', 'end', values=data['item0'])
		# self.checkDate.insert('', 'end', values=data['item1'])


		# # y滾動條
		# yscrollbar = Scrollbar(self.page3, orient=VERTICAL, command=self.checkDate.yview)
		# self.checkDate.configure(yscrollcommand=yscrollbar.set)
		# yscrollbar.pack(side=RIGHT, fill=Y)


		self.checkDate.pack(expand = 1, fill = BOTH)
		Button(self.page3, width=20, height=2, text="返回", bg='gray', font=("宋", 12),
							   relief='raise',command =self.backMain).pack(padx = 20, pady = 20)
		self.page3.pack()



	def backFirst(self):
		self.page2.pack_forget()
		self.page1.pack()
		# 釋放攝像頭資源
		self.camera.release()
		cv2.destroyAllWindows()

	def backMain(self):
		self.root.geometry('900x600')
		self.page3.pack_forget()
		self.page1.pack()

	def quitMain(self):
		sys.exit(0)

	# #  個人信息展示
	# def Dataview(self):
	# 	self.personalData = ttk.Treeview(self.root, show='headings', column=('sid', 'name', 'sex', 'address'))
	# 	self.personalData.column('sid', width=150, anchor="center")
	# 	self.personalData.column('name', width=150, anchor="center")
	# 	self.personalData.column('phone', width=150, anchor="center")
	# 	self.personalData.column('address', width=150, anchor="center")
	#
	# 	self.personalData.heading('sid', text='學號')
	# 	self.personalData.heading('name', text='名字')
	# 	self.personalData.heading('phone', text='電話')
	# 	self.personalData.heading('address', text='地址')
	# 	self.personalData.pack()



if __name__ == '__main__':

	demo = APP()


效果圖:
在這裏插入圖片描述
點擊簽到打卡
在這裏插入圖片描述

點擊查詢簽到信息
在這裏插入圖片描述

現在的問題:
1、GUI中顯示實時圖像使從攝像頭一幀一幀截取圖像並顯示在這裏,如果要藉助這個GUI來實現人臉簽到,必然會導致opencv爭奪攝像頭資源從而卡住,如何解決??(求助)
(可以退而求其次,直接不要這個GUI實時圖像,就按鈕,文字,也可以實現基本的功能)
2、opencv的窗口如何屏蔽掉?只顯示GUI上的圖像。

最終實現:https://www.cnblogs.com/blsx/p/11272744.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章