oskarastrom commited on
Commit
fbb3995
Β·
1 Parent(s): a2bc65a

Detection only script

Browse files
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 load_example_result, reset_state
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 cv2
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 gradio_components.upload_ui import Upload_Gradio
18
- from gradio_components.result_ui import Result_Gradio, update_result, table_headers, info_headers, js_update_tab_labels
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
- annotation_html = "<div style='display:flex'>"
194
- annotation_html += "<canvas id='canvas' style='width:50%' onmousedown='mouse_down(event)' onmousemove='mouse_move(event)' onmouseup='mouse_up()' onmouseleave='mouse_up()'></canvas>"
195
- annotation_html += "<div id='annotation_display' style='width:50%'></div>"
 
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, white);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- window.annotations = [];
46
- for (const annotation of annotations) {
47
- const bbox = annotation['bbox']
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
- document.getElementById("annotation_img").src = "data:image/png;base64," + frame;
 
 
 
 
 
 
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.rect;
81
  ctx.strokeStyle = color_from_id(annotation.id);
82
- ctx.strokeRect(rect.startX, rect.startY, rect.w, rect.h);
83
 
84
  ctx.font = "15px Arial";
85
  ctx.fillStyle = color_from_id(annotation.id);
86
  ctx.textAlign = "right";
87
- ctx.fillText(annotation.id, rect.startX + rect.w, rect.startY - 3);
88
  }
89
 
90
  if (hover && !dragging) {
91
  annotation = hover.annotation;
92
- rect = annotation.rect;
93
  handles = [
94
- [rect.startX, rect.startY],
95
- [rect.startX + rect.w, rect.startY],
96
- [rect.startX, rect.startY + rect.h],
97
- [rect.startX + rect.w, rect.startY + rect.h]
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
- color_from_id = (id) => {
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}>${"x"}</label>
131
- <label ${label_style}>${"y"}</label>
132
- <label ${label_style}>${"w"}</label>
133
- <label ${label_style}>${"h"}</label>
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.rect.startX)}'>
139
- <input ${input_style} type='text' value='${Math.round(annotation.rect.startY)}'>
140
- <input ${input_style} type='text' value='${Math.round(annotation.rect.w)}'>
141
- <input ${input_style} type='text' value='${Math.round(annotation.rect.h)}'>
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.rect
202
  corner = dragging.corner;
203
 
204
- console.log("drag", dragX, dragY)
205
- console.log("d", rect.startX - mouseX, rect.startY - mouseY)
206
  if (corner == window.TL) {
207
- rect.w += rect.startX - mouseX;
208
- rect.h += rect.startY - mouseY;
209
- rect.startX = mouseX;
210
- rect.startY = mouseY;
211
  } else if (corner == window.TR) {
212
- rect.w = mouseX - rect.startX;
213
- rect.h += rect.startY - mouseY;
214
- rect.startY = mouseY;
215
  } else if (corner == window.BL) {
216
- rect.w += rect.startX - mouseX;
217
- rect.h = mouseY - rect.startY;
218
- rect.startX = mouseX;
219
  } else if (corner == window.BR) {
220
- rect.w = mouseX - rect.startX;
221
- rect.h = mouseY - rect.startY
222
  }
223
 
224
- //rect.startX = Math.floor(rect.startX);
225
- //rect.startY = Math.floor(rect.startY);
226
- //rect.w = Math.floor(rect.w);
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 h < 0 we have swapped sides, switch to vertically opposite corner
238
- if (rect.h < 0) {
239
- rect.h = -rect.h;
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.rect;
269
  square_dists = [
270
- sqDistance(rect.startX, rect.startY),
271
- sqDistance(rect.startX + rect.w, rect.startY),
272
- sqDistance(rect.startX, rect.startY + rect.h),
273
- sqDistance(rect.startX + rect.w, rect.startY + rect.h),
274
  ]
275
 
276
  min_dist = Math.min(...square_dists);
@@ -282,14 +274,19 @@
282
  }
283
  }
284
 
 
 
 
 
 
285
  create_annotation = () => {
286
 
287
  new_annotation = {
288
- rect: {
289
- startX: mouseX,
290
- startY: mouseY,
291
- w: 0,
292
- h: 0
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.3 # detection
28
- NMS_IOU = 0.3 # NMS IOU
 
 
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
- all_preds, real_width, real_height = do_suppression(dataloader, inference, width, height, gp=gp)
 
 
 
 
 
 
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(dataloader, inference, width, height, gp=None, batch_size=BATCH_SIZE, conf_thres=CONF_THRES, iou_thres=NMS_IOU, verbose=True):
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
- all_preds = {}
150
- with tqdm(total=len(dataloader)*batch_size, desc="Running suppression", ncols=0, disable=not verbose) as pbar:
151
- for batch_i, (img, _, shapes) in enumerate(dataloader):
152
 
153
- if gp: gp(batch_i / len(dataloader), pbar.__str__())
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(output):
 
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(img[si].shape[1:], box, shapes[si][0], shapes[si][1]) # to original shape
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 = shapes[si][0][1]
172
- real_height = shapes[si][0][0]
173
- do_norm = partial(norm, w=shapes[si][0][1], h=shapes[si][0][0])
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
- flask
2
- requests
3
- black
4
 
5
- matplotlib>=3.2.2
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
- thop # FLOPs computation
21
 
22
- pycocotools
23
  filterpy
24
- celery
25
- redis
26
- boto3
27
-
28
-
29
- # YOLOv5 requirements
30
- # Usage: pip install -r requirements.txt
31
 
32
- # Base ------------------------------------------------------------------------
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 aris import create_manual_marking, create_metadata_dictionary, prep_for_mm
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 = ["kenai-val"]
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
- all_preds, real_width, real_height = do_suppression(dataloader, inference, width, height, conf_thres=config['conf_threshold'], iou_thres=config['nms_iou'], verbose=verbose)
 
 
 
 
 
 
 
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):