{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":16880,"databundleVersionId":858837,"sourceType":"competition"}],"dockerImageVersionId":29844,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# Install dlib (usually pre-installed on Kaggle, but just in case)\n!pip install dlib opencv-python imutils\n\nimport cv2\nimport dlib\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom imutils import face_utils\nfrom scipy.spatial import distance as dist\nimport matplotlib.pyplot as plt\nimport time\nfrom torchvision import models, transforms\nfrom PIL import Image\n\n# --- CONFIGURATION ---\nSHAPE_PREDICTOR_PATH = \"/kaggle/input/shape-predictor-68-face-landmarks/shape_predictor_68_face_landmarks.dat\"\nEYE_ASPECT_RATIO_THRESHOLD = 0.25  # Below this is a blink\nBLINK_CONSEC_FRAMES = 3            # Frames to register a blink\nOPTICAL_FLOW_THRESHOLD = 1.5       # Variance threshold for \"Frozen Face\"\nCONFIDENCE_TRIGGER = 0.4           # Heuristic score that triggers the Neural Net\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"Running on: {device}\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-02T15:48:24.105602Z","iopub.execute_input":"2026-01-02T15:48:24.106013Z","iopub.status.idle":"2026-01-02T15:48:37.408157Z","shell.execute_reply.started":"2026-01-02T15:48:24.105937Z","shell.execute_reply":"2026-01-02T15:48:37.406285Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class HeuristicSentry:\n    def __init__(self, predictor_path):\n        print(\"Loading Face Predictor...\")\n        self.detector = dlib.get_frontal_face_detector()\n        self.predictor = dlib.shape_predictor(predictor_path)\n        \n        # Blink variables\n        self.blink_counter = 0\n        self.total_blinks = 0\n        \n        # Optical Flow variables\n        self.prev_gray = None\n        self.prev_pts = None\n        self.flow_variances = []\n\n    def eye_aspect_ratio(self, eye):\n        # Compute euclidean distances between the two sets of vertical eye landmarks\n        A = dist.euclidean(eye[1], eye[5])\n        B = dist.euclidean(eye[2], eye[4])\n        # Compute euclidean distance between horizontal landmarks\n        C = dist.euclidean(eye[0], eye[3])\n        # Calculate EAR\n        ear = (A + B) / (2.0 * C)\n        return ear\n\n    def check_liveness_heuristics(self, frame, gray):\n        rects = self.detector(gray, 0)\n        \n        # If no face found, safe/nothing to detect\n        if len(rects) == 0:\n            return 0.0, None \n            \n        rect = rects[0]\n        shape = self.predictor(gray, rect)\n        shape = face_utils.shape_to_np(shape)\n\n        # --- 1. BLINK DETECTION (Physiological) ---\n        (lStart, lEnd) = face_utils.FACIAL_LANDMARKS_IDXS[\"left_eye\"]\n        (rStart, rEnd) = face_utils.FACIAL_LANDMARKS_IDXS[\"right_eye\"]\n        leftEye = shape[lStart:lEnd]\n        rightEye = shape[rStart:rEnd]\n        \n        leftEAR = self.eye_aspect_ratio(leftEye)\n        rightEAR = self.eye_aspect_ratio(rightEye)\n        ear = (leftEAR + rightEAR) / 2.0\n\n        if ear < EYE_ASPECT_RATIO_THRESHOLD:\n            self.blink_counter += 1\n        else:\n            if self.blink_counter >= BLINK_CONSEC_FRAMES:\n                self.total_blinks += 1\n            self.blink_counter = 0\n\n        # --- 2. OPTICAL FLOW (Physics/Temporal) ---\n        # We track nose points to check if face moves naturally vs \"Frozen\" deepfake\n        nose_idx = face_utils.FACIAL_LANDMARKS_IDXS[\"nose\"]\n        curr_pts = shape[nose_idx[0]:nose_idx[1]].astype(np.float32).reshape(-1, 1, 2)\n        \n        flow_score = 0\n        if self.prev_gray is not None:\n            # Lucas-Kanade Optical Flow\n            p1, st, err = cv2.calcOpticalFlowPyrLK(self.prev_gray, gray, self.prev_pts, None)\n            if p1 is not None and self.prev_pts is not None:\n                # Calculate movement magnitude\n                motion = np.linalg.norm(p1 - self.prev_pts, axis=2)\n                variance = np.var(motion)\n                self.flow_variances.append(variance)\n                \n                # Real faces have micro-movements (pulse/breathing) -> Higher variance\n                # \"Frozen\" deepfakes often have artificially low variance in static regions\n                if variance < 0.05: # Suspiciously stable\n                    flow_score = 0.5 # High suspicion\n        \n        # Update for next frame\n        self.prev_gray = gray.copy()\n        self.prev_pts = curr_pts\n\n        # --- FUSION SCORE ---\n        # If no blinks for a long time + Low Variance = High Suspicion\n        heuristic_risk_score = 0.1\n        if self.total_blinks == 0 and len(self.flow_variances) > 30:\n            heuristic_risk_score += 0.4 # Add risk if no blinking\n        \n        heuristic_risk_score += flow_score\n        \n        return min(heuristic_risk_score, 1.0), rect","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class DeepVerifier:\n    def __init__(self):\n        print(\"Loading Distilled CNN (MobileNetV3)...\")\n        # In production: weights=None, load_state_dict('distilled_weights.pth')\n        # For POC demo: We use pretrained just to show inference speed/structure\n        self.model = models.mobilenet_v3_small(weights='DEFAULT') \n        \n        # Replace last layer for binary classification (Real vs Fake)\n        self.model.classifier[3] = nn.Linear(1024, 2)\n        \n        self.model.to(device)\n        self.model.eval()\n        \n        self.transform = transforms.Compose([\n            transforms.Resize((224, 224)),\n            transforms.ToTensor(),\n            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n        ])\n\n    def verify(self, frame_crop):\n        # Convert CV2 to PIL\n        img = Image.fromarray(cv2.cvtColor(frame_crop, cv2.COLOR_BGR2RGB))\n        input_tensor = self.transform(img).unsqueeze(0).to(device)\n        \n        start_time = time.time()\n        with torch.no_grad():\n            output = self.model(input_tensor)\n            probs = F.softmax(output, dim=1)\n            fake_prob = probs[0][1].item() # Assume index 1 is \"Fake\"\n            \n        latency = (time.time() - start_time) * 1000 # ms\n        return fake_prob, latency","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def run_sentinel_simulation(video_path=None):\n    # Initialize Agents\n    sentry = HeuristicSentry(SHAPE_PREDICTOR_PATH)\n    verifier = DeepVerifier()\n    \n    # Use webcam (0) or video file\n    cap = cv2.VideoCapture(video_path if video_path else 0)\n    \n    frame_count = 0\n    \n    # Metrics\n    cpu_mode_frames = 0\n    gpu_mode_frames = 0\n    \n    print(\"\\n--- STARTING SENTINEL-EDGE AGENT ---\")\n    \n    try:\n        while True:\n            ret, frame = cap.read()\n            if not ret: break\n            \n            frame = cv2.resize(frame, (640, 480))\n            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n            frame_count += 1\n            \n            # --- STAGE 1: HEURISTIC (Always On) ---\n            t0 = time.time()\n            risk_score, face_rect = sentry.check_liveness_heuristics(frame, gray)\n            stage1_latency = (time.time() - t0) * 1000\n            \n            status = \"SAFE (Heuristic)\"\n            color = (0, 255, 0) # Green\n            final_score = risk_score\n            \n            # --- STAGE 2: CONDITIONAL TRIGGER ---\n            if risk_score > CONFIDENCE_TRIGGER and face_rect is not None:\n                # Trigger the Heavy Model!\n                gpu_mode_frames += 1\n                status = \"ANALYZING (Deep Net)\"\n                color = (0, 255, 255) # Yellow\n                \n                # Crop Face\n                (x, y, w, h) = face_utils.rect_to_bb(face_rect)\n                face_crop = frame[y:y+h, x:x+w]\n                \n                if face_crop.size != 0:\n                    nn_prob, nn_latency = verifier.verify(face_crop)\n                    \n                    # Fusion Logic: Weighted Avg\n                    final_score = (risk_score * 0.4) + (nn_prob * 0.6)\n                    \n                    if final_score > 0.7:\n                        status = \"DEEPFAKE DETECTED\"\n                        color = (0, 0, 255) # Red\n            else:\n                cpu_mode_frames += 1\n\n            # --- VISUALIZATION ---\n            # Simulate Dashboard\n            cv2.putText(frame, f\"MODE: {status}\", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2)\n            cv2.putText(frame, f\"Risk Score: {final_score:.2f}\", (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1)\n            cv2.putText(frame, f\"Latency: {stage1_latency:.1f}ms\", (10, 90), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1)\n            \n            # For Kaggle Display (Show only every 10th frame to prevent lag in notebook)\n            if frame_count % 10 == 0:\n                plt.figure(figsize=(10,10))\n                plt.imshow(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))\n                plt.axis('off')\n                plt.show()\n                # Break after 50 frames for demo purposes\n                if frame_count > 50: break\n                \n    except Exception as e:\n        print(f\"Error: {e}\")\n    finally:\n        cap.release()\n        print(f\"\\n--- SIMULATION REPORT ---\")\n        print(f\"Total Frames: {frame_count}\")\n        print(f\"Low Power (Heuristic Only): {cpu_mode_frames} frames\")\n        print(f\"High Power (CNN Triggered): {gpu_mode_frames} frames\")\n        print(f\"Efficiency Ratio: {cpu_mode_frames/frame_count*100:.1f}% Battery Savings\")\n\n# Create a dummy video file for Kaggle (since no webcam)\n# You can upload a real video to test!\nrun_sentinel_simulation()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 1. Install Dlib binary from Conda-Forge (No compilation needed)\n!conda install -y -c conda-forge dlib\n\n# 2. Install other dependencies\n!pip install opencv-python imutils torch torchvision\n\nimport dlib\nprint(\"Dlib installed successfully!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-02T15:54:09.131734Z","iopub.execute_input":"2026-01-02T15:54:09.132081Z","execution_failed":"2026-01-02T16:20:03.856Z"}},"outputs":[],"execution_count":null}]}