Graduate

Make register GUI version

1 CREATE TABLE lecture( 1 CREATE TABLE lecture(
2 lecture_id VARCHAR(20) NOT NULL, 2 lecture_id VARCHAR(20) NOT NULL,
3 lecture_name VARCHAR(50), 3 lecture_name VARCHAR(50),
4 -lecture_room VARCHAR(50) NOT NULL,
5 PRIMARY KEY(lecture_id) 4 PRIMARY KEY(lecture_id)
6 ); 5 );
7 6
...@@ -32,7 +31,8 @@ FOREIGN KEY (lecture_id) REFERENCES lecture(lecture_id) ...@@ -32,7 +31,8 @@ FOREIGN KEY (lecture_id) REFERENCES lecture(lecture_id)
32 31
33 CREATE TABLE lecture_schedule( 32 CREATE TABLE lecture_schedule(
34 lecture_id VARCHAR(20) NOT NULL, 33 lecture_id VARCHAR(20) NOT NULL,
35 -lecture_day VARCHAR(20) NOT NULL, 34 +lecture_day TINYINT NOT NULL,
35 +lecture_room VARCHAR(50) NOT NULL,
36 lecture_start_time TIME NOT NULL, 36 lecture_start_time TIME NOT NULL,
37 lecture_end_time TIME NOT NULL, 37 lecture_end_time TIME NOT NULL,
38 FOREIGN KEY (lecture_id) REFERENCES lecture(lecture_id) 38 FOREIGN KEY (lecture_id) REFERENCES lecture(lecture_id)
......
...@@ -2,6 +2,10 @@ ...@@ -2,6 +2,10 @@
2 #1. webcam에서 얼굴을 인식합니다 2 #1. webcam에서 얼굴을 인식합니다
3 #2. 인식한 얼굴을 등록합니다 3 #2. 인식한 얼굴을 등록합니다
4 ################################################## 4 ##################################################
5 +import tkinter as tk
6 +import tkinter.font
7 +import tkinter.messagebox
8 +import threading
5 import torch 9 import torch
6 import numpy as np 10 import numpy as np
7 import cv2 11 import cv2
...@@ -11,52 +15,109 @@ import json ...@@ -11,52 +15,109 @@ import json
11 import os 15 import os
12 import timeit 16 import timeit
13 import base64 17 import base64
18 +import time
14 19
15 -from PIL import Image 20 +from PIL import Image, ImageTk
16 from io import BytesIO 21 from io import BytesIO
17 import requests 22 import requests
18 23
19 from models.mtcnn import MTCNN 24 from models.mtcnn import MTCNN
20 25
21 -device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu') 26 +class Register(tk.Frame):
22 -print('Running on device: {}'.format(device)) 27 + def __init__(self, parent, *args, **kwargs):
28 + tk.Frame.__init__(self, parent, *args, **kwargs)
23 29
24 -mtcnn = MTCNN(keep_all=True, device=device) 30 + # tkinter GUI
31 + self.width = 740
32 + self.height = 640
25 33
26 -uri = 'ws://169.56.95.131:8765' 34 + self.parent = parent
35 + self.parent.geometry("%dx%d+100+100" % (self.width, self.height))
36 + self.pack()
37 + self.create_widgets()
27 38
28 -async def send_face(face_list, image_list): 39 + # URI
29 - global uri 40 + self.uri = 'ws://169.56.95.131:8765'
30 - async with websockets.connect(uri) as websocket:
31 - for face, image in zip(face_list, image_list):
32 - #type: np.float32
33 - send = json.dumps({'action': 'register', 'student_id':'2014101898', 'student_name':'김다솜', 'MTCNN': face.tolist()})
34 - await websocket.send(send)
35 - recv = await websocket.recv()
36 - data = json.loads(recv)
37 - if data['status'] == 'success':
38 - # 성공
39 - print(data['student_id'], 'is registered')
40 41
41 -def detect_face(frame): 42 + # Pytorch Model
42 - # If required, create a face detection pipeline using MTCNN: 43 + self.device = device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
43 - global mtcnn 44 + self.mtcnn = MTCNN(keep_all=True, device=device)
44 - results = mtcnn.detect(frame) 45 +
45 - image_list = [] 46 + # OpenCV
46 - if results[1][0] == None: 47 + self.cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
47 - return [] 48 + self.cam_width = 640
48 - for box, prob in zip(results[0], results[1]): 49 + self.cam_height = 480
49 - if prob < 0.95: 50 + self.cap.set(3, self.cam_width)
50 - continue 51 + self.cap.set(4, self.cam_height)
51 - print('face detected. prob:', prob) 52 +
52 - x1, y1, x2, y2 = box 53 + # Application Function
53 - image = frame[int(y1-10):int(y2+10), int(x1-10):int(x2+10)] 54 + self.detecting_square = (200, 200)
54 - image_list.append(image) 55 + self.detected = False
55 - return image_list 56 + self.face_list = []
57 + self.image_list = []
58 +
59 + # Event loop and Thread
60 + # self.event_loop = asyncio.new_event_loop()
61 + self.thread = threading.Thread(target=self.mainthread)
62 + self.thread.start()
63 +
64 +
65 + def create_widgets(self):
66 + image = np.zeros([480,640,3], dtype=np.uint8)
67 + image = Image.fromarray(image)
68 + image = ImageTk.PhotoImage(image)
69 +
70 + font = tk.font.Font(family="맑은 고딕", size=15)
71 +
72 + self.alert = tk.Label(self, text="카메라를 정면으로 향하고 화면의 사각형에 얼굴을 맞춰주세요", font=font)
73 + self.alert.grid(row=0, column=0, columnspan=20)
74 + self.label = tk.Label(self, image=image)
75 + self.label.grid(row=1, column=0, columnspan=20)
76 +
77 + self.studentID = tk.StringVar()
78 + self.studentIdLabel = tk.Label(self, text="학번")
79 + self.studentIdLabel.grid(row=2, column=10)
80 + self.studentIdEntry = tk.Entry(self, width=20, textvariable=self.studentID)
81 + self.studentIdEntry.grid(row=2, column=11)
82 +
83 + self.studentName = tk.StringVar()
84 + self.studentNameLabel = tk.Label(self, text="이름")
85 + self.studentNameLabel.grid(row=3, column=10)
86 + self.studentNameEntry = tk.Entry(self, width=20, textvariable=self.studentName)
87 + self.studentNameEntry.grid(row=3, column=11)
88 +
89 + self.registerButton = tk.Button(self, text="등록", fg="blue", command=self.register_face)
90 + self.registerButton.grid(row=4, column=10)
91 +
92 + self.registerButton = tk.Button(self, text="다시촬영", command=self.restart)
93 + self.registerButton.grid(row=4, column=11)
94 +
95 + self.quit = tk.Button(self, text="나가기", fg="red", command=self.stop)
96 + self.quit.grid(row=5, column=10)
97 +
98 + def register_face(self):
99 + if not self.detected:
100 + tk.messagebox.showinfo("경고", "얼굴이 인식되지 않았습니다.")
101 + return
102 + asyncio.get_event_loop().run_until_complete(self.send_face())
56 103
57 -def detect_face(frame): 104 +
58 - results = mtcnn.detect(frame) 105 + def restart(self):
59 - faces = mtcnn(frame, return_prob = False) 106 + if not self.thread.isAlive():
107 + self.cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
108 + self.cap.set(3, self.cam_width)
109 + self.cap.set(4, self.cam_height)
110 +
111 + self.detected = False
112 + self.face_list = []
113 + self.image_list = []
114 +
115 + self.thread = threading.Thread(target=self.mainthread)
116 + self.thread.start()
117 +
118 + def detect_face(self, frame):
119 + results = self.mtcnn.detect(frame)
120 + faces = self.mtcnn(frame, return_prob = False)
60 image_list = [] 121 image_list = []
61 face_list = [] 122 face_list = []
62 if results[1][0] == None: 123 if results[1][0] == None:
...@@ -64,23 +125,96 @@ def detect_face(frame): ...@@ -64,23 +125,96 @@ def detect_face(frame):
64 for box, face, prob in zip(results[0], faces, results[1]): 125 for box, face, prob in zip(results[0], faces, results[1]):
65 if prob < 0.97: 126 if prob < 0.97:
66 continue 127 continue
67 - print('face detected. prob:', prob) 128 + # for debug
129 + # print('face detected. prob:', prob)
68 x1, y1, x2, y2 = box 130 x1, y1, x2, y2 = box
69 if (x2-x1) * (y2-y1) < 15000: 131 if (x2-x1) * (y2-y1) < 15000:
70 # 얼굴 해상도가 너무 낮으면 무시 132 # 얼굴 해상도가 너무 낮으면 무시
133 + self.alert.config(text= "인식된 얼굴이 너무 작습니다. 카메라에 더 가까이 접근해주세요.", fg="red")
134 + self.alert.update()
71 continue 135 continue
72 - # 얼굴 주변 ±3 영역 저장 136 + image = frame
73 - image = frame[int(y1-3):int(y2+3), int(x1-3):int(x2+3)]
74 image_list.append(image) 137 image_list.append(image)
75 # MTCNN 데이터 저장 138 # MTCNN 데이터 저장
76 face_list.append(face.numpy()) 139 face_list.append(face.numpy())
77 - return image_list, face_list
78 -
79 -cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
80 -cap.set(3, 720)
81 -cap.set(4, 480)
82 -ret, frame = cap.read()
83 -frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
84 -image_list, face_list = detect_face(frame)
85 -if face_list:
86 - asyncio.get_event_loop().run_until_complete(send_face(face_list, image_list))
...\ No newline at end of file ...\ No newline at end of file
140 + return face_list, image_list
141 +
142 + def mainthread(self):
143 + t = threading.currentThread()
144 + #asyncio.set_event_loop(self.event_loop)
145 + x1 = int(self.cam_width / 2 - self.detecting_square[0] / 2)
146 + x2 = int(self.cam_width / 2 + self.detecting_square[0] / 2)
147 + y1 = int(self.cam_height / 2 - self.detecting_square[1] / 2)
148 + y2 = int(self.cam_height / 2 + self.detecting_square[1] / 2)
149 + detected_time = None
150 + while getattr(t, "do_run", True):
151 + ret, frame = self.cap.read()
152 +
153 + # model에 이용하기 위해 convert
154 + converted = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
155 +
156 + # 사각형 영역만 검사 (속도 차이 큼)
157 + face_list, image_list = self.detect_face(converted[y1:y2, x1:x2])
158 +
159 + # 얼굴이 인식된 경우 파란색 사각형을 띄움
160 + if face_list:
161 + frame = cv2.rectangle(frame, (x1, y1), (x2, y2), (255, 0, 0), 3)
162 + else:
163 + frame = cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 3)
164 +
165 + # show image
166 + converted = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
167 + image = Image.fromarray(converted)
168 + image = ImageTk.PhotoImage(image)
169 + self.label.configure(image=image)
170 + self.label.image = image # kind of double buffering
171 +
172 + # 얼굴이 인식되면 멤버함수에 넣음
173 + if face_list:
174 + self.face_list = face_list
175 + self.image_list = image_list
176 + # 2초 후에 사진이 찍힘
177 + if detected_time is None:
178 + detected_time = time.time()
179 + else:
180 + self.alert.config(text= "얼굴이 인식되었습니다. %f초 후 사진을 촬영합니다"%(2-(time.time()-detected_time)), fg="red")
181 + if time.time() - detected_time >= 2:
182 + self.thread.do_run = False
183 + self.detected = True
184 + self.alert.config(text= "얼굴을 등록해주세요. 올바르게 촬영되지 않았을 경우 다시촬영을 눌러주세요.", fg="blue")
185 + else:
186 + detected_time = None
187 + self.face_list = []
188 + self.image_list = []
189 +
190 +
191 + async def wait(self, n):
192 + await asyncio.sleep(n)
193 +
194 + async def send_face(self):
195 + try:
196 + async with websockets.connect(self.uri) as websocket:
197 + for face, image in zip(self.face_list, self.image_list):
198 + #type: np.float32
199 + send = json.dumps({'action': 'register', 'student_id':self.studentID, 'student_name':self.studentName, 'MTCNN': face.tolist()})
200 + await websocket.send(send)
201 + recv = await websocket.recv()
202 + data = json.loads(recv)
203 + if data['status'] == 'success':
204 + tk.messagebox.showinfo("등록완료", self.studentID.get() + ' ' + self.studentName.get())
205 + except Exception as e:
206 + tk.messagebox.showinfo("등록실패", e)
207 +
208 + def stop(self):
209 + self.thread.do_run = False
210 + # self.thread.join() # there is a freeze problem
211 + # self.event_loop.close()
212 + self.cap.release()
213 + self.parent.destroy()
214 +
215 +
216 +if __name__ == '__main__':
217 + root = tk.Tk()
218 + Register(root)
219 + root.mainloop()
220 +
......