diff --git a/Backend/proctor_core.py b/Backend/proctor_core.py new file mode 100644 index 0000000..ed48076 --- /dev/null +++ b/Backend/proctor_core.py @@ -0,0 +1,121 @@ +# Backend/proctor_core.py + +import cv2 +import mediapipe as mp +import numpy as np +from typing import Dict, List, Tuple +import logging +import os + +# Local imports +from .detection import run_detection +from .head_pose import pose +from .object_detection import detect_objects +from .audio import process_audio +from .screen_recorder import capture_screen + +class ProctorCore: + def __init__(self): + self.mp_face_detection = mp.solutions.face_detection + self.mp_pose = mp.solutions.pose + self.face_detection = self.mp_face_detection.FaceDetection(min_detection_confidence=0.7) + self.pose_detection = self.mp_pose.Pose(min_detection_confidence=0.7) + self.logger = self._setup_logger() + + def _setup_logger(self) -> logging.Logger: + """Configure logging for the proctoring system""" + logger = logging.getLogger('ProctorCore') + logger.setLevel(logging.INFO) + handler = logging.StreamHandler() + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + handler.setFormatter(formatter) + logger.addHandler(handler) + return logger + + def start_monitoring(self): + """Initialize and start all monitoring components""" + try: + # Start detection systems + detection_result = run_detection() + pose_result = pose() + screen_capture = capture_screen() + + # Process and combine results + combined_results = self._process_results( + detection_result, + pose_result, + screen_capture + ) + + return combined_results + + except Exception as e: + self.logger.error(f"Error in monitoring: {str(e)}") + raise + + def _process_results(self, detection_data, pose_data, screen_data) -> Dict: + """Process and combine results from different detection systems""" + results = { + 'timestamp': np.datetime64('now'), + 'detection': detection_data, + 'pose': pose_data, + 'screen': screen_data, + 'suspicious_level': 0.0 + } + + # Calculate suspicious level based on combined factors + suspicious_factors = [ + detection_data.get('suspicious_score', 0), + pose_data.get('deviation_score', 0), + screen_data.get('activity_score', 0) + ] + + results['suspicious_level'] = np.mean([x for x in suspicious_factors if x is not None]) + + return results + + def save_results(self, results: Dict, output_path: str = None): + """Save monitoring results to specified location""" + if output_path is None: + output_path = os.path.join( + os.path.dirname(__file__), + 'Dataset', + f'proctor_results_{np.datetime64("now")}.json' + ) + + try: + import json + with open(output_path, 'w') as f: + json.dump(results, f, indent=4, default=str) + self.logger.info(f"Results saved to {output_path}") + except Exception as e: + self.logger.error(f"Error saving results: {str(e)}") + + def analyze_behavior(self, results: Dict) -> Dict: + """Analyze monitored behavior and generate insights""" + analysis = { + 'timestamp': np.datetime64('now'), + 'overall_score': results.get('suspicious_level', 0), + 'warnings': [], + 'recommendations': [] + } + + # Generate warnings based on thresholds + if results.get('pose', {}).get('deviation_score', 0) > 0.7: + analysis['warnings'].append('Significant head movement detected') + + if results.get('detection', {}).get('suspicious_score', 0) > 0.7: + analysis['warnings'].append('Suspicious objects detected') + + if results.get('screen', {}).get('activity_score', 0) > 0.7: + analysis['warnings'].append('Unusual screen activity detected') + + return analysis + + def cleanup(self): + """Cleanup resources and close connections""" + try: + cv2.destroyAllWindows() + self.logger.info("Cleanup completed successfully") + except Exception as e: + self.logger.error(f"Error during cleanup: {str(e)}") \ No newline at end of file diff --git a/Backend/run.py b/Backend/run.py index 7fa04de..a6dad64 100644 --- a/Backend/run.py +++ b/Backend/run.py @@ -1,28 +1,110 @@ -import head_pose -import detection -import threading as th - -def run_threads(): - try: - # Create threads for each target function - head_pose_thread = th.Thread(target=head_pose.pose) - # audio_thread = th.Thread(target=audio.sound) # Uncomment if audio module is needed - detection_thread = th.Thread(target=detection.run_detection) +# Backend/run.py - # Start the threads - head_pose_thread.start() - # audio_thread.start() # Uncomment to start audio thread - detection_thread.start() +import threading as th +import logging +import os +from typing import Dict, List +import queue +from .proctor_core import ProctorCore - # Wait for the threads to complete - head_pose_thread.join() - # audio_thread.join() # Uncomment to wait for audio thread - detection_thread.join() +class ProctorManager: + def __init__(self): + self.result_queue = queue.Queue() + self.proctor = ProctorCore() + self.is_running = False + self.threads: List[th.Thread] = [] + self.logger = self._setup_logger() + + def _setup_logger(self): + logger = logging.getLogger('ProctorManager') + logger.setLevel(logging.INFO) + handler = logging.StreamHandler() + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + handler.setFormatter(formatter) + logger.addHandler(handler) + return logger + + def _monitoring_worker(self): + """Worker function for continuous monitoring""" + while self.is_running: + try: + results = self.proctor.start_monitoring() + self.result_queue.put(results) + except Exception as e: + self.logger.error(f"Error in monitoring worker: {str(e)}") + break + + def _analysis_worker(self): + """Worker function for analyzing results""" + while self.is_running: + try: + results = self.result_queue.get(timeout=1) + if results: + analysis = self.proctor.analyze_behavior(results) + self.proctor.save_results(analysis) + except queue.Empty: + continue + except Exception as e: + self.logger.error(f"Error in analysis worker: {str(e)}") + break + + def start(self): + """Start the proctoring system""" + try: + self.is_running = True + + # Create worker threads + monitoring_thread = th.Thread(target=self._monitoring_worker) + analysis_thread = th.Thread(target=self._analysis_worker) + + # Start threads + self.threads = [monitoring_thread, analysis_thread] + for thread in self.threads: + thread.start() + + self.logger.info("Proctoring system started successfully") + + except Exception as e: + self.logger.error(f"Error starting proctoring system: {str(e)}") + self.stop() + + def stop(self): + """Stop the proctoring system""" + self.is_running = False + + # Wait for threads to complete + for thread in self.threads: + thread.join() + + # Cleanup resources + self.proctor.cleanup() + self.logger.info("Proctoring system stopped") +def main(): + # Setup logging + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' + ) + logger = logging.getLogger(__name__) + + try: + # Initialize and start the proctoring system + manager = ProctorManager() + manager.start() + + # Keep running until interrupted + while True: + pass + + except KeyboardInterrupt: + logger.info("Received shutdown signal") except Exception as e: - print(f"An error occurred: {e}") + logger.error(f"Unexpected error: {str(e)}") finally: - print("All threads have been joined.") + if 'manager' in locals(): + manager.stop() + logger.info("Application shutdown complete") if __name__ == "__main__": - run_threads() + main() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 3850801..0a37db7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,16 @@ +numpy==2.1.1 +opencv-python==4.10.0.84 +opencv-contrib-python==4.10.0.84 +mediapipe==0.10.14 +PyAudio==0.2.14 +sounddevice==0.5.0 +tensorflow>=2.14.0 +torch>=2.1.0 +mediapipe==0.10.14 +protobuf==4.25.5 +scipy==1.14.1 +matplotlib==3.9.2 +pillow==10.4.0 absl-py==2.1.0 attrs==24.2.0 cffi==1.17.1 @@ -9,23 +22,13 @@ glob2==0.7 jax==0.4.33 jaxlib==0.4.33 kiwisolver==1.4.7 -matplotlib==3.9.2 -mediapipe==0.10.14 ml_dtypes==0.5.0 -numpy==2.1.1 -opencv-contrib-python==4.10.0.84 -opencv-python==4.10.0.84 opt_einsum==3.4.0 packaging==24.1 -pillow==10.4.0 -protobuf==4.25.5 -PyAudio==0.2.14 pycparser==2.22 pyparsing==3.1.4 python-dateutil==2.9.0.post0 -pywin32==306 -scipy==1.14.1 six==1.16.0 -sounddevice==0.5.0 +pywin32==306 WMI==1.5.1 -tensor \ No newline at end of file +psutil>=5.9.0 \ No newline at end of file