Spaces:
Build error
Build error
charlesollion
commited on
Commit
•
54c4dfe
1
Parent(s):
4bc04ea
adding icons and small tracking update
Browse files- app.py +18 -12
- data/icons/bouteille.png +0 -0
- data/icons/briquet.png +0 -0
- data/icons/chaussure.png +0 -0
- data/icons/contenant.png +0 -0
- data/icons/dechet.png +0 -0
- data/icons/emballage.png +0 -0
- data/icons/fragment.png +0 -0
- data/icons/hamecon.png +0 -0
- data/icons/mousse.png +0 -0
- data/icons/pneu.png +0 -0
- tools/files.py +29 -3
- tracking/track_video.py +1 -1
- tracking/utils.py +47 -14
app.py
CHANGED
@@ -2,7 +2,7 @@ from webbrowser import get
|
|
2 |
import gradio as gr
|
3 |
import os
|
4 |
import os.path as op
|
5 |
-
from tools.files import download_from_url, create_unique_folder
|
6 |
|
7 |
import json
|
8 |
from typing import Dict, List, Tuple
|
@@ -62,8 +62,11 @@ id_categories = {
|
|
62 |
|
63 |
|
64 |
config_track = DotDict({
|
65 |
-
"
|
66 |
-
"
|
|
|
|
|
|
|
67 |
"downsampling_factor": 4,
|
68 |
"noise_covariances_path": "data/tracking_parameters",
|
69 |
"output_shape": (960,544),
|
@@ -86,7 +89,7 @@ os.environ["VERBOSE"] = "False"
|
|
86 |
URL_MODEL = "https://github.com/surfriderfoundationeurope/IA_Pau/releases/download/v0.1/yolov5.pt"
|
87 |
FILE_MODEL = "yolov5.pt"
|
88 |
model_path = download_from_url(URL_MODEL, FILE_MODEL, logger, "./models")
|
89 |
-
model_yolo = load_model(model_path, config_track.device)
|
90 |
|
91 |
|
92 |
logger.info('---Centernet model...')
|
@@ -113,6 +116,8 @@ video3_path = op.join("./data", FILE_DEMO3)
|
|
113 |
JSON_FILE_PATH = "data/"
|
114 |
|
115 |
|
|
|
|
|
116 |
def track(args):
|
117 |
device = torch.device("cpu")
|
118 |
|
@@ -206,14 +211,15 @@ def run_model(video_path, model_type, seconds, skip, tau, kappa, gps_file):
|
|
206 |
progress_bar=True,
|
207 |
preload=False,
|
208 |
max_frame=config_track.max_length)
|
209 |
-
|
210 |
# Get GPS Data
|
211 |
gps_data = get_filled_gps(gps_file,video_path)
|
212 |
|
213 |
# Generate new video
|
214 |
generate_video_with_annotations(reader, output_json, output_path,
|
215 |
config_track.skip_frames, config_track.max_length,
|
216 |
-
config_track.downscale_output, logger,gps_data
|
|
|
217 |
output_label = count_objects(output_json, id_categories)
|
218 |
|
219 |
|
@@ -225,7 +231,7 @@ def run_model(video_path, model_type, seconds, skip, tau, kappa, gps_file):
|
|
225 |
with open(output_json_path) as json_file:
|
226 |
predictions = json.load(json_file)
|
227 |
trash_df = get_df_prediction(predictions, reader.fps)
|
228 |
-
if len(trash_df) != 0 :
|
229 |
# Get Trash prediction alongside GPS data
|
230 |
trash_gps_df = get_trash_gps_df(trash_df,gps_data)
|
231 |
trash_gps_geo_df = get_trash_gps_geo_df(trash_gps_df)
|
@@ -235,10 +241,10 @@ def run_model(video_path, model_type, seconds, skip, tau, kappa, gps_file):
|
|
235 |
map_path = get_plastic_map(center_lat,center_long,trash_gps_geo_df,out_folder)
|
236 |
html_content = codecs.open(map_path, 'r')
|
237 |
map_html = html_content.read()
|
238 |
-
map_frame = f"""<iframe style="width: 100%; height: 480px" name="result" allow="midi; geolocation; microphone; camera;
|
239 |
-
display-capture; encrypted-media;" sandbox="allow-modals allow-forms
|
240 |
-
allow-scripts allow-same-origin allow-popups
|
241 |
-
allow-top-navigation-by-user-activation allow-downloads" allowfullscreen=""
|
242 |
allowpaymentrequest="" frameborder="0" srcdoc='{map_html}'></iframe>"""
|
243 |
|
244 |
logger.info('---Surfnet End processing...')
|
@@ -275,7 +281,7 @@ def get_plastic_map(center_lat,center_long,trash_gps_gdf,out_folder)->str:
|
|
275 |
Returns:
|
276 |
map_html_path (str): full path to html map
|
277 |
"""
|
278 |
-
|
279 |
m = folium.Map([center_lat, center_long], zoom_start=16)
|
280 |
locs = zip(trash_gps_gdf.geometry.y,trash_gps_gdf.geometry.x)
|
281 |
labels = list(trash_gps_gdf['label'])
|
|
|
2 |
import gradio as gr
|
3 |
import os
|
4 |
import os.path as op
|
5 |
+
from tools.files import download_from_url, create_unique_folder, load_trash_icons
|
6 |
|
7 |
import json
|
8 |
from typing import Dict, List, Tuple
|
|
|
62 |
|
63 |
|
64 |
config_track = DotDict({
|
65 |
+
"yolo_conf_thrld": 0.35,
|
66 |
+
"yolo_iou_thrld": 0.5,
|
67 |
+
|
68 |
+
"confidence_threshold": 0.004, # for the tracking part
|
69 |
+
"detection_threshold": 0.3, # for centernet
|
70 |
"downsampling_factor": 4,
|
71 |
"noise_covariances_path": "data/tracking_parameters",
|
72 |
"output_shape": (960,544),
|
|
|
89 |
URL_MODEL = "https://github.com/surfriderfoundationeurope/IA_Pau/releases/download/v0.1/yolov5.pt"
|
90 |
FILE_MODEL = "yolov5.pt"
|
91 |
model_path = download_from_url(URL_MODEL, FILE_MODEL, logger, "./models")
|
92 |
+
model_yolo = load_model(model_path, config_track.device, config_track.yolo_conf_thrld, config_track.yolo_iou_thrld)
|
93 |
|
94 |
|
95 |
logger.info('---Centernet model...')
|
|
|
116 |
JSON_FILE_PATH = "data/"
|
117 |
|
118 |
|
119 |
+
labels2icons = load_trash_icons("./data/icons/")
|
120 |
+
|
121 |
def track(args):
|
122 |
device = torch.device("cpu")
|
123 |
|
|
|
211 |
progress_bar=True,
|
212 |
preload=False,
|
213 |
max_frame=config_track.max_length)
|
214 |
+
|
215 |
# Get GPS Data
|
216 |
gps_data = get_filled_gps(gps_file,video_path)
|
217 |
|
218 |
# Generate new video
|
219 |
generate_video_with_annotations(reader, output_json, output_path,
|
220 |
config_track.skip_frames, config_track.max_length,
|
221 |
+
config_track.downscale_output, logger, gps_data=gps_data,
|
222 |
+
labels2icons=labels2icons)
|
223 |
output_label = count_objects(output_json, id_categories)
|
224 |
|
225 |
|
|
|
231 |
with open(output_json_path) as json_file:
|
232 |
predictions = json.load(json_file)
|
233 |
trash_df = get_df_prediction(predictions, reader.fps)
|
234 |
+
if len(trash_df) != 0 :
|
235 |
# Get Trash prediction alongside GPS data
|
236 |
trash_gps_df = get_trash_gps_df(trash_df,gps_data)
|
237 |
trash_gps_geo_df = get_trash_gps_geo_df(trash_gps_df)
|
|
|
241 |
map_path = get_plastic_map(center_lat,center_long,trash_gps_geo_df,out_folder)
|
242 |
html_content = codecs.open(map_path, 'r')
|
243 |
map_html = html_content.read()
|
244 |
+
map_frame = f"""<iframe style="width: 100%; height: 480px" name="result" allow="midi; geolocation; microphone; camera;
|
245 |
+
display-capture; encrypted-media;" sandbox="allow-modals allow-forms
|
246 |
+
allow-scripts allow-same-origin allow-popups
|
247 |
+
allow-top-navigation-by-user-activation allow-downloads" allowfullscreen=""
|
248 |
allowpaymentrequest="" frameborder="0" srcdoc='{map_html}'></iframe>"""
|
249 |
|
250 |
logger.info('---Surfnet End processing...')
|
|
|
281 |
Returns:
|
282 |
map_html_path (str): full path to html map
|
283 |
"""
|
284 |
+
|
285 |
m = folium.Map([center_lat, center_long], zoom_start=16)
|
286 |
locs = zip(trash_gps_gdf.geometry.y,trash_gps_gdf.geometry.x)
|
287 |
labels = list(trash_gps_gdf['label'])
|
data/icons/bouteille.png
ADDED
data/icons/briquet.png
ADDED
data/icons/chaussure.png
ADDED
data/icons/contenant.png
ADDED
data/icons/dechet.png
ADDED
data/icons/emballage.png
ADDED
data/icons/fragment.png
ADDED
data/icons/hamecon.png
ADDED
data/icons/mousse.png
ADDED
data/icons/pneu.png
ADDED
tools/files.py
CHANGED
@@ -1,11 +1,13 @@
|
|
1 |
import os
|
2 |
import os.path as op
|
|
|
3 |
from urllib.request import urlretrieve
|
4 |
import datetime
|
|
|
5 |
|
6 |
|
7 |
def create_unique_folder(base_folder, filename):
|
8 |
-
"""Creates a unique folder based on the filename and timestamp
|
9 |
"""
|
10 |
folder_name = op.splitext(op.basename(filename))[0] + "_out_"
|
11 |
folder_name += datetime.datetime.now().strftime('%Y%m%d%H%M%S%f')
|
@@ -14,9 +16,9 @@ def create_unique_folder(base_folder, filename):
|
|
14 |
os.mkdir(output_dir)
|
15 |
return output_dir
|
16 |
|
|
|
17 |
def download_from_url(url, filename, logger, folder="./data/"):
|
18 |
-
"""
|
19 |
-
Download a file and place it in the corresponding folder if it does
|
20 |
not already exists
|
21 |
"""
|
22 |
filepath = op.realpath(op.join(folder, filename))
|
@@ -26,3 +28,27 @@ def download_from_url(url, filename, logger, folder="./data/"):
|
|
26 |
else:
|
27 |
logger.info('---File already downloaded.')
|
28 |
return filepath
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import os
|
2 |
import os.path as op
|
3 |
+
from pathlib import Path
|
4 |
from urllib.request import urlretrieve
|
5 |
import datetime
|
6 |
+
import cv2
|
7 |
|
8 |
|
9 |
def create_unique_folder(base_folder, filename):
|
10 |
+
""" Creates a unique folder based on the filename and timestamp
|
11 |
"""
|
12 |
folder_name = op.splitext(op.basename(filename))[0] + "_out_"
|
13 |
folder_name += datetime.datetime.now().strftime('%Y%m%d%H%M%S%f')
|
|
|
16 |
os.mkdir(output_dir)
|
17 |
return output_dir
|
18 |
|
19 |
+
|
20 |
def download_from_url(url, filename, logger, folder="./data/"):
|
21 |
+
""" Download a file and place it in the corresponding folder if it does
|
|
|
22 |
not already exists
|
23 |
"""
|
24 |
filepath = op.realpath(op.join(folder, filename))
|
|
|
28 |
else:
|
29 |
logger.info('---File already downloaded.')
|
30 |
return filepath
|
31 |
+
|
32 |
+
|
33 |
+
def load_trash_icons(folder_path):
|
34 |
+
""" loads all icons using cv2 format and returns a dict class -> opened icon
|
35 |
+
"""
|
36 |
+
folder_path = Path(folder_path)
|
37 |
+
id_path = {
|
38 |
+
'Fragment': folder_path / "fragment.png",#'Fragment', #'Sheet / tarp / plastic bag / fragment',
|
39 |
+
'Insulating': folder_path / "mousse.png",#'Insulating', #'Insulating material',
|
40 |
+
'Bottle': folder_path / "bouteille.png",#'Bottle', #'Bottle-shaped',
|
41 |
+
'Can': folder_path / "briquet.png",#'Can', #'Can-shaped',
|
42 |
+
'Drum': folder_path / "contenant.png",#'Drum',
|
43 |
+
'Packaging': folder_path / "emballage.png",#'Packaging', #'Other packaging',
|
44 |
+
'Tire': folder_path / "pneu.png",#'Tire',
|
45 |
+
'Fishing net': folder_path / "hamecon.png",#'Fishing net', #'Fishing net / cord',
|
46 |
+
'Easily namable': folder_path / "chaussure.png",#'Easily namable',
|
47 |
+
'Unclear': folder_path / "dechet.png"#'Unclear'
|
48 |
+
}
|
49 |
+
out_dict = {}
|
50 |
+
for idx, path in id_path.items():
|
51 |
+
img = cv2.imread(path.resolve().as_posix(), cv2.IMREAD_UNCHANGED)
|
52 |
+
resized_img = cv2.resize(img, (100,60), interpolation = cv2.INTER_AREA)
|
53 |
+
out_dict[idx] = resized_img
|
54 |
+
return out_dict
|
tracking/track_video.py
CHANGED
@@ -109,7 +109,7 @@ def track_video(reader, detections, args, engine, transition_variance, observati
|
|
109 |
detections_for_frame, confs, labels = interpret_detection(detections_for_frame, args.downsampling_factor, is_yolo)
|
110 |
|
111 |
max_distance = euclidean(reader.output_shape, np.array([0,0]))
|
112 |
-
delta = 0.
|
113 |
|
114 |
if display is not None and display.on:
|
115 |
|
|
|
109 |
detections_for_frame, confs, labels = interpret_detection(detections_for_frame, args.downsampling_factor, is_yolo)
|
110 |
|
111 |
max_distance = euclidean(reader.output_shape, np.array([0,0]))
|
112 |
+
delta = 0.005*max_distance
|
113 |
|
114 |
if display is not None and display.on:
|
115 |
|
tracking/utils.py
CHANGED
@@ -83,7 +83,45 @@ def get_detections_for_video(reader, detector, batch_size=16, device=None):
|
|
83 |
return detections
|
84 |
|
85 |
|
86 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
fps = 24
|
88 |
logger.info("---Intepreting json")
|
89 |
results = defaultdict(list)
|
@@ -113,11 +151,17 @@ def generate_video_with_annotations(reader, output_detected, output_filename, sk
|
|
113 |
'-vcodec': 'libx264',
|
114 |
'-b': '5000000'})
|
115 |
|
116 |
-
font = cv2.
|
117 |
for frame_nb, frame in enumerate(reader):
|
118 |
detections_for_frame = results[frame_nb]
|
119 |
for detection in detections_for_frame:
|
120 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
|
122 |
if gps_data is not None:
|
123 |
latitude = gps_data[frame_nb//fps]['Latitude']
|
@@ -126,21 +170,10 @@ def generate_video_with_annotations(reader, output_detected, output_filename, sk
|
|
126 |
|
127 |
frame = downscale_local_mean(frame, (downscale,downscale,1)).astype(np.uint8)
|
128 |
writer.writeFrame(frame[:,:,::-1])
|
129 |
-
# moviepy version
|
130 |
-
# frames.append(frame[:,:,::-1])
|
131 |
-
|
132 |
-
#ret, frame, frame_nb = video.read()
|
133 |
-
#if frame_nb > maxframes:
|
134 |
-
# break
|
135 |
|
136 |
writer.close()
|
137 |
reader.video.release()
|
138 |
|
139 |
-
# version with moviepy
|
140 |
-
#clip = ImageSequenceClip(sequence=frames, fps=fps)
|
141 |
-
#clip.write_videofile(output_filename, fps=fps)
|
142 |
-
#del frames
|
143 |
-
|
144 |
logger.info("---finished writing video")
|
145 |
|
146 |
|
|
|
83 |
return detections
|
84 |
|
85 |
|
86 |
+
def overlay_transparent(background, overlay, x, y):
|
87 |
+
""" Overlays a transparent image over a background at topleft corner (x,y)
|
88 |
+
"""
|
89 |
+
background_width = background.shape[1]
|
90 |
+
background_height = background.shape[0]
|
91 |
+
|
92 |
+
if x >= background_width or y >= background_height:
|
93 |
+
return background
|
94 |
+
|
95 |
+
h, w = overlay.shape[0], overlay.shape[1]
|
96 |
+
|
97 |
+
if x + w > background_width:
|
98 |
+
w = background_width - x
|
99 |
+
overlay = overlay[:, :w]
|
100 |
+
|
101 |
+
if y + h > background_height:
|
102 |
+
h = background_height - y
|
103 |
+
overlay = overlay[:h]
|
104 |
+
|
105 |
+
if overlay.shape[2] < 4:
|
106 |
+
overlay = np.concatenate(
|
107 |
+
[
|
108 |
+
overlay,
|
109 |
+
np.ones((overlay.shape[0], overlay.shape[1], 1), dtype = overlay.dtype) * 255
|
110 |
+
],
|
111 |
+
axis = 2,
|
112 |
+
)
|
113 |
+
|
114 |
+
overlay_image = overlay[..., :3]
|
115 |
+
mask = overlay[..., 3:] / 255.0
|
116 |
+
|
117 |
+
background[y:y+h, x:x+w] = (1.0 - mask) * background[y:y+h, x:x+w] + mask * overlay_image
|
118 |
+
return background
|
119 |
+
|
120 |
+
|
121 |
+
def generate_video_with_annotations(reader, output_detected, output_filename, skip_frames,
|
122 |
+
maxframes, downscale, logger, gps_data=None, labels2icons=None):
|
123 |
+
""" Generates output video at 24 fps, with optional gps_data
|
124 |
+
"""
|
125 |
fps = 24
|
126 |
logger.info("---Intepreting json")
|
127 |
results = defaultdict(list)
|
|
|
151 |
'-vcodec': 'libx264',
|
152 |
'-b': '5000000'})
|
153 |
|
154 |
+
font = cv2.FONT_HERSHEY_TRIPLEX
|
155 |
for frame_nb, frame in enumerate(reader):
|
156 |
detections_for_frame = results[frame_nb]
|
157 |
for detection in detections_for_frame:
|
158 |
+
if labels2icons is None:
|
159 |
+
# write name of class
|
160 |
+
cv2.putText(frame, f'{detection[0]}/{detection[3]}', (int(detection[1]), int(detection[2])+5), font, 2, (0, 0, 255), 3, cv2.LINE_AA)
|
161 |
+
else:
|
162 |
+
# icons
|
163 |
+
overlay_transparent(frame, labels2icons[detection[3]], int(detection[1])+5, int(detection[2]))
|
164 |
+
cv2.putText(frame, f'{detection[0]}', (int(detection[1]+46+5), int(detection[2])+42), font, 1.2, (0, 0, 0), 2, cv2.LINE_AA)
|
165 |
|
166 |
if gps_data is not None:
|
167 |
latitude = gps_data[frame_nb//fps]['Latitude']
|
|
|
170 |
|
171 |
frame = downscale_local_mean(frame, (downscale,downscale,1)).astype(np.uint8)
|
172 |
writer.writeFrame(frame[:,:,::-1])
|
|
|
|
|
|
|
|
|
|
|
|
|
173 |
|
174 |
writer.close()
|
175 |
reader.video.release()
|
176 |
|
|
|
|
|
|
|
|
|
|
|
177 |
logger.info("---finished writing video")
|
178 |
|
179 |
|