Martlgap commited on
Commit
bffe7b3
1 Parent(s): 7867a3c

testing on hugging

Browse files
app.py CHANGED
@@ -1,79 +1,243 @@
1
  import streamlit as st
2
  import time
3
- from tools.webcam import init_webcam
 
4
  import logging
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
 
7
  # Set logging level to error (To avoid getting spammed by queue warnings etc.)
 
8
  logging.basicConfig(level=logging.ERROR)
9
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  # Set page layout for streamlit to wide
12
- st.set_page_config(layout="wide")
13
-
14
-
15
- class KPI:
16
- """Class for displaying KPIs in a row
17
- Args:
18
- keys (list): List of KPI names
19
- """
20
-
21
- def __init__(self, keys):
22
- self.kpi_texts = []
23
- row = st.columns(len(keys))
24
- for kpi, key in zip(row, keys):
25
- with kpi:
26
- item_row = st.columns(2)
27
- item_row[0].markdown(f"**{key}**:")
28
- self.kpi_texts.append(item_row[1].markdown("-"))
29
-
30
- def update_kpi(self, kpi_values):
31
- for kpi_text, kpi_value in zip(self.kpi_texts, kpi_values):
32
- kpi_text.write(
33
- f"<h5 style='text-align: center; color: red;'>{kpi_value:.2f}</h5>"
34
- if isinstance(kpi_value, float)
35
- else f"<h5 style='text-align: center; color: red;'>{kpi_value}</h5>",
36
- unsafe_allow_html=True,
37
- )
38
-
39
-
40
- # -----------------------------------------------------------------------------------------------
41
- # Streamlit App
42
- st.title("FaceID App Demonstration")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
- # Get Access to Webcam
45
- webcam = init_webcam()
46
 
47
- # KPI Section
48
- st.markdown("**Stats**")
49
- kpi = KPI(["**FrameRate**"])
50
- st.markdown("---")
51
 
52
- # Live Stream Display
53
- stream_display = st.empty()
54
- st.markdown("---")
55
 
56
- if webcam:
57
- prevTime = 0
58
- while True:
59
- try:
60
- # Get Frame from Webcam
61
- frame = webcam.get_frame(timeout=10)
62
-
63
- # Convert to OpenCV Image
64
- frame = frame.to_ndarray(format="rgb24")
65
- except:
66
- continue
67
-
68
- # DISPLAY THE LIVE STREAM --------------------------------------------------
69
- stream_display.image(
70
- frame, channels="RGB", caption="Live-Stream", use_column_width=True
71
- )
72
 
73
- # CALCULATE FPS -----------------------------------------------------------
74
- currTime = time.time()
75
- fps = 1 / (currTime - prevTime)
76
- prevTime = currTime
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
- # UPDATE KPIS -------------------------------------------------------------
79
- kpi.update_kpi([fps])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
  import time
3
+ from typing import List
4
+ from streamlit_webrtc import webrtc_streamer, WebRtcMode
5
  import logging
6
+ import mediapipe as mp
7
+ import tflite_runtime.interpreter as tflite
8
+ import av
9
+ import numpy as np
10
+ import queue
11
+ from streamlit_toggle import st_toggle_switch
12
+ import pandas as pd
13
+ from tools.nametypes import Stats, Detection
14
+ from pathlib import Path
15
+ from tools.utils import get_ice_servers, download_file, display_match, rgb
16
+ from tools.face_recognition import (
17
+ detect_faces,
18
+ align_faces,
19
+ inference,
20
+ draw_detections,
21
+ recognize_faces,
22
+ process_gallery,
23
+ )
24
+
25
+ # TODO Error Handling!
26
 
27
 
28
  # Set logging level to error (To avoid getting spammed by queue warnings etc.)
29
+ logger = logging.getLogger(__name__)
30
  logging.basicConfig(level=logging.ERROR)
31
 
32
+ ROOT = Path(__file__).parent
33
+
34
+ MODEL_URL = (
35
+ "https://github.com/Martlgap/FaceIDLight/releases/download/v.0.1/mobileNet.tflite"
36
+ )
37
+ MODEL_LOCAL_PATH = ROOT / "./models/mobileNet.tflite"
38
+
39
+ DETECTION_CONFIDENCE = 0.5
40
+ TRACKING_CONFIDENCE = 0.5
41
+ MAX_FACES = 2
42
 
43
  # Set page layout for streamlit to wide
