Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -5,6 +5,10 @@ import requests
|
|
5 |
import tempfile
|
6 |
import mediapipe as mp
|
7 |
import os
|
|
|
|
|
|
|
|
|
8 |
|
9 |
# === CONFIGURATION ===
|
10 |
API_KEY = "AIzaSyDojJrpauA0XZtCCDUuo9xeQHZQamYKsC4"
|
@@ -12,133 +16,123 @@ CCTVFEED_IDS = ['1KJRkSf2SKEZ1mXS9_si65IwMBtjs6p4n']
|
|
12 |
REG_FOLDER_ID = '1qkcR7nQTEtiMH9OFUv2bGxVn08E3dKjF'
|
13 |
INTRUDER_FOLDER_ID = '1PPAUWU-wMx7fek73p-hqPqYQypYtG8Ob'
|
14 |
|
15 |
-
# === SETUP ===
|
16 |
mp_face_detection = mp.solutions.face_detection
|
17 |
-
mp_drawing = mp.solutions.drawing_utils
|
18 |
face_detector = mp_face_detection.FaceDetection(model_selection=0, min_detection_confidence=0.5)
|
19 |
|
20 |
-
# ===
|
21 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
url = f"https://www.googleapis.com/drive/v3/files?q='{folder_id}'+in+parents&key={API_KEY}&fields=files(id,name,mimeType)"
|
23 |
-
|
24 |
-
files =
|
25 |
-
return [f for f in files if f["mimeType"]
|
26 |
|
27 |
-
def
|
28 |
url = f"https://drive.google.com/uc?id={file_id}&export=download"
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
|
|
|
|
33 |
|
34 |
-
def
|
35 |
-
rgb = cv2.cvtColor(
|
36 |
results = face_detector.process(rgb)
|
37 |
-
faces = []
|
38 |
if results.detections:
|
39 |
for det in results.detections:
|
40 |
bbox = det.location_data.relative_bounding_box
|
41 |
-
h, w, _ =
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
return
|
49 |
-
|
50 |
-
def
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
_, buffer = cv2.imencode('.jpg', img_array)
|
59 |
-
img_bytes = buffer.tobytes()
|
60 |
-
upload_url = f"https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&key={API_KEY}"
|
61 |
-
|
62 |
-
metadata = {
|
63 |
-
"name": f"{name_hint}.jpg",
|
64 |
-
"parents": [INTRUDER_FOLDER_ID]
|
65 |
-
}
|
66 |
-
|
67 |
-
boundary = "foo_bar_baz"
|
68 |
-
headers = {
|
69 |
-
"Content-Type": f"multipart/related; boundary={boundary}"
|
70 |
-
}
|
71 |
-
|
72 |
-
meta_json = str(metadata).replace("'", '"')
|
73 |
-
body_prefix = (
|
74 |
-
"--" + boundary + "\r\n"
|
75 |
-
"Content-Type: application/json; charset=UTF-8\r\n\r\n"
|
76 |
-
+ meta_json + "\r\n"
|
77 |
-
"--" + boundary + "\r\n"
|
78 |
-
"Content-Type: image/jpeg\r\n\r\n"
|
79 |
-
).encode("utf-8")
|
80 |
-
|
81 |
-
body_suffix = ("\r\n--" + boundary + "--").encode("utf-8")
|
82 |
-
multipart_body = body_prefix + img_bytes + body_suffix
|
83 |
-
|
84 |
-
r = requests.post(upload_url, headers=headers, data=multipart_body)
|
85 |
-
return r.status_code == 200
|
86 |
-
|
87 |
-
def download_video(link):
|
88 |
-
r = requests.get(link, stream=True)
|
89 |
-
temp = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4")
|
90 |
-
for chunk in r.iter_content(chunk_size=1024*1024):
|
91 |
-
temp.write(chunk)
|
92 |
-
temp.close()
|
93 |
-
return temp.name
|
94 |
-
|
95 |
-
# === MAIN ===
|
96 |
st.title("π Intruder Detection from CCTV Feed")
|
97 |
-
st.write("Streaming videos from Google Drive, detecting
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
with st.spinner("
|
112 |
-
|
113 |
for folder_id in CCTVFEED_IDS:
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
|
|
|
|
|
|
|
|
|
|
|
5 |
import tempfile
|
6 |
import mediapipe as mp
|
7 |
import os
|
8 |
+
import pickle
|
9 |
+
from googleapiclient.discovery import build
|
10 |
+
from googleapiclient.http import MediaFileUpload
|
11 |
+
from PIL import Image
|
12 |
|
13 |
# === CONFIGURATION ===
|
14 |
API_KEY = "AIzaSyDojJrpauA0XZtCCDUuo9xeQHZQamYKsC4"
|
|
|
16 |
REG_FOLDER_ID = '1qkcR7nQTEtiMH9OFUv2bGxVn08E3dKjF'
|
17 |
INTRUDER_FOLDER_ID = '1PPAUWU-wMx7fek73p-hqPqYQypYtG8Ob'
|
18 |
|
|
|
19 |
mp_face_detection = mp.solutions.face_detection
|
|
|
20 |
face_detector = mp_face_detection.FaceDetection(model_selection=0, min_detection_confidence=0.5)
|
21 |
|
22 |
+
# === INTRUDER UPLOAD ===
|
23 |
+
def get_drive_service():
|
24 |
+
try:
|
25 |
+
with open("token.pickle", "rb") as token:
|
26 |
+
creds = pickle.load(token)
|
27 |
+
return build("drive", "v3", credentials=creds)
|
28 |
+
except Exception as e:
|
29 |
+
print(f"β Drive auth failed: {e}")
|
30 |
+
return None
|
31 |
+
|
32 |
+
def upload_intruder_face(image_path):
|
33 |
+
service = get_drive_service()
|
34 |
+
if service is None:
|
35 |
+
print("π Upload skipped (auth failed)")
|
36 |
+
return
|
37 |
+
|
38 |
+
metadata = {"name": os.path.basename(image_path), "parents": [INTRUDER_FOLDER_ID]}
|
39 |
+
media = MediaFileUpload(image_path, mimetype="image/jpeg")
|
40 |
+
try:
|
41 |
+
service.files().create(body=metadata, media_body=media, fields="id").execute()
|
42 |
+
print(f"β
Uploaded to INTRUDER folder: {image_path}")
|
43 |
+
except Exception as e:
|
44 |
+
print(f"β Upload failed: {e}")
|
45 |
+
|
46 |
+
# === HELPERS ===
|
47 |
+
def get_drive_files(folder_id, allowed_mime):
|
48 |
url = f"https://www.googleapis.com/drive/v3/files?q='{folder_id}'+in+parents&key={API_KEY}&fields=files(id,name,mimeType)"
|
49 |
+
response = requests.get(url)
|
50 |
+
files = response.json().get("files", [])
|
51 |
+
return [(f["name"], f["id"]) for f in files if f["mimeType"] in allowed_mime]
|
52 |
|
53 |
+
def download_file(file_id, suffix):
|
54 |
url = f"https://drive.google.com/uc?id={file_id}&export=download"
|
55 |
+
response = requests.get(url, stream=True)
|
56 |
+
temp = tempfile.NamedTemporaryFile(delete=False, suffix=suffix)
|
57 |
+
for chunk in response.iter_content(chunk_size=1024 * 1024):
|
58 |
+
temp.write(chunk)
|
59 |
+
temp.close()
|
60 |
+
return temp.name
|
61 |
|
62 |
+
def extract_face_embedding(image):
|
63 |
+
rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
64 |
results = face_detector.process(rgb)
|
|
|
65 |
if results.detections:
|
66 |
for det in results.detections:
|
67 |
bbox = det.location_data.relative_bounding_box
|
68 |
+
h, w, _ = image.shape
|
69 |
+
x1 = int(bbox.xmin * w)
|
70 |
+
y1 = int(bbox.ymin * h)
|
71 |
+
x2 = int((bbox.xmin + bbox.width) * w)
|
72 |
+
y2 = int((bbox.ymin + bbox.height) * h)
|
73 |
+
face_crop = image[y1:y2, x1:x2]
|
74 |
+
return cv2.resize(face_crop, (128, 128))
|
75 |
+
return None
|
76 |
+
|
77 |
+
def is_match(face1, face2):
|
78 |
+
if face1 is None or face2 is None:
|
79 |
+
return False
|
80 |
+
diff = np.linalg.norm(face1.astype("float") - face2.astype("float"))
|
81 |
+
return diff < 30.0
|
82 |
+
|
83 |
+
# === STREAMLIT UI ===
|
84 |
+
st.set_page_config(page_title="Intruder Detection", layout="wide")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
st.title("π Intruder Detection from CCTV Feed")
|
86 |
+
st.write("Streaming videos from Google Drive, detecting and matching faces.")
|
87 |
+
|
88 |
+
sidebar_faces = []
|
89 |
+
|
90 |
+
with st.spinner("Loading registered faces..."):
|
91 |
+
registered = []
|
92 |
+
reg_images = get_drive_files(REG_FOLDER_ID, ["image/jpeg", "image/png"])
|
93 |
+
for name, fid in reg_images:
|
94 |
+
img_path = download_file(fid, ".jpg")
|
95 |
+
img = cv2.imread(img_path)
|
96 |
+
emb = extract_face_embedding(img)
|
97 |
+
if emb is not None:
|
98 |
+
registered.append((name, emb))
|
99 |
+
|
100 |
+
with st.spinner("Scanning CCTV feed..."):
|
101 |
+
total_intruders = 0
|
102 |
for folder_id in CCTVFEED_IDS:
|
103 |
+
videos = get_drive_files(folder_id, ["video/mp4", "video/x-msvideo", "video/avi"])
|
104 |
+
for vname, fid in videos:
|
105 |
+
st.subheader(f"π₯ {vname}")
|
106 |
+
vid_path = download_file(fid, ".mp4")
|
107 |
+
cap = cv2.VideoCapture(vid_path)
|
108 |
+
frame_count = 0
|
109 |
+
matched_faces = 0
|
110 |
+
unmatched_faces = 0
|
111 |
+
|
112 |
+
while cap.isOpened():
|
113 |
+
ret, frame = cap.read()
|
114 |
+
if not ret:
|
115 |
+
break
|
116 |
+
face_img = extract_face_embedding(frame)
|
117 |
+
if face_img is not None:
|
118 |
+
match_found = any(is_match(face_img, emb) for _, emb in registered)
|
119 |
+
if match_found:
|
120 |
+
matched_faces += 1
|
121 |
+
else:
|
122 |
+
unmatched_faces += 1
|
123 |
+
temp = tempfile.NamedTemporaryFile(delete=False, suffix=".jpg")
|
124 |
+
cv2.imwrite(temp.name, face_img)
|
125 |
+
upload_intruder_face(temp.name)
|
126 |
+
sidebar_faces.append(temp.name)
|
127 |
+
frame_count += 1
|
128 |
+
cap.release()
|
129 |
+
|
130 |
+
st.success(f"π― Processed {frame_count} frames β Matched: {matched_faces}, Unmatched: {unmatched_faces}")
|
131 |
+
total_intruders += unmatched_faces
|
132 |
+
|
133 |
+
st.info(f"π― Completed processing all videos. Intruders detected: {total_intruders}")
|
134 |
+
|
135 |
+
with st.sidebar:
|
136 |
+
st.markdown("## π¨ Intruder Snapshots")
|
137 |
+
for face_path in sidebar_faces:
|
138 |
+
st.image(face_path, width=160)
|