Align DLC outputs with filtered detections
Browse filesCarry the filtered MegaDetector bounding boxes through cropping, visualization, and JSON export so pose results stay matched to the correct animal detections. This also removes the obsolete save_results module and hardens DLC inference by handling empty crop lists, converting crop channel order for DLCLive, and masking low-confidence keypoints with NaN.
- app.py +15 -17
- detection_utils.py +4 -2
- dlc_utils.py +11 -9
- save_results.py +0 -56
- viz_utils.py +7 -16
app.py
CHANGED
|
@@ -70,10 +70,10 @@ def predict_pipeline(img_input,
|
|
| 70 |
size=640) #Image.fromarray(results.imgs[0])
|
| 71 |
|
| 72 |
################################################################
|
| 73 |
-
# Obtain animal crops
|
| 74 |
-
list_crops = crop_animal_detections(img_input,
|
| 75 |
-
|
| 76 |
-
|
| 77 |
|
| 78 |
############################################################
|
| 79 |
|
|
@@ -135,8 +135,9 @@ def predict_pipeline(img_input,
|
|
| 135 |
md_results.ims[0].shape[0]))
|
| 136 |
|
| 137 |
# draw keypoints on each crop and paste to background img
|
| 138 |
-
for
|
| 139 |
-
|
|
|
|
| 140 |
|
| 141 |
img_crop = Image.fromarray(np_crop)
|
| 142 |
|
|
@@ -152,20 +153,17 @@ def predict_pipeline(img_input,
|
|
| 152 |
marker_size=marker_size)
|
| 153 |
|
| 154 |
# Paste crop in original image
|
| 155 |
-
img_background.paste(img_crop,
|
| 156 |
-
box = tuple([int(t) for t in
|
| 157 |
|
| 158 |
# Plot bbox
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
font_size=font_size) # TODO: add selectable color for bbox?
|
| 165 |
-
|
| 166 |
-
|
| 167 |
# Save detection results as json
|
| 168 |
-
download_file = save_results_as_json(md_results,list_kpts_per_crop,map_label_id_to_str,
|
| 169 |
|
| 170 |
return img_background, download_file
|
| 171 |
|
|
|
|
| 70 |
size=640) #Image.fromarray(results.imgs[0])
|
| 71 |
|
| 72 |
################################################################
|
| 73 |
+
# Obtain animal crops (and their bboxes) with confidence above th
|
| 74 |
+
list_crops, list_bboxes = crop_animal_detections(img_input,
|
| 75 |
+
md_results,
|
| 76 |
+
bbox_likelihood_th)
|
| 77 |
|
| 78 |
############################################################
|
| 79 |
|
|
|
|
| 135 |
md_results.ims[0].shape[0]))
|
| 136 |
|
| 137 |
# draw keypoints on each crop and paste to background img
|
| 138 |
+
for np_crop, kpts_crop, bb_per_animal in zip(list_crops,
|
| 139 |
+
list_kpts_per_crop,
|
| 140 |
+
list_bboxes):
|
| 141 |
|
| 142 |
img_crop = Image.fromarray(np_crop)
|
| 143 |
|
|
|
|
| 153 |
marker_size=marker_size)
|
| 154 |
|
| 155 |
# Paste crop in original image
|
| 156 |
+
img_background.paste(img_crop,
|
| 157 |
+
box = tuple([int(t) for t in bb_per_animal[:2]]))
|
| 158 |
|
| 159 |
# Plot bbox
|
| 160 |
+
draw_bbox_w_text(img_background,
|
| 161 |
+
bb_per_animal,
|
| 162 |
+
font_size=font_size) # TODO: add selectable color for bbox?
|
| 163 |
+
|
| 164 |
+
|
|
|
|
|
|
|
|
|
|
| 165 |
# Save detection results as json
|
| 166 |
+
download_file = save_results_as_json(md_results,list_kpts_per_crop,list_bboxes,map_label_id_to_str,dlc_model_input_str,mega_model_input)
|
| 167 |
|
| 168 |
return img_background, download_file
|
| 169 |
|
detection_utils.py
CHANGED
|
@@ -56,8 +56,9 @@ def crop_animal_detections(img_in,
|
|
| 56 |
likelihood_th):
|
| 57 |
|
| 58 |
## Extract animal crops
|
| 59 |
-
list_labels_as_str = [i for i in yolo_results.names.values()] # ['animal', 'person', 'vehicle']
|
| 60 |
list_np_animal_crops = []
|
|
|
|
| 61 |
|
| 62 |
# image to crop (scale as input for megadetector)
|
| 63 |
img_in = img_in.resize((yolo_results.ims[0].shape[1],
|
|
@@ -88,5 +89,6 @@ def crop_animal_detections(img_in,
|
|
| 88 |
|
| 89 |
# add to list
|
| 90 |
list_np_animal_crops.append(crop_np)
|
|
|
|
| 91 |
|
| 92 |
-
return list_np_animal_crops
|
|
|
|
| 56 |
likelihood_th):
|
| 57 |
|
| 58 |
## Extract animal crops
|
| 59 |
+
list_labels_as_str = [i for i in yolo_results.names.values()] # ['animal', 'person', 'vehicle']
|
| 60 |
list_np_animal_crops = []
|
| 61 |
+
list_animal_bboxes = [] # detection rows [x1,y1,x2,y2,conf,label] matching each crop
|
| 62 |
|
| 63 |
# image to crop (scale as input for megadetector)
|
| 64 |
img_in = img_in.resize((yolo_results.ims[0].shape[1],
|
|
|
|
| 89 |
|
| 90 |
# add to list
|
| 91 |
list_np_animal_crops.append(crop_np)
|
| 92 |
+
list_animal_bboxes.append(det_array[j,:].tolist())
|
| 93 |
|
| 94 |
+
return list_np_animal_crops, list_animal_bboxes
|
dlc_utils.py
CHANGED
|
@@ -11,22 +11,24 @@ def predict_dlc(list_np_crops,
|
|
| 11 |
dlc_model_folder,
|
| 12 |
dlc_proc):
|
| 13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
# run dlc thru list of crops
|
| 15 |
dlc_live = DLCLive(dlc_model_folder, processor=dlc_proc)
|
| 16 |
dlc_live.init_inference(list_np_crops[0])
|
| 17 |
|
| 18 |
list_kpts_per_crop = []
|
| 19 |
-
all_kypts = []
|
| 20 |
-
np_aux = np.empty((1,3)) # can I avoid hardcoding here?
|
| 21 |
for crop in list_np_crops:
|
| 22 |
-
# scale crop here?
|
| 23 |
keypts_xyp = dlc_live.get_pose(crop) # third column is llk!
|
| 24 |
# set kpts below threhsold to nan
|
| 25 |
-
|
| 26 |
-
#
|
| 27 |
-
keypts_xyp[keypts_xyp[:,-1] < kpts_likelihood_th,:] = np_aux.fill(np.nan)
|
| 28 |
-
# add kpts of this crop to list
|
| 29 |
list_kpts_per_crop.append(keypts_xyp)
|
| 30 |
-
|
| 31 |
-
|
| 32 |
return list_kpts_per_crop
|
|
|
|
| 11 |
dlc_model_folder,
|
| 12 |
dlc_proc):
|
| 13 |
|
| 14 |
+
# no animal detected: nothing to run
|
| 15 |
+
if len(list_np_crops) == 0:
|
| 16 |
+
return []
|
| 17 |
+
|
| 18 |
+
# DLCLive always converts 3-channel frames BGR->RGB, but our crops are
|
| 19 |
+
# already RGB (PIL), so pass them as BGR for the model to see RGB
|
| 20 |
+
list_np_crops = [np.ascontiguousarray(crop[..., ::-1]) for crop in list_np_crops]
|
| 21 |
+
|
| 22 |
# run dlc thru list of crops
|
| 23 |
dlc_live = DLCLive(dlc_model_folder, processor=dlc_proc)
|
| 24 |
dlc_live.init_inference(list_np_crops[0])
|
| 25 |
|
| 26 |
list_kpts_per_crop = []
|
|
|
|
|
|
|
| 27 |
for crop in list_np_crops:
|
|
|
|
| 28 |
keypts_xyp = dlc_live.get_pose(crop) # third column is llk!
|
| 29 |
# set kpts below threhsold to nan
|
| 30 |
+
keypts_xyp[keypts_xyp[:,-1] < kpts_likelihood_th,:] = np.nan
|
| 31 |
+
# add kpts of this crop to list
|
|
|
|
|
|
|
| 32 |
list_kpts_per_crop.append(keypts_xyp)
|
| 33 |
+
|
|
|
|
| 34 |
return list_kpts_per_crop
|
save_results.py
DELETED
|
@@ -1,56 +0,0 @@
|
|
| 1 |
-
import json
|
| 2 |
-
import numpy as np
|
| 3 |
-
import pdb
|
| 4 |
-
|
| 5 |
-
dict_pred = {0: 'animal', 1: 'person', 2: 'vehicle'}
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
def save_results(md_results, dlc_outputs,map_label_id_to_str,thr,output_file = 'dowload_predictions.json'):
|
| 9 |
-
|
| 10 |
-
"""
|
| 11 |
-
|
| 12 |
-
write json
|
| 13 |
-
|
| 14 |
-
"""
|
| 15 |
-
info = {}
|
| 16 |
-
## info megaDetector
|
| 17 |
-
info['file']= md_results.files[0]
|
| 18 |
-
number_bb = len(md_results.xyxy[0].tolist())
|
| 19 |
-
info['number_of_bb'] = number_bb
|
| 20 |
-
number_bb_thr = len(dlc_outputs)
|
| 21 |
-
labels = [n for n in map_label_id_to_str.values()]
|
| 22 |
-
#pdb.set_trace()
|
| 23 |
-
new_index = []
|
| 24 |
-
for i in range(number_bb):
|
| 25 |
-
corner_x1,corner_y1,corner_x2,corner_y2,confidence, _ = md_results.xyxy[0].tolist()[i]
|
| 26 |
-
|
| 27 |
-
if confidence > thr:
|
| 28 |
-
new_index.append(i)
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
for i in range(number_bb_thr):
|
| 32 |
-
aux={}
|
| 33 |
-
corner_x1,corner_y1,corner_x2,corner_y2,confidence, _ = md_results.xyxy[0].tolist()[new_index[i]]
|
| 34 |
-
aux['corner_1'] = (corner_x1,corner_y1)
|
| 35 |
-
aux['corner_2'] = (corner_x2,corner_y2)
|
| 36 |
-
aux['predict MD'] = md_results.names[0]
|
| 37 |
-
aux['confidence MD'] = confidence
|
| 38 |
-
|
| 39 |
-
## info dlc
|
| 40 |
-
kypts = []
|
| 41 |
-
for s in dlc_outputs[i]:
|
| 42 |
-
aux1 = []
|
| 43 |
-
for j in s:
|
| 44 |
-
aux1.append(float(j))
|
| 45 |
-
|
| 46 |
-
kypts.append(aux1)
|
| 47 |
-
aux['dlc_pred'] = dict(zip(labels,kypts))
|
| 48 |
-
info['bb_' + str(new_index[i]) ]=aux
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
with open(output_file, 'w') as f:
|
| 52 |
-
json.dump(info, f, indent=1)
|
| 53 |
-
print('Output file saved at {}'.format(output_file))
|
| 54 |
-
|
| 55 |
-
return output_file
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
viz_utils.py
CHANGED
|
@@ -62,7 +62,6 @@ def draw_keypoints_on_image(image,
|
|
| 62 |
keypoints_y = tuple([im_height * y for y in keypoints_y])
|
| 63 |
|
| 64 |
#cmap = matplotlib.cm.get_cmap('hsv')
|
| 65 |
-
cmap2 = matplotlib.cm.get_cmap('Greys')
|
| 66 |
# draw ellipses around keypoints
|
| 67 |
for i, (keypoint_x, keypoint_y) in enumerate(zip(keypoints_x, keypoints_y)):
|
| 68 |
round_fill = list(cm.viridis(norm(colores[i]),bytes=True))#[round(num*255) for num in list(cmap(i))[:3]] #check!
|
|
@@ -113,11 +112,12 @@ def draw_bbox_w_text(img,
|
|
| 113 |
return imgR
|
| 114 |
|
| 115 |
###########################################
|
| 116 |
-
def save_results_as_json(md_results, dlc_outputs,
|
| 117 |
|
| 118 |
"""
|
| 119 |
Output detections as json file
|
| 120 |
|
|
|
|
| 121 |
"""
|
| 122 |
# initialise dict to save to json
|
| 123 |
info = {}
|
|
@@ -128,29 +128,20 @@ def save_results_as_json(md_results, dlc_outputs, map_dlc_label_id_to_str, thr,m
|
|
| 128 |
number_bb = len(md_results.xyxy[0].tolist())
|
| 129 |
info['number_of_bb'] = number_bb
|
| 130 |
# info from DLC
|
| 131 |
-
|
| 132 |
labels = [n for n in map_dlc_label_id_to_str.values()]
|
| 133 |
-
|
| 134 |
-
# create list of bboxes above th
|
| 135 |
-
new_index = []
|
| 136 |
-
for i in range(number_bb):
|
| 137 |
-
corner_x1,corner_y1,corner_x2,corner_y2,confidence, _ = md_results.xyxy[0].tolist()[i]
|
| 138 |
-
|
| 139 |
-
if confidence > thr:
|
| 140 |
-
new_index.append(i)
|
| 141 |
|
| 142 |
-
# define aux dict for every bounding box above threshold
|
| 143 |
-
for i in range(
|
| 144 |
aux={}
|
| 145 |
# MD output
|
| 146 |
-
corner_x1,corner_y1,corner_x2,corner_y2,confidence, _ =
|
| 147 |
aux['corner_1'] = (corner_x1,corner_y1)
|
| 148 |
aux['corner_2'] = (corner_x2,corner_y2)
|
| 149 |
aux['predict MD'] = md_results.names[0]
|
| 150 |
aux['confidence MD'] = confidence
|
| 151 |
|
| 152 |
# DLC output
|
| 153 |
-
info['dlc_model'] = model
|
| 154 |
kypts = []
|
| 155 |
for s in dlc_outputs[i]:
|
| 156 |
aux1 = []
|
|
@@ -159,7 +150,7 @@ def save_results_as_json(md_results, dlc_outputs, map_dlc_label_id_to_str, thr,m
|
|
| 159 |
|
| 160 |
kypts.append(aux1)
|
| 161 |
aux['dlc_pred'] = dict(zip(labels,kypts))
|
| 162 |
-
info['bb_' + str(
|
| 163 |
|
| 164 |
# save dict as json
|
| 165 |
with open(path_to_output_file, 'w') as f:
|
|
|
|
| 62 |
keypoints_y = tuple([im_height * y for y in keypoints_y])
|
| 63 |
|
| 64 |
#cmap = matplotlib.cm.get_cmap('hsv')
|
|
|
|
| 65 |
# draw ellipses around keypoints
|
| 66 |
for i, (keypoint_x, keypoint_y) in enumerate(zip(keypoints_x, keypoints_y)):
|
| 67 |
round_fill = list(cm.viridis(norm(colores[i]),bytes=True))#[round(num*255) for num in list(cmap(i))[:3]] #check!
|
|
|
|
| 112 |
return imgR
|
| 113 |
|
| 114 |
###########################################
|
| 115 |
+
def save_results_as_json(md_results, dlc_outputs, animal_bboxes, map_dlc_label_id_to_str, model,mega_model_input, path_to_output_file = 'download_predictions.json'):
|
| 116 |
|
| 117 |
"""
|
| 118 |
Output detections as json file
|
| 119 |
|
| 120 |
+
animal_bboxes: detection rows [x1,y1,x2,y2,conf,label], one per entry of dlc_outputs
|
| 121 |
"""
|
| 122 |
# initialise dict to save to json
|
| 123 |
info = {}
|
|
|
|
| 128 |
number_bb = len(md_results.xyxy[0].tolist())
|
| 129 |
info['number_of_bb'] = number_bb
|
| 130 |
# info from DLC
|
| 131 |
+
info['dlc_model'] = model
|
| 132 |
labels = [n for n in map_dlc_label_id_to_str.values()]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
|
| 134 |
+
# define aux dict for every animal bounding box above threshold
|
| 135 |
+
for i in range(len(dlc_outputs)):
|
| 136 |
aux={}
|
| 137 |
# MD output
|
| 138 |
+
corner_x1,corner_y1,corner_x2,corner_y2,confidence, _ = animal_bboxes[i]
|
| 139 |
aux['corner_1'] = (corner_x1,corner_y1)
|
| 140 |
aux['corner_2'] = (corner_x2,corner_y2)
|
| 141 |
aux['predict MD'] = md_results.names[0]
|
| 142 |
aux['confidence MD'] = confidence
|
| 143 |
|
| 144 |
# DLC output
|
|
|
|
| 145 |
kypts = []
|
| 146 |
for s in dlc_outputs[i]:
|
| 147 |
aux1 = []
|
|
|
|
| 150 |
|
| 151 |
kypts.append(aux1)
|
| 152 |
aux['dlc_pred'] = dict(zip(labels,kypts))
|
| 153 |
+
info['bb_' + str(i) ]=aux
|
| 154 |
|
| 155 |
# save dict as json
|
| 156 |
with open(path_to_output_file, 'w') as f:
|