44
+ st.set_page_config(
45
+ layout="wide", page_title="FaceID App Demo", page_icon=":sunglasses:"
46
+ )
47
+ with st.sidebar:
48
+ st.markdown("# Preferences")
49
+ face_rec_on = st_toggle_switch(
50
+ "Face Recognition",
51
+ key="activate_face_rec",
52
+ default_value=True,
53
+ active_color=rgb(255, 75, 75),
54
+ track_color=rgb(50, 50, 50),
55
+ )
56
+
57
+ st.markdown("## Webcam")
58
+ resolution = st.selectbox(
59
+ "Webcam Resolution",
60
+ [(1920, 1080), (1280, 720), (640, 360)],
61
+ index=2,
62
+ )
63
+ st.markdown("## Face Detection")
64
+ max_faces = st.number_input("Maximum Number of Faces", value=2, min_value=1)
65
+ detection_confidence = st.slider(
66
+ "Min Detection Confidence", min_value=0.0, max_value=1.0, value=0.5
67
+ )
68
+ tracking_confidence = st.slider(
69
+ "Min Tracking Confidence", min_value=0.0, max_value=1.0, value=0.9
70
+ )
71
+ on_draw = st_toggle_switch(
72
+ "Show Drawings",
73
+ key="show_drawings",
74
+ default_value=True,
75
+ active_color=rgb(255, 75, 75),
76
+ track_color=rgb(100, 100, 100),
77
+ )
78
+ st.markdown("## Face Recognition")
79
+ similarity_threshold = st.slider(
80
+ "Similarity Threshold", min_value=0.0, max_value=2.0, value=0.67
81
+ )
82
+
83
+ download_file(
84
+ MODEL_URL,
85
+ MODEL_LOCAL_PATH,
86
+ file_hash="6c19b789f661caa8da735566490bfd8895beffb2a1ec97a56b126f0539991aa6",
87
+ )
88
+
89
+ # Session-specific caching of the face recognition model
90
+ cache_key = "face_id_model"
91
+ if cache_key in st.session_state:
92
+ face_recognition_model = st.session_state[cache_key]
93
+ else:
94
+ face_recognition_model = tflite.Interpreter(model_path=MODEL_LOCAL_PATH.as_posix())
95
+ st.session_state[cache_key] = face_recognition_model
96
+
97
+ # Session-specific caching of the face detection model
98
+ cache_key = "face_detection_model"
99
+ if cache_key in st.session_state:
100
+ face_detection_model = st.session_state[cache_key]
101
+ else:
102
+ face_detection_model = mp.solutions.face_mesh.FaceMesh(
103
+ refine_landmarks=True,
104
+ min_detection_confidence=detection_confidence,
105
+ min_tracking_confidence=tracking_confidence,
106
+ max_num_faces=max_faces,
107
+ )
108
+ st.session_state[cache_key] = face_detection_model
109
+
110
+ stats_queue: "queue.Queue[Stats]" = queue.Queue()
111
+ detections_queue: "queue.Queue[List[Detection]]" = queue.Queue()
112
+
113
+
114
+ def video_frame_callback(frame: av.VideoFrame) -> av.VideoFrame:
115
+ detections = None
116
+ frame_start = time.time()
117
+
118
+ # Convert frame to numpy array
119
+ frame = frame.to_ndarray(format="rgb24")
120
+
121
+ # Get frame resolution
122
+ resolution = frame.shape
123
+
124
+ start = time.time()
125
+ if face_rec_on:
126
+ detections = detect_faces(frame, face_detection_model)
127
+ time_detection = (time.time() - start) * 1000
128
+
129
+ start = time.time()
130
+ if face_rec_on:
131
+ detections = align_faces(frame, detections)
132
+ time_normalization = (time.time() - start) * 1000
133
+
134
+ start = time.time()
135
+ if face_rec_on:
136
+ detections = inference(detections, face_recognition_model)
137
+ time_inference = (time.time() - start) * 1000
138
+
139
+ start = time.time()
140
+ if face_rec_on:
141
+ detections = recognize_faces(detections, gallery, similarity_threshold)
142
+ time_recognition = (time.time() - start) * 1000
143
+
144
+ start = time.time()
145
+ if face_rec_on and on_draw:
146
+ frame = draw_detections(frame, detections)
147
+ time_drawing = (time.time() - start) * 1000
148
+
149
+ # Convert frame back to av.VideoFrame
150
+ frame = av.VideoFrame.from_ndarray(frame, format="rgb24")
151
+
152
+ # Put detections, stats and timings into queues (to be accessible by other thread)
153
+ if face_rec_on:
154
+ detections_queue.put(detections)
155
+ stats_queue.put(
156
+ Stats(
157
+ fps=1 / (time.time() - frame_start),
158
+ resolution=resolution,
159
+ num_faces=len(detections) if detections else 0,
160
+ detection=time_detection,
161
+ normalization=time_normalization,
162
+ inference=time_inference,
163
+ recognition=time_recognition,
164
+ drawing=time_drawing,
165
+ )
166
+ )
167
 
168
+ return frame
 
169
 
 
 
 
 
170
 
171
+ # Streamlit app
172
+ st.title("FaceID App Demonstration")
 
173
 
174
+ st.sidebar.markdown("**Gallery**")
175
+ gallery = st.sidebar.file_uploader(
176
+ "Upload images to gallery", type=["png", "jpg", "jpeg"], accept_multiple_files=True
177
+ )
178
+ if gallery:
179
+ gallery = process_gallery(gallery, face_detection_model, face_recognition_model)
180
+ st.sidebar.markdown("**Gallery Images**")
181
+ st.sidebar.image(
182
+ [identity.image for identity in gallery],
183
+ caption=[identity.name for identity in gallery],
184
+ width=112,
185
+ )
 
 
 
 
186
 
187
+ st.markdown("**Stats**")
188
+ stats = st.empty()
189
+
190
+ ctx = webrtc_streamer(
191
+ key="FaceIDAppDemo",
192
+ mode=WebRtcMode.SENDRECV,
193
+ rtc_configuration={"iceServers": get_ice_servers("twilio")},
194
+ video_frame_callback=video_frame_callback,
195
+ media_stream_constraints={
196
+ "video": {
197
+ "width": {
198
+ "min": resolution[0],
199
+ "ideal": resolution[0],
200
+ "max": resolution[0],
201
+ }
202
+ },
203
+ "audio": False,
204
+ },
205
+ async_processing=False, # WHAT IS THIS?
206
+ )
207
+
208
+ st.markdown("**Timings [ms]**")
209
+ timings = st.empty()
210
+
211
+ st.markdown("**Identified Faces**")
212
+ identified_faces = st.empty()
213
+
214
+ st.markdown("**Detections**")
215
+ detections = st.empty()
216
+
217
+ # Display Live Stats
218
+ if ctx.state.playing:
219
+ while True:
220
+ stats_dataframe = pd.DataFrame([stats_queue.get()])
221
+ stats.dataframe(stats_dataframe.style.format(thousands=" ", precision=2))
222
 
