Graduate

Haar cascade with margin

1 [server] 1 [server]
2 uri=ws://localhost:3000 2 uri=ws://localhost:3000
3 +[client]
4 +image_size=160
5 +margin=44
...\ No newline at end of file ...\ No newline at end of file
......
1 ################################################## 1 ##################################################
2 #1. webcam에서 얼굴을 인식합니다. 2 #1. webcam에서 얼굴을 인식합니다.
3 -#2. 얼굴일 확률이 97% 이상이고 영역이 15000 이상인 이미지를 서버에 전송 3 +#2. 얼굴일 영역이 15000 이상인 이미지를 서버에 전송
4 ################################################## 4 ##################################################
5 import tkinter as tk 5 import tkinter as tk
6 import tkinter.font 6 import tkinter.font
...@@ -39,7 +39,9 @@ class Client(tk.Frame): ...@@ -39,7 +39,9 @@ class Client(tk.Frame):
39 self.cap.set(4, self.cam_height) 39 self.cap.set(4, self.cam_height)
40 40
41 # Preprocessing 41 # Preprocessing
42 - self.image_size = (160, 160) 42 + self.margin = int(config['client']['margin'])
43 + image_size = int(config['client']['image_size'])
44 + self.image_size = (image_size, image_size)
43 45
44 # cam에서 MTCNN 적용하는 영역 46 # cam에서 MTCNN 적용하는 영역
45 self.detecting_square = (500, 300) 47 self.detecting_square = (500, 300)
...@@ -96,7 +98,8 @@ class Client(tk.Frame): ...@@ -96,7 +98,8 @@ class Client(tk.Frame):
96 minSize=self.image_size 98 minSize=self.image_size
97 ) 99 )
98 for (x, y, w, h) in faces: 100 for (x, y, w, h) in faces:
99 - image = frame[y:y+h, x:x+w] 101 + margin = int(self.margin / 2)
102 + image = frame[y-margin:y+h+margin, x-margin:x+w+margin]
100 return image, True 103 return image, True
101 return [], False 104 return [], False
102 105
...@@ -126,10 +129,13 @@ class Client(tk.Frame): ...@@ -126,10 +129,13 @@ class Client(tk.Frame):
126 self.label.image = image # kind of double buffering 129 self.label.image = image # kind of double buffering
127 130
128 @asyncio.coroutine 131 @asyncio.coroutine
129 - def set_rectangle(self): 132 + def set_rectangle_success(self):
130 self.rectangle_color = (255, 0, 0) 133 self.rectangle_color = (255, 0, 0)
131 yield from asyncio.sleep(2) 134 yield from asyncio.sleep(2)
132 self.rectangle_color = (0, 0, 255) 135 self.rectangle_color = (0, 0, 255)
136 +
137 + def set_rectangle_fail(self):
138 + self.rectangle_color = (0, 0, 255)
133 139
134 async def send_face(self, image): 140 async def send_face(self, image):
135 try: 141 try:
...@@ -137,24 +143,30 @@ class Client(tk.Frame): ...@@ -137,24 +143,30 @@ class Client(tk.Frame):
137 img = base64.b64encode(cv2.imencode('.jpg', image)[1]).decode() 143 img = base64.b64encode(cv2.imencode('.jpg', image)[1]).decode()
138 send = json.dumps({'action': 'verify', 'image': img}) 144 send = json.dumps({'action': 'verify', 'image': img})
139 await websocket.send(send) 145 await websocket.send(send)
140 - recv = await websocket.recv() 146 + recv = await asyncio.wait_for(websocket.recv(), timeout=1000)
141 data = json.loads(recv) 147 data = json.loads(recv)
142 - if data['action'] == 'success': 148 + if data['status'] == 'attend':
143 - self.logging('succeed') 149 + student_id = data['student_id']
150 + student_name = data['student_name']
151 + self.logging(student_id + ' ' + student_name + ' is attend')
144 # change rectangle color 152 # change rectangle color
145 - asyncio.ensure_future(self.set_rectangle()) 153 + asyncio.ensure_future(self.set_rectangle_success())
146 - elif data['action'] == 'fail': 154 + elif data['status'] == 'already':
155 + # self.logging('already attend')
156 + # change rectangle color
157 + asyncio.ensure_future(self.set_rectangle_success())
158 + elif data['status'] == 'fail':
147 self.logging('failed') 159 self.logging('failed')
160 + self.set_rectangle_fail()
148 except Exception as e: 161 except Exception as e:
149 self.logging(e) 162 self.logging(e)
150 163
151 def stop(self): 164 def stop(self):
152 self.thread.do_run = False 165 self.thread.do_run = False
153 - self.thread.join() 166 + #self.thread.join()
154 - self.event_loop.close() 167 + #self.event_loop.close()
155 self.cap.release() 168 self.cap.release()
156 self.parent.destroy() 169 self.parent.destroy()
157 -
158 170
159 if __name__ == '__main__': 171 if __name__ == '__main__':
160 root = tk.Tk() 172 root = tk.Tk()
......
1 [server] 1 [server]
2 uri=ws://localhost:3000 2 uri=ws://localhost:3000
3 +[register]
4 +taking_time=0.2
5 +image_size=160
6 +margin=44
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -26,7 +26,6 @@ class Register(tk.Frame): ...@@ -26,7 +26,6 @@ class Register(tk.Frame):
26 26
27 # URI 27 # URI
28 self.uri = config['server']['uri'] 28 self.uri = config['server']['uri']
29 -
30 # Cascade Model 29 # Cascade Model
31 self.cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml") 30 self.cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
32 31
...@@ -36,9 +35,12 @@ class Register(tk.Frame): ...@@ -36,9 +35,12 @@ class Register(tk.Frame):
36 self.cam_height = 480 35 self.cam_height = 480
37 self.cap.set(3, self.cam_width) 36 self.cap.set(3, self.cam_width)
38 self.cap.set(4, self.cam_height) 37 self.cap.set(4, self.cam_height)
39 - self.image_size = (160, 160) 38 + image_size = int(config['register']['image_size'])
39 + self.image_size = (image_size, image_size)
40 + self.margin = int(config['register']['margin'])
40 41
41 # Application Function 42 # Application Function
43 + self.taking_time = float(config['register']['taking_time'])
42 self.detecting_square = (400, 400) 44 self.detecting_square = (400, 400)
43 self.detected = False 45 self.detected = False
44 self.face = [] 46 self.face = []
...@@ -120,7 +122,8 @@ class Register(tk.Frame): ...@@ -120,7 +122,8 @@ class Register(tk.Frame):
120 minSize=self.image_size 122 minSize=self.image_size
121 ) 123 )
122 for (x, y, w, h) in faces: 124 for (x, y, w, h) in faces:
123 - image = frame[y:y+h, x:x+w] 125 + margin = int(self.margin / 2)
126 + image = frame[y-margin:y+h+margin, x-margin:x+w+margin]
124 return image, True 127 return image, True
125 return [], False 128 return [], False
126 129
...@@ -157,12 +160,12 @@ class Register(tk.Frame): ...@@ -157,12 +160,12 @@ class Register(tk.Frame):
157 # 얼굴이 인식되면 멤버함수에 넣음 160 # 얼굴이 인식되면 멤버함수에 넣음
158 if detected: 161 if detected:
159 self.face = face 162 self.face = face
160 - # 2초 후에 사진이 찍힘 163 + # 0.5초 후에 사진이 찍힘
161 if detected_time is None: 164 if detected_time is None:
162 detected_time = time.time() 165 detected_time = time.time()
163 else: 166 else:
164 - self.alert.config(text= "얼굴이 인식되었습니다. %f초 후 사진을 촬영합니다"%(2-(time.time()-detected_time)), fg="red") 167 + self.alert.config(text= "얼굴이 인식되었습니다. %f초 후 사진을 촬영합니다"%(self.taking_time-(time.time()-detected_time)), fg="red")
165 - if time.time() - detected_time >= 2: 168 + if time.time() - detected_time >= self.taking_time:
166 self.thread.do_run = False 169 self.thread.do_run = False
167 self.detected = True 170 self.detected = True
168 self.alert.config(text= "얼굴을 등록해주세요. 올바르게 촬영되지 않았을 경우 다시촬영을 눌러주세요.", fg="blue") 171 self.alert.config(text= "얼굴을 등록해주세요. 올바르게 촬영되지 않았을 경우 다시촬영을 눌러주세요.", fg="blue")
...@@ -180,9 +183,9 @@ class Register(tk.Frame): ...@@ -180,9 +183,9 @@ class Register(tk.Frame):
180 async with websockets.connect(self.uri) as websocket: 183 async with websockets.connect(self.uri) as websocket:
181 img = base64.b64encode(cv2.imencode('.jpg', self.face)[1]).decode() 184 img = base64.b64encode(cv2.imencode('.jpg', self.face)[1]).decode()
182 send = json.dumps({'action': 'register', 185 send = json.dumps({'action': 'register',
183 - 'student_id':self.studentID.get(), 186 + 'student_id': self.studentID.get(),
184 - 'student_name':self.studentName.get(), 187 + 'student_name': self.studentName.get(),
185 - 'tensor': img}) 188 + 'image': img})
186 await websocket.send(send) 189 await websocket.send(send)
187 recv = await websocket.recv() 190 recv = await websocket.recv()
188 data = json.loads(recv) 191 data = json.loads(recv)
......
1 [verification_server] 1 [verification_server]
2 host=localhost 2 host=localhost
3 port=3000 3 port=3000
4 -model=model/20200816-080621 4 +model=models/20200816-080621
5 threshold=0.75 5 threshold=0.75
6 image_size=160 6 image_size=160
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -11,7 +11,6 @@ import configparser ...@@ -11,7 +11,6 @@ import configparser
11 import logging 11 import logging
12 from datetime import datetime 12 from datetime import datetime
13 13
14 -lock = asyncio.Lock()
15 clients = set() 14 clients = set()
16 15
17 config = configparser.ConfigParser() 16 config = configparser.ConfigParser()
...@@ -27,23 +26,21 @@ image_size = int(config['verification_server']['image_size']) ...@@ -27,23 +26,21 @@ image_size = int(config['verification_server']['image_size'])
27 threshold = float(config['verification_server']['threshold']) 26 threshold = float(config['verification_server']['threshold'])
28 27
29 print('connect to DB') 28 print('connect to DB')
30 -db = pymysql.connect( 29 +attendance_db = pymysql.connect(
31 read_default_file="./DB.cnf" 30 read_default_file="./DB.cnf"
32 ) 31 )
33 32
34 async def register(websocket): 33 async def register(websocket):
35 - async with lock: 34 + clients.add(websocket)
36 - clients.add(websocket) 35 + remote_ip = websocket.remote_address[0]
37 - #remote_ip = websocket.remote_address[0] 36 + msg='[{ip}] connected'.format(ip=remote_ip)
38 - #msg='[{ip}] connected'.format(ip=remote_ip) 37 + print(msg)
39 - #print(msg)
40 38
41 async def unregister(websocket): 39 async def unregister(websocket):
42 - async with lock: 40 + clients.remove(websocket)
43 - clients.remove(websocket) 41 + remote_ip = websocket.remote_address[0]
44 - #remote_ip = websocket.remote_address[0] 42 + msg='[{ip}] disconnected'.format(ip=remote_ip)
45 - #msg='[{ip}] disconnected'.format(ip=remote_ip) 43 + print(msg)
46 - #print(msg)
47 44
48 def resize(image): 45 def resize(image):
49 resized = cv2.resize(image, (image_size, image_size), interpolation=cv2.INTER_CUBIC) 46 resized = cv2.resize(image, (image_size, image_size), interpolation=cv2.INTER_CUBIC)
...@@ -63,22 +60,20 @@ def get_distance(arr1, arr2): ...@@ -63,22 +60,20 @@ def get_distance(arr1, arr2):
63 60
64 async def thread(websocket, path): 61 async def thread(websocket, path):
65 await register(websocket) 62 await register(websocket)
66 - cursor = db.cursor(pymysql.cursors.DictCursor) 63 + cursor = attendance_db.cursor(pymysql.cursors.DictCursor)
64 + remote_ip = websocket.remote_address[0]
67 try: 65 try:
68 async for message in websocket: 66 async for message in websocket:
69 data = json.loads(message) 67 data = json.loads(message)
70 - remote_ip = websocket.remote_address[0]
71 - msg='[{ip}] connected'.format(ip=remote_ip)
72 - print(msg)
73 if data['action'] == 'register': 68 if data['action'] == 'register':
74 # log 69 # log
75 msg='[{ip}] register face'.format(ip=remote_ip) 70 msg='[{ip}] register face'.format(ip=remote_ip)
76 print(msg) 71 print(msg)
77 - 72 + student_id = data['student_id']
73 + student_name = data['student_name']
78 # 학생을 찾음 74 # 학생을 찾음
79 sql = "SELECT student_id FROM student WHERE student_id = %s;" 75 sql = "SELECT student_id FROM student WHERE student_id = %s;"
80 rows_count = cursor.execute(sql, (student_id)) 76 rows_count = cursor.execute(sql, (student_id))
81 -
82 # DB에 학생이 없으면 등록 77 # DB에 학생이 없으면 등록
83 if rows_count == 0: 78 if rows_count == 0:
84 sql = "INSERT INTO student(student_id, student_name) VALUES (%s, %s)" 79 sql = "INSERT INTO student(student_id, student_name) VALUES (%s, %s)"
...@@ -132,7 +127,8 @@ async def thread(websocket, path): ...@@ -132,7 +127,8 @@ async def thread(websocket, path):
132 for row_data in result: 127 for row_data in result:
133 db_embedding = np.frombuffer(row_data['embedding'], dtype=np.float32) 128 db_embedding = np.frombuffer(row_data['embedding'], dtype=np.float32)
134 db_embedding = db_embedding.reshape((1,512)) 129 db_embedding = db_embedding.reshape((1,512))
135 - distance = await get_distance(embedding, db_embedding) 130 + distance = get_distance(embedding, db_embedding)
131 + print(distance)
136 if (distance < threshold): 132 if (distance < threshold):
137 verified_id = row_data['student_id'] 133 verified_id = row_data['student_id']
138 break 134 break
...@@ -143,19 +139,22 @@ async def thread(websocket, path): ...@@ -143,19 +139,22 @@ async def thread(websocket, path):
143 # 인증 성공 139 # 인증 성공
144 # 오늘 이미 출석 됐는지 확인 140 # 오늘 이미 출석 됐는지 확인
145 sql = "SELECT DATE(timestamp) FROM student_attendance WHERE (lecture_id=%s) AND (student_id=%s) AND (DATE(timestamp) = CURDATE());" 141 sql = "SELECT DATE(timestamp) FROM student_attendance WHERE (lecture_id=%s) AND (student_id=%s) AND (DATE(timestamp) = CURDATE());"
146 - rows_count = cursor.execute(sql, ('0', verified_id)) 142 + cursor.execute(sql, ('0', verified_id))
147 -
148 # 출석 기록이 없는 경우에만 143 # 출석 기록이 없는 경우에만
149 - if rows_count == 0: 144 + if cursor.rowcount == 0:
150 # 테이블 맨 뒤에 datetime attribute가 있음. 서버 시간 가져오게 default로 설정해둠. 145 # 테이블 맨 뒤에 datetime attribute가 있음. 서버 시간 가져오게 default로 설정해둠.
151 sql = "INSERT INTO student_attendance(lecture_id, student_id, status) VALUES (%s, %s, %s)" 146 sql = "INSERT INTO student_attendance(lecture_id, student_id, status) VALUES (%s, %s, %s)"
152 # TODO: attend / late 처리 147 # TODO: attend / late 처리
153 cursor.execute(sql, ('0', verified_id, 'attend')) 148 cursor.execute(sql, ('0', verified_id, 'attend'))
154 attendance_db.commit() 149 attendance_db.commit()
150 + sql = "SELECT student_name FROM student WHERE student_id = %s"
151 + cursor.execute(sql, (verified_id))
152 + row_data = cursor.fetchone()
153 + verified_name = row_data['student_name']
155 # log 작성 154 # log 작성
156 msg='[{ip}] verification success {id}'.format(ip=remote_ip, id=verified_id) 155 msg='[{ip}] verification success {id}'.format(ip=remote_ip, id=verified_id)
157 print(msg) 156 print(msg)
158 - send = json.dumps({'status': 'success', 'student_id': verified_id}) 157 + send = json.dumps({'status': 'attend', 'student_id': verified_id, 'student_name': verified_name})
159 else: 158 else:
160 msg='[{ip}] verification failed: {id} is already verified'.format(ip=remote_ip, id=verified_id) 159 msg='[{ip}] verification failed: {id} is already verified'.format(ip=remote_ip, id=verified_id)
161 print(msg) 160 print(msg)
......