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.

Files changed (75) hide show
  1. api/batch_processing/data_preparation/manage_local_batch.py +297 -202
  2. api/batch_processing/data_preparation/manage_video_batch.py +7 -2
  3. api/batch_processing/postprocessing/add_max_conf.py +1 -0
  4. api/batch_processing/postprocessing/combine_api_outputs.py +2 -2
  5. api/batch_processing/postprocessing/compare_batch_results.py +111 -61
  6. api/batch_processing/postprocessing/convert_output_format.py +24 -6
  7. api/batch_processing/postprocessing/load_api_results.py +56 -72
  8. api/batch_processing/postprocessing/md_to_labelme.py +119 -51
  9. api/batch_processing/postprocessing/merge_detections.py +30 -5
  10. api/batch_processing/postprocessing/postprocess_batch_results.py +175 -55
  11. api/batch_processing/postprocessing/remap_detection_categories.py +163 -0
  12. api/batch_processing/postprocessing/render_detection_confusion_matrix.py +628 -0
  13. api/batch_processing/postprocessing/repeat_detection_elimination/find_repeat_detections.py +71 -23
  14. api/batch_processing/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +1 -1
  15. api/batch_processing/postprocessing/repeat_detection_elimination/repeat_detections_core.py +224 -76
  16. api/batch_processing/postprocessing/subset_json_detector_output.py +132 -5
  17. api/batch_processing/postprocessing/top_folders_to_bottom.py +1 -1
  18. classification/prepare_classification_script.py +191 -191
  19. data_management/cct_json_utils.py +7 -2
  20. data_management/coco_to_labelme.py +263 -0
  21. data_management/coco_to_yolo.py +72 -48
  22. data_management/databases/integrity_check_json_db.py +75 -64
  23. data_management/databases/subset_json_db.py +1 -1
  24. data_management/generate_crops_from_cct.py +1 -1
  25. data_management/get_image_sizes.py +44 -26
  26. data_management/importers/animl_results_to_md_results.py +3 -5
  27. data_management/importers/noaa_seals_2019.py +2 -2
  28. data_management/importers/zamba_results_to_md_results.py +2 -2
  29. data_management/labelme_to_coco.py +264 -127
  30. data_management/labelme_to_yolo.py +96 -53
  31. data_management/lila/create_lila_blank_set.py +557 -0
  32. data_management/lila/create_lila_test_set.py +2 -1
  33. data_management/lila/create_links_to_md_results_files.py +1 -1
  34. data_management/lila/download_lila_subset.py +138 -45
  35. data_management/lila/generate_lila_per_image_labels.py +23 -14
  36. data_management/lila/get_lila_annotation_counts.py +16 -10
  37. data_management/lila/lila_common.py +15 -42
  38. data_management/lila/test_lila_metadata_urls.py +116 -0
  39. data_management/read_exif.py +65 -16
  40. data_management/remap_coco_categories.py +84 -0
  41. data_management/resize_coco_dataset.py +14 -31
  42. data_management/wi_download_csv_to_coco.py +239 -0
  43. data_management/yolo_output_to_md_output.py +40 -13
  44. data_management/yolo_to_coco.py +313 -100
  45. detection/process_video.py +36 -14
  46. detection/pytorch_detector.py +1 -1
  47. detection/run_detector.py +73 -18
  48. detection/run_detector_batch.py +116 -27
  49. detection/run_inference_with_yolov5_val.py +135 -27
  50. detection/run_tiled_inference.py +153 -43
  51. detection/tf_detector.py +2 -1
  52. detection/video_utils.py +4 -2
  53. md_utils/ct_utils.py +101 -6
  54. md_utils/md_tests.py +264 -17
  55. md_utils/path_utils.py +326 -47
  56. md_utils/process_utils.py +26 -7
  57. md_utils/split_locations_into_train_val.py +215 -0
  58. md_utils/string_utils.py +10 -0
  59. md_utils/url_utils.py +66 -3
  60. md_utils/write_html_image_list.py +12 -2
  61. md_visualization/visualization_utils.py +380 -74
  62. md_visualization/visualize_db.py +41 -10
  63. md_visualization/visualize_detector_output.py +185 -104
  64. {megadetector-5.0.6.dist-info → megadetector-5.0.8.dist-info}/METADATA +11 -13
  65. {megadetector-5.0.6.dist-info → megadetector-5.0.8.dist-info}/RECORD +74 -67
  66. {megadetector-5.0.6.dist-info → megadetector-5.0.8.dist-info}/WHEEL +1 -1
  67. taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +1 -1
  68. taxonomy_mapping/map_new_lila_datasets.py +43 -39
  69. taxonomy_mapping/prepare_lila_taxonomy_release.py +5 -2
  70. taxonomy_mapping/preview_lila_taxonomy.py +27 -27
  71. taxonomy_mapping/species_lookup.py +33 -13
  72. taxonomy_mapping/taxonomy_csv_checker.py +7 -5
  73. md_visualization/visualize_megadb.py +0 -183
  74. {megadetector-5.0.6.dist-info → megadetector-5.0.8.dist-info}/LICENSE +0 -0
  75. {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
- right_edge_quantization_threshold is an off-by-default hack to handle cases where
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
- return
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
- return
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
- right_edge_quantization_threshold=None,
128
- overwrite_behavior='overwrite'):
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
- right_edge_quantization_threshold is an off-by-default hack to handle cases where
138
- boxes that really should be running off the right side of the image only extend like 99%
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
- returns category_name_to_category_id, whether it was passed in or constructed.
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
- labelme_files_relative = recursive_file_list(labelme_folder,return_relative_paths=True)
147
- labelme_files_relative = [fn for fn in labelme_files_relative if fn.endswith('.json')]
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
- print('{} of {} files are valid'.format(len(valid_labelme_files_relative),
167
- len(labelme_files_relative)))
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
- # ...for each file
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
- return category_name_to_category_id
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
- labelme_folder = os.path.expanduser('~/tmp/labels')
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