223
+ detections_data = detections_queue.get()
224
+ detections_dataframe = pd.DataFrame(detections_data).drop(
225
+ columns=["face", "face_match"], errors="ignore"
226
+ )
227
+ # Apply formatting to DataFrame
228
+ # print(detections_dataframe.columns)
229
+ # detections_dataframe["embedding"] = detections_dataframe["embedding"].embedding.applymap(format_floats)
230
+
231
+ detections.dataframe(detections_dataframe)
232
+
233
+ identified_faces.image(
234
+ [display_match(d) for d in detections_data if d.name is not None],
235
+ caption=[
236
+ d.name + f"({d.distance:2f})"
237
+ for d in detections_data
238
+ if d.name is not None
239
+ ],
240
+ width=112,
241
+ ) # TODO formatting
242
+
243
+ # time.sleep(1)
app_bak.py DELETED
@@ -1,299 +0,0 @@
1
- import streamlit as st
2
- import streamlit_toggle as tog
3
- import time
4
- import numpy as np
5
- import cv2
6
- from tools.annotation import draw_mesh, draw_landmarks, draw_bounding_box, draw_text
7
- from tools.alignment import align_faces
8
- from tools.identification import load_identification_model, inference, identify
9
- from tools.utils import show_images, show_faces, rgb
10
- from tools.detection import load_detection_model, detect_faces
11
- from tools.webcam import init_webcam
12
- import logging
13
-
14
-
15
- # Set logging level to error (To avoid getting spammed by queue warnings etc.)
16
- logging.basicConfig(level=logging.ERROR)
17
-
18
-
19
- # Set page layout for streamlit to wide
20
- st.set_page_config(layout="wide")
21
-
22
-
23
- # Initialize the Face Detection and Identification Models
24
- detection_model = load_detection_model(max_faces=2, detection_confidence=0.5, tracking_confidence=0.9)
25
- identification_model = load_identification_model(name="MobileNet")
26
-
27
-
28
- # Gallery Processing
29
- @st.cache_data
30
- def gallery_processing(gallery_files):
31
- """Process the gallery images (Complete Face Recognition Pipeline)
32
-
33
- Args:
34
- gallery_files (_type_): Files uploaded by the user
35
-
36
- Returns:
37
- _type_: Gallery Images, Gallery Embeddings, Gallery Names
38
- """
39
- gallery_images, gallery_embs, gallery_names = [], [], []
40
- if gallery_files is not None:
41
- for file in gallery_files:
42
- file_bytes = np.asarray(bytearray(file.read()), dtype=np.uint8)
43
- img = cv2.cvtColor(
44
- cv2.imdecode(file_bytes, cv2.IMREAD_COLOR), cv2.COLOR_BGR2RGB
45
- )
46
- gallery_names.append(
47
- file.name.split(".jpg")[0].split(".png")[0].split(".jpeg")[0]
48
- )
49
- detections = detect_faces(img, detection_model)
50
- aligned_faces = align_faces(img, np.asarray([detections[0]]))
51
- gallery_images.append(aligned_faces[0])
52
- gallery_embs.append(inference(aligned_faces, identification_model)[0])
53
- return gallery_images, gallery_embs, gallery_names
54
-
55
-
56
- class SideBar:
57
- """A class to handle the sidebar
58
- """
59
- def __init__(self):
60
- with st.sidebar:
61
- st.markdown("# Preferences")
62
- self.on_face_recognition = tog.st_toggle_switch(
63
- "Face Recognition", key="activate_face_rec", default_value=True, active_color=rgb(255, 75, 75), track_color=rgb(50, 50, 50)
64
- )
65
-
66
- st.markdown("---")
67
-
68
- st.markdown("## Webcam")
69
- self.resolution = st.selectbox(
70
- "Webcam Resolution",
71
- [(1920, 1080), (1280, 720), (640, 360)],
72
- index=2,
73
- )
74
- st.markdown("To change webcam resolution: Please refresh page and select resolution before starting webcam stream.")
75
-
76
- st.markdown("---")
77
- st.markdown("## Face Detection")
78
- self.max_faces = st.number_input(
79
- "Maximum Number of Faces", value=2, min_value=1
80
- )
81
- self.detection_confidence = st.slider(
82
- "Min Detection Confidence", min_value=0.0, max_value=1.0, value=0.5
83
- )
84
- self.tracking_confidence = st.slider(
85
- "Min Tracking Confidence", min_value=0.0, max_value=1.0, value=0.9
86
- )
87
- switch1, switch2 = st.columns(2)
88
- with switch1:
89
- self.on_bounding_box = tog.st_toggle_switch(
90
- "Show Bounding Box", key="show_bounding_box", default_value=True, active_color=rgb(255, 75, 75), track_color=rgb(50, 50, 50)
91
- )
92
- with switch2:
93
- self.on_five_landmarks = tog.st_toggle_switch(
94
- "Show Five Landmarks", key="show_five_landmarks", default_value=True, active_color=rgb(255, 75, 75),
95
- track_color=rgb(50, 50, 50)
96
- )
97
- switch3, switch4 = st.columns(2)
98
- with switch3:
99
- self.on_mesh = tog.st_toggle_switch(
100
- "Show Mesh", key="show_mesh", default_value=True, active_color=rgb(255, 75, 75),
101
- track_color=rgb(50, 50, 50)
102
- )
103
- with switch4:
104
- self.on_text = tog.st_toggle_switch(
105
- "Show Text", key="show_text", default_value=True, active_color=rgb(255, 75, 75),
106
- track_color=rgb(50, 50, 50)
107
- )
108
- st.markdown("---")
109
-
110
- st.markdown("## Face Recognition")
111
- self.similarity_threshold = st.slider(
112
- "Similarity Threshold", min_value=0.0, max_value=2.0, value=0.67
113
- )
114
-
115
- self.on_show_faces = tog.st_toggle_switch(
116
- "Show Recognized Faces", key="show_recognized_faces", default_value=True, active_color=rgb(255, 75, 75), track_color=rgb(50, 50, 50)
117
- )
118
-
119
- self.model_name = st.selectbox(
120
- "Model",
121
- ["MobileNet", "ResNet"],
122
- index=0,
123
- )
124
- st.markdown("---")
125
-
126
- st.markdown("## Gallery")
127
- self.uploaded_files = st.file_uploader(
128
- "Choose multiple images to upload", accept_multiple_files=True
129
- )
130
-
131
- self.gallery_images, self.gallery_embs, self.gallery_names= gallery_processing(self.uploaded_files)
132
-
133
- st.markdown("**Gallery Faces**")
134
- show_images(self.gallery_images, self.gallery_names, 3)
135
- st.markdown("---")
136
-
137
-
138
- class KPI:
139
- """Class for displaying KPIs in a row
140
- Args:
141
- keys (list): List of KPI names
142
- """
143
- def __init__(self, keys):
144
- self.kpi_texts = []
145
- row = st.columns(len(keys))
146
- for kpi, key in zip(row, keys):
147
- with kpi:
148
- item_row = st.columns(2)
149
- item_row[0].markdown(f"**{key}**:")
150
- self.kpi_texts.append(item_row[1].markdown("-"))
151
-
152
- def update_kpi(self, kpi_values):
153
- for kpi_text, kpi_value in zip(self.kpi_texts, kpi_values):
154
- kpi_text.write(
155
- f"<h5 style='text-align: center; color: red;'>{kpi_value:.2f}</h5>"
156
- if isinstance(kpi_value, float)
157
- else f"<h5 style='text-align: center; color: red;'>{kpi_value}</h5>",
158
- unsafe_allow_html=True,
159
- )
160
-
161
- # -----------------------------------------------------------------------------------------------
162
- # Streamlit App
163
- st.title("FaceID App Demonstration")
164
-
165
- # Sidebar
166
- sb = SideBar()
167
-
168
- # Get Access to Webcam
169
- webcam = init_webcam(width=sb.resolution[0])
170
-
171
- # KPI Section
172
- st.markdown("**Stats**")
173
- kpi = KPI([
174
- "**FrameRate**",
175
- "**Detected Faces**",
176
- "**Image Dims**",
177
- "**Detection [ms]**",
178
- "**Normalization [ms]**",
179
- "**Inference [ms]**",
180
- "**Recognition [ms]**",
181
- "**Annotations [ms]**",
182
- "**Show Faces [ms]**",
183
- ])
184
- st.markdown("---")
185
-
186
- # Live Stream Display
187
- stream_display = st.empty()
188
- st.markdown("---")
189
-
190
- # Display Detected Faces
191
- st.markdown("**Detected Faces**")
192
- face_window = st.empty()
193
- st.markdown("---")
194
-
195
-
196
- if webcam:
197
- prevTime = 0
198
- while True:
199
- # Init times to "-" to show something if face recognition is turned off
200
- time_detection = "-"
201
- time_alignment = "-"
202
- time_inference = "-"
203
- time_identification = "-"
204
- time_annotations = "-"
205
- time_show_faces = "-"
206
-
207
- try:
208
- # Get Frame from Webcam
209
- frame = webcam.get_frame(timeout=1)
210
-
211
- # Convert to OpenCV Image
212
- frame = frame.to_ndarray(format="rgb24")
213
- except:
214
- continue
215
-
216
- # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
217
- # FACE RECOGNITION PIPELINE
218
- if sb.on_face_recognition:
219
- # FACE DETECTION ---------------------------------------------------------
220
- start_time = time.time()
221
- detections = detect_faces(frame, detection_model)
222
- time_detection = (time.time() - start_time) * 1000
223
-
224
- # FACE ALIGNMENT ---------------------------------------------------------
225
- start_time = time.time()
226
- aligned_faces = align_faces(frame, detections)
227
- time_alignment = (time.time() - start_time) * 1000
228
-
229
- # INFERENCE --------------------------------------------------------------
230
- start_time = time.time()
231
- if len(sb.gallery_embs) > 0:
232
- faces_embs = inference(aligned_faces, identification_model)
233
- else:
234
- faces_embs = []
235
- time_inference = (time.time() - start_time) * 1000
236
-
237
- # FACE IDENTIFCATION -----------------------------------------------------
238
- start_time = time.time()
239
- if len(faces_embs) > 0 and len(sb.gallery_embs) > 0:
240
- ident_names, ident_dists, ident_imgs = identify(faces_embs, sb.gallery_embs, sb.gallery_names, sb.gallery_images, thresh=sb.similarity_threshold)
241
- else:
242
- ident_names, ident_dists, ident_imgs = [], [], []
243
- time_identification = (time.time() - start_time) * 1000
244
-
245
- # ANNOTATIONS ------------------------------------------------------------
246
- start_time = time.time()
247
- frame = cv2.resize(frame, (1920, 1080)) # to make annotation in HD
248
- frame.flags.writeable = True # (hack to make annotations faster)
249
- if sb.on_mesh:
250
- frame = draw_mesh(frame, detections)
251
- if sb.on_five_landmarks:
252
- frame = draw_landmarks(frame, detections)
253
- if sb.on_bounding_box:
254
- frame = draw_bounding_box(frame, detections, ident_names)
255
- if sb.on_text:
256
- frame = draw_text(frame, detections, ident_names)
257
- time_annotations = (time.time() - start_time) * 1000
258
-
259
- # DISPLAY DETECTED FACES -------------------------------------------------
260
- start_time = time.time()
261
- if sb.on_show_faces:
262
- show_faces(
263
- aligned_faces,
264
- ident_names,
265
- ident_dists,
266
- ident_imgs,
267
- num_cols=3,
268
- channels="RGB",
269
- display=face_window,
270
- )
271
- time_show_faces = (time.time() - start_time) * 1000
272
- # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
273
-
274
-
275
-
276
- # DISPLAY THE LIVE STREAM --------------------------------------------------
277
- stream_display.image(
278
- frame, channels="RGB", caption="Live-Stream", use_column_width=True
279
- )
280
-
281
- # CALCULATE FPS -----------------------------------------------------------
282
- currTime = time.time()
283
- fps = 1 / (currTime - prevTime)
284
- prevTime = currTime
285
-
286
- # UPDATE KPIS -------------------------------------------------------------
287
- kpi.update_kpi(
288
- [
289
- fps,
290
- len(detections),
291
- sb.resolution,
292
- time_detection,
293
- time_alignment,
294
- time_inference,
295
- time_identification,
296
- time_annotations,
297
- time_show_faces,
298
- ]
299
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
packages.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ libgl1-mesa-glx
tools/.DS_Store DELETED
Binary file (6.15 kB)
 
tools/alignment.py DELETED
@@ -1,39 +0,0 @@
1
- import numpy as np
2
- import cv2
3
- from skimage.transform import SimilarityTransform
4
-
5
-
6
- FIVE_LANDMARKS = [470, 475, 1, 57, 287]
7
-
8
-
9
- def align(img, landmarks, target_size=(112, 112)):
10
- dst = np.array(
11
- [
12
- [
13
- landmarks.landmark[i].x * img.shape[1],
14
- landmarks.landmark[i].y * img.shape[0],
15
- ]
16
- for i in FIVE_LANDMARKS
17
- ],
18
- )
19
-
20
- src = np.array(
21
- [
22
- [38.2946, 51.6963],
23
- [73.5318, 51.5014],
24
- [56.0252, 71.7366],
25
- [41.5493, 92.3655],
26
- [70.7299, 92.2041],
27
- ],
28
- dtype=np.float32,
29
- )
30
- tform = SimilarityTransform()
31
- tform.estimate(dst, src)
32
- tmatrix = tform.params[0:2, :]
33
- return cv2.warpAffine(img, tmatrix, target_size, borderValue=0.0)
34
-
35
-
36
-
37
- def align_faces(img, detections):
38
- aligned_faces = [align(img, detection.multi_face_landmarks) for detection in detections]
39
- return aligned_faces
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tools/annotation.py DELETED
@@ -1,121 +0,0 @@
1
- import cv2
2
- import mediapipe as mp
3
- import streamlit as st
4
-
5
-
6
- FIVE_LANDMARKS = [470, 475, 1, 57, 287]
7
- FACE_CONNECTIONS = mp.solutions.face_mesh_connections.FACEMESH_TESSELATION
8
-
9
-
10
-
11
- def draw_bounding_box(img, detections, ident_names, margin=10):
12
- # Draw the bounding box on the original frame
13
- for detection, name in zip(detections, ident_names):
14
-
15
- color = (255, 0, 0) if name == "Unknown" else (0, 255, 0)
16
-
17
- x_coords = [
18
- landmark.x * img.shape[1] for landmark in detection.multi_face_landmarks.landmark
19
- ]
20
- y_coords = [
21
- landmark.y * img.shape[0] for landmark in detection.multi_face_landmarks.landmark
22
- ]
23
-
24
- x_min, x_max = int(min(x_coords) - margin), int(max(x_coords) + margin)
25
- y_min, y_max = int(min(y_coords) - margin), int(max(y_coords) + margin)
26
-
27
- cv2.rectangle(img, (x_min, y_min), (x_max, y_max), color, 2)
28
- cv2.rectangle(img, (x_min, y_min - img.shape[0] // 25), (x_max, y_min), color, -1)
29
-
30
- return img
31
-
32
-
33
- def draw_text(
34
- img,
35
- detections,
36
- ident_names,
37
- margin=10,
38
- font_scale=1,
39
- font_color=(0, 0, 0),
40
- font=cv2.FONT_HERSHEY_SIMPLEX,
41
- ):
42
-
43
- font_scale = img.shape[0] / 1000
44
- for detection, name in zip(detections, ident_names):
45
- x_coords = [
46
- landmark.x * img.shape[1] for landmark in detection.multi_face_landmarks.landmark
47
- ]
48
- y_coords = [
49
- landmark.y * img.shape[0] for landmark in detection.multi_face_landmarks.landmark
50
- ]
51
-
52
- x_min = int(min(x_coords) - margin)
53
- y_min = int(min(y_coords) - margin)
54
-
55
- cv2.putText(
56
- img,
57
- name,
58
- (x_min + img.shape[0] // 400, y_min - img.shape[0] // 100),
59
- font,
60
- font_scale,
61
- font_color,
62
- 2,
63
- )
64
-
65
- return img
66
-
67
-
68
- def draw_mesh(img, detections):
69
- for detection in detections:
70
- # Draw the connections
71
- for connection in FACE_CONNECTIONS:
72
- cv2.line(
73
- img,
74
- (
75
- int(detection.multi_face_landmarks.landmark[connection[0]].x * img.shape[1]),
76
- int(detection.multi_face_landmarks.landmark[connection[0]].y * img.shape[0]),
77
- ),
78
- (
79
- int(detection.multi_face_landmarks.landmark[connection[1]].x * img.shape[1]),
80
- int(detection.multi_face_landmarks.landmark[connection[1]].y * img.shape[0]),
81
- ),
82
- (255, 255, 255),
83
- 1,
84
- )
85
-
86
- # Draw the landmarks
87
- for points in detection.multi_face_landmarks.landmark:
88
- cv2.circle(
89
- img,
90
- (
91
- int(points.x * img.shape[1]),
92
- int(points.y * img.shape[0]),
93
- ),
94
- 1,
95
- (0, 255, 0),
96
- -1,
97
- )
98
- return img
99
-
100
-
101
- def draw_landmarks(img, detections):
102
- # Draw the face landmarks on the original frame
103
- for points in FIVE_LANDMARKS:
104
- for detection in detections:
105
- cv2.circle(
106
- img,
107
- (
108
- int(
109
- detection.multi_face_landmarks.landmark[points].x
110
- * img.shape[1]
111
- ),
112
- int(
113
- detection.multi_face_landmarks.landmark[points].y
114
- * img.shape[0]
115
- ),
116
- ),
117
- 5,
118
- (0, 0, 255),
119
- -1,
120
- )
121
- return img
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tools/detection.py DELETED
@@ -1,44 +0,0 @@
1
- import mediapipe as mp
2
- import streamlit as st
3
-
4
-
5
- class Detection:
6
- multi_face_bboxes = []
7
- multi_face_landmarks = []
8
-
9
-
10
- #@st.cache_resource
11
- def load_detection_model(max_faces=2, detection_confidence=0.5, tracking_confidence=0.5):
12
- model = mp.solutions.face_mesh.FaceMesh(
13
- refine_landmarks=True,
14
- min_detection_confidence=detection_confidence,
15
- min_tracking_confidence=tracking_confidence,
16
- max_num_faces=max_faces,
17
- )
18
- return model
19
-
20
-
21
- def detect_faces(frame, model):
22
-
23
- # Process the frame with MediaPipe Face Mesh
24
- results = model.process(frame)
25
-
26
- # Get the Bounding Boxes from the detected faces
27
- detections = []
28
- if results.multi_face_landmarks:
29
- for landmarks in results.multi_face_landmarks:
30
- x_coords = [
31
- landmark.x * frame.shape[1] for landmark in landmarks.landmark
32
- ]
33
- y_coords = [
34
- landmark.y * frame.shape[0] for landmark in landmarks.landmark
35
- ]
36
-
37
- x_min, x_max = int(min(x_coords)), int(max(x_coords))
38
- y_min, y_max = int(min(y_coords)), int(max(y_coords))
39
-
40
- detection = Detection()
41
- detection.multi_face_bboxes=[x_min, y_min, x_max, y_max]
42
- detection.multi_face_landmarks=landmarks
43
- detections.append(detection)
44
- return detections
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tools/face_recognition.py ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .nametypes import Detection, Identity
2
+ import numpy as np
3
+ import cv2
4
+ from sklearn.metrics.pairwise import cosine_distances
5
+ from skimage.transform import SimilarityTransform
6
+
7
+
8
+ def detect_faces(frame, model):
9
+ # Process the frame with MediaPipe Face Mesh
10
+ results = model.process(frame)
11
+
12
+ # Get the Bounding Boxes from the detected faces
13
+ detections = []
14
+ if results.multi_face_landmarks:
15
+ for face in results.multi_face_landmarks:
16
+ xs = [landmark.x for landmark in face.landmark]
17
+ ys = [landmark.y for landmark in face.landmark]
18
+ bbox = [min(xs), min(ys), max(xs), max(ys)]
19
+
20
+ FIVE_LANDMARKS = [470, 475, 1, 57, 287]
21
+
22
+ landmarks = [
23
+ [face.landmark[i].x, face.landmark[i].y] for i in FIVE_LANDMARKS
24
+ ]
25
+
26
+ detections.append(Detection(bbox=bbox, landmarks=landmarks))
27
+ return detections
28
+
29
+
30
+ def align(img, landmarks, target_size=(112, 112)):
31
+ # Transform to Landmark-Coordinates from relative landmark positions
32
+ dst = np.asarray(landmarks) * img.shape[:2][::-1]
33
+
34
+ # Target Landmarks-Coordinates from ArcFace Paper
35
+ src = np.array(
36
+ [
37
+ [38.2946, 51.6963],
38
+ [73.5318, 51.5014],
39
+ [56.0252, 71.7366],
40
+ [41.5493, 92.3655],
41
+ [70.7299, 92.2041],
42
+ ],
43
+ dtype=np.float32,
44
+ )
45
+
46
+ # Estimate the transformation matrix
47
+ tform = SimilarityTransform()
48
+ tform.estimate(dst, src)
49
+ tmatrix = tform.params[0:2, :]
50
+
51
+ # Apply the transformation matrix
52
+ img = cv2.warpAffine(img, tmatrix, target_size, borderValue=0.0)
53
+
54
+ return img
55
+
56
+
57
+ def align_faces(img, detections):
58
+ updated_detections = []
59
+ for detection in detections:
60
+ updated_detections.append(
61
+ detection._replace(face=align(img, detection.landmarks))
62
+ )
63
+ return updated_detections
64
+
65
+ # TODO Error when uploading image while running!
66
+ def inference(detections, model):
67
+ updated_detections = []
68
+ faces = [detection.face for detection in detections if detection.face is not None]
69
+
70
+ if len(faces) > 0:
71
+ faces = np.asarray(faces).astype(np.float32) / 255
72
+ model.resize_tensor_input(model.get_input_details()[0]["index"], faces.shape)
73
+ model.allocate_tensors()
74
+ model.set_tensor(model.get_input_details()[0]["index"], faces)
75
+ model.invoke()
76
+ embs = [model.get_tensor(elem["index"]) for elem in model.get_output_details()][
77
+ 0
78
+ ]
79
+
80
+ for idx, detection in enumerate(detections):
81
+ updated_detections.append(detection._replace(emdedding=embs[idx]))
82
+ return updated_detections
83
+
84
+
85
+ def recognize_faces(detections, gallery, thresh=0.67):
86
+
87
+ if len(gallery) == 0 or len(detections) == 0:
88
+ return detections
89
+
90
+ gallery_embs = np.asarray([identity.embedding for identity in gallery])
91
+ detection_embs = np.asarray([detection.emdedding for detection in detections])
92
+
93
+ cos_distances = cosine_distances(detection_embs, gallery_embs)
94
+
95
+ updated_detections = []
96
+ for idx, detection in enumerate(detections):
97
+ idx_min = np.argmin(cos_distances[idx])
98
+ if thresh and cos_distances[idx][idx_min] > thresh:
99
+ dist = cos_distances[idx][idx_min]
100
+ pred = None
101
+ else:
102
+ dist = cos_distances[idx][idx_min]
103
+ pred = idx_min
104
+ updated_detections.append(
105
+ detection._replace(
106
+ name=gallery[pred].name.split(".jpg")[0].split(".png")[0].split(".jpeg")[0] if pred is not None else None,
107
+ emdedding_match=gallery[pred].embedding if pred is not None else None,
108
+ face_match=gallery[pred].image if pred is not None else None,
109
+ distance=dist,
110
+ )
111
+ )
112
+
113
+ return updated_detections
114
+
115
+
116
+ def process_gallery(files, face_detection_model, face_recognition_model):
117
+ gallery = []
118
+ for file in files:
119
+ file_bytes = np.asarray(bytearray(file.read()), dtype=np.uint8)
120
+ img = cv2.cvtColor(
121
+ cv2.imdecode(file_bytes, cv2.IMREAD_COLOR), cv2.COLOR_BGR2RGB
122
+ )
123
+
124
+ detections = detect_faces(img, face_detection_model)
125
+
126
+ # We accept only one face per image!
127
+ if detections == []:
128
+ continue
129
+ elif len(detections) > 1:
130
+ detections = detections[:1]
131
+
132
+ detections = align_faces(img, detections)
133
+ detections = inference(detections, face_recognition_model)
134
+
135
+ gallery.append(
136
+ Identity(
137
+ name=file.name,
138
+ embedding=detections[0].emdedding,
139
+ image=detections[0].face,
140
+ )
141
+ )
142
+
143
+ return gallery
144
+
145
+
146
+ def draw_detections(
147
+ frame, detections, bbox=True, landmarks=True, name=True, upscale=True
148
+ ):
149
+ if upscale:
150
+ frame = cv2.resize(
151
+ frame, (1920, 1080)
152
+ ) # Upscale frame for better visualization
153
+
154
+ shape = np.asarray(frame.shape[:2][::-1])
155
+
156
+ for detection in detections:
157
+ # Draw Landmarks
158
+ if landmarks:
159
+ for landmark in detection.landmarks:
160
+ cv2.circle(
161
+ frame,
162
+ (np.asarray(landmark) * shape).astype(int),
163
+ 5,
164
+ (0, 0, 255),
165
+ -1,
166
+ )
167
+
168
+ # Draw Bounding Box
169
+ if bbox:
170
+ cv2.rectangle(
171
+ frame,
172
+ (np.asarray(detection.bbox[:2]) * shape).astype(int),
173
+ (np.asarray(detection.bbox[2:]) * shape).astype(int),
174
+ (0, 255, 0),
175
+ 2,
176
+ )
177
+
178
+ # Draw Name
179
+ if name:
180
+ cv2.rectangle(
181
+ frame,
182
+ (
183
+ int(detection.bbox[0] * shape[0]),
184
+ int(detection.bbox[1] * shape[1] - (shape[1] // 25)),
185
+ ),
186
+ (int(detection.bbox[2] * shape[0]), int(detection.bbox[1] * shape[1])),
187
+ (255, 255, 255),
188
+ -1,
189
+ )
190
+
191
+ cv2.putText(
192
+ frame,
193
+ detection.name,
194
+ (
195
+ int(detection.bbox[0] * shape[0] + shape[0] // 400),
196
+ int(detection.bbox[1] * shape[1] - shape[1] // 100),
197
+ ),
198
+ cv2.FONT_HERSHEY_SIMPLEX,
199
+ 1,
200
+ (0, 0, 0),
201
+ 2,
202
+ )
203
+
204
+ return frame
tools/identification.py DELETED
@@ -1,47 +0,0 @@
1
- import numpy as np
2
- import tflite_runtime.interpreter as tflite
3
- from sklearn.metrics.pairwise import cosine_distances
4
- import streamlit as st
5
- import time
6
-
7
-
8
- MODEL_PATHS = {
9
- "MobileNet": "./models/mobileNet.tflite",
10
- "ResNet": "./models/resNet.tflite",
11
- }
12
-
13
-
14
- #@st.cache_resource
15
- def load_identification_model(name="MobileNet"):
16
- model = tflite.Interpreter(model_path=MODEL_PATHS[name])
17
- return model
18
-
19
-
20
- def inference(imgs, model):
21
- if len(imgs) > 0:
22
- imgs = np.asarray(imgs).astype(np.float32) / 255
23
- model.resize_tensor_input(model.get_input_details()[0]["index"], imgs.shape)
24
- model.allocate_tensors()
25
- model.set_tensor(model.get_input_details()[0]["index"], imgs)
26
- model.invoke()
27
- embs = [model.get_tensor(elem["index"]) for elem in model.get_output_details()]
28
- return embs[0]
29
- else:
30
- return []
31
-
32
-
33
- def identify(embs_src, embs_gal, labels_gal, imgs_gal, thresh=None):
34
- all_dists = cosine_distances(embs_src, embs_gal)
35
- ident_names, ident_dists, ident_imgs = [], [], []
36
- for dists in all_dists:
37
- idx_min = np.argmin(dists)
38
- if thresh and dists[idx_min] > thresh:
39
- dist = dists[idx_min]
40
- pred = None
41
- else:
42
- dist = dists[idx_min]
43
- pred = idx_min
44
- ident_names.append(labels_gal[pred] if pred is not None else "Unknown")
45
- ident_dists.append(dist)
46
- ident_imgs.append(imgs_gal[pred] if pred is not None else None)
47
- return ident_names, ident_dists, ident_imgs
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tools/nametypes.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import NamedTuple, List
2
+ import numpy as np
3
+
4
+
5
+ class Detection(NamedTuple):
6
+ bbox: List[int]
7
+ landmarks: List[List[int]]
8
+ name: str = None
9
+ face: np.ndarray = None
10
+ emdedding: np.ndarray = None
11
+ emdedding_match: np.ndarray = None
12
+ face_match: np.ndarray = None
13
+ distance: float = None
14
+
15
+
16
+ class Stats(NamedTuple):
17
+ fps: float
18
+ resolution: List[int]
19
+ num_faces: int
20
+ detection: float
21
+ normalization: float
22
+ inference: float
23
+ recognition: float
24
+ drawing: float
25
+
26
+
27
+ class Identity(NamedTuple):
28
+ name: str
29
+ embedding: np.ndarray
30
+ image: np.ndarray
tools/utils.py CHANGED
@@ -1,66 +1,172 @@
 
 
 
 
1
  import streamlit as st
 
 
2
  import cv2
 
 
3
 
4
- def rgb(r, g, b):
5
- return '#{:02x}{:02x}{:02x}'.format(r, g, b)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
 
8
- def show_images(images, names, num_cols, channels="RGB"):
9
- num_images = len(images)
10
-
11
- # Calculate the number of rows and columns
12
- num_rows = -(
13
- -num_images // num_cols
14
- ) # This also handles the case when num_images is not a multiple of num_cols
15
-
16
- for row in range(num_rows):
17
- # Create the columns
18
- cols = st.sidebar.columns(num_cols)
19
-
20
- for i, col in enumerate(cols):
21
- idx = row * num_cols + i
22
-
23
- if idx < num_images:
24
- img = images[idx]
25
- if len(names) == 0:
26
- names = ["Unknown"] * len(images)
27
- name = names[idx]
28
- col.image(img, caption=name, channels=channels, width=112)
29
-
30
-
31
- def show_faces(images, names, distances, gal_images, num_cols, channels="RGB", display=st):
32
- if len(images) == 0 or len(names) == 0:
33
- display.write("No faces detected, or gallery empty!")
34
- return
35
- # Calculate the number of rows and columns
36
- num_rows = -(
37
- -len(images) // num_cols
38
- ) # This also handles the case when num_images is not a multiple of num_cols
39
-
40
- for row in range(num_rows):
41
- # Create the columns
42
- cols = display.columns(num_cols)
43
-
44
- for i, col in enumerate(cols):
45
- idx = row * num_cols + i
46
-
47
- if idx < len(images):
48
- img = images[idx]
49
- name = names[idx]
50
- dist = distances[idx]
51
- col.image(img, channels=channels, width=112)
52
-
53
- if gal_images[idx] is not None:
54
- col.text(" ⬍ matching ⬍")
55
- col.image(gal_images[idx], caption=name, channels=channels, width=112)
56
- else:
57
- col.markdown("")
58
- col.write("No match found")
59
- col.markdown(
60
- f"**Distance: {dist:.4f}**" if dist else f"**Distance: -**"
61
  )
 
62
  else:
63
- col.empty()
64
- col.markdown("")
65
- col.empty()
66
- col.markdown("")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import os
3
+ import urllib.request
4
+ from pathlib import Path
5
  import streamlit as st
6
+ from twilio.rest import Client
7
+ import os
8
  import cv2
9
+ import numpy as np
10
+ import hashlib
11
 
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ @st.cache_data
17
+ def get_ice_servers(name="twilio"):
18
+ """Get ICE servers from Twilio.
19
+ Returns:
20
+ List of ICE servers.
21
+ """
22
+ if name == "twilio":
23
+ # Ref: https://www.twilio.com/docs/stun-turn/api
24
+ try:
25
+ account_sid = os.environ["TWILIO_ACCOUNT_SID"]
26
+ auth_token = os.environ["TWILIO_AUTH_TOKEN"]
27
+ except KeyError:
28
+ logger.warning(
29
+ "Twilio credentials are not set. Fallback to a free STUN server from Google."
30
+ )
31
+ return [{"urls": ["stun:stun.l.google.com:19302"]}]
32
+
33
+ client = Client(account_sid, auth_token)
34
+
35
+ token = client.tokens.create()
36
+
37
+ return token.ice_servers
38
+
39
+ elif name == "metered":
40
+ try:
41
+ username = os.environ["METERED_USERNAME"]
42
+ credential = os.environ["METERED_CREDENTIAL"]
43
+ except KeyError:
44
+ logger.warning(
45
+ "Metered credentials are not set. Fallback to a free STUN server from Google."
46
+ )
47
+ return [{"urls": ["stun:stun.l.google.com:19302"]}]
48
+
49
+ ice_servers = [
50
+ {"url": "stun:a.relay.metered.ca:80", "urls": "stun:a.relay.metered.ca:80"},
51
+ {
52
+ "url": "turn:a.relay.metered.ca:80",
53
+ "username": username,
54
+ "urls": "turn:a.relay.metered.ca:80",
55
+ "credential": credential,
56
+ },
57
+ {
58
+ "url": "turn:a.relay.metered.ca:80?transport=tcp",
59
+ "username": username,
60
+ "urls": "turn:a.relay.metered.ca:80?transport=tcp",
61
+ "credential": credential,
62
+ },
63
+ {
64
+ "url": "turn:a.relay.metered.ca:443",
65
+ "username": username,
66
+ "urls": "turn:a.relay.metered.ca:443",
67
+ "credential": credential,
68
+ },
69
+ {
70
+ "url": "turn:a.relay.metered.ca:443?transport=tcp",
71
+ "username": username,
72
+ "urls": "turn:a.relay.metered.ca:443?transport=tcp",
73
+ "credential": credential,
74
+ },
75
+ ]
76
+ return ice_servers
77
+ else:
78
+ raise ValueError(f"Unknown name: {name}")
79
+
80
+
81
+ def get_hash(filepath):
82
+ hasher = hashlib.sha256()
83
+ with open(filepath, "rb") as file:
84
+ for chunk in iter(lambda: file.read(65535), b""):
85
+ hasher.update(chunk)
86
+ return hasher.hexdigest()
87
 
88
 
89
+ def download_file(url, model_path: Path, file_hash=None):
90
+ if model_path.exists():
91
+ if file_hash:
92
+ hasher = hashlib.sha256()
93
+ with open(model_path, "rb") as file:
94
+ for chunk in iter(lambda: file.read(65535), b""):
95
+ hasher.update(chunk)
96
+ if not hasher.hexdigest() == file_hash:
97
+ print(
98
+ "A local file was found, but it seems to be incomplete or outdated because the file hash does not "
99
+ "match the original value of "
100
+ + file_hash
101
+ + " so data will be downloaded."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  )
103
+ download = True
104
  else:
105
+ print("Using a verified local file.")
106
+ download = False
107
+ else:
108
+ model_path.mkdir(parents=True, exist_ok=True)
109
+ print("Downloading data ...")
110
+ download = True
111
+
112
+ if download:
113
+
114
+ # These are handles to two visual elements to animate.
115
+ weights_warning, progress_bar = None, None
116
+ try:
117
+ weights_warning = st.warning("Downloading %s..." % url)
118
+ progress_bar = st.progress(0)
119
+ with open(model_path, "wb") as output_file:
120
+ with urllib.request.urlopen(url) as response:
121
+ length = int(response.info()["Content-Length"])
122
+ counter = 0.0
123
+ MEGABYTES = 2.0**20.0
124
+ while True:
125
+ data = response.read(8192)
126
+ if not data:
127
+ break
128
+ counter += len(data)
129
+ output_file.write(data)
130
+
131
+ # We perform animation by overwriting the elements.
132
+ weights_warning.warning(
133
+ "Downloading %s... (%6.2f/%6.2f MB)"
134
+ % (url, counter / MEGABYTES, length / MEGABYTES)
135
+ )
136
+ progress_bar.progress(min(counter / length, 1.0))
137
+
138
+ # Finally, we remove these visual elements by calling .empty().
139
+ finally:
140
+ if weights_warning is not None:
141
+ weights_warning.empty()
142
+ if progress_bar is not None:
143
+ progress_bar.empty()
144
+
145
+
146
+ # Function to format floats within a list
147
+ def format_floats(val):
148
+ if isinstance(val, list):
149
+ return [f"{num:.2f}" for num in val]
150
+ if isinstance(val, np.ndarray):
151
+ return np.asarray([f"{num:.2f}" for num in val])
152
+ else:
153
+ return val
154
+
155
+
156
+ def display_match(d):
157
+ im = np.concatenate([d.face, d.face_match])
158
+ border_size = 2
159
+ border = cv2.copyMakeBorder(
160
+ im,
161
+ top=border_size,
162
+ bottom=border_size,
163
+ left=border_size,
164
+ right=border_size,
165
+ borderType=cv2.BORDER_CONSTANT,
166
+ value=(255, 255, 120)
167
+ )
168
+ return border
169
+
170
+
171
+ def rgb(r, g, b):
172
+ return '#{:02x}{:02x}{:02x}'.format(r, g, b)
tools/webcam.py DELETED
@@ -1,38 +0,0 @@
1
- import streamlit as st
2
- from streamlit_webrtc import webrtc_streamer, WebRtcMode
3
- import os
4
- from twilio.rest import Client
5
-
6
-
7
- account_sid = os.environ['TWILIO_ACCOUNT_SID']
8
- auth_token = os.environ['TWILIO_AUTH_TOKEN']
9
- client = Client(account_sid, auth_token)
10
-
11
- token = client.tokens.create()
12
-
13
-
14
- RTC_CONFIGURATION={
15
- "iceServers": token.ice_servers
16
- }
17
-
18
- def init_webcam(width=680):
19
- ctx = webrtc_streamer(
20
- key="FaceIDAppDemo",
21
- mode=WebRtcMode.SENDONLY,
22
- rtc_configuration=RTC_CONFIGURATION,
23
- media_stream_constraints={
24
- "video": {
25
- "width": {
26
- "min": width,
27
- "ideal": width,
28
- "max": width,
29
- },
30
- },
31
- "audio": False,
32
- },
33
-
34
- video_receiver_size=1,
35
- async_processing=True,
36
- )
37
- return ctx.video_receiver
38
-