diff --git a/.gitignore b/.gitignore index 47d1dc5..df2412e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,58 +1,4 @@ -# Python -__pycache__/ -*.py[cod] -*$py.class -*.so -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# Virtual environments +templates/ .venv/ -venv/ -ENV/ -env/ -.env - -# IDE -.vscode/ -.idea/ -*.swp -*.swo -*~ - -# OS -.DS_Store -Thumbs.db - -# Logs -*.log -logs/ - -# Temporary files -tmp/ -temp/ -tmp_templates/ - -# Model files (if large) -models/*.pkl -models/*.h5 -models/*.model - -# Configuration files with sensitive data -config/local.yaml -config/production.yaml +__pycache__/ +*.pyc \ No newline at end of file diff --git a/26.0.1 b/26.0.1 new file mode 100644 index 0000000..e69de29 diff --git a/50ms b/50ms new file mode 100644 index 0000000..e69de29 diff --git a/config/default.yaml b/config/default.yaml index 8b7c09b..3df80c7 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -39,7 +39,7 @@ liveness: enable_depth_analysis: true enable_texture_analysis: true enable_motion_analysis: true - liveness_threshold: 0.5 + liveness_threshold: 0.2 # Quality assessment thresholds quality: diff --git a/config/development.yaml b/config/development.yaml new file mode 100644 index 0000000..cc1fcdb --- /dev/null +++ b/config/development.yaml @@ -0,0 +1,18 @@ +# Lockless Configuration +system: + log_level: "DEBUG" # Use DEBUG for development + data_directory: "./data" + +camera: + device_id: 0 # Change if you have multiple cameras + +authentication: + similarity_threshold: 0.6 # Lower for testing (normally 0.7) + quality_threshold: 0.5 # Lower for testing (normally 0.6) + +enrollment: + required_samples: 3 # Fewer samples for testing (normally 5) + quality_threshold: 0.5 + +performance: + enable_gpu_acceleration: false # Set to true if you have compatible GPU diff --git a/src/biometric/authentication.py b/src/biometric/authentication.py index 6df8f2d..981f8e3 100644 --- a/src/biometric/authentication.py +++ b/src/biometric/authentication.py @@ -218,6 +218,124 @@ def authenticate_user(self, user_id: str, password: str, finally: self._cleanup_camera() + def authenticate_user_from_frames( + self, user_id: str, password: str, frames: List[np.ndarray] + ) -> AuthenticationResponse: + """Authenticate using pre-captured frames (no camera access).""" + start_time = time.time() + + try: + if not frames: + return AuthenticationResponse( + success=False, + user_id=user_id, + result=AuthenticationResult.ERROR, + error_message="No frames provided", + ) + + if self._is_user_locked_out(user_id): + return AuthenticationResponse( + success=False, + user_id=user_id, + result=AuthenticationResult.REJECTED, + error_message="User account temporarily locked", + ) + + template = self._load_user_template(user_id, password) + if template is None: + return AuthenticationResponse( + success=False, + user_id=user_id, + result=AuthenticationResult.TEMPLATE_NOT_FOUND, + error_message="User template not found", + ) + + threshold = self._get_user_threshold(user_id) + best_similarity = 0.0 + best_quality = 0.0 + best_liveness: Optional[float] = None + processed_faces = 0 + + for frame in frames: + faces = self.face_detector.detect_faces(frame) + if not faces: + continue + + face_bbox = max(faces, key=lambda f: f[2] * f[3]) + x, y, w, h = face_bbox + face_image = frame[y:y+h, x:x+w] + if face_image.size == 0: + continue + + quality_score = self.quality_assessor.assess_quality(face_image) + if quality_score < self.config.quality_threshold: + continue + + liveness_score = None + if self.liveness_detector: + liveness_score = self.liveness_detector.detect_liveness( + frame, face_bbox + ) + if liveness_score < 0.5: + continue + + features = self.feature_extractor.extract_features(face_image) + if features is None: + continue + + processed_faces += 1 + similarity = self.feature_extractor.compute_similarity( + features, template + ) + + if similarity > best_similarity: + best_similarity = similarity + best_quality = quality_score + best_liveness = liveness_score + + if similarity >= threshold: + response = AuthenticationResponse( + success=True, + user_id=user_id, + confidence=similarity, + result=AuthenticationResult.SUCCESS, + processing_time=time.time() - start_time, + quality_score=quality_score, + liveness_score=liveness_score, + ) + self._update_user_statistics(user_id, True) + return response + + processing_time = time.time() - start_time + if processed_faces == 0: + result = AuthenticationResult.NO_FACE_DETECTED + error_message = "No valid face found in provided frames" + else: + result = AuthenticationResult.REJECTED + error_message = "Authentication threshold not met" + + self._update_user_statistics(user_id, False) + return AuthenticationResponse( + success=False, + user_id=user_id, + confidence=best_similarity, + result=result, + processing_time=processing_time, + quality_score=best_quality, + liveness_score=best_liveness, + error_message=error_message, + ) + + except Exception as e: + logger.error(f"Frame-based authentication failed for user {user_id}: {e}") + return AuthenticationResponse( + success=False, + user_id=user_id, + result=AuthenticationResult.ERROR, + processing_time=time.time() - start_time, + error_message=str(e), + ) + def authenticate_any_user(self, enrolled_users: List[str], passwords: Dict[str, str], timeout: Optional[float] = None diff --git a/src/biometric/enrollment.py b/src/biometric/enrollment.py index ff8941f..469b735 100644 --- a/src/biometric/enrollment.py +++ b/src/biometric/enrollment.py @@ -5,6 +5,9 @@ to biometric template generation and storage. """ +from pyexpat import features +from tempfile import template + import cv2 import numpy as np import time @@ -235,6 +238,79 @@ def _initialize_camera(self): except Exception as e: logger.error(f"Camera initialization failed: {e}") raise CameraError(f"Failed to initialize camera: {e}") + + def enroll_from_frames(self, user_id, password, frames): + print("πŸ“Έ Using frames from GUI") + + samples = [] + + for frame in frames: + faces = self.face_detector.detect_faces(frame) + + if not faces: + continue + + # βœ… Get bounding box + x, y, w, h = faces[0] + + # βœ… Crop face from frame + face_img = frame[y:y+h, x:x+w] + + # Safety check + if face_img is None or face_img.size == 0: + continue + + # βœ… Quality check (PASS IMAGE) + quality_score = self.quality_assessor.assess_quality(face_img) + + if quality_score < self.config.quality_threshold: + continue + + # βœ… Feature extraction (PASS IMAGE) + from time import time + + features = self.feature_extractor.extract_features(face_img) + + if features is None: + continue + + sample = EnrollmentSample( + image=face_img, + face_bbox=(x, y, w, h), + quality_score=quality_score, + features=features, + timestamp=time() + ) + + samples.append(sample) + + if len(samples) == 0: + return EnrollmentResult( + success=False, + user_id=user_id, + samples_collected=0, + error_message="No valid samples collected", + processing_time=0.0 + ) + + template = self._generate_template(samples) + + # βœ… correct order + self._store_template(user_id, template, password) + + print("βœ… Enrollment successful (GUI mode)") + + return EnrollmentResult( + success=True, + user_id=user_id, + template_id=None, + samples_collected=len(samples), + average_quality=float( + np.mean([s.quality_score for s in samples]) + ), + error_message=None, + processing_time=0.0 + ) def _collect_enrollment_samples(self, user_id: str ) -> List[EnrollmentSample]: diff --git a/src/biometric/face_detection.py b/src/biometric/face_detection.py index cdbe31f..620cbb9 100644 --- a/src/biometric/face_detection.py +++ b/src/biometric/face_detection.py @@ -14,8 +14,16 @@ except Exception: # pragma: no cover - runtime dependency guard ort = None # type: ignore[assignment] -from core.logging import get_logger, PerformanceTimer -from core.exceptions import FaceDetectionError, ModelLoadError +try: + from core.logging import get_logger, PerformanceTimer + from core.exceptions import FaceDetectionError, ModelLoadError +except ModuleNotFoundError: + try: + from src.core.logging import get_logger, PerformanceTimer + from src.core.exceptions import FaceDetectionError, ModelLoadError + except ModuleNotFoundError: + from LockLess.src.core.logging import get_logger, PerformanceTimer + from LockLess.src.core.exceptions import FaceDetectionError, ModelLoadError logger = get_logger(__name__) @@ -416,22 +424,37 @@ def _resolve_haarcascade_path(self) -> str: return "haarcascade_frontalface_default.xml" -# Example usage and testing if __name__ == "__main__": - # Test face detection - detector = FaceDetector(backend="opencv") # Use OpenCV for testing + detector = FaceDetector(backend="opencv") + + cap = cv2.VideoCapture(0) + + if not cap.isOpened(): + print("Error: Cannot access camera") + exit() + + print("Press 'q' to exit") - print("Testing face detection...") + while True: + ret, frame = cap.read() + if not ret: + break - # Create a test image (in practice, this would come from camera) - test_image = np.zeros((480, 640, 3), dtype=np.uint8) + # Detect faces + faces = detector.detect_faces(frame) + + print("Faces detected:", len(faces)) + + # Draw boxes + for (x, y, w, h) in faces: + cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2) - # Test detection - faces = detector.detect_faces(test_image) - print(f"Detected {len(faces)} faces") + # SHOW WINDOW πŸ”₯ + cv2.imshow("Face Detection", frame) - if faces: - largest_face = detector.detect_largest_face(test_image) - print(f"Largest face: {largest_face}") + # Exit on 'q' + if cv2.waitKey(1) & 0xFF == ord('q'): + break - print("Face detection test completed!") + cap.release() + cv2.destroyAllWindows() diff --git a/src/biometric/liveness.py b/src/biometric/liveness.py index 687865d..41bd8c6 100644 --- a/src/biometric/liveness.py +++ b/src/biometric/liveness.py @@ -44,11 +44,11 @@ class SpoofingType(Enum): @dataclass class LivenessConfig: """Configuration for liveness detection.""" - enable_depth_analysis: bool = True - enable_texture_analysis: bool = True + enable_depth_analysis: bool = False + enable_texture_analysis: bool = False enable_motion_analysis: bool = True enable_challenge_response: bool = False - liveness_threshold: float = 0.5 + liveness_threshold: float = 0.2 depth_threshold: float = 0.3 texture_threshold: float = 0.6 motion_threshold: float = 0.4 @@ -86,7 +86,7 @@ def __init__(self, config: Optional[LivenessConfig] = None): config: Liveness detection configuration """ self.config = config or LivenessConfig() - + # Frame buffer for temporal analysis self.frame_buffer: List[np.ndarray] = [] self.depth_buffer: List[np.ndarray] = [] diff --git a/src/ui/main_window.py b/src/ui/main_window.py index ba18881..c43a65c 100644 --- a/src/ui/main_window.py +++ b/src/ui/main_window.py @@ -2,7 +2,13 @@ from __future__ import annotations from functools import partial +from time import time +import cv2 +from PyQt5.QtWidgets import QScrollArea +from PyQt5.QtCore import QTimer +from PyQt5.QtGui import QImage, QPixmap from typing import Any, Callable, Dict, List, Optional +from urllib import response from PyQt5.QtCore import Qt from PyQt5.QtGui import QFont, QPixmap @@ -26,6 +32,7 @@ QVBoxLayout, QWidget, ) +import cv2 from .services import ( ServiceResponse, @@ -47,6 +54,7 @@ def __init__(self, config_path: Optional[str] = None) -> None: super().__init__() self._config_path = config_path self._runners: List[WorkerRunner] = [] + self.current_frame = None self.setWindowTitle("Lockless Biometric Suite") self.resize(1024, 720) @@ -67,6 +75,11 @@ def __init__(self, config_path: Optional[str] = None) -> None: self._populate_system_summary() self._refresh_users() + # Camera setup + self.cap = None + self.timer = QTimer() + self.timer.timeout.connect(self.update_frame) + # region UI construction ------------------------------------------------- def _build_dashboard_tab(self) -> None: dashboard = QWidget() @@ -104,15 +117,24 @@ def _build_dashboard_tab(self) -> None: self.system_info_box = QGroupBox("System summary") self.system_info_form = QFormLayout(self.system_info_box) layout.addWidget(self.system_info_box) + # πŸ‘‡ ADD THIS + self.camera_label = QLabel() + self.camera_label.setFixedHeight(300) + self.camera_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + self.camera_label.setAlignment(Qt.AlignCenter) + layout.addWidget(self.camera_label) button_row = QHBoxLayout() button_row.addStretch(1) self.dashboard_camera_button = QPushButton("Test camera") - self.dashboard_camera_button.clicked.connect( - self._on_dashboard_camera_test) + self.dashboard_camera_button.clicked.connect(self.start_camera) button_row.addWidget(self.dashboard_camera_button) + self.stop_camera_button = QPushButton("Stop Camera") + self.stop_camera_button.clicked.connect(self.stop_camera) + button_row.addWidget(self.stop_camera_button) + self.dashboard_refresh_button = QPushButton("Refresh user list") self.dashboard_refresh_button.clicked.connect(self._refresh_users) button_row.addWidget(self.dashboard_refresh_button) @@ -120,7 +142,11 @@ def _build_dashboard_tab(self) -> None: button_row.addStretch(1) layout.addLayout(button_row) - self.tabs.addTab(dashboard, "Dashboard") + scroll = QScrollArea() + scroll.setWidgetResizable(True) + scroll.setWidget(dashboard) + + self.tabs.addTab(scroll, "Dashboard") def _build_enrollment_tab(self) -> None: page = QWidget() @@ -154,6 +180,12 @@ def _build_enrollment_tab(self) -> None: layout.addWidget(form_box) + # πŸ‘‡ ADD THIS + self.enroll_camera_label = QLabel() + self.enroll_camera_label.setFixedHeight(300) + self.enroll_camera_label.setAlignment(Qt.AlignCenter) + layout.addWidget(self.enroll_camera_label) + self.enroll_output = QPlainTextEdit() self.enroll_output.setReadOnly(True) self.enroll_output.setPlaceholderText( @@ -203,6 +235,12 @@ def _build_authentication_tab(self) -> None: layout.addWidget(form_box) + # πŸ‘‡ ADD THIS + self.auth_camera_label = QLabel() + self.auth_camera_label.setFixedHeight(300) + self.auth_camera_label.setAlignment(Qt.AlignCenter) + layout.addWidget(self.auth_camera_label) + self.auth_output = QPlainTextEdit() self.auth_output.setReadOnly(True) self.auth_output.setPlaceholderText( @@ -290,21 +328,25 @@ def _run_async( self._runners.append(runner) def _cleanup(payload: Dict[str, Any]) -> None: - runner.dispose() - if runner in self._runners: - self._runners.remove(runner) - response = ServiceResponse(**payload) - callback(response) - if on_complete: - on_complete() + try: + runner.dispose() + if runner in self._runners: + self._runners.remove(runner) + response = ServiceResponse(**payload) + callback(response) + finally: + if on_complete: + on_complete() def _handle_failure(message: str) -> None: - runner.dispose() - if runner in self._runners: - self._runners.remove(runner) - QMessageBox.critical(self, "Operation failed", message) - if on_complete: - on_complete() + try: + runner.dispose() + if runner in self._runners: + self._runners.remove(runner) + QMessageBox.critical(self, "Operation failed", message) + finally: + if on_complete: + on_complete() def _announce_start() -> None: self.status_bar.showMessage(f"{description} in progress…") @@ -332,11 +374,32 @@ def _start_enrollment(self) -> None: self.enroll_button.setEnabled(False) self.enroll_output.clear() + self.start_camera() def task() -> ServiceResponse: - return enroll_user_service(user, password, config) + import time + + print("πŸš€ Enrollment started") + + frames = [] + start_time = time.time() + + while len(frames) < 20: # capture 20 frames + if self.current_frame is not None: + frames.append(self.current_frame.copy()) + print("βœ… Frame captured") + + else: + print("❌ No frame yet") # βœ… ADD THIS + + time.sleep(0.1) + + print("🎯 Frames collected, calling service") + + return enroll_user_service(user, password, config, frames=frames) def on_complete() -> None: + self.stop_camera() self.enroll_button.setEnabled(True) self.status_bar.clearMessage() self._refresh_users() @@ -375,11 +438,24 @@ def _start_authentication(self) -> None: self.auth_button.setEnabled(False) self.auth_output.clear() + self.start_camera() def task() -> ServiceResponse: - return authenticate_user_service(user, password, config) + import time + + frames = [] + start_time = time.time() + + while len(frames) < 15: + if self.current_frame is not None: + frames.append(self.current_frame.copy()) + + time.sleep(0.1) + + return authenticate_user_service(user, password, config, frames=frames) def on_complete() -> None: + self.stop_camera() self.auth_button.setEnabled(True) self.status_bar.clearMessage() @@ -449,21 +525,39 @@ def callback(response: ServiceResponse) -> None: self._refresh_users() self._run_async("Deleting user", task, callback) - def _on_dashboard_camera_test(self) -> None: button = self.dashboard_camera_button button.setEnabled(False) def task() -> ServiceResponse: - summary = get_system_summary(self._config_path) - camera_id = summary.get("camera_id", 0) - return test_camera_service(camera_id) + import cv2 + + cap = cv2.VideoCapture(0) + + if not cap.isOpened(): + return ServiceResponse(success=False, message="Camera not accessible") + + while True: + ret, frame = cap.read() + if not ret: + break + + cv2.imshow("Camera Preview - Press Q to exit", frame) + + # Press Q to close camera + if cv2.waitKey(1) & 0xFF == ord('q'): + break + + cap.release() + cv2.destroyAllWindows() + + return ServiceResponse(success=True, message="Camera preview closed") def callback(response: ServiceResponse) -> None: QMessageBox.information( self, "Camera test", - response.message, + response.message ) def on_complete() -> None: @@ -472,6 +566,71 @@ def on_complete() -> None: self._run_async("Camera test", task, callback, on_complete) + # ================= CAMERA FUNCTIONS ================= + + def start_camera(self): + print("πŸ“· Starting camera...") + # Always reset previous session first to avoid stale timers/captures. + self.stop_camera() + + self.cap = cv2.VideoCapture(0, cv2.CAP_DSHOW) + if not self.cap.isOpened(): + print("❌ Camera failed to open") + self.cap.release() + self.cap = None + return + + if not self.timer.isActive(): + self.timer.start(30) + + def update_frame(self): + if self.cap is None or not self.cap.isOpened(): + return + + ret, frame = self.cap.read() + if not ret: + print("❌ Frame not received") + return + + self.current_frame = frame + + rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + + h, w, ch = rgb.shape + bytes_per_line = ch * w + qt_img = QImage(rgb.data, w, h, bytes_per_line, QImage.Format_RGB888) + + pixmap = QPixmap.fromImage(qt_img) + + # Dashboard + if hasattr(self, "camera_label"): + self.camera_label.setPixmap(pixmap) + + # Enrollment tab + if hasattr(self, "enroll_camera_label"): + self.enroll_camera_label.setPixmap(pixmap) + + # Authentication tab + if hasattr(self, "auth_camera_label"): + self.auth_camera_label.setPixmap(pixmap) + + def stop_camera(self): + if hasattr(self, "timer") and self.timer.isActive(): + self.timer.stop() + + if hasattr(self, "cap") and self.cap is not None: + self.cap.release() + self.cap = None + self.current_frame = None + + # Clear all camera views + if hasattr(self, "camera_label"): + self.camera_label.clear() + if hasattr(self, "enroll_camera_label"): + self.enroll_camera_label.clear() + if hasattr(self, "auth_camera_label"): + self.auth_camera_label.clear() + # endregion ------------------------------------------------------------- def closeEvent(self, event) -> None: # type: ignore[override] @@ -479,6 +638,7 @@ def closeEvent(self, event) -> None: # type: ignore[override] runner.dispose() self._runners.clear() super().closeEvent(event) + self.stop_camera() def run_standalone() -> int: diff --git a/src/ui/services.py b/src/ui/services.py index 1d2b9f1..2283b26 100644 --- a/src/ui/services.py +++ b/src/ui/services.py @@ -40,7 +40,7 @@ def _load_config(config_path: Optional[str]) -> ConfigManager: def enroll_user_service( - user_id: str, password: str, config_path: Optional[str] = None + user_id: str, password: str, config_path: Optional[str] = None, frames=None ) -> ServiceResponse: """Run the biometric enrollment workflow.""" @@ -57,7 +57,14 @@ def enroll_user_service( ) enrollment = BiometricEnrollment(enrollment_config) - result = enrollment.enroll_user(user_id, password) + + # βœ… NEW: If GUI is sending frames + if frames is not None: + print("βœ… Using GUI frames, not opening camera again") + result = enrollment.enroll_from_frames(user_id, password, frames) + else: + # old behavior (CLI mode) + result = enrollment.enroll_user(user_id, password) payload: Dict[str, Any] = {"result": asdict(result)} if result.success: @@ -78,7 +85,7 @@ def enroll_user_service( def authenticate_user_service( user_id: str, password: str, - config_path: Optional[str] = None, + config_path: Optional[str] = None, frames=None ) -> ServiceResponse: """Execute authentication for a specific user.""" @@ -99,7 +106,12 @@ def authenticate_user_service( ) engine = AuthenticationEngine(auth_config) - response = engine.authenticate_user(user_id, password) + if frames is not None: + response = engine.authenticate_user_from_frames( + user_id, password, frames + ) + else: + response = engine.authenticate_user(user_id, password) payload: Dict[str, Any] = {"response": asdict(response)} if response.success: diff --git a/templates/1160130875fda081.enc b/templates/1160130875fda081.enc deleted file mode 100644 index eaae372..0000000 --- a/templates/1160130875fda081.enc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "encrypted_template": "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", - "metadata": { - "created_at": 1759585805.1345472, - "feature_dimension": 512, - "version": "1.0", - "algorithm": "face_recognition" - }, - "checksum": "36891604e40a5c2f2b8b52aa01cea5a7cc92a7a62bcbfbfde031e6bb0667b812" -} \ No newline at end of file diff --git a/test_camera.py b/test_camera.py new file mode 100644 index 0000000..0f5b0e0 --- /dev/null +++ b/test_camera.py @@ -0,0 +1,25 @@ +import cv2 + +cap = cv2.VideoCapture(0, cv2.CAP_DSHOW) + +if not cap.isOpened(): + print("❌ Camera not opening") + exit() + +print("βœ… Camera started") + +while True: + ret, frame = cap.read() + + if not ret: + print("❌ Frame not received") + break + + cv2.imshow("Camera Test", frame) + + # Press ESC to exit + if cv2.waitKey(1) & 0xFF == 27: + break + +cap.release() +cv2.destroyAllWindows() \ No newline at end of file diff --git a/test_face_detection.py b/test_face_detection.py new file mode 100644 index 0000000..3859401 --- /dev/null +++ b/test_face_detection.py @@ -0,0 +1,32 @@ +import cv2 +import sys +from pathlib import Path + +SRC_PATH = Path(__file__).resolve().parent / "src" +if str(SRC_PATH) not in sys.path: + sys.path.insert(0, str(SRC_PATH)) + +from biometric.face_detection import FaceDetector +detector = FaceDetector(backend="opencv") + +cap = cv2.VideoCapture(0) + +while True: + ret, frame = cap.read() + if not ret: + break + + faces = detector.detect_faces(frame) + + print("Faces detected:", len(faces)) # πŸ‘ˆ IMPORTANT + + for (x, y, w, h) in faces: + cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2) + + cv2.imshow("Face Detection", frame) + + if cv2.waitKey(1) & 0xFF == ord('q'): + break + +cap.release() +cv2.destroyAllWindows()