Arnel Gwen Nuqui commited on
Commit
db5cc42
·
1 Parent(s): 7d86c65

fix head movement

Browse files
Files changed (1) hide show
  1. routes/webrtc_routes.py +22 -8
routes/webrtc_routes.py CHANGED
@@ -89,8 +89,8 @@ def _bbox_from_landmarks(lms, w, h, pad=0.03):
89
  x2n, y2n = min(1.0, max(xs) + pad), min(1.0, max(ys) + pad)
90
  return (int(x1n*w), int(y1n*h), int(x2n*w), int(y2n*h))
91
 
92
- # Thresholds
93
- YAW_DEG_TRIG, PITCH_UP, PITCH_DOWN = 12, 10, 16
94
  DX_TRIG, DY_UP, DY_DOWN = 0.06, 0.08, 0.10
95
  SMOOTH_N, CAPTURE_MIN_MS = 5, 1200
96
  HOLD_FRAMES_HEAD, HOLD_FRAMES_NOFACE, HOLD_FRAMES_HAND = 3, 3, 5
@@ -105,7 +105,7 @@ class ProctorDetector:
105
  def _pose_angles(self, lms, w, h):
106
  try:
107
  pts2d = _landmarks_to_pts(lms, w, h)
108
- cam = np.array([[w, 0, w/2], [0, w, h/2], [0, 0, 1]], dtype=np.float32)
109
  ok, rvec, _ = cv2.solvePnP(MODEL_3D, pts2d, cam, np.zeros((4,1)))
110
  if not ok: return None, None
111
  R, _ = cv2.Rodrigues(rvec)
@@ -124,12 +124,27 @@ class ProctorDetector:
124
  self.noface_streak += 1
125
  return "No Face", None, rgb
126
  self.noface_streak = 0
 
127
  lms = res.multi_face_landmarks[0].landmark
128
  yaw, pitch = self._pose_angles(lms, w, h)
129
  label = "Looking Forward"
130
- if yaw and abs(yaw) > YAW_DEG_TRIG: label = "Looking Left" if yaw < 0 else "Looking Right"
131
- if pitch and pitch > PITCH_DOWN: label = "Looking Down"
132
- if pitch and -pitch > PITCH_UP: label = "Looking Up"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  return label, _bbox_from_landmarks(lms, w, h), rgb
134
 
135
  def detect_hands_anywhere(self, rgb):
@@ -147,7 +162,7 @@ class ProctorDetector:
147
  detectors = defaultdict(ProctorDetector)
148
 
149
  # ----------------------------------------------------------------------
150
- # CAPTURE HANDLER — NOW CALLS RAILWAY API
151
  # ----------------------------------------------------------------------
152
  def _maybe_capture(student_id: str, exam_id: str, bgr, label: str):
153
  ok, buf = cv2.imencode(".jpg", bgr)
@@ -158,7 +173,6 @@ def _maybe_capture(student_id: str, exam_id: str, bgr, label: str):
158
  img_b64 = base64.b64encode(buf).decode("utf-8")
159
  log("CAPTURE_ENQUEUE", student_id, exam_id, label=label, bytes=len(buf))
160
 
161
- # 👉 send to Railway backend instead of local DB
162
  _send_to_railway("/api/save_behavior_log", {
163
  "user_id": int(student_id),
164
  "exam_id": int(exam_id),
 
89
  x2n, y2n = min(1.0, max(xs) + pad), min(1.0, max(ys) + pad)
90
  return (int(x1n*w), int(y1n*h), int(x2n*w), int(y2n*h))
91
 
92
+ # Tuned thresholds
93
+ YAW_DEG_TRIG, PITCH_UP, PITCH_DOWN = 7, 8, 12
94
  DX_TRIG, DY_UP, DY_DOWN = 0.06, 0.08, 0.10
95
  SMOOTH_N, CAPTURE_MIN_MS = 5, 1200
96
  HOLD_FRAMES_HEAD, HOLD_FRAMES_NOFACE, HOLD_FRAMES_HAND = 3, 3, 5
 
105
  def _pose_angles(self, lms, w, h):
106
  try:
107
  pts2d = _landmarks_to_pts(lms, w, h)
108
+ cam = np.array([[w, 0, w/2], [0, h, h/2], [0, 0, 1]], dtype=np.float32) # corrected camera matrix
109
  ok, rvec, _ = cv2.solvePnP(MODEL_3D, pts2d, cam, np.zeros((4,1)))
110
  if not ok: return None, None
111
  R, _ = cv2.Rodrigues(rvec)
 
124
  self.noface_streak += 1
125
  return "No Face", None, rgb
126
  self.noface_streak = 0
127
+
128
  lms = res.multi_face_landmarks[0].landmark
129
  yaw, pitch = self._pose_angles(lms, w, h)
130
  label = "Looking Forward"
131
+
132
+ if yaw is not None and pitch is not None:
133
+ yaw *= -1 # flip sign for mirrored camera
134
+ if abs(yaw) > YAW_DEG_TRIG:
135
+ label = "Looking Left" if yaw > 0 else "Looking Right"
136
+ elif pitch > PITCH_DOWN:
137
+ label = "Looking Down"
138
+ elif pitch < -PITCH_UP:
139
+ label = "Looking Up"
140
+ else:
141
+ label = "Looking Forward"
142
+
143
+ # Debug angles
144
+ if time.time() - self.last_print > 1.5:
145
+ log("ANGLES", sid, eid, yaw=round(yaw or 0, 2), pitch=round(pitch or 0, 2), label=label)
146
+ self.last_print = time.time()
147
+
148
  return label, _bbox_from_landmarks(lms, w, h), rgb
149
 
150
  def detect_hands_anywhere(self, rgb):
 
162
  detectors = defaultdict(ProctorDetector)
163
 
164
  # ----------------------------------------------------------------------
165
+ # CAPTURE HANDLER — SENDS TO RAILWAY
166
  # ----------------------------------------------------------------------
167
  def _maybe_capture(student_id: str, exam_id: str, bgr, label: str):
168
  ok, buf = cv2.imencode(".jpg", bgr)
 
173
  img_b64 = base64.b64encode(buf).decode("utf-8")
174
  log("CAPTURE_ENQUEUE", student_id, exam_id, label=label, bytes=len(buf))
175
 
 
176
  _send_to_railway("/api/save_behavior_log", {
177
  "user_id": int(student_id),
178
  "exam_id": int(exam_id),