Spaces:
Runtime error
Runtime error
Commit ·
d9c7dce
1
Parent(s): 53af29c
Bug fixes
Browse files- .gitignore +1 -0
- InferenceConfig.py +1 -1
- app.py +62 -46
- gradio_scripts/pdf_handler.py +116 -14
- gradio_scripts/result_ui.py +4 -4
- gradio_scripts/upload_ui.py +17 -17
- inference.py +6 -2
- lib/fish_eye/{associative.py → bytetrack.py} +0 -0
- lib/fish_eye/tracker.py +2 -2
- multipage_pdf.pdf +0 -0
.gitignore
CHANGED
|
@@ -13,4 +13,5 @@ models/*
|
|
| 13 |
*.jpg
|
| 14 |
*.aris
|
| 15 |
*.log
|
|
|
|
| 16 |
*.DS_STORE
|
|
|
|
| 13 |
*.jpg
|
| 14 |
*.aris
|
| 15 |
*.log
|
| 16 |
+
*.pdf
|
| 17 |
*.DS_STORE
|
InferenceConfig.py
CHANGED
|
@@ -20,7 +20,7 @@ NMS_IOU = 0.25 # NMS IOU
|
|
| 20 |
MAX_AGE = 20 # time until missing fish get's new id
|
| 21 |
MIN_HITS = 11 # minimum number of frames with a specific fish for it to count
|
| 22 |
MIN_LENGTH = 0.3 # minimum fish length, in meters
|
| 23 |
-
MAX_LENGTH =
|
| 24 |
IOU_THRES = 0.01 # IOU threshold for tracking
|
| 25 |
MIN_TRAVEL = 0 # Minimum distance a track has to travel
|
| 26 |
DEFAULT_TRACKER = TrackerType.BYTETRACK
|
|
|
|
| 20 |
MAX_AGE = 20 # time until missing fish get's new id
|
| 21 |
MIN_HITS = 11 # minimum number of frames with a specific fish for it to count
|
| 22 |
MIN_LENGTH = 0.3 # minimum fish length, in meters
|
| 23 |
+
MAX_LENGTH = 0 # maximum fish length, in meters
|
| 24 |
IOU_THRES = 0.01 # IOU threshold for tracking
|
| 25 |
MIN_TRAVEL = 0 # Minimum distance a track has to travel
|
| 26 |
DEFAULT_TRACKER = TrackerType.BYTETRACK
|
app.py
CHANGED
|
@@ -17,6 +17,8 @@ from InferenceConfig import InferenceConfig
|
|
| 17 |
|
| 18 |
WEBAPP_VERSION = "1.0"
|
| 19 |
|
|
|
|
|
|
|
| 20 |
#Initialize State & Result
|
| 21 |
state = {
|
| 22 |
'files': [],
|
|
@@ -31,7 +33,15 @@ result = {}
|
|
| 31 |
|
| 32 |
|
| 33 |
# Called when an Aris file is uploaded for inference
|
| 34 |
-
def on_aris_input(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
print(output_formats)
|
| 37 |
|
|
@@ -61,20 +71,25 @@ def on_aris_input(file_list, model_id, conf_thresh, iou_thresh, min_hits, max_ag
|
|
| 61 |
state['config'].enable_sort_track()
|
| 62 |
|
| 63 |
print(" ")
|
| 64 |
-
print("
|
| 65 |
print(state['config'].to_dict())
|
| 66 |
print(" ")
|
| 67 |
|
| 68 |
# Update loading_space to start inference on first file
|
| 69 |
return {
|
| 70 |
inference_handler: gr.update(value = str(np.random.rand()), visible=True),
|
| 71 |
-
components['
|
| 72 |
-
components['skipBtn']: gr.update(visible=True),
|
| 73 |
master_tabs: gr.update(selected=1)
|
| 74 |
}
|
| 75 |
|
| 76 |
# Called when a result zip file is uploaded for result review
|
| 77 |
-
def on_result_upload(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
|
| 79 |
if (zip_list == None):
|
| 80 |
zip_list = [("static/example/example_result.zip", None)]
|
|
@@ -87,7 +102,6 @@ def on_result_upload(zip_list, aris_list):
|
|
| 87 |
state['outputs'] = ["Annotated Video", "Manual Marking", "PDF"]
|
| 88 |
|
| 89 |
component_updates = {
|
| 90 |
-
master_tabs: gr.update(selected=1),
|
| 91 |
tab_labeler: gr.update(value = len(zip_list))
|
| 92 |
}
|
| 93 |
|
|
@@ -214,8 +228,7 @@ def cancel_inference():
|
|
| 214 |
return {
|
| 215 |
master_tabs: gr.update(selected=0),
|
| 216 |
inference_handler: gr.update(visible=False),
|
| 217 |
-
components['
|
| 218 |
-
components['skipBtn']: gr.update(visible=False)
|
| 219 |
}
|
| 220 |
|
| 221 |
|
|
@@ -257,11 +270,11 @@ def load_annotation(_, progress=gr.Progress()):
|
|
| 257 |
annotation_info, state['frame_index'] = init_frames(annotation_dataset, result['json_result'][result_index], state['frame_index'], gp=set_progress)
|
| 258 |
|
| 259 |
# save as html element
|
| 260 |
-
|
| 261 |
|
| 262 |
return {
|
| 263 |
annotation_editor: gr.update(),
|
| 264 |
-
annotation_progress: gr.update(value=
|
| 265 |
}
|
| 266 |
|
| 267 |
# If complete, start annotation editor
|
|
@@ -296,7 +309,7 @@ demo = gr.Blocks()
|
|
| 296 |
with demo:
|
| 297 |
with gr.Blocks() as inner_body:
|
| 298 |
|
| 299 |
-
# Title of page
|
| 300 |
gr.HTML(
|
| 301 |
"""
|
| 302 |
<h1 align="center" style="font-size:xxx-large">Caltech Fisheye</h1>
|
|
@@ -349,6 +362,9 @@ with demo:
|
|
| 349 |
|
| 350 |
|
| 351 |
# Define annotation progress bar for event listeres, but unrender since it will be displayed later on
|
|
|
|
|
|
|
|
|
|
| 352 |
annotation_progress = gr.HTML("", visible=False).unrender()
|
| 353 |
components['annotation_progress'] = annotation_progress
|
| 354 |
|
|
@@ -356,35 +372,35 @@ with demo:
|
|
| 356 |
vis_components = Result_Gradio(prepare_annotation, components)
|
| 357 |
|
| 358 |
# Master Tab for annotation editing
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
|
| 389 |
# Disclaimer at the bottom of page
|
| 390 |
gr.HTML(
|
|
@@ -403,7 +419,7 @@ with demo:
|
|
| 403 |
tab_labeler = components['tab_labeler']
|
| 404 |
|
| 405 |
|
| 406 |
-
inference_comps = [inference_handler, master_tabs, components['
|
| 407 |
|
| 408 |
# When a file is uploaded to the input, tell the inference_handler to start inference
|
| 409 |
input.upload(on_aris_input, [input] + components['hyperparams'], inference_comps)
|
|
@@ -416,14 +432,14 @@ with demo:
|
|
| 416 |
result_handler.change(on_result_ready, None, vis_components + [inference_handler])
|
| 417 |
|
| 418 |
# Cancel and skip buttons
|
| 419 |
-
components['
|
| 420 |
-
components['skipBtn'].click(cancel_inference, None, inference_comps, cancels=[inference_event])
|
| 421 |
|
| 422 |
# Button to load a previous result and view visualization
|
| 423 |
-
components['
|
| 424 |
-
|
|
|
|
| 425 |
[components['result_input'], components['result_aris_input']],
|
| 426 |
-
vis_components + [
|
| 427 |
)
|
| 428 |
|
| 429 |
demo.queue().launch()
|
|
|
|
| 17 |
|
| 18 |
WEBAPP_VERSION = "1.0"
|
| 19 |
|
| 20 |
+
enable_annotation_editor = False
|
| 21 |
+
|
| 22 |
#Initialize State & Result
|
| 23 |
state = {
|
| 24 |
'files': [],
|
|
|
|
| 33 |
|
| 34 |
|
| 35 |
# Called when an Aris file is uploaded for inference
|
| 36 |
+
def on_aris_input(
|
| 37 |
+
file_list,
|
| 38 |
+
model_id,
|
| 39 |
+
conf_thresh, iou_thresh,
|
| 40 |
+
min_hits, max_age,
|
| 41 |
+
associative_tracker, boost_power, boost_decay, byte_low_conf, byte_high_conf,
|
| 42 |
+
min_length, max_length, min_travel,
|
| 43 |
+
output_formats
|
| 44 |
+
):
|
| 45 |
|
| 46 |
print(output_formats)
|
| 47 |
|
|
|
|
| 71 |
state['config'].enable_sort_track()
|
| 72 |
|
| 73 |
print(" ")
|
| 74 |
+
print("Inference with:")
|
| 75 |
print(state['config'].to_dict())
|
| 76 |
print(" ")
|
| 77 |
|
| 78 |
# Update loading_space to start inference on first file
|
| 79 |
return {
|
| 80 |
inference_handler: gr.update(value = str(np.random.rand()), visible=True),
|
| 81 |
+
components['cancel_btn']: gr.update(visible=True),
|
|
|
|
| 82 |
master_tabs: gr.update(selected=1)
|
| 83 |
}
|
| 84 |
|
| 85 |
# Called when a result zip file is uploaded for result review
|
| 86 |
+
def on_result_upload():
|
| 87 |
+
return {
|
| 88 |
+
master_tabs: gr.update(selected=1),
|
| 89 |
+
result_uploader: gr.update(value=str(np.random.rand()))
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
def on_result_upload_finish(zip_list, aris_list):
|
| 93 |
|
| 94 |
if (zip_list == None):
|
| 95 |
zip_list = [("static/example/example_result.zip", None)]
|
|
|
|
| 102 |
state['outputs'] = ["Annotated Video", "Manual Marking", "PDF"]
|
| 103 |
|
| 104 |
component_updates = {
|
|
|
|
| 105 |
tab_labeler: gr.update(value = len(zip_list))
|
| 106 |
}
|
| 107 |
|
|
|
|
| 228 |
return {
|
| 229 |
master_tabs: gr.update(selected=0),
|
| 230 |
inference_handler: gr.update(visible=False),
|
| 231 |
+
components['cancel_btn']: gr.update(visible=False)
|
|
|
|
| 232 |
}
|
| 233 |
|
| 234 |
|
|
|
|
| 270 |
annotation_info, state['frame_index'] = init_frames(annotation_dataset, result['json_result'][result_index], state['frame_index'], gp=set_progress)
|
| 271 |
|
| 272 |
# save as html element
|
| 273 |
+
annotation_content = "<p id='annotation_info' style='display:none'>" + json.dumps(annotation_info) + "</p>"
|
| 274 |
|
| 275 |
return {
|
| 276 |
annotation_editor: gr.update(),
|
| 277 |
+
annotation_progress: gr.update(value=annotation_content)
|
| 278 |
}
|
| 279 |
|
| 280 |
# If complete, start annotation editor
|
|
|
|
| 309 |
with demo:
|
| 310 |
with gr.Blocks() as inner_body:
|
| 311 |
|
| 312 |
+
# Title of page + style
|
| 313 |
gr.HTML(
|
| 314 |
"""
|
| 315 |
<h1 align="center" style="font-size:xxx-large">Caltech Fisheye</h1>
|
|
|
|
| 362 |
|
| 363 |
|
| 364 |
# Define annotation progress bar for event listeres, but unrender since it will be displayed later on
|
| 365 |
+
result_uploader = gr.HTML("", visible=False)
|
| 366 |
+
components['result_uploader'] = result_uploader
|
| 367 |
+
|
| 368 |
annotation_progress = gr.HTML("", visible=False).unrender()
|
| 369 |
components['annotation_progress'] = annotation_progress
|
| 370 |
|
|
|
|
| 372 |
vis_components = Result_Gradio(prepare_annotation, components)
|
| 373 |
|
| 374 |
# Master Tab for annotation editing
|
| 375 |
+
if enable_annotation_editor:
|
| 376 |
+
with gr.Tab("Annotation Editor", id=2):
|
| 377 |
+
|
| 378 |
+
# Draw the annotation loading bar here
|
| 379 |
+
annotation_progress.render()
|
| 380 |
+
|
| 381 |
+
# Add annotation editor component
|
| 382 |
+
annotation_editor = gr.HTML("", visible=False)
|
| 383 |
+
|
| 384 |
+
# Event listener for opening annotation
|
| 385 |
+
annotation_progress.change(load_annotation, annotation_progress, [annotation_editor, annotation_progress], _js="""
|
| 386 |
+
() => {
|
| 387 |
+
info_string = document.getElementById("annotation_info").innerHTML;
|
| 388 |
+
info = JSON.parse(info_string);
|
| 389 |
+
console.log(info)
|
| 390 |
+
if (info.length == 0) {
|
| 391 |
+
window.annotation_info = [];
|
| 392 |
+
return false;
|
| 393 |
+
}
|
| 394 |
+
window.annotation_info = window.annotation_info.concat(info)
|
| 395 |
+
console.log(window.annotation_info)
|
| 396 |
+
return true;
|
| 397 |
+
}
|
| 398 |
+
""")
|
| 399 |
+
|
| 400 |
+
# Event listener for running javascript defined in 'annotation_editor.js'
|
| 401 |
+
# show_annotation
|
| 402 |
+
with open('gradio_scripts/annotation_editor.js', 'r') as f:
|
| 403 |
+
annotation_editor.change(lambda x: gr.update(), None, annotation_editor, _js=f.read())
|
| 404 |
|
| 405 |
# Disclaimer at the bottom of page
|
| 406 |
gr.HTML(
|
|
|
|
| 419 |
tab_labeler = components['tab_labeler']
|
| 420 |
|
| 421 |
|
| 422 |
+
inference_comps = [inference_handler, master_tabs, components['cancel_btn']]
|
| 423 |
|
| 424 |
# When a file is uploaded to the input, tell the inference_handler to start inference
|
| 425 |
input.upload(on_aris_input, [input] + components['hyperparams'], inference_comps)
|
|
|
|
| 432 |
result_handler.change(on_result_ready, None, vis_components + [inference_handler])
|
| 433 |
|
| 434 |
# Cancel and skip buttons
|
| 435 |
+
components['cancel_btn'].click(cancel_inference, None, inference_comps, cancels=[inference_event])
|
|
|
|
| 436 |
|
| 437 |
# Button to load a previous result and view visualization
|
| 438 |
+
components['open_result_btn'].click(on_result_upload, None, [result_uploader, master_tabs])
|
| 439 |
+
components['result_uploader'].change(
|
| 440 |
+
on_result_upload_finish,
|
| 441 |
[components['result_input'], components['result_aris_input']],
|
| 442 |
+
vis_components + [tab_labeler]
|
| 443 |
)
|
| 444 |
|
| 445 |
demo.queue().launch()
|
gradio_scripts/pdf_handler.py
CHANGED
|
@@ -42,7 +42,7 @@ def make_pdf(i, state, result, table_headers):
|
|
| 42 |
draw_combined_fish_graphs(pdf, json_result)
|
| 43 |
|
| 44 |
for i, fish in enumerate(json_result['fish']):
|
| 45 |
-
|
| 46 |
|
| 47 |
# We can also set the file's metadata via the PdfPages object:
|
| 48 |
d = pdf.infodict()
|
|
@@ -166,6 +166,7 @@ def calculate_fish_paths(result, dataset, id):
|
|
| 166 |
fish = result['metadata']['FISH'][id]
|
| 167 |
start_frame = fish['START_FRAME']
|
| 168 |
end_frame = fish['END_FRAME']
|
|
|
|
| 169 |
|
| 170 |
# Extract base frame (first frame for that fish)
|
| 171 |
w, h = 1, 2
|
|
@@ -175,13 +176,15 @@ def calculate_fish_paths(result, dataset, id):
|
|
| 175 |
images = dataset.didson.load_frames(start_frame=start_frame, end_frame=start_frame+1)
|
| 176 |
img = images[0]
|
| 177 |
|
|
|
|
|
|
|
| 178 |
frame_height = 2
|
| 179 |
scale_factor = frame_height / h
|
| 180 |
h = frame_height
|
| 181 |
w = int(scale_factor*w)
|
| 182 |
|
| 183 |
fish['base_frame'] = img
|
| 184 |
-
fish['scaled_frame_size'] = (
|
| 185 |
|
| 186 |
|
| 187 |
# Find frames for this fish
|
|
@@ -214,7 +217,7 @@ def calculate_fish_paths(result, dataset, id):
|
|
| 214 |
last_y = Y[-1]
|
| 215 |
dx = result['image_meter_width']*(last_x - x)/(missed+1)
|
| 216 |
dy = result['image_meter_height']*(last_y - y)/(missed+1)
|
| 217 |
-
v = math.sqrt(dx*dx + dy*dy)
|
| 218 |
|
| 219 |
# Interpolate over missing frames
|
| 220 |
if missed > 0:
|
|
@@ -243,30 +246,72 @@ def calculate_fish_paths(result, dataset, id):
|
|
| 243 |
|
| 244 |
|
| 245 |
def draw_combined_fish_graphs(pdf, result):
|
|
|
|
| 246 |
vel = []
|
| 247 |
log_vel = []
|
|
|
|
| 248 |
for fish in result['metadata']['FISH']:
|
| 249 |
-
|
| 250 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 251 |
|
| 252 |
-
fig, axs = plt.subplots(2, 2, sharey=True, figsize=STANDARD_FIG_SIZE)
|
| 253 |
axs[0,0].hist(log_vel, bins=20)
|
| 254 |
axs[0,0].set_title('Fish Log-Velocities between frames')
|
| 255 |
-
axs[0,0].set_xlabel("Log-Velocity (log(m/
|
|
|
|
| 256 |
axs[0,1].hist(vel, bins=20)
|
| 257 |
axs[0,1].set_title('Fish Velocities between frames')
|
| 258 |
-
axs[0,1].set_xlabel("Velocity (m/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 259 |
|
| 260 |
pdf.savefig(fig)
|
| 261 |
plt.close(fig)
|
| 262 |
|
| 263 |
|
| 264 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 265 |
|
| 266 |
fish = result['metadata']['FISH'][id]
|
| 267 |
start_frame = fish['START_FRAME']
|
| 268 |
end_frame = fish['END_FRAME']
|
| 269 |
|
|
|
|
|
|
|
| 270 |
fig, ax = plt.subplots(figsize=STANDARD_FIG_SIZE)
|
| 271 |
plt.axis('off')
|
| 272 |
|
|
@@ -277,7 +322,7 @@ def generate_fish_tracks(pdf, result, id):
|
|
| 277 |
plt.imshow(img, extent=(0, h, 0, w), cmap=plt.colormaps['Greys'])
|
| 278 |
|
| 279 |
# Title
|
| 280 |
-
plt.text(h/2,
|
| 281 |
|
| 282 |
X = fish['path']['X']
|
| 283 |
Y = fish['path']['Y']
|
|
@@ -302,9 +347,6 @@ def generate_fish_tracks(pdf, result, id):
|
|
| 302 |
colors.append(col)
|
| 303 |
ax.plot([h*(1-Y[i-1]), h*(1-Y[i])], [w*(1-X[i-1]), w*(1-X[i])], color=col, linewidth=1)
|
| 304 |
|
| 305 |
-
print(len(X))
|
| 306 |
-
print(len(Y))
|
| 307 |
-
print(len(colors))
|
| 308 |
for i in range(1, len(X)):
|
| 309 |
ax.plot(h*(1-Y[i]), w*(1-X[i]), color=colors[i], marker='o', markersize=3)
|
| 310 |
|
|
@@ -312,4 +354,64 @@ def generate_fish_tracks(pdf, result, id):
|
|
| 312 |
plt.ylim([0, w])
|
| 313 |
plt.xlim([0, h])
|
| 314 |
pdf.savefig(fig)
|
| 315 |
-
plt.close(fig)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
draw_combined_fish_graphs(pdf, json_result)
|
| 43 |
|
| 44 |
for i, fish in enumerate(json_result['fish']):
|
| 45 |
+
draw_fish_tracks(pdf, json_result, dataset, i)
|
| 46 |
|
| 47 |
# We can also set the file's metadata via the PdfPages object:
|
| 48 |
d = pdf.infodict()
|
|
|
|
| 166 |
fish = result['metadata']['FISH'][id]
|
| 167 |
start_frame = fish['START_FRAME']
|
| 168 |
end_frame = fish['END_FRAME']
|
| 169 |
+
fps = result['metadata']['FRAME_RATE']
|
| 170 |
|
| 171 |
# Extract base frame (first frame for that fish)
|
| 172 |
w, h = 1, 2
|
|
|
|
| 176 |
images = dataset.didson.load_frames(start_frame=start_frame, end_frame=start_frame+1)
|
| 177 |
img = images[0]
|
| 178 |
|
| 179 |
+
w, h = img.shape
|
| 180 |
+
|
| 181 |
frame_height = 2
|
| 182 |
scale_factor = frame_height / h
|
| 183 |
h = frame_height
|
| 184 |
w = int(scale_factor*w)
|
| 185 |
|
| 186 |
fish['base_frame'] = img
|
| 187 |
+
fish['scaled_frame_size'] = (h, w)
|
| 188 |
|
| 189 |
|
| 190 |
# Find frames for this fish
|
|
|
|
| 217 |
last_y = Y[-1]
|
| 218 |
dx = result['image_meter_width']*(last_x - x)/(missed+1)
|
| 219 |
dy = result['image_meter_height']*(last_y - y)/(missed+1)
|
| 220 |
+
v = math.sqrt(dx*dx + dy*dy) * fps
|
| 221 |
|
| 222 |
# Interpolate over missing frames
|
| 223 |
if missed > 0:
|
|
|
|
| 246 |
|
| 247 |
|
| 248 |
def draw_combined_fish_graphs(pdf, result):
|
| 249 |
+
|
| 250 |
vel = []
|
| 251 |
log_vel = []
|
| 252 |
+
eps = 0.00000000001
|
| 253 |
for fish in result['metadata']['FISH']:
|
| 254 |
+
for v in fish['path']['V']:
|
| 255 |
+
vel += [v]
|
| 256 |
+
if v > 0:
|
| 257 |
+
log_vel += [math.log(v)]
|
| 258 |
+
|
| 259 |
+
fig, axs = plt.subplots(2, 2, sharex=False, sharey=False, figsize=STANDARD_FIG_SIZE)
|
| 260 |
+
|
| 261 |
+
# Title
|
| 262 |
+
fig.suptitle('Fish velocities', fontsize=40, horizontalalignment='center', weight='bold')
|
| 263 |
|
|
|
|
| 264 |
axs[0,0].hist(log_vel, bins=20)
|
| 265 |
axs[0,0].set_title('Fish Log-Velocities between frames')
|
| 266 |
+
axs[0,0].set_xlabel("Log-Velocity (log(m/s))")
|
| 267 |
+
|
| 268 |
axs[0,1].hist(vel, bins=20)
|
| 269 |
axs[0,1].set_title('Fish Velocities between frames')
|
| 270 |
+
axs[0,1].set_xlabel("Velocity (m/s)")
|
| 271 |
+
|
| 272 |
+
for fish in result['metadata']['FISH']:
|
| 273 |
+
data = []
|
| 274 |
+
for v in fish['path']['V']:
|
| 275 |
+
if v > 0: data += [math.log(v)]
|
| 276 |
+
n, bin_c = make_hist(data)
|
| 277 |
+
axs[1,0].plot(bin_c, n)
|
| 278 |
+
axs[1,0].set_title('Fish Log-Velocities between frames (per fish)')
|
| 279 |
+
axs[1,0].set_xlabel("Log-Velocity (log(m/s))")
|
| 280 |
+
|
| 281 |
+
for fish in result['metadata']['FISH']:
|
| 282 |
+
data = fish['path']['V']
|
| 283 |
+
n, bin_c = make_hist(data)
|
| 284 |
+
axs[1,1].plot(bin_c, n)
|
| 285 |
+
axs[1,1].set_title('Fish Velocities between frames (per fish)')
|
| 286 |
+
axs[1,1].set_xlabel("Velocity (m/s)")
|
| 287 |
|
| 288 |
pdf.savefig(fig)
|
| 289 |
plt.close(fig)
|
| 290 |
|
| 291 |
|
| 292 |
+
def make_hist(data):
|
| 293 |
+
'''histogram and return vectors for plotting'''
|
| 294 |
+
|
| 295 |
+
# figure out the bins
|
| 296 |
+
min_bin = np.min(data)
|
| 297 |
+
max_bin = np.max(data)
|
| 298 |
+
PTS_PER_BIN = 6 #np.sqrt(len(data)) #300
|
| 299 |
+
bin_sz = (max_bin-min_bin)/(len(data)/PTS_PER_BIN)
|
| 300 |
+
bins = np.arange(min_bin-bin_sz,max_bin+2*bin_sz,bin_sz)
|
| 301 |
+
bin_centers = (bins[0:-1]+bins[1:])/2 # bin centers
|
| 302 |
+
|
| 303 |
+
# compute the histogram
|
| 304 |
+
n,b = np.histogram(data,bins=bins,density=False)
|
| 305 |
+
return n,bin_centers
|
| 306 |
+
|
| 307 |
+
def draw_fish_tracks(pdf, result, dataset, id):
|
| 308 |
|
| 309 |
fish = result['metadata']['FISH'][id]
|
| 310 |
start_frame = fish['START_FRAME']
|
| 311 |
end_frame = fish['END_FRAME']
|
| 312 |
|
| 313 |
+
print(fish)
|
| 314 |
+
|
| 315 |
fig, ax = plt.subplots(figsize=STANDARD_FIG_SIZE)
|
| 316 |
plt.axis('off')
|
| 317 |
|
|
|
|
| 322 |
plt.imshow(img, extent=(0, h, 0, w), cmap=plt.colormaps['Greys'])
|
| 323 |
|
| 324 |
# Title
|
| 325 |
+
plt.text(h/2,2,f'Fish {id+1} (frames {start_frame}-{end_frame})',fontsize=40, color="black", horizontalalignment='center', zorder=5)
|
| 326 |
|
| 327 |
X = fish['path']['X']
|
| 328 |
Y = fish['path']['Y']
|
|
|
|
| 347 |
colors.append(col)
|
| 348 |
ax.plot([h*(1-Y[i-1]), h*(1-Y[i])], [w*(1-X[i-1]), w*(1-X[i])], color=col, linewidth=1)
|
| 349 |
|
|
|
|
|
|
|
|
|
|
| 350 |
for i in range(1, len(X)):
|
| 351 |
ax.plot(h*(1-Y[i]), w*(1-X[i]), color=colors[i], marker='o', markersize=3)
|
| 352 |
|
|
|
|
| 354 |
plt.ylim([0, w])
|
| 355 |
plt.xlim([0, h])
|
| 356 |
pdf.savefig(fig)
|
| 357 |
+
plt.close(fig)
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
|
| 361 |
+
|
| 362 |
+
|
| 363 |
+
|
| 364 |
+
|
| 365 |
+
if (dataset is not None):
|
| 366 |
+
indices = [start_frame, int(2/3*start_frame + end_frame/3), int(1/3*start_frame + 2/3*end_frame), end_frame]
|
| 367 |
+
fig, axs = plt.subplots(2, len(indices), sharex=False, sharey=False, figsize=STANDARD_FIG_SIZE)
|
| 368 |
+
|
| 369 |
+
print("id", id)
|
| 370 |
+
print('indices', indices)
|
| 371 |
+
for i, frame_index in enumerate(indices):
|
| 372 |
+
img = dataset.didson.load_frames(start_frame=frame_index, end_frame=frame_index+1)[0]
|
| 373 |
+
box = None
|
| 374 |
+
for fi in range(frame_index, min(frame_index+10, len(result['frames']))):
|
| 375 |
+
for ann in result['frames'][fi]['fish']:
|
| 376 |
+
if ann['fish_id'] == id+1:
|
| 377 |
+
box = ann['bbox']
|
| 378 |
+
frame_index = fi
|
| 379 |
+
break
|
| 380 |
+
|
| 381 |
+
batch_i = math.floor(frame_index/32)
|
| 382 |
+
fi = frame_index - batch_i*32
|
| 383 |
+
batch = dataset[batch_i]
|
| 384 |
+
(rgb_img, _, shapes) = batch[fi]
|
| 385 |
+
rgb_img = rgb_img.permute(1, 2, 0)
|
| 386 |
+
print(type(batch))
|
| 387 |
+
print(type(rgb_img))
|
| 388 |
+
print(rgb_img.shape)
|
| 389 |
+
|
| 390 |
+
print("box", i, box)
|
| 391 |
+
if box is not None:
|
| 392 |
+
h, w = img.shape
|
| 393 |
+
print(w, h)
|
| 394 |
+
x1, x2, y1, y2 = int(box[0]*w), int(box[2]*w), int(box[1]*h), int(box[3]*h)
|
| 395 |
+
cx, cy = int((x2 + x1)/2), int((y2 + y1)/2)
|
| 396 |
+
s = min(int(max(x2 - x1, y2 - y1)*5/2), cx, cy, w-cx, h-cy)
|
| 397 |
+
print(x1, x2, y1, y2)
|
| 398 |
+
print(cx, cy, s)
|
| 399 |
+
cropped_img = img[cy-s:cy+s, cx-s:cx+s]
|
| 400 |
+
axs[0, i].imshow(cropped_img, extent=(cx-s, cx+s, cy-s, cy+s), cmap=plt.colormaps['Greys_r'])
|
| 401 |
+
axs[0, i].plot([x1, x1, x2, x2, x1], [y1, y2, y2, y1, y1], color="red")
|
| 402 |
+
axs[0, i].set_title('Frame ' + str(frame_index))
|
| 403 |
+
|
| 404 |
+
h, w, _ = rgb_img.shape
|
| 405 |
+
print(w, h)
|
| 406 |
+
x1, x2, y1, y2 = int(box[0]*w), int(box[2]*w), int(box[1]*h), int(box[3]*h)
|
| 407 |
+
cx, cy = int((x2 + x1)/2), int((y2 + y1)/2)
|
| 408 |
+
s = min(int(max(x2 - x1, y2 - y1)*5/2), cx, cy, w-cx, h-cy)
|
| 409 |
+
print(x1, x2, y1, y2)
|
| 410 |
+
print(cx, cy, s)
|
| 411 |
+
cropped_img = rgb_img[cy-s:cy+s, cx-s:cx+s, :]
|
| 412 |
+
axs[1, i].imshow(cropped_img, extent=(cx-s, cx+s, cy-s, cy+s), cmap=plt.colormaps['Greys_r'])
|
| 413 |
+
axs[1, i].plot([x1, x1, x2, x2, x1], [y1, y2, y2, y1, y1], color="red")
|
| 414 |
+
axs[1, i].set_title('Frame ' + str(frame_index))
|
| 415 |
+
|
| 416 |
+
pdf.savefig(fig)
|
| 417 |
+
plt.close(fig)
|
gradio_scripts/result_ui.py
CHANGED
|
@@ -41,8 +41,10 @@ def update_result(i, state, result, inference_handler):
|
|
| 41 |
|
| 42 |
annotation_avaliable = not (result["aris_input"][i] == None)
|
| 43 |
|
| 44 |
-
if '
|
|
|
|
| 45 |
make_pdf(state['index']-1, state, result, table_headers)
|
|
|
|
| 46 |
|
| 47 |
# Check if files exist
|
| 48 |
video_path = result["path_video"][i]
|
|
@@ -78,9 +80,7 @@ def Result_Gradio(prepare_annotation, components):
|
|
| 78 |
components['tab_labeler'] = gr.Text(value="", visible=False, elem_id="tab_labeler")
|
| 79 |
components['tab_labeler'].change(lambda x: x, None, None, _js=js_update_tab_labels)
|
| 80 |
|
| 81 |
-
|
| 82 |
-
components['cancelBtn'] = gr.Button("Cancel Inference", visible=False)
|
| 83 |
-
components['skipBtn'] = gr.Button("Skip this file", visible=False)
|
| 84 |
|
| 85 |
# List of all UI components that will recieve outputs from the result_handler
|
| 86 |
visual_components = []
|
|
|
|
| 41 |
|
| 42 |
annotation_avaliable = not (result["aris_input"][i] == None)
|
| 43 |
|
| 44 |
+
if 'PDF' in state['outputs']:
|
| 45 |
+
print("making pdf")
|
| 46 |
make_pdf(state['index']-1, state, result, table_headers)
|
| 47 |
+
print("done pdf")
|
| 48 |
|
| 49 |
# Check if files exist
|
| 50 |
video_path = result["path_video"][i]
|
|
|
|
| 80 |
components['tab_labeler'] = gr.Text(value="", visible=False, elem_id="tab_labeler")
|
| 81 |
components['tab_labeler'].change(lambda x: x, None, None, _js=js_update_tab_labels)
|
| 82 |
|
| 83 |
+
components['cancel_btn'] = gr.Button("Cancel Inference", visible=False)
|
|
|
|
|
|
|
| 84 |
|
| 85 |
# List of all UI components that will recieve outputs from the result_handler
|
| 86 |
visual_components = []
|
gradio_scripts/upload_ui.py
CHANGED
|
@@ -19,43 +19,43 @@ def Upload_Gradio(gradio_components):
|
|
| 19 |
gr.HTML("<p align='center' style='font-size: large;font-style: italic;'>Submit an .aris file to analyze result.</p>")
|
| 20 |
|
| 21 |
default_settings = InferenceConfig()
|
| 22 |
-
|
| 23 |
with gr.Accordion("Advanced Settings", open=False):
|
| 24 |
default_model = default_settings.find_model(models)
|
| 25 |
-
|
| 26 |
|
| 27 |
gr.Markdown("Detection Parameters")
|
| 28 |
with gr.Row():
|
| 29 |
-
|
| 30 |
-
|
| 31 |
|
| 32 |
gr.Markdown("Tracking Parameters")
|
| 33 |
with gr.Row():
|
| 34 |
-
|
| 35 |
-
|
| 36 |
|
| 37 |
default_tracker = TrackerType.toString(default_settings.associative_tracker)
|
| 38 |
tracker = gr.Dropdown(["None", "Confidence Boost", "ByteTrack"], value=default_tracker, label="Associative Tracking")
|
| 39 |
-
|
| 40 |
with gr.Row(visible=default_tracker=="Confidence Boost") as track_row:
|
| 41 |
-
|
| 42 |
-
|
| 43 |
tracker.change(lambda x: gr.update(visible=(x=="Confidence Boost")), tracker, track_row)
|
| 44 |
with gr.Row(visible=default_tracker=="ByteTrack") as track_row:
|
| 45 |
-
|
| 46 |
-
|
| 47 |
tracker.change(lambda x: gr.update(visible=(x=="ByteTrack")), tracker, track_row)
|
| 48 |
|
| 49 |
gr.Markdown("Other")
|
| 50 |
with gr.Row():
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
|
| 55 |
-
gradio_components['hyperparams'] =
|
| 56 |
|
| 57 |
with gr.Row():
|
| 58 |
-
|
| 59 |
|
| 60 |
#Input field for aris submission
|
| 61 |
gradio_components['input'] = File(file_types=[".aris", ".ddf"], type="binary", label="ARIS Input", file_count="multiple")
|
|
@@ -74,4 +74,4 @@ def Upload_Gradio(gradio_components):
|
|
| 74 |
gradio_components['result_aris_input'] = File(file_types=[".aris", ".ddf"], type="binary", label="Upload aris file (optional)", file_count="multiple")
|
| 75 |
|
| 76 |
# Button for initializing review
|
| 77 |
-
gradio_components['
|
|
|
|
| 19 |
gr.HTML("<p align='center' style='font-size: large;font-style: italic;'>Submit an .aris file to analyze result.</p>")
|
| 20 |
|
| 21 |
default_settings = InferenceConfig()
|
| 22 |
+
hyperparams = []
|
| 23 |
with gr.Accordion("Advanced Settings", open=False):
|
| 24 |
default_model = default_settings.find_model(models)
|
| 25 |
+
hyperparams.append(gr.Dropdown(label="Model", value=default_model, choices=list(models.keys())))
|
| 26 |
|
| 27 |
gr.Markdown("Detection Parameters")
|
| 28 |
with gr.Row():
|
| 29 |
+
hyperparams.append(gr.Slider(0, 1, value=default_settings.conf_thresh, label="Confidence Threshold", info="Confidence cutoff for detection boxes"))
|
| 30 |
+
hyperparams.append(gr.Slider(0, 1, value=default_settings.nms_iou, label="NMS IoU", info="IoU threshold for non-max suppression"))
|
| 31 |
|
| 32 |
gr.Markdown("Tracking Parameters")
|
| 33 |
with gr.Row():
|
| 34 |
+
hyperparams.append(gr.Slider(0, 100, value=default_settings.min_hits, label="Min Hits", info="Minimum number of frames a fish has to appear in to count"))
|
| 35 |
+
hyperparams.append(gr.Slider(0, 100, value=default_settings.max_age, label="Max Age", info="Max age of occlusion before track is split"))
|
| 36 |
|
| 37 |
default_tracker = TrackerType.toString(default_settings.associative_tracker)
|
| 38 |
tracker = gr.Dropdown(["None", "Confidence Boost", "ByteTrack"], value=default_tracker, label="Associative Tracking")
|
| 39 |
+
hyperparams.append(tracker)
|
| 40 |
with gr.Row(visible=default_tracker=="Confidence Boost") as track_row:
|
| 41 |
+
hyperparams.append(gr.Slider(0, 5, value=default_settings.boost_power, label="Boost Power", info=""))
|
| 42 |
+
hyperparams.append(gr.Slider(0, 1, value=default_settings.boost_decay, label="Boost Decay", info=""))
|
| 43 |
tracker.change(lambda x: gr.update(visible=(x=="Confidence Boost")), tracker, track_row)
|
| 44 |
with gr.Row(visible=default_tracker=="ByteTrack") as track_row:
|
| 45 |
+
hyperparams.append(gr.Slider(0, 1, value=default_settings.byte_low_conf, label="Low Conf Threshold", info=""))
|
| 46 |
+
hyperparams.append(gr.Slider(0, 1, value=default_settings.byte_high_conf, label="High Conf Threshold", info=""))
|
| 47 |
tracker.change(lambda x: gr.update(visible=(x=="ByteTrack")), tracker, track_row)
|
| 48 |
|
| 49 |
gr.Markdown("Other")
|
| 50 |
with gr.Row():
|
| 51 |
+
hyperparams.append(gr.Slider(0, 3, value=default_settings.min_length, label="Min Length", info="Minimum length of fish (meters) in order for it to count"))
|
| 52 |
+
hyperparams.append(gr.Slider(0, 3, value=default_settings.max_length, label="Max Length", info="Maximum length of fish (meters) in order for it to count"))
|
| 53 |
+
hyperparams.append(gr.Slider(0, 10, value=default_settings.min_travel, label="Min Travel", info="Minimum travel distance of track (meters) in order for it to count"))
|
| 54 |
|
| 55 |
+
gradio_components['hyperparams'] = hyperparams
|
| 56 |
|
| 57 |
with gr.Row():
|
| 58 |
+
hyperparams.append(gr.CheckboxGroup(["Annotated Video", "Manual Marking", "PDF"], label="Output formats", interactive=True, value=["Annotated Video", "Manual Marking"]))
|
| 59 |
|
| 60 |
#Input field for aris submission
|
| 61 |
gradio_components['input'] = File(file_types=[".aris", ".ddf"], type="binary", label="ARIS Input", file_count="multiple")
|
|
|
|
| 74 |
gradio_components['result_aris_input'] = File(file_types=[".aris", ".ddf"], type="binary", label="Upload aris file (optional)", file_count="multiple")
|
| 75 |
|
| 76 |
# Button for initializing review
|
| 77 |
+
gradio_components['open_result_btn'] = gr.Button("View Result")
|
inference.py
CHANGED
|
@@ -19,7 +19,7 @@ import torchvision
|
|
| 19 |
|
| 20 |
from InferenceConfig import InferenceConfig, TrackerType
|
| 21 |
from lib.fish_eye.tracker import Tracker
|
| 22 |
-
from lib.fish_eye.
|
| 23 |
|
| 24 |
|
| 25 |
### Configuration options
|
|
@@ -458,7 +458,11 @@ def non_max_suppression(
|
|
| 458 |
# width filter
|
| 459 |
pix2width = image_meter_width/image_pixel_width
|
| 460 |
width = prediction[..., 2]*pix2width
|
| 461 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 462 |
|
| 463 |
|
| 464 |
# Settings
|
|
|
|
| 19 |
|
| 20 |
from InferenceConfig import InferenceConfig, TrackerType
|
| 21 |
from lib.fish_eye.tracker import Tracker
|
| 22 |
+
from lib.fish_eye.bytetrack import Associate
|
| 23 |
|
| 24 |
|
| 25 |
### Configuration options
|
|
|
|
| 458 |
# width filter
|
| 459 |
pix2width = image_meter_width/image_pixel_width
|
| 460 |
width = prediction[..., 2]*pix2width
|
| 461 |
+
if max_length > 0:
|
| 462 |
+
wc = width < max_length
|
| 463 |
+
else:
|
| 464 |
+
# If max_length is 0, ignore
|
| 465 |
+
wc = width > max_length
|
| 466 |
|
| 467 |
|
| 468 |
# Settings
|
lib/fish_eye/{associative.py → bytetrack.py}
RENAMED
|
File without changes
|
lib/fish_eye/tracker.py
CHANGED
|
@@ -6,7 +6,7 @@ import numpy as np
|
|
| 6 |
|
| 7 |
from fish_length import Fish_Length
|
| 8 |
from lib.fish_eye.sort import Sort
|
| 9 |
-
from lib.fish_eye.
|
| 10 |
import lib
|
| 11 |
|
| 12 |
class Tracker:
|
|
@@ -36,7 +36,7 @@ class Tracker:
|
|
| 36 |
if (score < min_score):
|
| 37 |
min_score = score
|
| 38 |
conf = det[4]
|
| 39 |
-
elif type(self.algorithm) == lib.fish_eye.
|
| 40 |
for det in dets[0]:
|
| 41 |
score = sum(abs(det[0:4] - track[0:4]))
|
| 42 |
if (score < min_score):
|
|
|
|
| 6 |
|
| 7 |
from fish_length import Fish_Length
|
| 8 |
from lib.fish_eye.sort import Sort
|
| 9 |
+
from lib.fish_eye.bytetrack import Associate
|
| 10 |
import lib
|
| 11 |
|
| 12 |
class Tracker:
|
|
|
|
| 36 |
if (score < min_score):
|
| 37 |
min_score = score
|
| 38 |
conf = det[4]
|
| 39 |
+
elif type(self.algorithm) == lib.fish_eye.bytetrack.Associate:
|
| 40 |
for det in dets[0]:
|
| 41 |
score = sum(abs(det[0:4] - track[0:4]))
|
| 42 |
if (score < min_score):
|
multipage_pdf.pdf
CHANGED
|
Binary files a/multipage_pdf.pdf and b/multipage_pdf.pdf differ
|
|
|