Spaces:
Build error
Build error
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 |
|