huy00001 commited on
Commit
8f5bde4
·
verified ·
1 Parent(s): 5f5d827

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +250 -0
app.py ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, jsonify
2
+ from flask_cors import CORS
3
+ import base64
4
+ import secrets
5
+ import os
6
+ from deepface import DeepFace
7
+ import cv2
8
+ import numpy as np
9
+ from pymongo import MongoClient
10
+ app = Flask(__name__)
11
+ CORS(app)
12
+
13
+ # ========================
14
+ # KẾT NỐI MONGODB
15
+ # ========================
16
+ mongo_uri = "mongodb+srv://huyh01480_db_user:zxvAwzAhr8yk3lWe@cluster0.n8pboqq.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0"
17
+ client = MongoClient(mongo_uri)
18
+
19
+ db = client["userdb"] # database
20
+ users_col = db["users"] # collection tương đương bảng user
21
+
22
+
23
+ # ========================
24
+ # REGISTER USER
25
+ # ========================
26
+
27
+
28
+ from deepface import DeepFace
29
+ import cv2
30
+ import numpy as np
31
+ import base64
32
+
33
+ @app.route('/api/register', methods=['POST'])
34
+ def register():
35
+ data = request.json
36
+
37
+ username = data.get('username')
38
+ password = data.get('password')
39
+ img_data = data.get('img')
40
+
41
+ fullName = data.get('fullName')
42
+ email = data.get('email')
43
+ phone = data.get('phone')
44
+ gender = data.get('gender')
45
+
46
+ # Kiểm tra dữ liệu bắt buộc
47
+ if not username or not password or not img_data:
48
+ return jsonify({'error': 'username, password và ảnh là bắt buộc'}), 400
49
+
50
+ img_str = img_data.split(",")[1] if "," in img_data else img_data
51
+
52
+ # Kiểm tra Base64 hợp lệ
53
+ try:
54
+ decoded = base64.b64decode(img_str)
55
+ except Exception:
56
+ return jsonify({'error': 'Ảnh không hợp lệ, phải là Base64'}), 400
57
+
58
+ # Chuyển base64 → ảnh OpenCV
59
+ try:
60
+ nparr = np.frombuffer(decoded, np.uint8)
61
+ img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
62
+
63
+ if img is None:
64
+ return jsonify({'error': 'Ảnh không thể đọc được'}), 400
65
+
66
+ except Exception:
67
+ return jsonify({'error': 'Không thể xử lý ảnh'}), 400
68
+
69
+ # ===== KIỂM TRA ẢNH CÓ MẶT KHÔNG =====
70
+ try:
71
+ DeepFace.extract_faces(img, enforce_detection=True)
72
+ except Exception:
73
+ return jsonify({'error': 'Ảnh không có khuôn mặt hợp lệ'}), 400
74
+
75
+ try:
76
+ # Kiểm tra username trùng
77
+ existing_user = users_col.find_one({"userName": username})
78
+ if existing_user:
79
+ return jsonify({'error': 'Tên người dùng đã tồn tại'}), 409
80
+
81
+ # Dữ liệu người dùng chỉ gồm các trường yêu cầu
82
+ user_info = {
83
+ "userName": username,
84
+ "password": password,
85
+ "img": img_str,
86
+ "fullName": fullName,
87
+ "email": email,
88
+ "phone": phone,
89
+ "gender": gender,
90
+ }
91
+
92
+ # Lưu vào MongoDB
93
+ users_col.insert_one(user_info)
94
+
95
+ return jsonify({'message': 'Đăng ký thành công!', 'user': username}), 201
96
+
97
+ except Exception as e:
98
+ return jsonify({'error': str(e)}), 500
99
+ # LOGIN + KIỂM TRA KHUÔN MẶT
100
+ # ========================
101
+ @app.route('/api/login', methods=['POST'])
102
+ def login():
103
+ data = request.json
104
+ username = data.get('username')
105
+ password = data.get('password')
106
+ img_data = data.get('img')
107
+
108
+ if not username or not password or not img_data:
109
+ return jsonify({'error': 'username, password và ảnh là bắt buộc'}), 400
110
+
111
+ img_str = img_data.split(",")[1] if "," in img_data else img_data
112
+
113
+ try:
114
+ # Lấy user từ MongoDB
115
+ user = users_col.find_one({"userName": username, "password": password})
116
+
117
+ if not user:
118
+ return jsonify({'error': 'Username hoặc password sai'}), 401
119
+
120
+ # Base64 → ảnh
121
+ nparr_input = np.frombuffer(base64.b64decode(img_str), np.uint8)
122
+ img_input = cv2.imdecode(nparr_input, cv2.IMREAD_COLOR)
123
+
124
+ nparr_db = np.frombuffer(base64.b64decode(user['img']), np.uint8)
125
+ img_db = cv2.imdecode(nparr_db, cv2.IMREAD_COLOR)
126
+
127
+ # So sánh mặt
128
+ try:
129
+ result = DeepFace.verify(img_input, img_db, enforce_detection=True)
130
+ except Exception as e:
131
+ return jsonify({'error': f'Lỗi khi nhận diện khuôn mặt: {e}'}), 400
132
+
133
+ # Tính %
134
+ distance = result.get("distance", 1)
135
+ similarity = max(0, (1 - distance)) * 100
136
+ similarity = round(similarity, 2)
137
+
138
+ if result['verified']:
139
+ token = secrets.token_hex(16)
140
+
141
+ # Xóa _id vì không serializable
142
+ user_info = {
143
+ "userName": user.get("userName"),
144
+ "fullName": user.get("fullName"),
145
+ "email": user.get("email"),
146
+ "phone": user.get("phone"),
147
+ "gender": user.get("gender"),
148
+ "img": user.get("img")
149
+ }
150
+
151
+ return jsonify({
152
+ 'message': 'Login thành công!',
153
+ 'token': token,
154
+ 'similarity': similarity,
155
+ 'user': user_info
156
+ })
157
+ else:
158
+ return jsonify({
159
+ 'error': 'Khuôn mặt không trùng khớp',
160
+ 'similarity': similarity
161
+ }), 401
162
+
163
+ except Exception as e:
164
+ return jsonify({'error': str(e)}), 500
165
+
166
+ # ========================
167
+ # PHÂN TÍCH KHUÔN MẶT
168
+ # ========================
169
+ def convert_numpy(obj):
170
+ if isinstance(obj, dict):
171
+ return {k: convert_numpy(v) for k, v in obj.items()}
172
+ elif isinstance(obj, list):
173
+ return [convert_numpy(i) for i in obj]
174
+ elif isinstance(obj, (np.float32, np.float64)):
175
+ return float(obj)
176
+ elif isinstance(obj, (np.int32, np.int64)):
177
+ return int(obj)
178
+ else:
179
+ return obj
180
+
181
+ @app.route('/api/analyze', methods=['POST'])
182
+ def analyze_face():
183
+ data = request.json
184
+ img_data = data.get('img')
185
+
186
+ if not img_data:
187
+ return jsonify({'error': 'Ảnh là bắt buộc'}), 400
188
+
189
+ img_str = img_data.split(",")[1] if "," in img_data else img_data
190
+
191
+ try:
192
+ nparr = np.frombuffer(base64.b64decode(img_str), np.uint8)
193
+ img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
194
+
195
+ result = DeepFace.analyze(img, actions=['age','gender','emotion','race'], enforce_detection=True)
196
+ result = convert_numpy(result)
197
+
198
+ return jsonify({'result': result})
199
+
200
+ except Exception as e:
201
+ return jsonify({'error': f'Lỗi khi phân tích khuôn mặt: {e}'}), 400
202
+
203
+ @app.route('/api/compare', methods=['POST'])
204
+ def compare_faces():
205
+ data = request.json
206
+ img1_data = data.get("img1")
207
+ img2_data = data.get("img2")
208
+
209
+ if not img1_data or not img2_data:
210
+ return jsonify({'error': 'Cần 2 ảnh base64 để so sánh'}), 400
211
+
212
+ # Tách phần base64 (nếu có prefix data:image/png;base64,)
213
+ img1_str = img1_data.split(",")[1] if "," in img1_data else img1_data
214
+ img2_str = img2_data.split(",")[1] if "," in img2_data else img2_data
215
+
216
+ try:
217
+ # Base64 → OpenCV image
218
+ nparr1 = np.frombuffer(base64.b64decode(img1_str), np.uint8)
219
+ img1 = cv2.imdecode(nparr1, cv2.IMREAD_COLOR)
220
+
221
+ nparr2 = np.frombuffer(base64.b64decode(img2_str), np.uint8)
222
+ img2 = cv2.imdecode(nparr2, cv2.IMREAD_COLOR)
223
+
224
+ # So sánh khuôn mặt bằng DeepFace
225
+ try:
226
+ result = DeepFace.verify(img1, img2, enforce_detection=True)
227
+ except Exception as e:
228
+ return jsonify({'error': f'Lỗi nhận diện: {e}'}), 400
229
+
230
+ # Tính phần trăm giống nhau
231
+ distance = result.get("distance", 1)
232
+ similarity = max(0, (1 - distance)) * 100
233
+ similarity = round(similarity, 2)
234
+
235
+ return jsonify({
236
+ "verified": result.get("verified", False),
237
+ "distance": distance,
238
+ "similarity": similarity
239
+ })
240
+
241
+ except Exception as e:
242
+ return jsonify({'error': str(e)}), 500
243
+
244
+ # ========================
245
+ # RUN
246
+ # ========================
247
+
248
+ if __name__ == '__main__':
249
+ port = int(os.environ.get("PORT", 7860))
250
+ app.run(host='0.0.0.0', port=port, debug=True)