From 6b2af863d24dba83b16f526abc7d1cf05e72dacd Mon Sep 17 00:00:00 2001 From: Akshit Agarwal Date: Fri, 27 Mar 2026 12:16:48 +0530 Subject: [PATCH 1/4] Added face detection and enrollment working with OpenCV fallback --- templates/246123bfddaa83d8.enc | 10 ++++++++++ test_camera.py | 19 +++++++++++++++++++ test_face_detection.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 templates/246123bfddaa83d8.enc create mode 100644 test_camera.py create mode 100644 test_face_detection.py diff --git a/templates/246123bfddaa83d8.enc b/templates/246123bfddaa83d8.enc new file mode 100644 index 0000000..143457b --- /dev/null +++ b/templates/246123bfddaa83d8.enc @@ -0,0 +1,10 @@ +{ + "encrypted_template": "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", + "metadata": { + "created_at": 1774593122.4140468, + "feature_dimension": 512, + "version": "1.0", + "algorithm": "face_recognition" + }, + "checksum": "dc580c65e4f3e142dc97e06bbc356b3674d78160fca85c3810d0b75048e07d89" +} \ No newline at end of file diff --git a/test_camera.py b/test_camera.py new file mode 100644 index 0000000..53262f5 --- /dev/null +++ b/test_camera.py @@ -0,0 +1,19 @@ +import cv2 + +cap = cv2.VideoCapture(0) + +while True: + ret, frame = cap.read() + + if not ret: + print("Camera not working ❌") + break + + cv2.imshow("Camera Test", frame) + + # Press ESC to exit + if cv2.waitKey(1) & 0xFF == ord('q'): + 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..21e901d --- /dev/null +++ b/test_face_detection.py @@ -0,0 +1,28 @@ +import cv2 +import sys, os +sys.path.append(os.path.abspath("LockLess/src")) + +from src.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() \ No newline at end of file From bed0249b390c3b9ac96ef38b542360d2a9717e76 Mon Sep 17 00:00:00 2001 From: Akshit Agarwal Date: Fri, 27 Mar 2026 12:21:17 +0530 Subject: [PATCH 2/4] Added gitignore and removed sensitive files --- .gitignore | 60 ++-------------------------------- templates/246123bfddaa83d8.enc | 10 ------ 2 files changed, 3 insertions(+), 67 deletions(-) delete mode 100644 templates/246123bfddaa83d8.enc 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/templates/246123bfddaa83d8.enc b/templates/246123bfddaa83d8.enc deleted file mode 100644 index 143457b..0000000 --- a/templates/246123bfddaa83d8.enc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "encrypted_template": "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", - "metadata": { - "created_at": 1774593122.4140468, - "feature_dimension": 512, - "version": "1.0", - "algorithm": "face_recognition" - }, - "checksum": "dc580c65e4f3e142dc97e06bbc356b3674d78160fca85c3810d0b75048e07d89" -} \ No newline at end of file From 1bc6dd3b6d21d870fcfaaef08bebccd1f5a3f3db Mon Sep 17 00:00:00 2001 From: Akshit Agarwal Date: Sun, 29 Mar 2026 17:55:51 +0530 Subject: [PATCH 3/4] Added camera window display and optimized face detection with resizing --- 50ms | 0 src/biometric/face_detection.py | 45 ++++++++++++++++++++++----------- test_camera.py | 28 +++++++++++++++++--- 3 files changed, 54 insertions(+), 19 deletions(-) create mode 100644 50ms diff --git a/50ms b/50ms new file mode 100644 index 0000000..e69de29 diff --git a/src/biometric/face_detection.py b/src/biometric/face_detection.py index cdbe31f..0081b57 100644 --- a/src/biometric/face_detection.py +++ b/src/biometric/face_detection.py @@ -14,8 +14,8 @@ 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 +from src.core.logging import get_logger, PerformanceTimer +from src.core.exceptions import FaceDetectionError, ModelLoadError logger = get_logger(__name__) @@ -416,22 +416,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") - print("Testing face detection...") + cap = cv2.VideoCapture(0) - # Create a test image (in practice, this would come from camera) - test_image = np.zeros((480, 640, 3), dtype=np.uint8) + if not cap.isOpened(): + print("Error: Cannot access camera") + exit() - # Test detection - faces = detector.detect_faces(test_image) - print(f"Detected {len(faces)} faces") + print("Press 'q' to exit") - if faces: - largest_face = detector.detect_largest_face(test_image) - print(f"Largest face: {largest_face}") + while True: + ret, frame = cap.read() + if not ret: + break - print("Face detection test completed!") + # 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) + + # SHOW WINDOW πŸ”₯ + cv2.imshow("Face Detection", frame) + + # Exit on 'q' + if cv2.waitKey(1) & 0xFF == ord('q'): + break + + cap.release() + cv2.destroyAllWindows() \ No newline at end of file diff --git a/test_camera.py b/test_camera.py index 53262f5..d075082 100644 --- a/test_camera.py +++ b/test_camera.py @@ -1,18 +1,38 @@ +from src.biometric.face_detection import FaceDetector import cv2 +detector = FaceDetector(backend="opencv") + cap = cv2.VideoCapture(0) +if not cap.isOpened(): + print("❌ Cannot access camera") + exit() + while True: ret, frame = cap.read() - if not ret: - print("Camera not working ❌") break - cv2.imshow("Camera Test", frame) + # Resize frame for faster processing + small_frame = cv2.resize(frame, (0, 0), fx=0.5, fy=0.5) + + # Detect faces on smaller frame + faces = detector.detect_faces(small_frame) + + # Scale faces back to original size + scaled_faces = [] + for (x, y, w, h) in faces: + scaled_faces.append((x * 2, y * 2, w * 2, h * 2)) + + # Draw on original frame + frame = detector.visualize_detections(frame, scaled_faces) + + # SHOW WINDOW (correct indentation) + cv2.imshow("Face Detection", frame) # Press ESC to exit - if cv2.waitKey(1) & 0xFF == ord('q'): + if cv2.waitKey(1) & 0xFF == 27: break cap.release() From cc687d4f9f8d1dbc527db2c6d61fd481e2f287cf Mon Sep 17 00:00:00 2001 From: Akshit Agarwal Date: Sun, 3 May 2026 19:42:24 +0530 Subject: [PATCH 4/4] Fixed camera issues --- 26.0.1 | 0 config/default.yaml | 2 +- config/development.yaml | 18 +++ src/biometric/authentication.py | 118 ++++++++++++++++++ src/biometric/enrollment.py | 76 ++++++++++++ src/biometric/face_detection.py | 14 ++- src/biometric/liveness.py | 8 +- src/ui/main_window.py | 206 ++++++++++++++++++++++++++++---- src/ui/services.py | 20 +++- templates/1160130875fda081.enc | 10 -- test_camera.py | 28 ++--- test_face_detection.py | 12 +- 12 files changed, 442 insertions(+), 70 deletions(-) create mode 100644 26.0.1 create mode 100644 config/development.yaml delete mode 100644 templates/1160130875fda081.enc diff --git a/26.0.1 b/26.0.1 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 0081b57..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 src.core.logging import get_logger, PerformanceTimer -from src.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__) @@ -449,4 +457,4 @@ def _resolve_haarcascade_path(self) -> str: break cap.release() - cv2.destroyAllWindows() \ No newline at end of file + 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 index d075082..0f5b0e0 100644 --- a/test_camera.py +++ b/test_camera.py @@ -1,35 +1,21 @@ -from src.biometric.face_detection import FaceDetector import cv2 -detector = FaceDetector(backend="opencv") - -cap = cv2.VideoCapture(0) +cap = cv2.VideoCapture(0, cv2.CAP_DSHOW) if not cap.isOpened(): - print("❌ Cannot access camera") + print("❌ Camera not opening") exit() +print("βœ… Camera started") + while True: ret, frame = cap.read() + if not ret: + print("❌ Frame not received") break - # Resize frame for faster processing - small_frame = cv2.resize(frame, (0, 0), fx=0.5, fy=0.5) - - # Detect faces on smaller frame - faces = detector.detect_faces(small_frame) - - # Scale faces back to original size - scaled_faces = [] - for (x, y, w, h) in faces: - scaled_faces.append((x * 2, y * 2, w * 2, h * 2)) - - # Draw on original frame - frame = detector.visualize_detections(frame, scaled_faces) - - # SHOW WINDOW (correct indentation) - cv2.imshow("Face Detection", frame) + cv2.imshow("Camera Test", frame) # Press ESC to exit if cv2.waitKey(1) & 0xFF == 27: diff --git a/test_face_detection.py b/test_face_detection.py index 21e901d..3859401 100644 --- a/test_face_detection.py +++ b/test_face_detection.py @@ -1,8 +1,12 @@ import cv2 -import sys, os -sys.path.append(os.path.abspath("LockLess/src")) +import sys +from pathlib import Path -from src.biometric.face_detection import FaceDetector +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) @@ -25,4 +29,4 @@ break cap.release() -cv2.destroyAllWindows() \ No newline at end of file +cv2.destroyAllWindows()