허진호
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)
10 - 11 +- client: (i7-7700HQ | 16 GB | Windows)
11 -# 활용기술 12 +
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)
15 - VGGFace2(http://www.robots.ox.ac.uk/~vgg/data/vgg_face2) 17 - VGGFace2(http://www.robots.ox.ac.uk/~vgg/data/vgg_face2)
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)
...\ No newline at end of file ...\ No newline at end of file
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
......
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 +
This diff is collapsed. Click to expand it.
...@@ -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 }
......