megadetector 5.0.6__py3-none-any.whl → 5.0.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of megadetector might be problematic. Click here for more details.
- api/batch_processing/data_preparation/manage_local_batch.py +297 -202
- api/batch_processing/data_preparation/manage_video_batch.py +7 -2
- api/batch_processing/postprocessing/add_max_conf.py +1 -0
- api/batch_processing/postprocessing/combine_api_outputs.py +2 -2
- api/batch_processing/postprocessing/compare_batch_results.py +111 -61
- api/batch_processing/postprocessing/convert_output_format.py +24 -6
- api/batch_processing/postprocessing/load_api_results.py +56 -72
- api/batch_processing/postprocessing/md_to_labelme.py +119 -51
- api/batch_processing/postprocessing/merge_detections.py +30 -5
- api/batch_processing/postprocessing/postprocess_batch_results.py +175 -55
- api/batch_processing/postprocessing/remap_detection_categories.py +163 -0
- api/batch_processing/postprocessing/render_detection_confusion_matrix.py +628 -0
- api/batch_processing/postprocessing/repeat_detection_elimination/find_repeat_detections.py +71 -23
- api/batch_processing/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +1 -1
- api/batch_processing/postprocessing/repeat_detection_elimination/repeat_detections_core.py +224 -76
- api/batch_processing/postprocessing/subset_json_detector_output.py +132 -5
- api/batch_processing/postprocessing/top_folders_to_bottom.py +1 -1
- classification/prepare_classification_script.py +191 -191
- data_management/cct_json_utils.py +7 -2
- data_management/coco_to_labelme.py +263 -0
- data_management/coco_to_yolo.py +72 -48
- data_management/databases/integrity_check_json_db.py +75 -64
- data_management/databases/subset_json_db.py +1 -1
- data_management/generate_crops_from_cct.py +1 -1
- data_management/get_image_sizes.py +44 -26
- data_management/importers/animl_results_to_md_results.py +3 -5
- data_management/importers/noaa_seals_2019.py +2 -2
- data_management/importers/zamba_results_to_md_results.py +2 -2
- data_management/labelme_to_coco.py +264 -127
- data_management/labelme_to_yolo.py +96 -53
- data_management/lila/create_lila_blank_set.py +557 -0
- data_management/lila/create_lila_test_set.py +2 -1
- data_management/lila/create_links_to_md_results_files.py +1 -1
- data_management/lila/download_lila_subset.py +138 -45
- data_management/lila/generate_lila_per_image_labels.py +23 -14
- data_management/lila/get_lila_annotation_counts.py +16 -10
- data_management/lila/lila_common.py +15 -42
- data_management/lila/test_lila_metadata_urls.py +116 -0
- data_management/read_exif.py +65 -16
- data_management/remap_coco_categories.py +84 -0
- data_management/resize_coco_dataset.py +14 -31
- data_management/wi_download_csv_to_coco.py +239 -0
- data_management/yolo_output_to_md_output.py +40 -13
- data_management/yolo_to_coco.py +313 -100
- detection/process_video.py +36 -14
- detection/pytorch_detector.py +1 -1
- detection/run_detector.py +73 -18
- detection/run_detector_batch.py +116 -27
- detection/run_inference_with_yolov5_val.py +135 -27
- detection/run_tiled_inference.py +153 -43
- detection/tf_detector.py +2 -1
- detection/video_utils.py +4 -2
- md_utils/ct_utils.py +101 -6
- md_utils/md_tests.py +264 -17
- md_utils/path_utils.py +326 -47
- md_utils/process_utils.py +26 -7
- md_utils/split_locations_into_train_val.py +215 -0
- md_utils/string_utils.py +10 -0
- md_utils/url_utils.py +66 -3
- md_utils/write_html_image_list.py +12 -2
- md_visualization/visualization_utils.py +380 -74
- md_visualization/visualize_db.py +41 -10
- md_visualization/visualize_detector_output.py +185 -104
- {megadetector-5.0.6.dist-info → megadetector-5.0.8.dist-info}/METADATA +11 -13
- {megadetector-5.0.6.dist-info → megadetector-5.0.8.dist-info}/RECORD +74 -67
- {megadetector-5.0.6.dist-info → megadetector-5.0.8.dist-info}/WHEEL +1 -1
- taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +1 -1
- taxonomy_mapping/map_new_lila_datasets.py +43 -39
- taxonomy_mapping/prepare_lila_taxonomy_release.py +5 -2
- taxonomy_mapping/preview_lila_taxonomy.py +27 -27
- taxonomy_mapping/species_lookup.py +33 -13
- taxonomy_mapping/taxonomy_csv_checker.py +7 -5
- md_visualization/visualize_megadb.py +0 -183
- {megadetector-5.0.6.dist-info → megadetector-5.0.8.dist-info}/LICENSE +0 -0
- {megadetector-5.0.6.dist-info → megadetector-5.0.8.dist-info}/top_level.txt +0 -0
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
import os
|
|
12
12
|
import json
|
|
13
13
|
|
|
14
|
+
from multiprocessing.pool import Pool, ThreadPool
|
|
15
|
+
from functools import partial
|
|
16
|
+
|
|
14
17
|
from md_utils.path_utils import recursive_file_list
|
|
15
18
|
from tqdm import tqdm
|
|
16
19
|
|
|
@@ -21,22 +24,21 @@ def labelme_file_to_yolo_file(labelme_file,
|
|
|
21
24
|
category_name_to_category_id,
|
|
22
25
|
yolo_file=None,
|
|
23
26
|
required_token=None,
|
|
24
|
-
right_edge_quantization_threshold=None,
|
|
25
27
|
overwrite_behavior='overwrite'):
|
|
26
28
|
"""
|
|
27
29
|
Convert the single .json file labelme_file to yolo format, writing the results to the text
|
|
28
30
|
file yolo_file (defaults to s/json/txt).
|
|
29
31
|
|
|
30
|
-
If required_token is not None and the labelme_file does not contain the key [required_token],
|
|
31
|
-
no-ops.
|
|
32
|
+
If required_token is not None and the dict in labelme_file does not contain the key [required_token],
|
|
33
|
+
this function no-ops (i.e., does not generate a YOLO file).
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
boxes that really should be running off the right side of the image only extend like 99%
|
|
35
|
-
of the way there, due to what appears to be a slight bias inherent to MD. If a box extends
|
|
36
|
-
within [right_edge_quantization_threshold] (a small number, from 0 to 1, but probably around
|
|
37
|
-
0.02) of the right edge of the image, it will be extended to the far right edge.
|
|
35
|
+
overwrite_behavior should be 'skip' or 'overwrite' (default).
|
|
38
36
|
"""
|
|
39
37
|
|
|
38
|
+
result = {}
|
|
39
|
+
result['labelme_file'] = labelme_file
|
|
40
|
+
result['status'] = 'unknown'
|
|
41
|
+
|
|
40
42
|
assert os.path.isfile(labelme_file), 'Could not find labelme .json file {}'.format(labelme_file)
|
|
41
43
|
assert labelme_file.endswith('.json'), 'Illegal labelme .json file {}'.format(labelme_file)
|
|
42
44
|
|
|
@@ -45,7 +47,8 @@ def labelme_file_to_yolo_file(labelme_file,
|
|
|
45
47
|
|
|
46
48
|
if os.path.isfile(yolo_file):
|
|
47
49
|
if overwrite_behavior == 'skip':
|
|
48
|
-
|
|
50
|
+
result['status'] = 'skip-exists'
|
|
51
|
+
return result
|
|
49
52
|
else:
|
|
50
53
|
assert overwrite_behavior == 'overwrite', \
|
|
51
54
|
'Unrecognized overwrite behavior {}'.format(overwrite_behavior)
|
|
@@ -54,7 +57,8 @@ def labelme_file_to_yolo_file(labelme_file,
|
|
|
54
57
|
labelme_data = json.load(f)
|
|
55
58
|
|
|
56
59
|
if required_token is not None and required_token not in labelme_data:
|
|
57
|
-
|
|
60
|
+
result['status'] = 'skip-no-required-token'
|
|
61
|
+
return result
|
|
58
62
|
|
|
59
63
|
im_height = labelme_data['imageHeight']
|
|
60
64
|
im_width = labelme_data['imageWidth']
|
|
@@ -83,10 +87,12 @@ def labelme_file_to_yolo_file(labelme_file,
|
|
|
83
87
|
|
|
84
88
|
if (minx_abs >= (im_width-1)) or (maxx_abs <= 0) or \
|
|
85
89
|
(miny_abs >= (im_height-1)) or (maxy_abs <= 0):
|
|
86
|
-
print('Skipping invalid shape in {}'.format(labelme_file))
|
|
90
|
+
print('Skipping invalid shape in {}'.format(labelme_file))
|
|
87
91
|
continue
|
|
88
92
|
|
|
89
|
-
# Clip to [0,1]
|
|
93
|
+
# Clip to [0,1]... it's not obvious that the YOLO format doesn't allow bounding
|
|
94
|
+
# boxes to extend outside the image, but YOLOv5 and YOLOv8 get sad about boxes
|
|
95
|
+
# that extend outside the image.
|
|
90
96
|
maxx_abs = min(maxx_abs,im_width-1)
|
|
91
97
|
maxy_abs = min(maxy_abs,im_height-1)
|
|
92
98
|
minx_abs = max(minx_abs,0.0)
|
|
@@ -97,11 +103,6 @@ def labelme_file_to_yolo_file(labelme_file,
|
|
|
97
103
|
miny_rel = miny_abs / (im_height-1)
|
|
98
104
|
maxy_rel = maxy_abs / (im_height-1)
|
|
99
105
|
|
|
100
|
-
if (right_edge_quantization_threshold is not None):
|
|
101
|
-
right_edge_distance = 1.0 - maxx_rel
|
|
102
|
-
if right_edge_distance < right_edge_quantization_threshold:
|
|
103
|
-
maxx_rel = 1.0
|
|
104
|
-
|
|
105
106
|
assert maxx_rel >= minx_rel
|
|
106
107
|
assert maxy_rel >= miny_rel
|
|
107
108
|
|
|
@@ -119,32 +120,45 @@ def labelme_file_to_yolo_file(labelme_file,
|
|
|
119
120
|
with open(yolo_file,'w') as f:
|
|
120
121
|
for s in yolo_lines:
|
|
121
122
|
f.write(s + '\n')
|
|
122
|
-
|
|
123
|
+
|
|
124
|
+
result['status'] = 'converted'
|
|
125
|
+
return result
|
|
126
|
+
|
|
123
127
|
|
|
124
128
|
def labelme_folder_to_yolo(labelme_folder,
|
|
125
129
|
category_name_to_category_id=None,
|
|
126
130
|
required_token=None,
|
|
127
|
-
|
|
128
|
-
|
|
131
|
+
overwrite_behavior='overwrite',
|
|
132
|
+
relative_filenames_to_convert=None,
|
|
133
|
+
n_workers=1,
|
|
134
|
+
use_threads=True):
|
|
129
135
|
"""
|
|
130
136
|
Given a folder with images and labelme .json files, convert the .json files
|
|
131
137
|
to YOLO .txt format. If category_name_to_category_id is None, first reads
|
|
132
138
|
all the labels in the folder to build a zero-indexed name --> ID mapping.
|
|
133
139
|
|
|
134
140
|
If required_token is not None and a labelme_file does not contain the key [required_token],
|
|
135
|
-
it won't be converted.
|
|
141
|
+
it won't be converted. Typically used to specify a field that indicates which files have
|
|
142
|
+
been reviewed.
|
|
136
143
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
of the way there, due to what appears to be a slight bias inherent to MD. If a box extends
|
|
140
|
-
within [right_edge_quantization_threshold] (a small number, from 0 to 1, but probably around
|
|
141
|
-
0.02) of the right edge of the image, it will be extended to the far right edge.
|
|
144
|
+
If relative_filenames_to_convert is not None, this should be a list of .json (not image)
|
|
145
|
+
files that should get converted, relative to the base folder.
|
|
142
146
|
|
|
143
|
-
|
|
147
|
+
overwrite_behavior should be 'skip' or 'overwrite' (default).
|
|
148
|
+
|
|
149
|
+
returns a dict with:
|
|
150
|
+
'category_name_to_category_id', whether it was passed in or constructed
|
|
151
|
+
'image_results': a list of results for each image (converted, skipped, error)
|
|
152
|
+
|
|
144
153
|
"""
|
|
145
154
|
|
|
146
|
-
|
|
147
|
-
|
|
155
|
+
if relative_filenames_to_convert is not None:
|
|
156
|
+
labelme_files_relative = relative_filenames_to_convert
|
|
157
|
+
assert all([fn.endswith('.json') for fn in labelme_files_relative]), \
|
|
158
|
+
'relative_filenames_to_convert contains non-json files'
|
|
159
|
+
else:
|
|
160
|
+
labelme_files_relative = recursive_file_list(labelme_folder,return_relative_paths=True)
|
|
161
|
+
labelme_files_relative = [fn for fn in labelme_files_relative if fn.endswith('.json')]
|
|
148
162
|
|
|
149
163
|
if required_token is None:
|
|
150
164
|
valid_labelme_files_relative = labelme_files_relative
|
|
@@ -163,9 +177,9 @@ def labelme_folder_to_yolo(labelme_folder,
|
|
|
163
177
|
|
|
164
178
|
valid_labelme_files_relative.append(fn_relative)
|
|
165
179
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
180
|
+
print('{} of {} files are valid'.format(len(valid_labelme_files_relative),
|
|
181
|
+
len(labelme_files_relative)))
|
|
182
|
+
|
|
169
183
|
del labelme_files_relative
|
|
170
184
|
|
|
171
185
|
if category_name_to_category_id is None:
|
|
@@ -184,26 +198,54 @@ def labelme_folder_to_yolo(labelme_folder,
|
|
|
184
198
|
# ...for each file
|
|
185
199
|
|
|
186
200
|
# ...if we need to build a category mapping
|
|
187
|
-
|
|
188
|
-
for fn_relative in tqdm(valid_labelme_files_relative):
|
|
189
|
-
|
|
190
|
-
fn_abs = os.path.join(labelme_folder,fn_relative)
|
|
191
|
-
labelme_file_to_yolo_file(fn_abs,
|
|
192
|
-
category_name_to_category_id,
|
|
193
|
-
yolo_file=None,
|
|
194
|
-
required_token=required_token,
|
|
195
|
-
right_edge_quantization_threshold=\
|
|
196
|
-
right_edge_quantization_threshold,
|
|
197
|
-
overwrite_behavior=overwrite_behavior)
|
|
198
201
|
|
|
199
|
-
|
|
202
|
+
image_results = []
|
|
203
|
+
|
|
204
|
+
n_workers = min(n_workers,len(valid_labelme_files_relative))
|
|
205
|
+
|
|
206
|
+
if n_workers <= 1:
|
|
207
|
+
for fn_relative in tqdm(valid_labelme_files_relative):
|
|
208
|
+
|
|
209
|
+
fn_abs = os.path.join(labelme_folder,fn_relative)
|
|
210
|
+
image_result = labelme_file_to_yolo_file(fn_abs,
|
|
211
|
+
category_name_to_category_id,
|
|
212
|
+
yolo_file=None,
|
|
213
|
+
required_token=required_token,
|
|
214
|
+
overwrite_behavior=overwrite_behavior)
|
|
215
|
+
image_results.append(image_result)
|
|
216
|
+
# ...for each file
|
|
217
|
+
else:
|
|
218
|
+
if use_threads:
|
|
219
|
+
pool = ThreadPool(n_workers)
|
|
220
|
+
else:
|
|
221
|
+
pool = Pool(n_workers)
|
|
222
|
+
|
|
223
|
+
valid_labelme_files_abs = [os.path.join(labelme_folder,fn_relative) for \
|
|
224
|
+
fn_relative in valid_labelme_files_relative]
|
|
225
|
+
|
|
226
|
+
image_results = list(tqdm(pool.imap(
|
|
227
|
+
partial(labelme_file_to_yolo_file,
|
|
228
|
+
category_name_to_category_id=category_name_to_category_id,
|
|
229
|
+
yolo_file=None,
|
|
230
|
+
required_token=required_token,
|
|
231
|
+
overwrite_behavior=overwrite_behavior),
|
|
232
|
+
valid_labelme_files_abs),
|
|
233
|
+
total=len(valid_labelme_files_abs)))
|
|
234
|
+
|
|
235
|
+
assert len(valid_labelme_files_relative) == len(image_results)
|
|
200
236
|
|
|
201
237
|
print('Converted {} labelme .json files to YOLO'.format(
|
|
202
238
|
len(valid_labelme_files_relative)))
|
|
203
239
|
|
|
204
|
-
|
|
240
|
+
labelme_to_yolo_results = {}
|
|
241
|
+
labelme_to_yolo_results['category_name_to_category_id'] = category_name_to_category_id
|
|
242
|
+
labelme_to_yolo_results['image_results'] = image_results
|
|
205
243
|
|
|
206
|
-
|
|
244
|
+
return labelme_to_yolo_results
|
|
245
|
+
|
|
246
|
+
# ...def labelme_folder_to_yolo(...)
|
|
247
|
+
|
|
248
|
+
|
|
207
249
|
#%% Interactive driver
|
|
208
250
|
|
|
209
251
|
if False:
|
|
@@ -212,18 +254,19 @@ if False:
|
|
|
212
254
|
|
|
213
255
|
#%%
|
|
214
256
|
|
|
215
|
-
import os
|
|
216
257
|
labelme_file = os.path.expanduser('~/tmp/labels/x.json')
|
|
217
|
-
yolo_file = None
|
|
218
258
|
required_token = 'saved_by_labelme'
|
|
219
|
-
right_edge_quantization_threshold = 0.015
|
|
220
259
|
category_name_to_category_id = {'animal':0}
|
|
260
|
+
labelme_folder = os.path.expanduser('~/tmp/labels')
|
|
221
261
|
|
|
222
262
|
#%%
|
|
223
263
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
264
|
+
category_name_to_category_id = \
|
|
265
|
+
labelme_folder_to_yolo(labelme_folder,
|
|
266
|
+
category_name_to_category_id=category_name_to_category_id,
|
|
267
|
+
required_token=required_token,
|
|
268
|
+
overwrite_behavior='overwrite')
|
|
269
|
+
|
|
227
270
|
#%% Command-line driver
|
|
228
271
|
|
|
229
|
-
# TODO
|
|
272
|
+
# TODO
|