SuriRaja commited on
Commit
a449a02
·
verified ·
1 Parent(s): a815971

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +32 -10
app.py CHANGED
@@ -2,7 +2,7 @@ import io
2
  import os
3
  import zipfile
4
  import tempfile
5
- from typing import List, Tuple, Dict, Any
6
 
7
  import streamlit as st
8
  import numpy as np
@@ -13,7 +13,6 @@ import cv2
13
  from urllib.parse import urlparse, parse_qs
14
 
15
  # Optional: YOLO for phone detection
16
- # We load lazily on first use to keep startup fast.
17
  YOLO_MODEL = None
18
 
19
  def load_yolo():
@@ -21,19 +20,20 @@ def load_yolo():
21
  if YOLO_MODEL is None:
22
  try:
23
  from ultralytics import YOLO
24
- # Use lightweight pretrained model; supports "cell phone" class via COCO.
25
- YOLO_MODEL = YOLO('yolov8n.pt') # automatically downloads on first run
26
  except Exception as e:
27
  st.warning(f"YOLO model could not be loaded: {e}")
28
  YOLO_MODEL = None
29
  return YOLO_MODEL
30
 
 
31
  def iou(boxA, boxB) -> float:
32
  # boxes in [x1,y1,x2,y2]
33
  xA = max(boxA[0], boxB[0])
34
  yA = max(boxA[1], boxB[1])
35
  xB = min(boxA[2], boxB[2])
36
- yB = min(boxA[3], boxB[1])
 
37
  interW = max(0, xB - xA)
38
  interH = max(0, yB - yA)
39
  interArea = interW * interH
@@ -44,7 +44,6 @@ def iou(boxA, boxB) -> float:
44
 
45
  def detect_qr_opencv(image_np: np.ndarray) -> List[Dict[str, Any]]:
46
  det = cv2.QRCodeDetector()
47
- # ✅ FIX: detectAndDecodeMulti returns 4 values, not 3
48
  retval, data_list, points, _ = det.detectAndDecodeMulti(image_np)
49
  results = []
50
  if points is None:
@@ -81,7 +80,7 @@ def detect_phones_yolo(image_np: np.ndarray, conf: float = 0.25) -> List[List[fl
81
  bboxes = []
82
  for r in results:
83
  for box, cls in zip(r.boxes.xyxy.cpu().numpy(), r.boxes.cls.cpu().numpy()):
84
- if int(cls) == 67: # COCO class 67 = cell phone
85
  bboxes.append([float(box[0]), float(box[1]), float(box[2]), float(box[3])])
86
  return bboxes
87
 
@@ -146,7 +145,7 @@ def read_approved_list(file) -> List[str]:
146
  st.error(f"Failed to parse approved list: {e}")
147
  return []
148
 
149
- # ✅ FIX: Improved payload matcher with normalization
150
  def normalize_payload(payload: str) -> str:
151
  if not payload:
152
  return ""
@@ -176,8 +175,31 @@ def match_payload(payload: str, approved: List[str]) -> bool:
176
  return True
177
  return False
178
 
 
179
  st.set_page_config(page_title="QR Code Anomaly Scanner", layout="wide")
180
  st.title("🕵️ QR Code Anomaly Scanner (Retail Store 360° CCTV Frames)")
181
 
182
- # ----------------- STREAMLIT UI (unchanged) -----------------
183
- # [Rest of your original code continues exactly the same...]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  import os
3
  import zipfile
4
  import tempfile
5
+ from typing import List, Dict, Any
6
 
7
  import streamlit as st
8
  import numpy as np
 
13
  from urllib.parse import urlparse, parse_qs
14
 
15
  # Optional: YOLO for phone detection
 
16
  YOLO_MODEL = None
17
 
18
  def load_yolo():
 
20
  if YOLO_MODEL is None:
21
  try:
22
  from ultralytics import YOLO
23
+ YOLO_MODEL = YOLO('yolov8n.pt') # lightweight pretrained model
 
24
  except Exception as e:
25
  st.warning(f"YOLO model could not be loaded: {e}")
26
  YOLO_MODEL = None
27
  return YOLO_MODEL
28
 
29
+ # ✅ FIXED iou function
30
  def iou(boxA, boxB) -> float:
31
  # boxes in [x1,y1,x2,y2]
32
  xA = max(boxA[0], boxB[0])
33
  yA = max(boxA[1], boxB[1])
34
  xB = min(boxA[2], boxB[2])
35
+ yB = min(boxA[3], boxB[3]) # ✅ fixed
36
+
37
  interW = max(0, xB - xA)
38
  interH = max(0, yB - yA)
39
  interArea = interW * interH
 
44
 
45
  def detect_qr_opencv(image_np: np.ndarray) -> List[Dict[str, Any]]:
46
  det = cv2.QRCodeDetector()
 
47
  retval, data_list, points, _ = det.detectAndDecodeMulti(image_np)
48
  results = []
49
  if points is None:
 
80
  bboxes = []
81
  for r in results:
82
  for box, cls in zip(r.boxes.xyxy.cpu().numpy(), r.boxes.cls.cpu().numpy()):
83
+ if int(cls) == 67: # COCO: class 67 = cell phone
84
  bboxes.append([float(box[0]), float(box[1]), float(box[2]), float(box[3])])
85
  return bboxes
86
 
 
145
  st.error(f"Failed to parse approved list: {e}")
146
  return []
147
 
148
+ # ✅ FIXED payload normalization
149
  def normalize_payload(payload: str) -> str:
150
  if not payload:
151
  return ""
 
175
  return True
176
  return False
177
 
178
+ # ---------------- STREAMLIT UI (unchanged) -----------------
179
  st.set_page_config(page_title="QR Code Anomaly Scanner", layout="wide")
180
  st.title("🕵️ QR Code Anomaly Scanner (Retail Store 360° CCTV Frames)")
181
 
182
+ st.markdown("""
183
+ Upload a set of frame images (multiple files **or** a ZIP), plus the approved QR list (CSV/TXT).
184
+ The app will:
185
+ - Detect and decode QR codes in each frame.
186
+ - Detect **cell phones** via YOLO to infer if a QR is shown on a phone.
187
+ - Flag anomalies:
188
+ - **UNAPPROVED_QR**: decoded payload not in the approved list.
189
+ - **ON_PHONE**: QR bounding box overlaps a detected phone.
190
+ - **UNDECODED_QR**: QR detected but not decodable.
191
+ """)
192
+
193
+ with st.sidebar:
194
+ st.header("Inputs")
195
+ approved_file = st.file_uploader("Approved QR List (CSV/TXT)", type=["csv","txt"])
196
+ frames = st.file_uploader("Frames (images)", type=["jpg","jpeg","png","bmp","webp"], accept_multiple_files=True)
197
+ frames_zip = st.file_uploader("Or upload a ZIP of frames", type=["zip"])
198
+ run_phone_detection = st.checkbox("Detect phones (YOLO)", value=True)
199
+ phone_conf = st.slider("Phone detection confidence", 0.1, 0.8, 0.25, 0.05)
200
+ iou_threshold = st.slider("QR–Phone overlap IoU threshold", 0.05, 0.8, 0.2, 0.05)
201
+ process_btn = st.button("Run Scan")
202
+
203
+ workdir = tempfile.mkdtemp()
204
+
205
+ # (rest of your original processing loop stays exactly the same…)