from flask import Flask, request, jsonify, render_template from verifier import AIFRKTPVerification from deepface import DeepFace import cv2 import numpy as np import base64 import os import uuid # This is the line Gunicorn looks for. It must be named 'app'. app = Flask(__name__) # Initialize the verifier once when the server starts face_verifier = AIFRKTPVerification() def image_to_base64(img_stream): """Converts an image stream to a base64 string for embedding in HTML.""" return base64.b64encode(img_stream).decode('utf-8') # --- Web Demo Routes --- @app.route('/', methods=['GET', 'POST']) def similarity_demo(): """Handles the visual web demo for similarity checking.""" if request.method == 'POST': if 'ktp_image' not in request.files or 'selfie_image' not in request.files: return "Missing one or both images", 400 ktp_file, selfie_file = request.files['ktp_image'], request.files['selfie_image'] ktp_stream, selfie_stream = ktp_file.read(), selfie_file.read() ktp_img = cv2.imdecode(np.frombuffer(ktp_stream, np.uint8), cv2.IMREAD_COLOR) selfie_img = cv2.imdecode(np.frombuffer(selfie_stream, np.uint8), cv2.IMREAD_COLOR) if ktp_img is None or selfie_img is None: return "Could not decode one or both images.", 400 result = face_verifier.verify_with_images(ktp_img, selfie_img) ktp_base64 = image_to_base64(ktp_stream) selfie_base64 = image_to_base64(selfie_stream) return render_template( 'index.html', verification_result=result, ktp_image_url=f"data:image/jpeg;base64,{ktp_base64}", selfie_image_url=f"data:image/jpeg;base64,{selfie_base64}" ) return render_template('index.html') @app.route('/liveness', methods=['GET', 'POST']) def liveness_demo(): """Handles the visual web demo for the liveness check.""" if request.method == 'POST': if 'liveness_image' not in request.files: return "Missing image file", 400 image_file = request.files['liveness_image'] image_stream = image_file.read() # Save temporarily for DeepFace, as it works best with a file path temp_filename = f"/tmp/{uuid.uuid4().hex}.jpg" with open(temp_filename, 'wb') as f: f.write(image_stream) try: face_objs = DeepFace.extract_faces( img_path=temp_filename, enforce_detection=False, detector_backend='opencv', anti_spoofing=True ) if not face_objs: return "No face detected in the image.", 400 is_real = face_objs[0]['is_real'] liveness_result = {'liveness_passed': bool(is_real)} image_base64 = image_to_base64(image_stream) return render_template( 'liveness.html', liveness_result=liveness_result, image_url=f"data:image/jpeg;base64,{image_base64}" ) finally: # Clean up the temporary file if os.path.exists(temp_filename): os.remove(temp_filename) return render_template('liveness.html') # --- API Routes for iOS App --- @app.route('/api/verify', methods=['POST']) def api_verify(): """Handles the similarity check for the iOS app and returns JSON.""" if 'ktp_image' not in request.files or 'selfie_image' not in request.files: return jsonify({'error': 'Two images are required'}), 400 try: ktp_file = request.files['ktp_image'].read() selfie_file = request.files['selfie_image'].read() ktp_img = cv2.imdecode(np.frombuffer(ktp_file, np.uint8), cv2.IMREAD_COLOR) selfie_img = cv2.imdecode(np.frombuffer(selfie_file, np.uint8), cv2.IMREAD_COLOR) if ktp_img is None or selfie_img is None: return jsonify({'error': 'Could not decode images.'}), 400 result = face_verifier.verify_with_images(ktp_img, selfie_img) if 'error' in result: return jsonify(result), 400 return jsonify(result) except Exception as e: print(f"Error in /api/verify: {e}") return jsonify({'error': 'Internal server error.'}), 500 @app.route('/api/liveness', methods=['POST']) def api_liveness_check(): """Handles the liveness check for the iOS app and returns JSON.""" if 'image' not in request.files: return jsonify({'error': 'An image file is required.'}), 400 image_file = request.files['image'] temp_filename = f"/tmp/{uuid.uuid4().hex}.jpg" image_file.save(temp_filename) try: face_objs = DeepFace.extract_faces( img_path=temp_filename, enforce_detection=False, detector_backend='opencv', anti_spoofing=True ) if not face_objs: return jsonify({'error': 'No face detected in the image.'}), 400 is_real = face_objs[0]['is_real'] confidence = face_objs[0]['confidence'] return jsonify({'liveness_passed': bool(is_real), 'confidence': float(confidence)}) except Exception as e: print(f"Error in /api/liveness: {e}") return jsonify({'error': 'An internal error occurred.'}), 500 finally: if os.path.exists(temp_filename): os.remove(temp_filename) if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)