허진호
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
......
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 }
......