Spaces:
Sleeping
Sleeping
from flask import Flask, render_template, request, Response, jsonify | |
import cv2 | |
import os | |
import numpy as np | |
import pickle | |
from datetime import datetime | |
# --- Flask App --- | |
app = Flask(__name__) | |
FACE_DATA_DIR = 'face_data' | |
FACE_CASCADE_PATH = cv2.data.haarcascades + 'haarcascade_frontalface_default.xml' | |
if not os.path.exists(FACE_DATA_DIR): | |
os.makedirs(FACE_DATA_DIR) | |
face_cascade = cv2.CascadeClassifier(FACE_CASCADE_PATH) | |
camera = None | |
face_recognizer = cv2.face.LBPHFaceRecognizer_create() | |
is_trained = False | |
has_webcam = os.path.exists("/dev/video0") # deteksi webcam di server | |
def load_face_data(): | |
global is_trained | |
faces, labels, names = [], [], [] | |
if os.path.exists(os.path.join(FACE_DATA_DIR, 'names.pkl')): | |
with open(os.path.join(FACE_DATA_DIR, 'names.pkl'), 'rb') as f: | |
names = pickle.load(f) | |
for idx, name in enumerate(names): | |
face_dir = os.path.join(FACE_DATA_DIR, name) | |
if os.path.exists(face_dir): | |
for filename in os.listdir(face_dir): | |
if filename.endswith('.jpg'): | |
img_path = os.path.join(face_dir, filename) | |
img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) | |
faces.append(img) | |
labels.append(idx) | |
if faces: | |
face_recognizer.train(faces, np.array(labels)) | |
is_trained = True | |
return names | |
return [] | |
def get_camera(): | |
global camera | |
if has_webcam: | |
if camera is None: | |
camera = cv2.VideoCapture(0) | |
return camera | |
return None | |
def index(): | |
names = load_face_data() | |
return render_template('index.html', registered_faces=names, has_webcam=has_webcam) | |
def register(): | |
return render_template('register.html', has_webcam=has_webcam) | |
def recognize(): | |
return render_template('recognize.html', has_webcam=has_webcam) | |
def video_feed(): | |
if not has_webcam: | |
return "Webcam tidak tersedia di server ini", 404 | |
def generate(): | |
cam = get_camera() | |
while True: | |
success, frame = cam.read() | |
if not success: | |
break | |
ret, buffer = cv2.imencode('.jpg', frame) | |
frame = buffer.tobytes() | |
yield (b'--frame\r\n' | |
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n') | |
return Response(generate(), mimetype='multipart/x-mixed-replace; boundary=frame') | |
def recognition_feed(): | |
if not has_webcam: | |
return "Webcam tidak tersedia di server ini", 404 | |
def generate(): | |
cam = get_camera() | |
names = load_face_data() | |
while True: | |
success, frame = cam.read() | |
if not success: | |
break | |
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) | |
faces = face_cascade.detectMultiScale(gray, 1.3, 5) | |
for (x, y, w, h) in faces: | |
cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2) | |
if is_trained and names: | |
roi_gray = gray[y:y+h, x:x+w] | |
roi_gray = cv2.resize(roi_gray, (100, 100)) | |
id_, confidence = face_recognizer.predict(roi_gray) | |
if confidence < 100: | |
name = names[id_] | |
confidence_text = f"{name} ({round(100-confidence)}%)" | |
else: | |
confidence_text = "Unknown" | |
cv2.putText(frame, confidence_text, (x, y-10), | |
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2) | |
ret, buffer = cv2.imencode('.jpg', frame) | |
frame = buffer.tobytes() | |
yield (b'--frame\r\n' | |
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n') | |
return Response(generate(), mimetype='multipart/x-mixed-replace; boundary=frame') | |
def capture_face(): | |
if has_webcam: | |
name = request.json.get('name', '').strip() | |
if not name: | |
return jsonify({'error': 'Nama tidak boleh kosong'}) | |
cam = get_camera() | |
success, frame = cam.read() | |
if not success: | |
return jsonify({'error': 'Gagal mengambil gambar dari kamera'}) | |
return save_face(name, frame) | |
else: | |
return jsonify({'error': 'Webcam tidak tersedia, gunakan /upload_face'}) | |
def upload_face(): | |
name = request.form.get('name', '').strip() | |
file = request.files.get('file') | |
if not name: | |
return jsonify({'error': 'Nama tidak boleh kosong'}) | |
if not file: | |
return jsonify({'error': 'File tidak ditemukan'}) | |
np_img = np.frombuffer(file.read(), np.uint8) | |
frame = cv2.imdecode(np_img, cv2.IMREAD_COLOR) | |
return save_face(name, frame) | |
def save_face(name, frame): | |
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) | |
faces = face_cascade.detectMultiScale(gray, 1.3, 5) | |
if len(faces) == 0: | |
return jsonify({'error': 'Tidak ada wajah yang terdeteksi'}) | |
if len(faces) > 1: | |
return jsonify({'error': 'Terdeteksi lebih dari satu wajah'}) | |
(x, y, w, h) = faces[0] | |
face_roi = gray[y:y+h, x:x+w] | |
face_roi = cv2.resize(face_roi, (100, 100)) | |
person_dir = os.path.join(FACE_DATA_DIR, name) | |
if not os.path.exists(person_dir): | |
os.makedirs(person_dir) | |
filename = f"{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg" | |
cv2.imwrite(os.path.join(person_dir, filename), face_roi) | |
names_file = os.path.join(FACE_DATA_DIR, 'names.pkl') | |
if os.path.exists(names_file): | |
with open(names_file, 'rb') as f: | |
names = pickle.load(f) | |
else: | |
names = [] | |
if name not in names: | |
names.append(name) | |
with open(names_file, 'wb') as f: | |
pickle.dump(names, f) | |
load_face_data() | |
return jsonify({'success': f'Wajah {name} berhasil didaftarkan'}) | |
# --- Wrapper untuk Hugging Face --- | |
from fastapi import FastAPI | |
from starlette.middleware.wsgi import WSGIMiddleware | |
flask_app = app | |
asgi_app = FastAPI() | |
asgi_app.mount("/", WSGIMiddleware(flask_app)) | |
if __name__ == '__main__': | |
flask_app.run(debug=True, host='0.0.0.0', port=5000) | |