Spaces:
Runtime error
Runtime error
Commit
Β·
fbb3995
1
Parent(s):
a2bc65a
Detection only script
Browse files- app.py +47 -16
- aris.py +2 -2
- bbox.py +0 -309
- annotation_editor.js β gradio_scripts/annotation_editor.js +90 -89
- annotation_handler.py β gradio_scripts/annotation_handler.py +0 -3
- aws_handler.py β gradio_scripts/aws_handler.py +0 -0
- file_reader.py β gradio_scripts/file_reader.py +0 -0
- {gradio_components β gradio_scripts}/result_ui.py +0 -0
- state_handler.py β gradio_scripts/state_handler.py +0 -0
- {gradio_components β gradio_scripts}/upload_ui.py +1 -1
- inference.py +126 -19
- requirements.txt +12 -61
- scripts/detect_frames.py +138 -0
- scripts/infer_frames.py +11 -5
- static/example/example_result.zip +3 -0
- visualizer.py +0 -3
app.py
CHANGED
@@ -1,21 +1,16 @@
|
|
1 |
import gradio as gr
|
2 |
from uploader import save_data_to_dir, create_data_dir, save_data
|
3 |
from main import predict_task
|
4 |
-
from state_handler import
|
5 |
-
from visualizer import is_fourcc_available
|
6 |
-
from file_reader import File
|
7 |
import numpy as np
|
8 |
-
from aws_handler import upload_file
|
9 |
from aris import create_metadata_table
|
10 |
-
import
|
11 |
-
import base64
|
12 |
-
from bbox import draggable_js
|
13 |
-
from annotation_handler import load_frames
|
14 |
import json
|
15 |
from zipfile import ZipFile
|
16 |
import os
|
17 |
-
from
|
18 |
-
from
|
19 |
|
20 |
|
21 |
#Initialize State & Result
|
@@ -46,6 +41,12 @@ def on_aris_input(file_list):
|
|
46 |
# Called when a result zip file is uploaded for result review
|
47 |
def on_result_upload(zip_list, aris_list):
|
48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
reset_state(result, state)
|
50 |
|
51 |
component_updates = {
|
@@ -92,7 +93,6 @@ def on_result_upload(zip_list, aris_list):
|
|
92 |
result["fish_table"].append(fish_table)
|
93 |
result["fish_info"].append(fish_info)
|
94 |
|
95 |
-
print(result['aris_input'])
|
96 |
update = update_result(i, state, result, inference_handler)
|
97 |
|
98 |
for key in update.keys():
|
@@ -190,10 +190,19 @@ def open_annotation(index):
|
|
190 |
if result["aris_input"][index]:
|
191 |
frame_info = load_frames(result["aris_input"][index], result['json_result'][index])
|
192 |
|
193 |
-
|
194 |
-
annotation_html += "<
|
195 |
-
annotation_html += "<
|
|
|
196 |
annotation_html += "</div>"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
197 |
annotation_html += "<p id='annotation_info' style='display:none'>" + json.dumps(frame_info) + "</p>"
|
198 |
annotation_html += "<img id='annotation_img' onload='draw()' style='display:none'></img>"
|
199 |
annotation_html += "<!--" + str(np.random.rand()) + "-->"
|
@@ -216,7 +225,29 @@ with demo:
|
|
216 |
display: none !important;
|
217 |
}
|
218 |
.selected.svelte-kqij2n {
|
219 |
-
background: linear-gradient(180deg, #66eecb47,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
220 |
}
|
221 |
</style>
|
222 |
<style id="tab_style"></style>
|
@@ -257,7 +288,7 @@ with demo:
|
|
257 |
annotation_progress.change(open_annotation, annotation_progress, [annotation_editor, annotation_progress], _js="() => window.annotation_index")
|
258 |
|
259 |
# Event listener for running javascript defined in 'annotation_editor.js'
|
260 |
-
with open('annotation_editor.js', 'r') as f:
|
261 |
annotation_editor.change(lambda x: gr.update(), None, annotation_editor, _js=f.read())
|
262 |
|
263 |
# Disclaimer at the bottom of page
|
|
|
1 |
import gradio as gr
|
2 |
from uploader import save_data_to_dir, create_data_dir, save_data
|
3 |
from main import predict_task
|
4 |
+
from gradio_scripts.state_handler import reset_state
|
|
|
|
|
5 |
import numpy as np
|
6 |
+
from gradio_scripts.aws_handler import upload_file
|
7 |
from aris import create_metadata_table
|
8 |
+
from gradio_scripts.annotation_handler import load_frames
|
|
|
|
|
|
|
9 |
import json
|
10 |
from zipfile import ZipFile
|
11 |
import os
|
12 |
+
from gradio_scripts.upload_ui import Upload_Gradio
|
13 |
+
from gradio_scripts.result_ui import Result_Gradio, update_result, table_headers, info_headers, js_update_tab_labels
|
14 |
|
15 |
|
16 |
#Initialize State & Result
|
|
|
41 |
# Called when a result zip file is uploaded for result review
|
42 |
def on_result_upload(zip_list, aris_list):
|
43 |
|
44 |
+
if (zip_list == None):
|
45 |
+
zip_list = [("static/example/example_result.zip", None)]
|
46 |
+
aris_path = "static/example/input_file.aris"
|
47 |
+
aris_list = [(aris_path, bytearray(open(aris_path, 'rb').read()))]
|
48 |
+
|
49 |
+
|
50 |
reset_state(result, state)
|
51 |
|
52 |
component_updates = {
|
|
|
93 |
result["fish_table"].append(fish_table)
|
94 |
result["fish_info"].append(fish_info)
|
95 |
|
|
|
96 |
update = update_result(i, state, result, inference_handler)
|
97 |
|
98 |
for key in update.keys():
|
|
|
190 |
if result["aris_input"][index]:
|
191 |
frame_info = load_frames(result["aris_input"][index], result['json_result'][index])
|
192 |
|
193 |
+
# Header
|
194 |
+
annotation_html += "<div id='annotation_header'>"
|
195 |
+
annotation_html += " <h1 id='annotation_frame_nbr'>Frame 0/100</h1>"
|
196 |
+
annotation_html += " <p id='annotation_edited'>(edited)</p>"
|
197 |
annotation_html += "</div>"
|
198 |
+
|
199 |
+
# Annotation Body
|
200 |
+
annotation_html += "<div style='display:flex'>"
|
201 |
+
annotation_html += " <canvas id='canvas' style='width:50%' onmousedown='mouse_down(event)' onmousemove='mouse_move(event)' onmouseup='mouse_up()' onmouseleave='mouse_up()'></canvas>"
|
202 |
+
annotation_html += " <div id='annotation_display' style='width:50%'></div>"
|
203 |
+
annotation_html += "</div>"
|
204 |
+
|
205 |
+
# Dummy objects
|
206 |
annotation_html += "<p id='annotation_info' style='display:none'>" + json.dumps(frame_info) + "</p>"
|
207 |
annotation_html += "<img id='annotation_img' onload='draw()' style='display:none'></img>"
|
208 |
annotation_html += "<!--" + str(np.random.rand()) + "-->"
|
|
|
225 |
display: none !important;
|
226 |
}
|
227 |
.selected.svelte-kqij2n {
|
228 |
+
background: linear-gradient(180deg, #66eecb47, transparent);
|
229 |
+
}
|
230 |
+
#annotation_frame_nbr {
|
231 |
+
left: calc(50% - 100px);
|
232 |
+
position: absolute;
|
233 |
+
width: 200px;
|
234 |
+
text-align: center;
|
235 |
+
font-size: x-large;
|
236 |
+
}
|
237 |
+
#annotation_header {
|
238 |
+
height: 40px;
|
239 |
+
}
|
240 |
+
#annotation_frame_nbr {
|
241 |
+
left: calc(50% - 100px);
|
242 |
+
position: absolute;
|
243 |
+
width: 200px;
|
244 |
+
text-align: center;
|
245 |
+
font-size: x-large;
|
246 |
+
}
|
247 |
+
#annotation_edited {
|
248 |
+
right: 0px;
|
249 |
+
position: absolute;
|
250 |
+
margin-top: 5px;
|
251 |
}
|
252 |
</style>
|
253 |
<style id="tab_style"></style>
|
|
|
288 |
annotation_progress.change(open_annotation, annotation_progress, [annotation_editor, annotation_progress], _js="() => window.annotation_index")
|
289 |
|
290 |
# Event listener for running javascript defined in 'annotation_editor.js'
|
291 |
+
with open('gradio_scripts/annotation_editor.js', 'r') as f:
|
292 |
annotation_editor.change(lambda x: gr.update(), None, annotation_editor, _js=f.read())
|
293 |
|
294 |
# Disclaimer at the bottom of page
|
aris.py
CHANGED
@@ -15,8 +15,8 @@ from copy import deepcopy
|
|
15 |
from multiprocessing import Pool
|
16 |
import math
|
17 |
|
18 |
-
import pyARIS
|
19 |
-
from tracker import Tracker
|
20 |
|
21 |
|
22 |
BEAM_WIDTH_DIR = 'lib/fish_eye/beam_widths/'
|
|
|
15 |
from multiprocessing import Pool
|
16 |
import math
|
17 |
|
18 |
+
import lib.fish_eye.pyARIS as pyARIS
|
19 |
+
from lib.fish_eye.tracker import Tracker
|
20 |
|
21 |
|
22 |
BEAM_WIDTH_DIR = 'lib/fish_eye/beam_widths/'
|
bbox.py
DELETED
@@ -1,309 +0,0 @@
|
|
1 |
-
draggable_js = """
|
2 |
-
() => {
|
3 |
-
window.canvas = document.getElementById('canvas');
|
4 |
-
window.ctx = canvas.getContext('2d');
|
5 |
-
window.rects = [];
|
6 |
-
window.mouseX;
|
7 |
-
window.mouseY;
|
8 |
-
window.closeEnough = 10;
|
9 |
-
window.keys = {};
|
10 |
-
window.hover = false;
|
11 |
-
|
12 |
-
window.TL = 0;
|
13 |
-
window.TR = 1;
|
14 |
-
window.BL = 2;
|
15 |
-
window.BR = 3;
|
16 |
-
|
17 |
-
|
18 |
-
window.init = () => {
|
19 |
-
window.frames = JSON.parse(document.getElementById("annotation_info").innerHTML);
|
20 |
-
window.frame_index = 0;
|
21 |
-
|
22 |
-
document.addEventListener('keydown', keydown);
|
23 |
-
document.addEventListener('keyup', keyup);
|
24 |
-
|
25 |
-
show_frame();
|
26 |
-
}
|
27 |
-
window.prev_frame = () => {
|
28 |
-
window.frame_index = Math.max(window.frame_index - 1, 0);
|
29 |
-
show_frame();
|
30 |
-
}
|
31 |
-
window.next_frame = () => {
|
32 |
-
window.frame_index = Math.min(window.frame_index + 1, window.frames.length - 1);
|
33 |
-
show_frame();
|
34 |
-
}
|
35 |
-
window.show_frame = () => {
|
36 |
-
const frame_info = window.frames[window.frame_index];
|
37 |
-
const annotations = frame_info['annotations'];
|
38 |
-
const frame = frame_info['frame'];
|
39 |
-
|
40 |
-
|
41 |
-
window.annotations = [];
|
42 |
-
for (const annotation of annotations) {
|
43 |
-
const bbox = annotation['bbox']
|
44 |
-
window.annotations.push({
|
45 |
-
rect: {
|
46 |
-
startX: bbox.left,
|
47 |
-
startY: bbox.top,
|
48 |
-
w: bbox.right - bbox.left,
|
49 |
-
h: bbox.bottom - bbox.top
|
50 |
-
},
|
51 |
-
id: annotation.id,
|
52 |
-
conf: annotation.conf
|
53 |
-
})
|
54 |
-
}
|
55 |
-
console.log(window.annotations)
|
56 |
-
window.dragging = false;
|
57 |
-
|
58 |
-
document.getElementById("annotation_img").src = "data:image/png;base64," + frame;
|
59 |
-
}
|
60 |
-
|
61 |
-
window.draw = () => {
|
62 |
-
ctx = window.ctx;
|
63 |
-
canvas = window.canvas;
|
64 |
-
canvas.width = document.getElementById("annotation_img").width;
|
65 |
-
canvas.height = document.getElementById("annotation_img").height;
|
66 |
-
canvas.style = ""
|
67 |
-
annotations = window.annotations;
|
68 |
-
|
69 |
-
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
70 |
-
|
71 |
-
ctx.drawImage(document.getElementById("annotation_img"), 0, 0);
|
72 |
-
|
73 |
-
for (const annotation of annotations) {
|
74 |
-
//ctx.globalAlpha = annotation.conf
|
75 |
-
|
76 |
-
const rect = annotation.rect;
|
77 |
-
ctx.strokeStyle = color_from_id(annotation.id);
|
78 |
-
ctx.strokeRect(rect.startX, rect.startY, rect.w, rect.h);
|
79 |
-
|
80 |
-
ctx.font = "15px Arial";
|
81 |
-
ctx.fillStyle = color_from_id(annotation.id);
|
82 |
-
ctx.textAlign = "right";
|
83 |
-
ctx.fillText(annotation.id, rect.startX + rect.w, rect.startY - 3);
|
84 |
-
}
|
85 |
-
|
86 |
-
if (hover && !dragging) {
|
87 |
-
annotation = hover.annotation;
|
88 |
-
rect = annotation.rect;
|
89 |
-
handles = [
|
90 |
-
[rect.startX, rect.startY],
|
91 |
-
[rect.startX + rect.w, rect.startY],
|
92 |
-
[rect.startX, rect.startY + rect.h],
|
93 |
-
[rect.startX + rect.w, rect.startY + rect.h]
|
94 |
-
];
|
95 |
-
|
96 |
-
handle = handles[hover.corner];
|
97 |
-
ctx.fillStyle = color_from_id(annotation.id);
|
98 |
-
ctx.beginPath();
|
99 |
-
s = 6;
|
100 |
-
ctx.rect(handle[0]-s/2, handle[1]-s/2, s, s);
|
101 |
-
ctx.fill();
|
102 |
-
}
|
103 |
-
|
104 |
-
prettify_annotation();
|
105 |
-
}
|
106 |
-
color_from_id = (id) => {
|
107 |
-
return 'hsl(' + Math.floor((id*id)*57 % 360) + ', 100%, 50%)'
|
108 |
-
}
|
109 |
-
|
110 |
-
window.prettify_annotation = () => {
|
111 |
-
label_style = "style='width: calc(16% - 6px); display: inline-block; text-align:center; font-weight: bold;'";
|
112 |
-
input_style_base = "style='width: calc(16% - 6px); display: inline-block; padding: 5px;'";
|
113 |
-
|
114 |
-
input_style_selected = "style='width: calc(16% - 6px); display: inline-block; padding: 5px; border-width: 3px; border-color: orange; border-radius: 5px;'";
|
115 |
-
|
116 |
-
html = ""
|
117 |
-
for (const annotation of window.annotations) {
|
118 |
-
input_style = (window.hover && annotation === window.hover.annotation) ? input_style_selected : input_style_base;
|
119 |
-
html += `
|
120 |
-
<div style='margin: 0 0 20px 10px'>
|
121 |
-
<div>
|
122 |
-
<label ${label_style}>${"id"}</label>
|
123 |
-
<label ${label_style}>${"x"}</label>
|
124 |
-
<label ${label_style}>${"y"}</label>
|
125 |
-
<label ${label_style}>${"w"}</label>
|
126 |
-
<label ${label_style}>${"h"}</label>
|
127 |
-
<label ${label_style}>${"conf"}</label>
|
128 |
-
</div>
|
129 |
-
<div style='height:40px'>
|
130 |
-
<input ${input_style} type='text' value='${annotation.id}'>
|
131 |
-
<input ${input_style} type='text' value='${annotation.rect.startX}'>
|
132 |
-
<input ${input_style} type='text' value='${annotation.rect.startY}'>
|
133 |
-
<input ${input_style} type='text' value='${annotation.rect.w}'>
|
134 |
-
<input ${input_style} type='text' value='${annotation.rect.h}'>
|
135 |
-
<input ${input_style} type='text' value='${annotation.conf}'>
|
136 |
-
</div>
|
137 |
-
</div>`;
|
138 |
-
}
|
139 |
-
document.getElementById("annotation_display").innerHTML = html;
|
140 |
-
}
|
141 |
-
|
142 |
-
window.keyup = (e) => {
|
143 |
-
delete keys[e.key.toLowerCase()];
|
144 |
-
}
|
145 |
-
window.keydown = (e) => {
|
146 |
-
console.log(e.key.toLowerCase())
|
147 |
-
keys[e.key.toLowerCase()] = true;
|
148 |
-
|
149 |
-
// if pressing x, delete hovered annotation
|
150 |
-
if (keys['x'] && window.hover) delete_annotation(window.hover.annotation);
|
151 |
-
if (keys['arrowright'] || keys['d']) next_frame();
|
152 |
-
if (keys['arrowleft'] || keys['a']) prev_frame();
|
153 |
-
}
|
154 |
-
|
155 |
-
window.mouse_down = (e) => {
|
156 |
-
|
157 |
-
update_mouse(e);
|
158 |
-
|
159 |
-
// If holding 'n', create new annotation
|
160 |
-
if (keys['n']) return create_annotation();
|
161 |
-
|
162 |
-
// else, start dragging hovered object
|
163 |
-
window.dragging = window.hover;
|
164 |
-
|
165 |
-
console.log(dragging)
|
166 |
-
|
167 |
-
window.draw()
|
168 |
-
}
|
169 |
-
|
170 |
-
window.mouse_up = () => {
|
171 |
-
console.log("mouseUp")
|
172 |
-
window.dragging = false;
|
173 |
-
}
|
174 |
-
|
175 |
-
window.mouse_move = (e) => {
|
176 |
-
ctx = window.ctx;
|
177 |
-
canvas = window.canvas;
|
178 |
-
dragging = window.dragging;
|
179 |
-
|
180 |
-
update_mouse(e);
|
181 |
-
|
182 |
-
if (!dragging) return window.draw();
|
183 |
-
|
184 |
-
annotation = dragging.annotation;
|
185 |
-
rect = annotation.rect
|
186 |
-
corner = dragging.corner;
|
187 |
-
|
188 |
-
if (corner == window.TL) {
|
189 |
-
rect.w += rect.startX - mouseX;
|
190 |
-
rect.h += rect.startY - mouseY;
|
191 |
-
rect.startX = mouseX;
|
192 |
-
rect.startY = mouseY;
|
193 |
-
} else if (corner == window.TR) {
|
194 |
-
rect.w = mouseX - rect.startX;
|
195 |
-
rect.h += rect.startY - mouseY;
|
196 |
-
rect.startY = mouseY;
|
197 |
-
} else if (corner == window.BL) {
|
198 |
-
rect.w += rect.startX - mouseX;
|
199 |
-
rect.h = mouseY - rect.startY;
|
200 |
-
rect.startX = mouseX;
|
201 |
-
} else if (corner == window.BR) {
|
202 |
-
rect.w = mouseX - rect.startX;
|
203 |
-
rect.h = mouseY - rect.startY
|
204 |
-
}
|
205 |
-
|
206 |
-
rect.startX = Math.round(rect.startX);
|
207 |
-
rect.startY = Math.round(rect.startY);
|
208 |
-
rect.w = Math.round(rect.w);
|
209 |
-
rect.h = Math.round(rect.h);
|
210 |
-
|
211 |
-
// If w < 0 we have swapped sides, switch to horizontally opposite corner
|
212 |
-
if (rect.w < 0) {
|
213 |
-
rect.w = -rect.w;
|
214 |
-
if (corner == window.TL) corner = window.TR;
|
215 |
-
else if (corner == window.TR) corner = window.TL;
|
216 |
-
else if (corner == window.BL) corner = window.BR;
|
217 |
-
else if (corner == window.BR) corner = window.BL;
|
218 |
-
}
|
219 |
-
//If h < 0 we have swapped sides, switch to vertically opposite corner
|
220 |
-
if (rect.h < 0) {
|
221 |
-
rect.h = -rect.h;
|
222 |
-
if (corner == window.TL) corner = window.BL;
|
223 |
-
else if (corner == window.BL) corner = window.TL;
|
224 |
-
else if (corner == window.TR) corner = window.BR;
|
225 |
-
else if (corner == window.BR) corner = window.TR;
|
226 |
-
}
|
227 |
-
if (dragging.corner !== corner) console.log(dragging.corner + " -> " + corner);
|
228 |
-
dragging.corner = corner;
|
229 |
-
|
230 |
-
window.draw()
|
231 |
-
}
|
232 |
-
|
233 |
-
update_mouse = (e) => {
|
234 |
-
bodyRect = document.body.getBoundingClientRect();
|
235 |
-
canvasRect = e.target.getBoundingClientRect();
|
236 |
-
offset_x = canvasRect.left - bodyRect.left;
|
237 |
-
offset_y = canvasRect.top - bodyRect.top;
|
238 |
-
mouseX = e.pageX - offset_x;
|
239 |
-
mouseY = e.pageY - offset_y;
|
240 |
-
|
241 |
-
function sqDistance(x, y) {
|
242 |
-
dx = mouseX - x;
|
243 |
-
dy = mouseY - y;
|
244 |
-
return dx*dx + dy*dy;
|
245 |
-
}
|
246 |
-
|
247 |
-
window.hover = false;
|
248 |
-
threshold = 100;
|
249 |
-
for (const annotation of annotations) {
|
250 |
-
rect = annotation.rect;
|
251 |
-
square_dists = [
|
252 |
-
sqDistance(rect.startX, rect.startY),
|
253 |
-
sqDistance(rect.startX + rect.w, rect.startY),
|
254 |
-
sqDistance(rect.startX, rect.startY + rect.h),
|
255 |
-
sqDistance(rect.startX + rect.w, rect.startY + rect.h),
|
256 |
-
]
|
257 |
-
|
258 |
-
min_dist = Math.min(...square_dists);
|
259 |
-
if (min_dist > threshold) continue;
|
260 |
-
|
261 |
-
threshold = min_dist;
|
262 |
-
corner = square_dists.indexOf(min_dist);
|
263 |
-
window.hover = { corner, annotation }
|
264 |
-
}
|
265 |
-
}
|
266 |
-
|
267 |
-
create_annotation = () => {
|
268 |
-
|
269 |
-
new_annotation = {
|
270 |
-
rect: {
|
271 |
-
startX: mouseX,
|
272 |
-
startY: mouseY,
|
273 |
-
w: 0,
|
274 |
-
h: 0
|
275 |
-
},
|
276 |
-
color: "rgb(255, 0, 0)",
|
277 |
-
id: 1,
|
278 |
-
conf: 1
|
279 |
-
};
|
280 |
-
annotations.push(new_annotation);
|
281 |
-
window.dragging = {
|
282 |
-
annotation: new_annotation,
|
283 |
-
corner: window.BL
|
284 |
-
}
|
285 |
-
|
286 |
-
window.draw()
|
287 |
-
}
|
288 |
-
|
289 |
-
delete_annotation = (annotation) => {
|
290 |
-
window.annotations = window.annotations.filter(function (a) {
|
291 |
-
return a !== annotation;
|
292 |
-
});
|
293 |
-
window.dragging = false;
|
294 |
-
window.hover = false;
|
295 |
-
window.draw();
|
296 |
-
}
|
297 |
-
|
298 |
-
window.init();
|
299 |
-
|
300 |
-
}
|
301 |
-
"""
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
annotation_editor.js β gradio_scripts/annotation_editor.js
RENAMED
@@ -4,8 +4,6 @@
|
|
4 |
window.rects = [];
|
5 |
window.mouseX;
|
6 |
window.mouseY;
|
7 |
-
window.dragX;
|
8 |
-
window.dragY;
|
9 |
window.closeEnough = 10;
|
10 |
window.keys = {};
|
11 |
window.hover = false;
|
@@ -14,7 +12,7 @@
|
|
14 |
window.TR = 1;
|
15 |
window.BL = 2;
|
16 |
window.BR = 3;
|
17 |
-
|
18 |
window.frame_index = 0;
|
19 |
|
20 |
|
@@ -28,6 +26,10 @@
|
|
28 |
show_frame();
|
29 |
}
|
30 |
|
|
|
|
|
|
|
|
|
31 |
window.prev_frame = () => {
|
32 |
window.frame_index = Math.max(window.frame_index - 1, 0);
|
33 |
show_frame();
|
@@ -36,33 +38,35 @@
|
|
36 |
window.frame_index = Math.min(window.frame_index + 1, window.frames.length - 1);
|
37 |
show_frame();
|
38 |
}
|
39 |
-
window.show_frame = () => {
|
40 |
-
const frame_info = window.frames[window.frame_index];
|
41 |
-
const annotations = frame_info['annotations'];
|
42 |
-
const frame = frame_info['frame'];
|
43 |
|
|
|
|
|
44 |
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
window.annotations.push({
|
49 |
-
rect: {
|
50 |
-
startX: bbox.left,
|
51 |
-
startY: bbox.top,
|
52 |
-
w: bbox.right - bbox.left,
|
53 |
-
h: bbox.bottom - bbox.top
|
54 |
-
},
|
55 |
-
id: annotation.id,
|
56 |
-
conf: annotation.conf
|
57 |
-
})
|
58 |
-
}
|
59 |
console.log(window.annotations)
|
60 |
window.dragging = false;
|
61 |
|
62 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
}
|
64 |
|
|
|
65 |
window.draw = () => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
ctx = window.ctx;
|
67 |
canvas = window.canvas;
|
68 |
canvas.width = document.getElementById("annotation_img").width;
|
@@ -77,24 +81,24 @@
|
|
77 |
for (const annotation of annotations) {
|
78 |
//ctx.globalAlpha = annotation.conf
|
79 |
|
80 |
-
const rect = annotation.
|
81 |
ctx.strokeStyle = color_from_id(annotation.id);
|
82 |
-
ctx.strokeRect(rect.
|
83 |
|
84 |
ctx.font = "15px Arial";
|
85 |
ctx.fillStyle = color_from_id(annotation.id);
|
86 |
ctx.textAlign = "right";
|
87 |
-
ctx.fillText(annotation.id, rect.
|
88 |
}
|
89 |
|
90 |
if (hover && !dragging) {
|
91 |
annotation = hover.annotation;
|
92 |
-
rect = annotation.
|
93 |
handles = [
|
94 |
-
[rect.
|
95 |
-
[rect.
|
96 |
-
[rect.
|
97 |
-
[rect.
|
98 |
];
|
99 |
|
100 |
handle = handles[hover.corner];
|
@@ -104,17 +108,8 @@
|
|
104 |
ctx.rect(handle[0]-s/2, handle[1]-s/2, s, s);
|
105 |
ctx.fill();
|
106 |
}
|
107 |
-
|
108 |
-
prettify_annotation();
|
109 |
}
|
110 |
-
|
111 |
-
//hue = Math.floor((number * 137.508 + 60) % 360)
|
112 |
-
power = Math.pow(2, Math.ceil(Math.log2(id)));
|
113 |
-
hue = (2*id - power - 1) / power;
|
114 |
-
return 'hsl(' + Math.floor(hue*359) + ', 100%, 50%)'
|
115 |
-
}
|
116 |
-
|
117 |
-
window.prettify_annotation = () => {
|
118 |
label_style = "style='width: calc(16% - 6px); display: inline-block; text-align:center; font-weight: bold;'";
|
119 |
input_style_base = "style='width: calc(16% - 6px); display: inline-block; padding: 5px;'";
|
120 |
|
@@ -127,25 +122,34 @@
|
|
127 |
<div style='margin: 0 0 20px 10px'>
|
128 |
<div>
|
129 |
<label ${label_style}>${"id"}</label>
|
130 |
-
<label ${label_style}>${"
|
131 |
-
<label ${label_style}>${"
|
132 |
-
<label ${label_style}>${"
|
133 |
-
<label ${label_style}>${"
|
134 |
<label ${label_style}>${"conf"}</label>
|
135 |
</div>
|
136 |
<div style='height:40px'>
|
137 |
<input ${input_style} type='text' value='${annotation.id}'>
|
138 |
-
<input ${input_style} type='text' value='${Math.round(annotation.
|
139 |
-
<input ${input_style} type='text' value='${Math.round(annotation.
|
140 |
-
<input ${input_style} type='text' value='${Math.round(annotation.
|
141 |
-
<input ${input_style} type='text' value='${Math.round(annotation.
|
142 |
<input ${input_style} type='text' value='${annotation.conf}'>
|
143 |
</div>
|
144 |
</div>`;
|
145 |
}
|
146 |
document.getElementById("annotation_display").innerHTML = html;
|
147 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
148 |
|
|
|
149 |
window.keyup = (e) => {
|
150 |
delete keys[e.key.toLowerCase()];
|
151 |
}
|
@@ -159,13 +163,12 @@
|
|
159 |
if (keys['arrowleft'] || keys['a']) prev_frame();
|
160 |
}
|
161 |
|
|
|
|
|
162 |
window.mouse_down = (e) => {
|
163 |
|
164 |
update_mouse(e);
|
165 |
|
166 |
-
dragX = mouseX;
|
167 |
-
dragY = mouseY;
|
168 |
-
|
169 |
// If holding 'n', create new annotation
|
170 |
if (keys['n']) return create_annotation();
|
171 |
|
@@ -198,54 +201,43 @@
|
|
198 |
if (!dragging) return window.draw();
|
199 |
|
200 |
annotation = dragging.annotation;
|
201 |
-
rect = annotation.
|
202 |
corner = dragging.corner;
|
203 |
|
204 |
-
|
205 |
-
console.log("d", rect.startX - mouseX, rect.startY - mouseY)
|
206 |
if (corner == window.TL) {
|
207 |
-
rect.
|
208 |
-
rect.h += rect.startY - mouseY;
|
209 |
-
rect.startX = mouseX;
|
210 |
-
rect.startY = mouseY;
|
211 |
} else if (corner == window.TR) {
|
212 |
-
rect.
|
213 |
-
rect.h += rect.startY - mouseY;
|
214 |
-
rect.startY = mouseY;
|
215 |
} else if (corner == window.BL) {
|
216 |
-
rect.
|
217 |
-
rect.h = mouseY - rect.startY;
|
218 |
-
rect.startX = mouseX;
|
219 |
} else if (corner == window.BR) {
|
220 |
-
rect.
|
221 |
-
rect.h = mouseY - rect.startY
|
222 |
}
|
223 |
|
224 |
-
//
|
225 |
-
|
226 |
-
|
227 |
-
//rect.h = Math.floor(rect.h);
|
228 |
-
|
229 |
-
// If w < 0 we have swapped sides, switch to horizontally opposite corner
|
230 |
-
if (rect.w < 0) {
|
231 |
-
rect.w = -rect.w;
|
232 |
if (corner == window.TL) corner = window.TR;
|
233 |
else if (corner == window.TR) corner = window.TL;
|
234 |
else if (corner == window.BL) corner = window.BR;
|
235 |
else if (corner == window.BR) corner = window.BL;
|
236 |
}
|
237 |
-
//If
|
238 |
-
if (rect.
|
239 |
-
rect.
|
240 |
if (corner == window.TL) corner = window.BL;
|
241 |
else if (corner == window.BL) corner = window.TL;
|
242 |
else if (corner == window.TR) corner = window.BR;
|
243 |
else if (corner == window.BR) corner = window.TR;
|
244 |
}
|
|
|
|
|
|
|
245 |
if (dragging.corner !== corner) console.log(dragging.corner + " -> " + corner);
|
246 |
dragging.corner = corner;
|
247 |
|
248 |
-
window.draw()
|
249 |
}
|
250 |
|
251 |
update_mouse = (e) => {
|
@@ -265,12 +257,12 @@
|
|
265 |
window.hover = false;
|
266 |
threshold = 100;
|
267 |
for (const annotation of annotations) {
|
268 |
-
rect = annotation.
|
269 |
square_dists = [
|
270 |
-
sqDistance(rect.
|
271 |
-
sqDistance(rect.
|
272 |
-
sqDistance(rect.
|
273 |
-
sqDistance(rect.
|
274 |
]
|
275 |
|
276 |
min_dist = Math.min(...square_dists);
|
@@ -282,14 +274,19 @@
|
|
282 |
}
|
283 |
}
|
284 |
|
|
|
|
|
|
|
|
|
|
|
285 |
create_annotation = () => {
|
286 |
|
287 |
new_annotation = {
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
},
|
294 |
color: "rgb(255, 0, 0)",
|
295 |
id: 1,
|
@@ -301,15 +298,19 @@
|
|
301 |
corner: window.BL
|
302 |
}
|
303 |
|
|
|
|
|
304 |
window.draw()
|
305 |
}
|
306 |
-
|
307 |
delete_annotation = (annotation) => {
|
308 |
window.annotations = window.annotations.filter(function (a) {
|
309 |
return a !== annotation;
|
310 |
});
|
311 |
window.dragging = false;
|
312 |
window.hover = false;
|
|
|
|
|
|
|
313 |
window.draw();
|
314 |
}
|
315 |
|
|
|
4 |
window.rects = [];
|
5 |
window.mouseX;
|
6 |
window.mouseY;
|
|
|
|
|
7 |
window.closeEnough = 10;
|
8 |
window.keys = {};
|
9 |
window.hover = false;
|
|
|
12 |
window.TR = 1;
|
13 |
window.BL = 2;
|
14 |
window.BR = 3;
|
15 |
+
|
16 |
window.frame_index = 0;
|
17 |
|
18 |
|
|
|
26 |
show_frame();
|
27 |
}
|
28 |
|
29 |
+
window.reset_annotation = () => {
|
30 |
+
window.frames = JSON.parse(document.getElementById("annotation_info").innerHTML);
|
31 |
+
show_frame();
|
32 |
+
}
|
33 |
window.prev_frame = () => {
|
34 |
window.frame_index = Math.max(window.frame_index - 1, 0);
|
35 |
show_frame();
|
|
|
38 |
window.frame_index = Math.min(window.frame_index + 1, window.frames.length - 1);
|
39 |
show_frame();
|
40 |
}
|
|
|
|
|
|
|
|
|
41 |
|
42 |
+
window.show_frame = () => {
|
43 |
+
window.frame = window.frames[window.frame_index];
|
44 |
|
45 |
+
// Load annotation from frame
|
46 |
+
const annotations = frame['annotations'];
|
47 |
+
window.annotations = annotations;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
console.log(window.annotations)
|
49 |
window.dragging = false;
|
50 |
|
51 |
+
// Load frame image
|
52 |
+
const frame_img = frame['frame'];
|
53 |
+
document.getElementById("annotation_img").src = "data:image/png;base64," + frame_img;
|
54 |
+
// Draw function is called by this element using the onloaded callback
|
55 |
+
|
56 |
+
document.getElementById("annotation_frame_nbr").innerHTML = "Frame " + window.frame_index + "/" + window.frames.length;
|
57 |
+
|
58 |
}
|
59 |
|
60 |
+
// DRAW FUNCTIONS
|
61 |
window.draw = () => {
|
62 |
+
draw_canvas();
|
63 |
+
draw_input_fields();
|
64 |
+
|
65 |
+
// Mark if frame is edited
|
66 |
+
document.getElementById("annotation_edited").style.display = (frame.edited) ? "block" : "none";
|
67 |
+
}
|
68 |
+
window.draw_canvas = () => {
|
69 |
+
|
70 |
ctx = window.ctx;
|
71 |
canvas = window.canvas;
|
72 |
canvas.width = document.getElementById("annotation_img").width;
|
|
|
81 |
for (const annotation of annotations) {
|
82 |
//ctx.globalAlpha = annotation.conf
|
83 |
|
84 |
+
const rect = annotation.bbox;
|
85 |
ctx.strokeStyle = color_from_id(annotation.id);
|
86 |
+
ctx.strokeRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
|
87 |
|
88 |
ctx.font = "15px Arial";
|
89 |
ctx.fillStyle = color_from_id(annotation.id);
|
90 |
ctx.textAlign = "right";
|
91 |
+
ctx.fillText(annotation.id, rect.right, rect.top - 3);
|
92 |
}
|
93 |
|
94 |
if (hover && !dragging) {
|
95 |
annotation = hover.annotation;
|
96 |
+
rect = annotation.bbox;
|
97 |
handles = [
|
98 |
+
[rect.left, rect.top],
|
99 |
+
[rect.right, rect.top],
|
100 |
+
[rect.left, rect.bottom],
|
101 |
+
[rect.right, rect.bottom]
|
102 |
];
|
103 |
|
104 |
handle = handles[hover.corner];
|
|
|
108 |
ctx.rect(handle[0]-s/2, handle[1]-s/2, s, s);
|
109 |
ctx.fill();
|
110 |
}
|
|
|
|
|
111 |
}
|
112 |
+
window.draw_input_fields = () => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
113 |
label_style = "style='width: calc(16% - 6px); display: inline-block; text-align:center; font-weight: bold;'";
|
114 |
input_style_base = "style='width: calc(16% - 6px); display: inline-block; padding: 5px;'";
|
115 |
|
|
|
122 |
<div style='margin: 0 0 20px 10px'>
|
123 |
<div>
|
124 |
<label ${label_style}>${"id"}</label>
|
125 |
+
<label ${label_style}>${"left"}</label>
|
126 |
+
<label ${label_style}>${"top"}</label>
|
127 |
+
<label ${label_style}>${"right"}</label>
|
128 |
+
<label ${label_style}>${"bottom"}</label>
|
129 |
<label ${label_style}>${"conf"}</label>
|
130 |
</div>
|
131 |
<div style='height:40px'>
|
132 |
<input ${input_style} type='text' value='${annotation.id}'>
|
133 |
+
<input ${input_style} type='text' value='${Math.round(annotation.bbox.left)}'>
|
134 |
+
<input ${input_style} type='text' value='${Math.round(annotation.bbox.top)}'>
|
135 |
+
<input ${input_style} type='text' value='${Math.round(annotation.bbox.right)}'>
|
136 |
+
<input ${input_style} type='text' value='${Math.round(annotation.bbox.bottom)}'>
|
137 |
<input ${input_style} type='text' value='${annotation.conf}'>
|
138 |
</div>
|
139 |
</div>`;
|
140 |
}
|
141 |
document.getElementById("annotation_display").innerHTML = html;
|
142 |
}
|
143 |
+
|
144 |
+
color_from_id = (id) => {
|
145 |
+
//hue = Math.floor((number * 137.508 + 60) % 360)
|
146 |
+
power = Math.pow(2, Math.ceil(Math.log2(id)));
|
147 |
+
hue = (2*id - power - 1) / power;
|
148 |
+
return 'hsl(' + Math.floor(hue*359) + ', 100%, 50%)'
|
149 |
+
}
|
150 |
+
|
151 |
|
152 |
+
// KEY EVENTS
|
153 |
window.keyup = (e) => {
|
154 |
delete keys[e.key.toLowerCase()];
|
155 |
}
|
|
|
163 |
if (keys['arrowleft'] || keys['a']) prev_frame();
|
164 |
}
|
165 |
|
166 |
+
|
167 |
+
// MOUSE EVENTS
|
168 |
window.mouse_down = (e) => {
|
169 |
|
170 |
update_mouse(e);
|
171 |
|
|
|
|
|
|
|
172 |
// If holding 'n', create new annotation
|
173 |
if (keys['n']) return create_annotation();
|
174 |
|
|
|
201 |
if (!dragging) return window.draw();
|
202 |
|
203 |
annotation = dragging.annotation;
|
204 |
+
rect = annotation.bbox;
|
205 |
corner = dragging.corner;
|
206 |
|
207 |
+
mouse = [mouseX, mouseY];
|
|
|
208 |
if (corner == window.TL) {
|
209 |
+
[rect.left, rect.top] = mouse;
|
|
|
|
|
|
|
210 |
} else if (corner == window.TR) {
|
211 |
+
[rect.right, rect.top] = mouse;
|
|
|
|
|
212 |
} else if (corner == window.BL) {
|
213 |
+
[rect.left, rect.bottom] = mouse;
|
|
|
|
|
214 |
} else if (corner == window.BR) {
|
215 |
+
[rect.right, rect.bottom] = mouse;
|
|
|
216 |
}
|
217 |
|
218 |
+
// If left > right we have swapped sides, switch to horizontally opposite corner
|
219 |
+
if (rect.left > rect.right) {
|
220 |
+
[rect.left, rect.right] = [rect.right, rect.left];
|
|
|
|
|
|
|
|
|
|
|
221 |
if (corner == window.TL) corner = window.TR;
|
222 |
else if (corner == window.TR) corner = window.TL;
|
223 |
else if (corner == window.BL) corner = window.BR;
|
224 |
else if (corner == window.BR) corner = window.BL;
|
225 |
}
|
226 |
+
//If top > bottom we have swapped sides, switch to vertically opposite corner
|
227 |
+
if (rect.top > rect.bottom) {
|
228 |
+
[rect.top, rect.bottom] = [rect.bottom, rect.top];
|
229 |
if (corner == window.TL) corner = window.BL;
|
230 |
else if (corner == window.BL) corner = window.TL;
|
231 |
else if (corner == window.TR) corner = window.BR;
|
232 |
else if (corner == window.BR) corner = window.TR;
|
233 |
}
|
234 |
+
|
235 |
+
mark_frame_as_edited();
|
236 |
+
|
237 |
if (dragging.corner !== corner) console.log(dragging.corner + " -> " + corner);
|
238 |
dragging.corner = corner;
|
239 |
|
240 |
+
window.draw();
|
241 |
}
|
242 |
|
243 |
update_mouse = (e) => {
|
|
|
257 |
window.hover = false;
|
258 |
threshold = 100;
|
259 |
for (const annotation of annotations) {
|
260 |
+
rect = annotation.bbox;
|
261 |
square_dists = [
|
262 |
+
sqDistance(rect.left, rect.top),
|
263 |
+
sqDistance(rect.right, rect.top),
|
264 |
+
sqDistance(rect.left, rect.bottom),
|
265 |
+
sqDistance(rect.right, rect.bottom)
|
266 |
]
|
267 |
|
268 |
min_dist = Math.min(...square_dists);
|
|
|
274 |
}
|
275 |
}
|
276 |
|
277 |
+
|
278 |
+
// ANNOTATION UPDATES
|
279 |
+
mark_frame_as_edited = () => {
|
280 |
+
frame.edited = true;
|
281 |
+
}
|
282 |
create_annotation = () => {
|
283 |
|
284 |
new_annotation = {
|
285 |
+
bbox: {
|
286 |
+
left: mouseX,
|
287 |
+
top: mouseY,
|
288 |
+
right: mouseX,
|
289 |
+
bottom: mouseY
|
290 |
},
|
291 |
color: "rgb(255, 0, 0)",
|
292 |
id: 1,
|
|
|
298 |
corner: window.BL
|
299 |
}
|
300 |
|
301 |
+
mark_frame_as_edited();
|
302 |
+
|
303 |
window.draw()
|
304 |
}
|
|
|
305 |
delete_annotation = (annotation) => {
|
306 |
window.annotations = window.annotations.filter(function (a) {
|
307 |
return a !== annotation;
|
308 |
});
|
309 |
window.dragging = false;
|
310 |
window.hover = false;
|
311 |
+
|
312 |
+
mark_frame_as_edited();
|
313 |
+
|
314 |
window.draw();
|
315 |
}
|
316 |
|
annotation_handler.py β gradio_scripts/annotation_handler.py
RENAMED
@@ -26,9 +26,6 @@ def load_frames(video, preds):
|
|
26 |
frames = dataset.didson.load_frames(start_frame=0)
|
27 |
else:
|
28 |
frames = video
|
29 |
-
|
30 |
-
|
31 |
-
color_map = { fish['id'] : fish['color'] for fish in preds['fish'] }
|
32 |
|
33 |
frame_info = []
|
34 |
if len(frames):
|
|
|
26 |
frames = dataset.didson.load_frames(start_frame=0)
|
27 |
else:
|
28 |
frames = video
|
|
|
|
|
|
|
29 |
|
30 |
frame_info = []
|
31 |
if len(frames):
|
aws_handler.py β gradio_scripts/aws_handler.py
RENAMED
File without changes
|
file_reader.py β gradio_scripts/file_reader.py
RENAMED
File without changes
|
{gradio_components β gradio_scripts}/result_ui.py
RENAMED
File without changes
|
state_handler.py β gradio_scripts/state_handler.py
RENAMED
File without changes
|
{gradio_components β gradio_scripts}/upload_ui.py
RENAMED
@@ -1,5 +1,5 @@
|
|
1 |
import gradio as gr
|
2 |
-
from file_reader import File
|
3 |
|
4 |
|
5 |
def Upload_Gradio(gradio_components):
|
|
|
1 |
import gradio as gr
|
2 |
+
from gradio_scripts.file_reader import File
|
3 |
|
4 |
|
5 |
def Upload_Gradio(gradio_components):
|
inference.py
CHANGED
@@ -7,6 +7,7 @@ import numpy as np
|
|
7 |
import json
|
8 |
import time
|
9 |
from unittest.mock import patch
|
|
|
10 |
|
11 |
# assumes yolov5 on sys.path
|
12 |
from lib.yolov5.models.experimental import attempt_load
|
@@ -24,12 +25,12 @@ WEIGHTS = 'models/v5m_896_300best.pt'
|
|
24 |
# will need to configure these based on GPU hardware
|
25 |
BATCH_SIZE = 32
|
26 |
|
27 |
-
CONF_THRES = 0.
|
28 |
-
NMS_IOU = 0.
|
|
|
|
|
29 |
MIN_LENGTH = 0.3 # minimum fish length, in meters
|
30 |
-
MAX_AGE = 20 # time until missing fish get's new id
|
31 |
IOU_THRES = 0.01 # IOU threshold for tracking
|
32 |
-
MIN_HITS = 11 # minimum number of frames with a specific fish for it to count
|
33 |
###
|
34 |
|
35 |
def norm(bbox, w, h):
|
@@ -60,14 +61,16 @@ def do_full_inference(dataloader, image_meter_width, image_meter_height, gp=None
|
|
60 |
inference = json_object['inference']
|
61 |
width = json_object['width']
|
62 |
height = json_object['height']
|
|
|
63 |
else:
|
64 |
-
inference, width, height = do_detection(dataloader, model, device, gp=gp)
|
65 |
|
66 |
if save:
|
67 |
json_object = {
|
68 |
'inference': inference,
|
69 |
'width': width,
|
70 |
-
'height': height
|
|
|
71 |
}
|
72 |
json_text = json.dumps(json_object, indent=4)
|
73 |
with open('static/example/inference_output.json', 'w') as f:
|
@@ -75,7 +78,13 @@ def do_full_inference(dataloader, image_meter_width, image_meter_height, gp=None
|
|
75 |
return
|
76 |
|
77 |
|
78 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
|
80 |
results = do_tracking(all_preds, image_meter_width, image_meter_height, gp=gp)
|
81 |
|
@@ -114,6 +123,7 @@ def do_detection(dataloader, model, device, gp=None, batch_size=BATCH_SIZE, verb
|
|
114 |
if (gp): gp(0, "Detection...")
|
115 |
|
116 |
inference = []
|
|
|
117 |
# Run detection
|
118 |
with tqdm(total=len(dataloader)*batch_size, desc="Running detection", ncols=0, disable=not verbose) as pbar:
|
119 |
for batch_i, (img, _, shapes) in enumerate(dataloader):
|
@@ -125,16 +135,24 @@ def do_detection(dataloader, model, device, gp=None, batch_size=BATCH_SIZE, verb
|
|
125 |
size = tuple(img.shape)
|
126 |
nb, _, height, width = size # batch size, channels, height, width
|
127 |
|
|
|
|
|
128 |
# Run model & NMS
|
129 |
with torch.no_grad():
|
130 |
inf_out, _ = model(img, augment=False)
|
131 |
|
|
|
|
|
|
|
|
|
|
|
|
|
132 |
inference.append(inf_out)
|
133 |
pbar.update(1*batch_size)
|
134 |
|
135 |
-
return inference, width, height
|
136 |
|
137 |
-
def do_suppression(
|
138 |
"""
|
139 |
Args:
|
140 |
frames_dir: a directory containing frames to be evaluated
|
@@ -146,31 +164,59 @@ def do_suppression(dataloader, inference, width, height, gp=None, batch_size=BAT
|
|
146 |
if (gp): gp(0, "Suppression...")
|
147 |
# keep predictions to feed them ordered into the Tracker
|
148 |
# TODO: how to deal with large files?
|
149 |
-
|
150 |
-
with tqdm(total=len(
|
151 |
-
for batch_i,
|
152 |
|
153 |
-
if gp: gp(batch_i / len(
|
154 |
|
155 |
-
inf_out = inference[batch_i]
|
156 |
with torch.no_grad():
|
157 |
output = non_max_suppression(inf_out, conf_thres=conf_thres, iou_thres=iou_thres)
|
158 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
159 |
# Format results
|
160 |
-
for si, pred in enumerate(
|
|
|
161 |
# Clip boxes to image bounds and resize to input shape
|
162 |
clip_boxes(pred, (height, width))
|
163 |
box = pred[:, :4].clone() # xyxy
|
164 |
confs = pred[:, 4].clone().tolist()
|
165 |
-
scale_boxes(
|
166 |
|
167 |
# get boxes into tracker input format - normalized xyxy with confidence score
|
168 |
# confidence score currently not used by tracker; set to 1.0
|
169 |
boxes = None
|
170 |
if box.shape[0]:
|
171 |
-
real_width =
|
172 |
-
real_height =
|
173 |
-
do_norm = partial(norm, w=
|
174 |
normed = list((map(do_norm, box[:, :4].tolist())))
|
175 |
boxes = np.stack([ [*bb, conf] for bb, conf in zip(normed, confs) ])
|
176 |
frame_num = (batch_i, si)
|
@@ -180,6 +226,67 @@ def do_suppression(dataloader, inference, width, height, gp=None, batch_size=BAT
|
|
180 |
|
181 |
return all_preds, real_width, real_height
|
182 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
183 |
def do_tracking(all_preds, image_meter_width, image_meter_height, gp=None, max_age=MAX_AGE, iou_thres=IOU_THRES, min_hits=MIN_HITS, min_length=MIN_LENGTH, verbose=True):
|
184 |
|
185 |
if (gp): gp(0, "Tracking...")
|
|
|
7 |
import json
|
8 |
import time
|
9 |
from unittest.mock import patch
|
10 |
+
import math
|
11 |
|
12 |
# assumes yolov5 on sys.path
|
13 |
from lib.yolov5.models.experimental import attempt_load
|
|
|
25 |
# will need to configure these based on GPU hardware
|
26 |
BATCH_SIZE = 32
|
27 |
|
28 |
+
CONF_THRES = 0.05 # detection
|
29 |
+
NMS_IOU = 0.2 # NMS IOU
|
30 |
+
MAX_AGE = 14 # time until missing fish get's new id
|
31 |
+
MIN_HITS = 16 # minimum number of frames with a specific fish for it to count
|
32 |
MIN_LENGTH = 0.3 # minimum fish length, in meters
|
|
|
33 |
IOU_THRES = 0.01 # IOU threshold for tracking
|
|
|
34 |
###
|
35 |
|
36 |
def norm(bbox, w, h):
|
|
|
61 |
inference = json_object['inference']
|
62 |
width = json_object['width']
|
63 |
height = json_object['height']
|
64 |
+
image_shapes = json_object['image_shapes']
|
65 |
else:
|
66 |
+
inference, image_shapes, width, height = do_detection(dataloader, model, device, gp=gp)
|
67 |
|
68 |
if save:
|
69 |
json_object = {
|
70 |
'inference': inference,
|
71 |
'width': width,
|
72 |
+
'height': height,
|
73 |
+
'image_shapes': image_shapes
|
74 |
}
|
75 |
json_text = json.dumps(json_object, indent=4)
|
76 |
with open('static/example/inference_output.json', 'w') as f:
|
|
|
78 |
return
|
79 |
|
80 |
|
81 |
+
outputs = do_suppression(inference, gp=gp)
|
82 |
+
|
83 |
+
do_confidence_boost(inference, outputs, gp=gp)
|
84 |
+
|
85 |
+
new_outputs = do_suppression(inference, gp=gp)
|
86 |
+
|
87 |
+
all_preds, real_width, real_height = format_predictions(image_shapes, new_outputs, width, height, gp=gp)
|
88 |
|
89 |
results = do_tracking(all_preds, image_meter_width, image_meter_height, gp=gp)
|
90 |
|
|
|
123 |
if (gp): gp(0, "Detection...")
|
124 |
|
125 |
inference = []
|
126 |
+
image_shapes = []
|
127 |
# Run detection
|
128 |
with tqdm(total=len(dataloader)*batch_size, desc="Running detection", ncols=0, disable=not verbose) as pbar:
|
129 |
for batch_i, (img, _, shapes) in enumerate(dataloader):
|
|
|
135 |
size = tuple(img.shape)
|
136 |
nb, _, height, width = size # batch size, channels, height, width
|
137 |
|
138 |
+
|
139 |
+
|
140 |
# Run model & NMS
|
141 |
with torch.no_grad():
|
142 |
inf_out, _ = model(img, augment=False)
|
143 |
|
144 |
+
# Save shapes for resizing to original shape
|
145 |
+
batch_shape = []
|
146 |
+
for si, pred in enumerate(inf_out):
|
147 |
+
batch_shape.append((img[si].shape[1:], shapes[si]))
|
148 |
+
image_shapes.append(batch_shape)
|
149 |
+
|
150 |
inference.append(inf_out)
|
151 |
pbar.update(1*batch_size)
|
152 |
|
153 |
+
return inference, image_shapes, width, height
|
154 |
|
155 |
+
def do_suppression(inference, gp=None, batch_size=BATCH_SIZE, conf_thres=CONF_THRES, iou_thres=NMS_IOU, verbose=True):
|
156 |
"""
|
157 |
Args:
|
158 |
frames_dir: a directory containing frames to be evaluated
|
|
|
164 |
if (gp): gp(0, "Suppression...")
|
165 |
# keep predictions to feed them ordered into the Tracker
|
166 |
# TODO: how to deal with large files?
|
167 |
+
outputs = []
|
168 |
+
with tqdm(total=len(inference)*batch_size, desc="Running suppression", ncols=0, disable=not verbose) as pbar:
|
169 |
+
for batch_i, inf_out in enumerate(inference):
|
170 |
|
171 |
+
if gp: gp(batch_i / len(inference), pbar.__str__())
|
172 |
|
|
|
173 |
with torch.no_grad():
|
174 |
output = non_max_suppression(inf_out, conf_thres=conf_thres, iou_thres=iou_thres)
|
175 |
|
176 |
+
|
177 |
+
print(type(output))
|
178 |
+
outputs.append(output)
|
179 |
+
|
180 |
+
pbar.update(1*batch_size)
|
181 |
+
|
182 |
+
return outputs
|
183 |
+
|
184 |
+
def format_predictions(image_shapes, outputs, width, height, gp=None, batch_size=BATCH_SIZE, verbose=True):
|
185 |
+
"""
|
186 |
+
Args:
|
187 |
+
frames_dir: a directory containing frames to be evaluated
|
188 |
+
image_meter_width: the width of each image, in meters (used for fish length calculation)
|
189 |
+
gp: a callback function which takes as input 1 parameter, (int) percent complete
|
190 |
+
prep_for_marking: re-index fish for manual marking output
|
191 |
+
"""
|
192 |
+
|
193 |
+
if (gp): gp(0, "Formatting...")
|
194 |
+
# keep predictions to feed them ordered into the Tracker
|
195 |
+
# TODO: how to deal with large files?
|
196 |
+
all_preds = {}
|
197 |
+
with tqdm(total=len(image_shapes)*batch_size, desc="Running formatting", ncols=0, disable=not verbose) as pbar:
|
198 |
+
for batch_i, batch in enumerate(outputs):
|
199 |
+
|
200 |
+
if gp: gp(batch_i / len(image_shapes), pbar.__str__())
|
201 |
+
|
202 |
+
batch_shapes = image_shapes[batch_i]
|
203 |
+
|
204 |
# Format results
|
205 |
+
for si, pred in enumerate(batch):
|
206 |
+
(image_shape, original_shape) = batch_shapes[si]
|
207 |
# Clip boxes to image bounds and resize to input shape
|
208 |
clip_boxes(pred, (height, width))
|
209 |
box = pred[:, :4].clone() # xyxy
|
210 |
confs = pred[:, 4].clone().tolist()
|
211 |
+
scale_boxes(image_shape, box, original_shape[0], original_shape[1]) # to original shape
|
212 |
|
213 |
# get boxes into tracker input format - normalized xyxy with confidence score
|
214 |
# confidence score currently not used by tracker; set to 1.0
|
215 |
boxes = None
|
216 |
if box.shape[0]:
|
217 |
+
real_width = original_shape[0][1]
|
218 |
+
real_height = original_shape[0][0]
|
219 |
+
do_norm = partial(norm, w=original_shape[0][1], h=original_shape[0][0])
|
220 |
normed = list((map(do_norm, box[:, :4].tolist())))
|
221 |
boxes = np.stack([ [*bb, conf] for bb, conf in zip(normed, confs) ])
|
222 |
frame_num = (batch_i, si)
|
|
|
226 |
|
227 |
return all_preds, real_width, real_height
|
228 |
|
229 |
+
def do_confidence_boost(inference, safe_preds, gp=None, batch_size=BATCH_SIZE, verbose=True):
|
230 |
+
"""
|
231 |
+
Args:
|
232 |
+
frames_dir: a directory containing frames to be evaluated
|
233 |
+
image_meter_width: the width of each image, in meters (used for fish length calculation)
|
234 |
+
gp: a callback function which takes as input 1 parameter, (int) percent complete
|
235 |
+
prep_for_marking: re-index fish for manual marking output
|
236 |
+
"""
|
237 |
+
|
238 |
+
if (gp): gp(0, "Confidence Boost...")
|
239 |
+
# keep predictions to feed them ordered into the Tracker
|
240 |
+
# TODO: how to deal with large files?
|
241 |
+
outputs = []
|
242 |
+
with tqdm(total=len(inference), desc="Running confidence boost", ncols=0, disable=not verbose) as pbar:
|
243 |
+
for batch_i in range(len(inference)):
|
244 |
+
|
245 |
+
if gp: gp(batch_i / len(inference), pbar.__str__())
|
246 |
+
|
247 |
+
safe = safe_preds[batch_i]
|
248 |
+
infer = inference[batch_i]
|
249 |
+
|
250 |
+
for i in range(len(safe)):
|
251 |
+
safe_frame = safe[i]
|
252 |
+
if len(safe_frame) == 0:
|
253 |
+
continue
|
254 |
+
|
255 |
+
has_next_batch = batch_i+1 < len(inference)
|
256 |
+
has_prev_batch = batch_i-1 >= 0
|
257 |
+
|
258 |
+
frames = [None, None]
|
259 |
+
|
260 |
+
next_frame = None
|
261 |
+
if i+1 < len(infer):
|
262 |
+
next_frame = infer[i+1]
|
263 |
+
elif has_next_batch:
|
264 |
+
next_frame = inference[batch_i + 1][0]
|
265 |
+
|
266 |
+
if next_frame != None:
|
267 |
+
boost_frame(safe_frame, next_frame, 1)
|
268 |
+
|
269 |
+
prev_frame = None
|
270 |
+
if i-1 >= 0:
|
271 |
+
prev_frame = infer[i-1]
|
272 |
+
elif has_prev_batch:
|
273 |
+
prev_frame = inference[batch_i - 1][len(inference[batch_i - 1]) - 1]
|
274 |
+
|
275 |
+
if prev_frame != None:
|
276 |
+
boost_frame(safe_frame, prev_frame, -1)
|
277 |
+
|
278 |
+
pbar.update(1*batch_size)
|
279 |
+
|
280 |
+
|
281 |
+
def boost_frame(safe_frame, base_frame, dt):
|
282 |
+
safe_boxes = safe_frame[:, :4]
|
283 |
+
boxes = xywh2xyxy(base_frame[:, :4]) # center_x, center_y, width, height) to (x1, y1, x2, y2)
|
284 |
+
ious = box_iou(boxes, safe_boxes)
|
285 |
+
score = torch.matmul(ious, safe_frame[:, 4])
|
286 |
+
# score = iou(safe_box, base_box) * confidence(safe_box)
|
287 |
+
base_frame[:, 4] *= 1 + (score)*math.exp(-dt*dt)
|
288 |
+
return base_frame
|
289 |
+
|
290 |
def do_tracking(all_preds, image_meter_width, image_meter_height, gp=None, max_age=MAX_AGE, iou_thres=IOU_THRES, min_hits=MIN_HITS, min_length=MIN_LENGTH, verbose=True):
|
291 |
|
292 |
if (gp): gp(0, "Tracking...")
|
requirements.txt
CHANGED
@@ -1,79 +1,30 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
black
|
4 |
|
5 |
-
matplotlib>=3.
|
6 |
numpy>=1.18.5
|
7 |
opencv-python>=4.1.2
|
8 |
-
Pillow
|
9 |
-
PyYAML>=5.3.1
|
10 |
-
scipy>=1.4.1
|
11 |
torch>=1.9.0
|
12 |
torchvision>=0.8.1
|
13 |
tqdm>=4.41.0
|
14 |
-
|
15 |
-
tensorboard>=2.4.1
|
16 |
-
|
17 |
-
seaborn>=0.11.0
|
18 |
pandas
|
19 |
|
20 |
-
|
21 |
|
22 |
-
|
23 |
filterpy
|
24 |
-
|
25 |
-
redis
|
26 |
-
boto3
|
27 |
-
|
28 |
-
|
29 |
-
# YOLOv5 requirements
|
30 |
-
# Usage: pip install -r requirements.txt
|
31 |
|
32 |
-
#
|
33 |
--extra-index-url https://download.pytorch.org/whl/cu113
|
34 |
-
torch
|
35 |
gitpython>=3.1.30
|
36 |
-
matplotlib>=3.3
|
37 |
-
numpy>=1.18.5
|
38 |
-
opencv-python>=4.1.1
|
39 |
-
Pillow>=7.1.2
|
40 |
psutil # system resources
|
41 |
-
PyYAML>=5.3.1
|
42 |
requests>=2.23.0
|
43 |
-
scipy>=1.4.1
|
44 |
-
thop>=0.1.1 # FLOPs computation
|
45 |
-
torch>=1.7.0 # see https://pytorch.org/get-started/locally (recommended)
|
46 |
-
torchvision>=0.8.1
|
47 |
-
tqdm>=4.64.0
|
48 |
ultralytics>=8.0.111
|
49 |
-
# protobuf<=3.20.1 # https://github.com/ultralytics/yolov5/issues/8012
|
50 |
-
|
51 |
-
# Logging ---------------------------------------------------------------------
|
52 |
-
# tensorboard>=2.4.1
|
53 |
-
# clearml>=1.2.0
|
54 |
-
# comet
|
55 |
-
|
56 |
-
# Plotting --------------------------------------------------------------------
|
57 |
-
pandas>=1.1.4
|
58 |
seaborn>=0.11.0
|
|
|
|
|
|
|
|
|
59 |
|
60 |
-
# Export ----------------------------------------------------------------------
|
61 |
-
# coremltools>=6.0 # CoreML export
|
62 |
-
# onnx>=1.10.0 # ONNX export
|
63 |
-
# onnx-simplifier>=0.4.1 # ONNX simplifier
|
64 |
-
# nvidia-pyindex # TensorRT export
|
65 |
-
# nvidia-tensorrt # TensorRT export
|
66 |
-
# scikit-learn<=1.1.2 # CoreML quantization
|
67 |
-
# tensorflow>=2.4.0 # TF exports (-cpu, -aarch64, -macos)
|
68 |
-
# tensorflowjs>=3.9.0 # TF.js export
|
69 |
-
# openvino-dev # OpenVINO export
|
70 |
-
|
71 |
-
# Deploy ----------------------------------------------------------------------
|
72 |
-
setuptools>=65.5.1 # Snyk vulnerability fix
|
73 |
-
# tritonclient[all]~=2.24.0
|
74 |
-
|
75 |
-
# Extras ----------------------------------------------------------------------
|
76 |
-
# ipython # interactive notebook
|
77 |
-
# mss # screenshots
|
78 |
-
# albumentations>=1.0.3
|
79 |
-
# pycocotools>=2.0.6 # COCO mAP
|
|
|
1 |
+
# Base Requirements
|
2 |
+
gradio
|
|
|
3 |
|
4 |
+
matplotlib>=3.3
|
5 |
numpy>=1.18.5
|
6 |
opencv-python>=4.1.2
|
7 |
+
Pillow>=7.1.2
|
|
|
|
|
8 |
torch>=1.9.0
|
9 |
torchvision>=0.8.1
|
10 |
tqdm>=4.41.0
|
|
|
|
|
|
|
|
|
11 |
pandas
|
12 |
|
13 |
+
boto3 #AWS connection
|
14 |
|
15 |
+
# Requirements: lib/fish_eye ----------------------------------------------------------------
|
16 |
filterpy
|
17 |
+
scipy>=1.4.1
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
|
19 |
+
# Requirements: lib/yolov5 -------------------------------------------------------------------
|
20 |
--extra-index-url https://download.pytorch.org/whl/cu113
|
|
|
21 |
gitpython>=3.1.30
|
|
|
|
|
|
|
|
|
22 |
psutil # system resources
|
|
|
23 |
requests>=2.23.0
|
|
|
|
|
|
|
|
|
|
|
24 |
ultralytics>=8.0.111
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
seaborn>=0.11.0
|
26 |
+
thop>=0.1.1 # FLOPs computation
|
27 |
+
pycocotools
|
28 |
+
tensorboard>=2.4.1
|
29 |
+
PyYAML>=5.3.1
|
30 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
scripts/detect_frames.py
ADDED
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import project_path
|
2 |
+
from lib.yolov5.utils.general import clip_boxes, scale_boxes
|
3 |
+
import argparse
|
4 |
+
from datetime import datetime
|
5 |
+
import torch
|
6 |
+
import os
|
7 |
+
from dataloader import create_dataloader_frames_only
|
8 |
+
from inference import setup_model, do_detection, do_suppression, do_confidence_boost, format_predictions, do_tracking
|
9 |
+
from visualizer import generate_video_batches
|
10 |
+
import json
|
11 |
+
from tqdm import tqdm
|
12 |
+
import numpy as np
|
13 |
+
|
14 |
+
|
15 |
+
def main(args, config={}, verbose=True):
|
16 |
+
"""
|
17 |
+
Main processing task to be run in gradio
|
18 |
+
- Writes aris frames to dirname(filepath)/frames/{i}.jpg
|
19 |
+
- Writes json output to dirname(filepath)/{filename}_results.json
|
20 |
+
- Writes manual marking to dirname(filepath)/{filename}_marking.txt
|
21 |
+
- Writes video output to dirname(filepath)/{filename}_results.mp4
|
22 |
+
- Zips all results to dirname(filepath)/{filename}_results.zip
|
23 |
+
Args:
|
24 |
+
filepath (str): path to aris file
|
25 |
+
|
26 |
+
TODO: Separate into subtasks in different queues; have a GPU-only queue.
|
27 |
+
"""
|
28 |
+
print("In task...")
|
29 |
+
print("Cuda available in task?", torch.cuda.is_available())
|
30 |
+
|
31 |
+
# setup config
|
32 |
+
if "conf_threshold" not in config: config['conf_threshold'] = 0.001
|
33 |
+
if "nms_iou" not in config: config['nms_iou'] = 0.6
|
34 |
+
if "min_length" not in config: config['min_length'] = 0.3
|
35 |
+
if "max_age" not in config: config['max_age'] = 20
|
36 |
+
if "iou_threshold" not in config: config['iou_threshold'] = 0.01
|
37 |
+
if "min_hits" not in config: config['min_hits'] = 11
|
38 |
+
|
39 |
+
print(config)
|
40 |
+
|
41 |
+
dirname = args.frames
|
42 |
+
|
43 |
+
locations = [
|
44 |
+
"elwha"
|
45 |
+
]
|
46 |
+
for loc in locations:
|
47 |
+
|
48 |
+
in_loc_dir = os.path.join(dirname, loc)
|
49 |
+
out_dir = os.path.join(args.output, loc, "tracker", "data")
|
50 |
+
os.makedirs(out_dir, exist_ok=True)
|
51 |
+
print(in_loc_dir)
|
52 |
+
print(out_dir)
|
53 |
+
|
54 |
+
# run detection + tracking
|
55 |
+
model, device = setup_model(args.weights)
|
56 |
+
|
57 |
+
seq_list = os.listdir(in_loc_dir)
|
58 |
+
idx = 1
|
59 |
+
|
60 |
+
ann_list = []
|
61 |
+
with tqdm(total=len(seq_list), desc="...", ncols=0) as pbar:
|
62 |
+
for seq in seq_list:
|
63 |
+
pbar.update(1)
|
64 |
+
if (seq.startswith(".")): continue
|
65 |
+
pbar.set_description("Processing " + seq)
|
66 |
+
if verbose:
|
67 |
+
print(" ")
|
68 |
+
print("(" + str(idx) + "/" + str(len(seq_list)) + ") " + seq)
|
69 |
+
print(" ")
|
70 |
+
idx += 1
|
71 |
+
in_seq_dir = os.path.join(in_loc_dir, seq)
|
72 |
+
frame_list = detect(in_seq_dir, out_dir, config, seq, model, device, verbose)
|
73 |
+
i = 0
|
74 |
+
for frame in frame_list:
|
75 |
+
print(frame)
|
76 |
+
if frame is not None:
|
77 |
+
for ann in frame:
|
78 |
+
print(ann)
|
79 |
+
ann_list.append({
|
80 |
+
'image_id': i,
|
81 |
+
'category_id': 0,
|
82 |
+
'bbox': [ann[0], ann[1], ann[2] - ann[0], ann[3] - ann[1]],
|
83 |
+
'score': ann[4]
|
84 |
+
})
|
85 |
+
i += 1
|
86 |
+
result = json.dumps(ann_list)
|
87 |
+
with open('../coco_eval/pred.json', 'w') as f:
|
88 |
+
f.write(result)
|
89 |
+
|
90 |
+
|
91 |
+
|
92 |
+
def detect(in_dir, out_dir, config, seq_name, model, device, verbose):
|
93 |
+
|
94 |
+
#progress_log = lambda p, m: 0
|
95 |
+
|
96 |
+
# create dataloader
|
97 |
+
dataloader = create_dataloader_frames_only(in_dir)
|
98 |
+
|
99 |
+
try:
|
100 |
+
inference, image_shapes, width, height = do_detection(dataloader, model, device, verbose=verbose)
|
101 |
+
except:
|
102 |
+
print("Error in " + seq_name)
|
103 |
+
with open(os.path.join(out_dir, "ERROR_" + seq_name + ".txt"), 'w') as f:
|
104 |
+
f.write("ERROR")
|
105 |
+
return
|
106 |
+
|
107 |
+
|
108 |
+
outputs = do_suppression(inference, conf_thres=config['conf_threshold'], iou_thres=config['nms_iou'], verbose=verbose)
|
109 |
+
|
110 |
+
frame_list = []
|
111 |
+
for batch_i, batch in enumerate(outputs):
|
112 |
+
|
113 |
+
batch_shapes = image_shapes[batch_i]
|
114 |
+
|
115 |
+
# Format results
|
116 |
+
for si, pred in enumerate(batch):
|
117 |
+
(image_shape, original_shape) = batch_shapes[si]
|
118 |
+
# Clip boxes to image bounds and resize to input shape
|
119 |
+
clip_boxes(pred, (height, width))
|
120 |
+
boxes = pred[:, :4].clone() # xyxy
|
121 |
+
confs = pred[:, 4].clone().tolist()
|
122 |
+
scale_boxes(image_shape, boxes, original_shape[0], original_shape[1]) # to original shape
|
123 |
+
ann = [ [*bb, conf] for bb, conf in zip(boxes.tolist(), confs) ]
|
124 |
+
|
125 |
+
frame_list.append(ann)
|
126 |
+
|
127 |
+
return frame_list
|
128 |
+
|
129 |
+
def argument_parser():
|
130 |
+
parser = argparse.ArgumentParser()
|
131 |
+
parser.add_argument("--frames", required=True, help="Path to frame directory. Required.")
|
132 |
+
parser.add_argument("--output", required=True, help="Path to output directory. Required.")
|
133 |
+
parser.add_argument("--weights", default='models/v5m_896_300best.pt', help="Path to saved YOLOv5 weights. Default: ../models/v5m_896_300best.pt")
|
134 |
+
return parser
|
135 |
+
|
136 |
+
if __name__ == "__main__":
|
137 |
+
args = argument_parser().parse_args()
|
138 |
+
main(args)
|
scripts/infer_frames.py
CHANGED
@@ -4,8 +4,7 @@ from datetime import datetime
|
|
4 |
import torch
|
5 |
import os
|
6 |
from dataloader import create_dataloader_frames_only
|
7 |
-
from
|
8 |
-
from inference import setup_model, do_suppression, do_detection, do_tracking, json_dump_round_float
|
9 |
from visualizer import generate_video_batches
|
10 |
import json
|
11 |
from tqdm import tqdm
|
@@ -39,7 +38,7 @@ def main(args, config={}, verbose=True):
|
|
39 |
|
40 |
dirname = args.frames
|
41 |
|
42 |
-
locations = ["
|
43 |
for loc in locations:
|
44 |
|
45 |
in_loc_dir = os.path.join(dirname, loc)
|
@@ -88,14 +87,21 @@ def infer_seq(in_dir, out_dir, config, seq_name, model, device, metadata_path, v
|
|
88 |
dataloader = create_dataloader_frames_only(in_dir)
|
89 |
|
90 |
try:
|
91 |
-
inference, width, height = do_detection(dataloader, model, device, verbose=verbose)
|
92 |
except:
|
93 |
print("Error in " + seq_name)
|
94 |
with open(os.path.join(out_dir, "ERROR_" + seq_name + ".txt"), 'w') as f:
|
95 |
f.write("ERROR")
|
96 |
return
|
97 |
|
98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
99 |
|
100 |
results = do_tracking(all_preds, image_meter_width, image_meter_height, min_length=config['min_length'], max_age=config['max_age'], iou_thres=config['iou_threshold'], min_hits=config['min_hits'], verbose=verbose)
|
101 |
|
|
|
4 |
import torch
|
5 |
import os
|
6 |
from dataloader import create_dataloader_frames_only
|
7 |
+
from inference import setup_model, do_detection, do_suppression, do_confidence_boost, format_predictions, do_tracking
|
|
|
8 |
from visualizer import generate_video_batches
|
9 |
import json
|
10 |
from tqdm import tqdm
|
|
|
38 |
|
39 |
dirname = args.frames
|
40 |
|
41 |
+
locations = ["test"]
|
42 |
for loc in locations:
|
43 |
|
44 |
in_loc_dir = os.path.join(dirname, loc)
|
|
|
87 |
dataloader = create_dataloader_frames_only(in_dir)
|
88 |
|
89 |
try:
|
90 |
+
inference, image_shapes, width, height = do_detection(dataloader, model, device, verbose=verbose)
|
91 |
except:
|
92 |
print("Error in " + seq_name)
|
93 |
with open(os.path.join(out_dir, "ERROR_" + seq_name + ".txt"), 'w') as f:
|
94 |
f.write("ERROR")
|
95 |
return
|
96 |
|
97 |
+
|
98 |
+
outputs = do_suppression(inference, conf_thres=config['conf_threshold'], iou_thres=config['nms_iou'], verbose=verbose)
|
99 |
+
|
100 |
+
do_confidence_boost(inference, outputs, verbose=verbose)
|
101 |
+
|
102 |
+
new_outputs = do_suppression(inference, conf_thres=config['conf_threshold'], iou_thres=config['nms_iou'], verbose=verbose)
|
103 |
+
|
104 |
+
all_preds, real_width, real_height = format_predictions(image_shapes, new_outputs, width, height)
|
105 |
|
106 |
results = do_tracking(all_preds, image_meter_width, image_meter_height, min_length=config['min_length'], max_age=config['max_age'], iou_thres=config['iou_threshold'], min_hits=config['min_hits'], verbose=verbose)
|
107 |
|
static/example/example_result.zip
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:cfb932e1041d232764b02af5747d43d1a226d51d80604493329a844aac552811
|
3 |
+
size 12565792
|
visualizer.py
CHANGED
@@ -35,9 +35,6 @@ def generate_video_batches(didson, preds, frame_rate, video_out_path, gp=None, i
|
|
35 |
if (gp): gp(0, "Generating results video...")
|
36 |
end_frame = didson.info['endframe'] or didson.info['numframes']
|
37 |
out = None # need to wait til we have height and width to instantiate video file
|
38 |
-
|
39 |
-
print(preds)
|
40 |
-
print(preds.keys())
|
41 |
|
42 |
with tqdm(total=end_frame, desc="Generating results video", ncols=0) as pbar:
|
43 |
for i in range(0, end_frame, batch_size):
|
|
|
35 |
if (gp): gp(0, "Generating results video...")
|
36 |
end_frame = didson.info['endframe'] or didson.info['numframes']
|
37 |
out = None # need to wait til we have height and width to instantiate video file
|
|
|
|
|
|
|
38 |
|
39 |
with tqdm(total=end_frame, desc="Generating results video", ncols=0) as pbar:
|
40 |
for i in range(0, end_frame, batch_size):
|