Merge branch 'master' of http://khuhub.khu.ac.kr/2020-1-capstone-design1/KHY_Project1
Showing
9 changed files
with
405 additions
and
67 deletions
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) | ... | ... |
1 | -# 주제 | 1 | +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) |
2 | -얼굴 인식 전자 출결 시스템 | 2 | +# Topic |
3 | +**얼굴 인식 전자 출결 시스템** | ||
3 | 4 | ||
4 | -# 팀원 | 5 | +# Team |
5 | - 정해갑(컴퓨터공학과, 2014104149) | 6 | - 정해갑(컴퓨터공학과, 2014104149) |
6 | - 허진호(컴퓨터공학과, 2014104161) | 7 | - 허진호(컴퓨터공학과, 2014104161) |
7 | 8 | ||
8 | -# 개발환경 | 9 | +# Hardware |
9 | -- Windows, IBM Cloud(Ubuntu 18.04.4 LTS), MySQL | 10 | +- server: IBM Cloud(2 vCPU | 4 GB | Ubuntu 18.04.4 LTS) |
11 | +- client: (i7-7700HQ | 16 GB | Windows) | ||
10 | 12 | ||
11 | -# 활용기술 | 13 | +# License |
12 | - pytorch(https://github.com/pytorch/pytorch) | 14 | - pytorch(https://github.com/pytorch/pytorch) |
13 | - facenet(https://github.com/davidsandberg/facenet) | 15 | - facenet(https://github.com/davidsandberg/facenet) |
14 | - facenet-pytorch(https://github.com/timesler/facenet-pytorch) | 16 | - facenet-pytorch(https://github.com/timesler/facenet-pytorch) |
... | @@ -16,3 +18,11 @@ | ... | @@ -16,3 +18,11 @@ |
16 | - NodeJS(https://nodejs.org) | 18 | - NodeJS(https://nodejs.org) |
17 | - MySQL(https://www.mysql.com) | 19 | - MySQL(https://www.mysql.com) |
18 | - PyMySQL(https://github.com/PyMySQL/PyMySQL) | 20 | - PyMySQL(https://github.com/PyMySQL/PyMySQL) |
21 | + | ||
22 | +# Usage | ||
23 | +## Server | ||
24 | +- python3 server/server.py & npm start --prefix webserver/myapp & | ||
25 | + | ||
26 | +## Client(windows) | ||
27 | +- execute register/register.py | ||
28 | +- execute client/client(window).py | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
client/client.py
0 → 100644
1 | +################################################## | ||
2 | +#1. webcam에서 얼굴을 인식합니다. | ||
3 | +#2. 얼굴일 확률이 97% 이상이고 영역이 15000 이상인 이미지를 서버에 전송 | ||
4 | +################################################## | ||
5 | +import tkinter as tk | ||
6 | +import tkinter.font | ||
7 | +import tkinter.messagebox | ||
8 | +import tkinter.scrolledtext | ||
9 | +import threading | ||
10 | +import torch | ||
11 | +import numpy as np | ||
12 | +import cv2 | ||
13 | +import asyncio | ||
14 | +import websockets | ||
15 | +import json | ||
16 | +import os | ||
17 | +import timeit | ||
18 | +import base64 | ||
19 | +import time | ||
20 | + | ||
21 | +from PIL import Image, ImageTk | ||
22 | +from io import BytesIO | ||
23 | +import requests | ||
24 | + | ||
25 | +from models.mtcnn import MTCNN | ||
26 | + | ||
27 | +device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu') | ||
28 | +print('Running on device: {}'.format(device)) | ||
29 | + | ||
30 | +mtcnn = MTCNN(keep_all=True, post_process=True, device=device) | ||
31 | + | ||
32 | +uri = 'ws://169.56.95.131:8765' | ||
33 | + | ||
34 | +class Client(tk.Frame): | ||
35 | + def __init__(self, parent, *args, **kwargs): | ||
36 | + tk.Frame.__init__(self, parent, *args, **kwargs) | ||
37 | + | ||
38 | + # URI | ||
39 | + self.uri = 'ws://169.56.95.131:8765' | ||
40 | + | ||
41 | + # Pytorch Model | ||
42 | + self.device = device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu') | ||
43 | + self.mtcnn = MTCNN(keep_all=True, device=device) | ||
44 | + | ||
45 | + # OpenCV | ||
46 | + self.cap = cv2.VideoCapture(0, cv2.CAP_DSHOW) | ||
47 | + self.cam_width = 640 | ||
48 | + self.cam_height = 480 | ||
49 | + self.cap.set(3, self.cam_width) | ||
50 | + self.cap.set(4, self.cam_height) | ||
51 | + | ||
52 | + # Application Function | ||
53 | + | ||
54 | + # cam에서 MTCNN 적용하는 영역 | ||
55 | + self.detecting_square = (500, 300) | ||
56 | + | ||
57 | + # 영상 위에 사각형 색상 지정 | ||
58 | + self.rectangle_color = (0, 0, 255) | ||
59 | + | ||
60 | + # tkinter GUI | ||
61 | + self.width = 740 | ||
62 | + self.height = 700 | ||
63 | + self.parent = parent | ||
64 | + self.parent.title("출석시스템") | ||
65 | + self.parent.geometry("%dx%d+100+100" % (self.width, self.height)) | ||
66 | + self.pack() | ||
67 | + self.create_widgets() | ||
68 | + | ||
69 | + # Event loop and Thread | ||
70 | + self.event_loop = asyncio.new_event_loop() | ||
71 | + self.thread = threading.Thread(target=self.mainthread) | ||
72 | + self.thread.start() | ||
73 | + | ||
74 | + def create_widgets(self): | ||
75 | + image = np.zeros([self.cam_height, self.cam_width, 3], dtype=np.uint8) | ||
76 | + image = Image.fromarray(image) | ||
77 | + image = ImageTk.PhotoImage(image) | ||
78 | + | ||
79 | + font = tk.font.Font(family="맑은 고딕", size=15) | ||
80 | + | ||
81 | + self.alert = tk.Label(self, text="출석시스템", font=font) | ||
82 | + self.alert.grid(row=0, column=0, columnspan=20) | ||
83 | + self.label = tk.Label(self, image=image) | ||
84 | + self.label.grid(row=1, column=0, columnspan=20) | ||
85 | + | ||
86 | + self.log = tk.scrolledtext.ScrolledText(self, wrap = tk.WORD, state=tk.DISABLED, width = 96, height = 10) | ||
87 | + self.log.grid(row=2, column=0, columnspan=20) | ||
88 | + | ||
89 | + | ||
90 | + self.quit = tk.Button(self, text="나가기", fg="red", command=self.stop) | ||
91 | + self.quit.grid(row=3, column=10) | ||
92 | + | ||
93 | + def logging(self, text): | ||
94 | + self.log.config(state=tk.NORMAL) | ||
95 | + self.log.insert(tkinter.CURRENT, text) | ||
96 | + self.log.insert(tkinter.CURRENT, '\n') | ||
97 | + self.log.config(state=tk.DISABLED) | ||
98 | + | ||
99 | + | ||
100 | + def detect_face(self, frame): | ||
101 | + results = self.mtcnn.detect(frame) | ||
102 | + faces = self.mtcnn(frame, return_prob = False) | ||
103 | + image_list = [] | ||
104 | + face_list = [] | ||
105 | + if results[1][0] == None: | ||
106 | + return [], [] | ||
107 | + for box, face, prob in zip(results[0], faces, results[1]): | ||
108 | + if prob < 0.97: | ||
109 | + continue | ||
110 | + # for debug | ||
111 | + # print('face detected. prob:', prob) | ||
112 | + x1, y1, x2, y2 = box | ||
113 | + if (x2-x1) * (y2-y1) < 15000: | ||
114 | + # 얼굴 해상도가 너무 낮으면 무시 | ||
115 | + continue | ||
116 | + image = frame[int(y1):int(y2), int(x1):int(x2)] | ||
117 | + image_list.append(image) | ||
118 | + # MTCNN 데이터 저장 | ||
119 | + face_list.append(face.numpy()) | ||
120 | + return face_list, image_list | ||
121 | + | ||
122 | + def mainthread(self): | ||
123 | + t = threading.currentThread() | ||
124 | + asyncio.set_event_loop(self.event_loop) | ||
125 | + x1 = int(self.cam_width / 2 - self.detecting_square[0] / 2) | ||
126 | + x2 = int(self.cam_width / 2 + self.detecting_square[0] / 2) | ||
127 | + y1 = int(self.cam_height / 2 - self.detecting_square[1] / 2) | ||
128 | + y2 = int(self.cam_height / 2 + self.detecting_square[1] / 2) | ||
129 | + while getattr(t, "do_run", True): | ||
130 | + ret, frame = self.cap.read() | ||
131 | + # model에 이용하기 위해 convert | ||
132 | + converted = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) | ||
133 | + face_list, image_list = self.detect_face(converted[y1:y2, x1:x2]) | ||
134 | + | ||
135 | + # 얼굴이 인식되면 출석요청 | ||
136 | + self.event_loop.run_until_complete(self.send_face(face_list, image_list)) | ||
137 | + | ||
138 | + # show image | ||
139 | + frame = cv2.rectangle(frame, (x1, y1), (x2, y2), self.rectangle_color, 3) | ||
140 | + converted = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) | ||
141 | + # 거울상으로 보여준다 | ||
142 | + converted = cv2.flip(converted,1) | ||
143 | + image = Image.fromarray(converted) | ||
144 | + image = ImageTk.PhotoImage(image) | ||
145 | + self.label.configure(image=image) | ||
146 | + self.label.image = image # kind of double buffering | ||
147 | + | ||
148 | + @asyncio.coroutine | ||
149 | + def set_rectangle(self): | ||
150 | + self.rectangle_color = (255, 0, 0) | ||
151 | + yield from asyncio.sleep(3) | ||
152 | + self.rectangle_color = (0, 0, 255) | ||
153 | + | ||
154 | + async def wait(self, n): | ||
155 | + await asyncio.sleep(n) | ||
156 | + | ||
157 | + async def send_face(self, face_list, image_list): | ||
158 | + try: | ||
159 | + async with websockets.connect(uri) as websocket: | ||
160 | + for face, image in zip(face_list, image_list): | ||
161 | + #type: np.float32 | ||
162 | + send = json.dumps({'action': 'verify', 'MTCNN': face.tolist()}) | ||
163 | + await websocket.send(send) | ||
164 | + recv = await websocket.recv() | ||
165 | + data = json.loads(recv) | ||
166 | + if data['status'] == 'success': | ||
167 | + # 성공 | ||
168 | + self.logging('출석확인: ' + data['student_id']) | ||
169 | + asyncio.ensure_future(self.set_rectangle()) | ||
170 | + else: | ||
171 | + # 이미지 DB에 저장, 일단 보류 | ||
172 | + #if data['status'] == 'fail': | ||
173 | + # send = json.dumps({'action': 'save_image', 'image': image.tolist()}) | ||
174 | + # await websocket.send(send) | ||
175 | + if data['status'] == 'already': | ||
176 | + asyncio.ensure_future(self.set_rectangle()) | ||
177 | + except Exception as e: | ||
178 | + self.logging(e) | ||
179 | + | ||
180 | + def stop(self): | ||
181 | + self.thread.do_run = False | ||
182 | + # self.thread.join() # there is a freeze problem | ||
183 | + self.event_loop.close() | ||
184 | + self.cap.release() | ||
185 | + self.parent.destroy() | ||
186 | + | ||
187 | + | ||
188 | +if __name__ == '__main__': | ||
189 | + root = tk.Tk() | ||
190 | + Client(root) | ||
191 | + root.mainloop() | ||
192 | + |
... | @@ -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 | + # URI |
31 | + self.uri = 'ws://169.56.95.131:8765' | ||
25 | 32 | ||
26 | -uri = 'ws://169.56.95.131:8765' | 33 | + # Pytorch Model |
34 | + self.device = device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu') | ||
35 | + self.mtcnn = MTCNN(keep_all=True, device=device) | ||
27 | 36 | ||
28 | -async def send_face(face_list, image_list): | 37 | + # OpenCV |
29 | - global uri | 38 | + self.cap = cv2.VideoCapture(0, cv2.CAP_DSHOW) |
30 | - async with websockets.connect(uri) as websocket: | 39 | + self.cam_width = 640 |
31 | - for face, image in zip(face_list, image_list): | 40 | + self.cam_height = 480 |
32 | - #type: np.float32 | 41 | + self.cap.set(3, self.cam_width) |
33 | - send = json.dumps({'action': 'register', 'student_id':'2014101898', 'student_name':'김다솜', 'MTCNN': face.tolist()}) | 42 | + self.cap.set(4, self.cam_height) |
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 | 43 | ||
41 | -def detect_face(frame): | 44 | + # Application Function |
42 | - # If required, create a face detection pipeline using MTCNN: | 45 | + self.detecting_square = (200, 200) |
43 | - global mtcnn | 46 | + self.detected = False |
44 | - results = mtcnn.detect(frame) | 47 | + self.face_list = [] |
45 | - image_list = [] | 48 | + self.image_list = [] |
46 | - if results[1][0] == None: | 49 | + |
47 | - return [] | 50 | + # tkinter GUI |
48 | - for box, prob in zip(results[0], results[1]): | 51 | + self.width = 740 |
49 | - if prob < 0.95: | 52 | + self.height = 640 |
50 | - continue | 53 | + self.parent = parent |
51 | - print('face detected. prob:', prob) | 54 | + self.parent.title("출석 데이터 등록") |
52 | - x1, y1, x2, y2 = box | 55 | + self.parent.geometry("%dx%d+100+100" % (self.width, self.height)) |
53 | - image = frame[int(y1-10):int(y2+10), int(x1-10):int(x2+10)] | 56 | + self.pack() |
54 | - image_list.append(image) | 57 | + self.create_widgets() |
55 | - return 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([self.cam_height,self.cam_width,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,98 @@ def detect_face(frame): | ... | @@ -64,23 +125,98 @@ 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 | + # 유저에게 보여줄 땐 거울상으로 보여준다 | ||
168 | + converted = cv2.flip(converted,1) | ||
169 | + image = Image.fromarray(converted) | ||
170 | + image = ImageTk.PhotoImage(image) | ||
171 | + self.label.configure(image=image) | ||
172 | + self.label.image = image # kind of double buffering | ||
173 | + | ||
174 | + # 얼굴이 인식되면 멤버함수에 넣음 | ||
175 | + if face_list: | ||
176 | + self.face_list = face_list | ||
177 | + self.image_list = image_list | ||
178 | + # 2초 후에 사진이 찍힘 | ||
179 | + if detected_time is None: | ||
180 | + detected_time = time.time() | ||
181 | + else: | ||
182 | + self.alert.config(text= "얼굴이 인식되었습니다. %f초 후 사진을 촬영합니다"%(2-(time.time()-detected_time)), fg="red") | ||
183 | + if time.time() - detected_time >= 2: | ||
184 | + self.thread.do_run = False | ||
185 | + self.detected = True | ||
186 | + self.alert.config(text= "얼굴을 등록해주세요. 올바르게 촬영되지 않았을 경우 다시촬영을 눌러주세요.", fg="blue") | ||
187 | + else: | ||
188 | + detected_time = None | ||
189 | + self.face_list = [] | ||
190 | + self.image_list = [] | ||
191 | + | ||
192 | + | ||
193 | + async def wait(self, n): | ||
194 | + await asyncio.sleep(n) | ||
195 | + | ||
196 | + async def send_face(self): | ||
197 | + try: | ||
198 | + async with websockets.connect(self.uri) as websocket: | ||
199 | + for face, image in zip(self.face_list, self.image_list): | ||
200 | + #type: np.float32 | ||
201 | + send = json.dumps({'action': 'register', 'student_id':self.studentID.get(), 'student_name':self.studentName.get(), 'MTCNN': face.tolist()}) | ||
202 | + await websocket.send(send) | ||
203 | + recv = await websocket.recv() | ||
204 | + data = json.loads(recv) | ||
205 | + if data['status'] == 'success': | ||
206 | + tk.messagebox.showinfo("등록완료", self.studentID.get() + ' ' + self.studentName.get()) | ||
207 | + except Exception as e: | ||
208 | + tk.messagebox.showinfo("등록실패", e) | ||
209 | + | ||
210 | + def stop(self): | ||
211 | + self.thread.do_run = False | ||
212 | + # self.thread.join() # there is a freeze problem | ||
213 | + # self.event_loop.close() | ||
214 | + self.cap.release() | ||
215 | + self.parent.destroy() | ||
216 | + | ||
217 | + | ||
218 | +if __name__ == '__main__': | ||
219 | + root = tk.Tk() | ||
220 | + Register(root) | ||
221 | + root.mainloop() | ||
222 | + | ... | ... |
... | @@ -51,18 +51,18 @@ async def register(websocket): | ... | @@ -51,18 +51,18 @@ async def register(websocket): |
51 | global clients | 51 | global clients |
52 | async with lock: | 52 | async with lock: |
53 | clients.add(websocket) | 53 | clients.add(websocket) |
54 | - remote_ip = websocket.remote_address[0] | 54 | + #remote_ip = websocket.remote_address[0] |
55 | - msg='[{ip}] connected'.format(ip=remote_ip) | 55 | + #msg='[{ip}] connected'.format(ip=remote_ip) |
56 | - print(msg) | 56 | + #print(msg) |
57 | 57 | ||
58 | async def unregister(websocket): | 58 | async def unregister(websocket): |
59 | global lock | 59 | global lock |
60 | global clients | 60 | global clients |
61 | async with lock: | 61 | async with lock: |
62 | clients.remove(websocket) | 62 | clients.remove(websocket) |
63 | - remote_ip = websocket.remote_address[0] | 63 | + #remote_ip = websocket.remote_address[0] |
64 | - msg='[{ip}] disconnected'.format(ip=remote_ip) | 64 | + #msg='[{ip}] disconnected'.format(ip=remote_ip) |
65 | - print(msg) | 65 | + #print(msg) |
66 | 66 | ||
67 | async def thread(websocket, path): | 67 | async def thread(websocket, path): |
68 | await register(websocket) | 68 | await register(websocket) |
... | @@ -130,12 +130,12 @@ async def thread(websocket, path): | ... | @@ -130,12 +130,12 @@ async def thread(websocket, path): |
130 | db_embedding = np.frombuffer(row_data['embedding'], dtype=np.float32) | 130 | db_embedding = np.frombuffer(row_data['embedding'], dtype=np.float32) |
131 | db_embedding = db_embedding.reshape((1,512)) | 131 | db_embedding = db_embedding.reshape((1,512)) |
132 | distance = await get_distance(embedding, db_embedding) | 132 | distance = await get_distance(embedding, db_embedding) |
133 | - if (distance < distance_min): | 133 | + if (distance < 0.4): |
134 | verified_id = row_data['student_id'] | 134 | verified_id = row_data['student_id'] |
135 | distance_min = distance | 135 | distance_min = distance |
136 | + break | ||
136 | 137 | ||
137 | # 출석 데이터 전송 | 138 | # 출석 데이터 전송 |
138 | - print('[debug] distance:', distance_min) | ||
139 | send = '' | 139 | send = '' |
140 | if distance_min < 0.4: | 140 | if distance_min < 0.4: |
141 | # 인증 성공 | 141 | # 인증 성공 | ... | ... |
1 | module.exports = (function (){ | 1 | module.exports = (function (){ |
2 | return { | 2 | return { |
3 | local: { | 3 | local: { |
4 | - host: 'yapp.cmarogp1dz0t.ap-northeast-2.rds.amazonaws.com', | 4 | + host: 'localhost', |
5 | - user: 'admin', | 5 | + user: 'root', |
6 | - password: 'findmyzone!', | 6 | + password: '1234', |
7 | database: 'attendance' | 7 | database: 'attendance' |
8 | } | 8 | } |
9 | } | 9 | } | ... | ... |
-
Please register or login to post a comment