megadetector 5.0.5__py3-none-any.whl → 5.0.7__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 (132) hide show
  1. api/batch_processing/data_preparation/manage_local_batch.py +302 -263
  2. api/batch_processing/data_preparation/manage_video_batch.py +81 -2
  3. api/batch_processing/postprocessing/add_max_conf.py +1 -0
  4. api/batch_processing/postprocessing/categorize_detections_by_size.py +50 -19
  5. api/batch_processing/postprocessing/compare_batch_results.py +110 -60
  6. api/batch_processing/postprocessing/load_api_results.py +56 -70
  7. api/batch_processing/postprocessing/md_to_coco.py +1 -1
  8. api/batch_processing/postprocessing/md_to_labelme.py +2 -1
  9. api/batch_processing/postprocessing/postprocess_batch_results.py +240 -81
  10. api/batch_processing/postprocessing/render_detection_confusion_matrix.py +625 -0
  11. api/batch_processing/postprocessing/repeat_detection_elimination/find_repeat_detections.py +71 -23
  12. api/batch_processing/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +1 -1
  13. api/batch_processing/postprocessing/repeat_detection_elimination/repeat_detections_core.py +227 -75
  14. api/batch_processing/postprocessing/subset_json_detector_output.py +132 -5
  15. api/batch_processing/postprocessing/top_folders_to_bottom.py +1 -1
  16. api/synchronous/api_core/animal_detection_api/detection/run_detector_batch.py +2 -2
  17. classification/prepare_classification_script.py +191 -191
  18. data_management/coco_to_yolo.py +68 -45
  19. data_management/databases/integrity_check_json_db.py +7 -5
  20. data_management/generate_crops_from_cct.py +3 -3
  21. data_management/get_image_sizes.py +8 -6
  22. data_management/importers/add_timestamps_to_icct.py +79 -0
  23. data_management/importers/animl_results_to_md_results.py +160 -0
  24. data_management/importers/auckland_doc_test_to_json.py +4 -4
  25. data_management/importers/auckland_doc_to_json.py +1 -1
  26. data_management/importers/awc_to_json.py +5 -5
  27. data_management/importers/bellevue_to_json.py +5 -5
  28. data_management/importers/carrizo_shrubfree_2018.py +5 -5
  29. data_management/importers/carrizo_trail_cam_2017.py +5 -5
  30. data_management/importers/cct_field_adjustments.py +2 -3
  31. data_management/importers/channel_islands_to_cct.py +4 -4
  32. data_management/importers/ena24_to_json.py +5 -5
  33. data_management/importers/helena_to_cct.py +10 -10
  34. data_management/importers/idaho-camera-traps.py +12 -12
  35. data_management/importers/idfg_iwildcam_lila_prep.py +8 -8
  36. data_management/importers/jb_csv_to_json.py +4 -4
  37. data_management/importers/missouri_to_json.py +1 -1
  38. data_management/importers/noaa_seals_2019.py +1 -1
  39. data_management/importers/pc_to_json.py +5 -5
  40. data_management/importers/prepare-noaa-fish-data-for-lila.py +4 -4
  41. data_management/importers/prepare_zsl_imerit.py +5 -5
  42. data_management/importers/rspb_to_json.py +4 -4
  43. data_management/importers/save_the_elephants_survey_A.py +5 -5
  44. data_management/importers/save_the_elephants_survey_B.py +6 -6
  45. data_management/importers/snapshot_safari_importer.py +9 -9
  46. data_management/importers/snapshot_serengeti_lila.py +9 -9
  47. data_management/importers/timelapse_csv_set_to_json.py +5 -7
  48. data_management/importers/ubc_to_json.py +4 -4
  49. data_management/importers/umn_to_json.py +4 -4
  50. data_management/importers/wellington_to_json.py +1 -1
  51. data_management/importers/wi_to_json.py +2 -2
  52. data_management/importers/zamba_results_to_md_results.py +181 -0
  53. data_management/labelme_to_coco.py +35 -7
  54. data_management/labelme_to_yolo.py +229 -0
  55. data_management/lila/add_locations_to_island_camera_traps.py +1 -1
  56. data_management/lila/add_locations_to_nacti.py +147 -0
  57. data_management/lila/create_lila_blank_set.py +474 -0
  58. data_management/lila/create_lila_test_set.py +2 -1
  59. data_management/lila/create_links_to_md_results_files.py +106 -0
  60. data_management/lila/download_lila_subset.py +46 -21
  61. data_management/lila/generate_lila_per_image_labels.py +23 -14
  62. data_management/lila/get_lila_annotation_counts.py +17 -11
  63. data_management/lila/lila_common.py +14 -11
  64. data_management/lila/test_lila_metadata_urls.py +116 -0
  65. data_management/ocr_tools.py +829 -0
  66. data_management/resize_coco_dataset.py +13 -11
  67. data_management/yolo_output_to_md_output.py +84 -12
  68. data_management/yolo_to_coco.py +38 -20
  69. detection/process_video.py +36 -14
  70. detection/pytorch_detector.py +23 -8
  71. detection/run_detector.py +76 -19
  72. detection/run_detector_batch.py +178 -63
  73. detection/run_inference_with_yolov5_val.py +326 -57
  74. detection/run_tiled_inference.py +153 -43
  75. detection/video_utils.py +34 -8
  76. md_utils/ct_utils.py +172 -1
  77. md_utils/md_tests.py +372 -51
  78. md_utils/path_utils.py +167 -39
  79. md_utils/process_utils.py +26 -7
  80. md_utils/split_locations_into_train_val.py +215 -0
  81. md_utils/string_utils.py +10 -0
  82. md_utils/url_utils.py +0 -2
  83. md_utils/write_html_image_list.py +9 -26
  84. md_visualization/plot_utils.py +12 -8
  85. md_visualization/visualization_utils.py +106 -7
  86. md_visualization/visualize_db.py +16 -8
  87. md_visualization/visualize_detector_output.py +208 -97
  88. {megadetector-5.0.5.dist-info → megadetector-5.0.7.dist-info}/METADATA +3 -6
  89. {megadetector-5.0.5.dist-info → megadetector-5.0.7.dist-info}/RECORD +98 -121
  90. {megadetector-5.0.5.dist-info → megadetector-5.0.7.dist-info}/WHEEL +1 -1
  91. taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +1 -1
  92. taxonomy_mapping/map_new_lila_datasets.py +43 -39
  93. taxonomy_mapping/prepare_lila_taxonomy_release.py +5 -2
  94. taxonomy_mapping/preview_lila_taxonomy.py +27 -27
  95. taxonomy_mapping/species_lookup.py +33 -13
  96. taxonomy_mapping/taxonomy_csv_checker.py +7 -5
  97. api/synchronous/api_core/yolov5/detect.py +0 -252
  98. api/synchronous/api_core/yolov5/export.py +0 -607
  99. api/synchronous/api_core/yolov5/hubconf.py +0 -146
  100. api/synchronous/api_core/yolov5/models/__init__.py +0 -0
  101. api/synchronous/api_core/yolov5/models/common.py +0 -738
  102. api/synchronous/api_core/yolov5/models/experimental.py +0 -104
  103. api/synchronous/api_core/yolov5/models/tf.py +0 -574
  104. api/synchronous/api_core/yolov5/models/yolo.py +0 -338
  105. api/synchronous/api_core/yolov5/train.py +0 -670
  106. api/synchronous/api_core/yolov5/utils/__init__.py +0 -36
  107. api/synchronous/api_core/yolov5/utils/activations.py +0 -103
  108. api/synchronous/api_core/yolov5/utils/augmentations.py +0 -284
  109. api/synchronous/api_core/yolov5/utils/autoanchor.py +0 -170
  110. api/synchronous/api_core/yolov5/utils/autobatch.py +0 -66
  111. api/synchronous/api_core/yolov5/utils/aws/__init__.py +0 -0
  112. api/synchronous/api_core/yolov5/utils/aws/resume.py +0 -40
  113. api/synchronous/api_core/yolov5/utils/benchmarks.py +0 -148
  114. api/synchronous/api_core/yolov5/utils/callbacks.py +0 -71
  115. api/synchronous/api_core/yolov5/utils/dataloaders.py +0 -1087
  116. api/synchronous/api_core/yolov5/utils/downloads.py +0 -178
  117. api/synchronous/api_core/yolov5/utils/flask_rest_api/example_request.py +0 -19
  118. api/synchronous/api_core/yolov5/utils/flask_rest_api/restapi.py +0 -46
  119. api/synchronous/api_core/yolov5/utils/general.py +0 -1018
  120. api/synchronous/api_core/yolov5/utils/loggers/__init__.py +0 -187
  121. api/synchronous/api_core/yolov5/utils/loggers/wandb/__init__.py +0 -0
  122. api/synchronous/api_core/yolov5/utils/loggers/wandb/log_dataset.py +0 -27
  123. api/synchronous/api_core/yolov5/utils/loggers/wandb/sweep.py +0 -41
  124. api/synchronous/api_core/yolov5/utils/loggers/wandb/wandb_utils.py +0 -577
  125. api/synchronous/api_core/yolov5/utils/loss.py +0 -234
  126. api/synchronous/api_core/yolov5/utils/metrics.py +0 -355
  127. api/synchronous/api_core/yolov5/utils/plots.py +0 -489
  128. api/synchronous/api_core/yolov5/utils/torch_utils.py +0 -314
  129. api/synchronous/api_core/yolov5/val.py +0 -394
  130. md_utils/matlab_porting_tools.py +0 -97
  131. {megadetector-5.0.5.dist-info → megadetector-5.0.7.dist-info}/LICENSE +0 -0
  132. {megadetector-5.0.5.dist-info → megadetector-5.0.7.dist-info}/top_level.txt +0 -0
@@ -3,8 +3,7 @@
3
3
  # visualize_detector_output.py
4
4
  #
5
5
  # Render images with bounding boxes annotated on them to a folder, based on a
6
- # detector output result file (json). The original images can be local or in
7
- # Azure Blob Storage.
6
+ # detector output result file (json), optionally writing an HTML index file.
8
7
  #
9
8
  ########
10
9
 
@@ -15,51 +14,115 @@ import json
15
14
  import os
16
15
  import random
17
16
  import sys
18
- from typing import List, Optional
17
+ from multiprocessing.pool import ThreadPool
18
+ from multiprocessing.pool import Pool
19
+ from typing import List
20
+ from functools import partial
19
21
 
20
22
  from tqdm import tqdm
21
23
 
22
- from data_management.annotations.annotation_constants import (
23
- detector_bbox_category_id_to_name) # here id is int
24
+ from data_management.annotations.annotation_constants import detector_bbox_category_id_to_name
24
25
  from md_visualization import visualization_utils as vis_utils
25
26
  from md_utils.ct_utils import get_max_conf
27
+ from md_utils import write_html_image_list
28
+ from detection.run_detector import get_typical_confidence_threshold_from_results
26
29
 
27
30
 
28
31
  #%% Constants
29
32
 
30
- # convert category ID from int to str
33
+ # This will only be used if a category mapping is not available in the results file.
31
34
  DEFAULT_DETECTOR_LABEL_MAP = {
32
35
  str(k): v for k, v in detector_bbox_category_id_to_name.items()
33
36
  }
34
37
 
35
38
 
39
+ #%% Support functions
40
+
41
+ def render_image(entry,
42
+ detector_label_map,classification_label_map,
43
+ confidence_threshold,classification_confidence_threshold,
44
+ render_detections_only,preserve_path_structure,out_dir,images_dir,
45
+ output_image_width):
46
+
47
+ rendering_result = {'failed_image':False,'missing_image':False,
48
+ 'skipped_image':False,'annotated_image_path':None,
49
+ 'max_conf':None,'file':entry['file']}
50
+
51
+ image_id = entry['file']
52
+
53
+ if 'failure' in entry and entry['failure'] is not None:
54
+ rendering_result['failed_image'] = True
55
+ return rendering_result
56
+
57
+ assert 'detections' in entry and entry['detections'] is not None
58
+
59
+ max_conf = get_max_conf(entry)
60
+ rendering_result['max_conf'] = max_conf
61
+
62
+ if (max_conf < confidence_threshold) and render_detections_only:
63
+ rendering_result['skipped_image'] = True
64
+ return rendering_result
65
+
66
+ image_obj = os.path.join(images_dir, image_id)
67
+ if not os.path.exists(image_obj):
68
+ print(f'Image {image_id} not found in images_dir')
69
+ rendering_result['missing_image'] = True
70
+ return rendering_result
71
+
72
+ # If output_image_width is -1 or None, this will just return the original image
73
+ image = vis_utils.resize_image(
74
+ vis_utils.open_image(image_obj), output_image_width)
75
+
76
+ vis_utils.render_detection_bounding_boxes(
77
+ entry['detections'], image,
78
+ label_map=detector_label_map,
79
+ classification_label_map=classification_label_map,
80
+ confidence_threshold=confidence_threshold,
81
+ classification_confidence_threshold=classification_confidence_threshold)
82
+
83
+ if not preserve_path_structure:
84
+ for char in ['/', '\\', ':']:
85
+ image_id = image_id.replace(char, '~')
86
+ annotated_img_path = os.path.join(out_dir, f'anno_{image_id}')
87
+ else:
88
+ assert not os.path.isabs(image_id), "Can't preserve paths when operating on absolute paths"
89
+ annotated_img_path = os.path.join(out_dir, image_id)
90
+ os.makedirs(os.path.dirname(annotated_img_path),exist_ok=True)
91
+
92
+ image.save(annotated_img_path)
93
+ rendering_result['annotated_image_path'] = annotated_img_path
94
+
95
+ return rendering_result
96
+
97
+
36
98
  #%% Main function
37
99
 
38
100
  def visualize_detector_output(detector_output_path: str,
39
101
  out_dir: str,
40
102
  images_dir: str,
41
- is_azure: bool = False,
42
103
  confidence_threshold: float = 0.15,
43
104
  sample: int = -1,
44
105
  output_image_width: int = 700,
45
- random_seed: Optional[int] = None,
106
+ random_seed: int = None,
46
107
  render_detections_only: bool = False,
47
108
  classification_confidence_threshold = 0.1,
48
- html_output_file=None,
49
- html_output_options=None,
50
- preserve_path_structure=False) -> List[str]:
109
+ html_output_file = None,
110
+ html_output_options = None,
111
+ preserve_path_structure = False,
112
+ parallelize_rendering = False,
113
+ parallelize_rendering_n_cores = 10,
114
+ parallelize_rendering_with_threads = True) -> List[str]:
51
115
 
52
116
  """
53
- Draw bounding boxes on images given the output of the detector.
117
+ Draw bounding boxes on images given the output of a detector.
54
118
 
55
119
  Args:
56
120
  detector_output_path: str, path to detector output json file
57
121
  out_dir: str, path to directory for saving annotated images
58
- images_dir: str, path to local images dir, or a SAS URL to an Azure Blob
59
- Storage container
60
- is_azure: bool, whether images_dir points to an Azure URL
122
+ images_dir: str, path to images dir
61
123
  confidence: float, threshold above which annotations will be rendered
62
124
  sample: int, maximum number of images to annotate, -1 for all
125
+ random_seed: seed for sampling (not relevant if sample == -1)
63
126
  output_image_width: int, width in pixels to resize images for display,
64
127
  set to -1 to use original image width
65
128
  random_seed: int, for deterministic image sampling when sample != -1
@@ -68,16 +131,11 @@ def visualize_detector_output(detector_output_path: str,
68
131
  Returns: list of str, paths to annotated images
69
132
  """
70
133
 
71
- assert confidence_threshold >= 0 and confidence_threshold <= 1, (
72
- f'Confidence threshold {confidence_threshold} is invalid, must be in (0, 1).')
134
+ assert os.path.exists(detector_output_path), \
135
+ 'Detector output file does not exist at {}'.format(detector_output_path)
73
136
 
74
- assert os.path.exists(detector_output_path), (
75
- f'Detector output file does not exist at {detector_output_path}.')
76
-
77
- if is_azure:
78
- from md_utils import sas_blob_utils
79
- else:
80
- assert os.path.isdir(images_dir)
137
+ assert os.path.isdir(images_dir), \
138
+ 'Image folder {} is not available'.format(images_dir)
81
139
 
82
140
  os.makedirs(out_dir, exist_ok=True)
83
141
 
@@ -89,12 +147,18 @@ def visualize_detector_output(detector_output_path: str,
89
147
  assert 'images' in detector_output, (
90
148
  'Detector output file should be a json with an "images" field.')
91
149
  images = detector_output['images']
92
-
150
+
151
+ if confidence_threshold is None:
152
+ confidence_threshold = get_typical_confidence_threshold_from_results(detector_output)
153
+
154
+ assert confidence_threshold >= 0 and confidence_threshold <= 1, (
155
+ f'Confidence threshold {confidence_threshold} is invalid, must be in (0, 1).')
156
+
93
157
  if 'detection_categories' in detector_output:
94
158
  print('Using custom label mapping')
95
159
  detector_label_map = detector_output['detection_categories']
96
160
  else:
97
- detector_label_map = DEFAULT_DETECTOR_LABEL_MAP
161
+ detector_label_map = DEFAULT_DETECTOR_LABEL_MAP
98
162
 
99
163
  num_images = len(images)
100
164
  print(f'Detector output file contains {num_images} entries.')
@@ -118,85 +182,89 @@ def visualize_detector_output(detector_output_path: str,
118
182
  print('Rendering detections above a confidence threshold of {}'.format(
119
183
  confidence_threshold))
120
184
 
121
- num_saved = 0
122
- annotated_img_paths = []
123
- failed_images = []
124
- missing_images = []
125
-
126
185
  classification_label_map = None
127
186
 
128
187
  if 'classification_categories' in detector_output:
129
188
  classification_label_map = detector_output['classification_categories']
130
189
 
131
- for entry in tqdm(images):
132
-
133
- image_id = entry['file']
134
-
135
- if 'failure' in entry and entry['failure'] is not None:
136
- failed_images.append(image_id)
137
- continue
138
-
139
- assert 'detections' in entry and entry['detections'] is not None
140
-
141
- max_conf = get_max_conf(entry)
142
- if (max_conf < confidence_threshold) and render_detections_only:
143
- continue
190
+ rendering_results = []
191
+
192
+ if parallelize_rendering:
144
193
 
145
- if is_azure:
146
- blob_uri = sas_blob_utils.build_blob_uri(
147
- container_uri=images_dir, blob_name=image_id)
148
- if not sas_blob_utils.check_blob_exists(blob_uri):
149
- container = sas_blob_utils.get_container_from_uri(images_dir)
150
- print(f'Image {image_id} not found in blob container '
151
- f'{container}; skipped.')
152
- continue
153
- # BytesIO object
154
- image_obj, _ = sas_blob_utils.download_blob_to_stream(blob_uri)
194
+ if parallelize_rendering_with_threads:
195
+ worker_string = 'threads'
155
196
  else:
156
- image_obj = os.path.join(images_dir, image_id)
157
- if not os.path.exists(image_obj):
158
- print(f'Image {image_id} not found in images_dir')
159
- missing_images.append(image_id)
160
- continue
161
-
162
- image = vis_utils.resize_image(
163
- vis_utils.open_image(image_obj), output_image_width)
164
-
165
- vis_utils.render_detection_bounding_boxes(
166
- entry['detections'], image, label_map=detector_label_map,
167
- classification_label_map = classification_label_map,
168
- confidence_threshold=confidence_threshold,
169
- classification_confidence_threshold=classification_confidence_threshold)
170
-
171
- if not preserve_path_structure:
172
- for char in ['/', '\\', ':']:
173
- image_id = image_id.replace(char, '~')
174
- annotated_img_path = os.path.join(out_dir, f'anno_{image_id}')
197
+ worker_string = 'processes'
198
+
199
+ if parallelize_rendering_n_cores is None:
200
+ if parallelize_rendering_with_threads:
201
+ pool = ThreadPool()
202
+ else:
203
+ pool = Pool()
175
204
  else:
176
- assert not os.path.isabs(image_id), "Can't preserve paths when operating on absolute paths"
177
- annotated_img_path = os.path.join(out_dir, image_id)
178
- os.makedirs(os.path.dirname(annotated_img_path),exist_ok=True)
179
- annotated_img_paths.append(annotated_img_path)
180
- image.save(annotated_img_path)
181
- num_saved += 1
182
-
183
- if is_azure:
184
- image_obj.close()
185
-
205
+ if parallelize_rendering_with_threads:
206
+ pool = ThreadPool(parallelize_rendering_n_cores)
207
+ else:
208
+ pool = Pool(parallelize_rendering_n_cores)
209
+ print('Rendering images with {} {}'.format(parallelize_rendering_n_cores,
210
+ worker_string))
211
+ rendering_results = list(tqdm(pool.imap(
212
+ partial(render_image,detector_label_map=detector_label_map,
213
+ classification_label_map=classification_label_map,
214
+ confidence_threshold=confidence_threshold,
215
+ classification_confidence_threshold=classification_confidence_threshold,
216
+ render_detections_only=render_detections_only,
217
+ preserve_path_structure=preserve_path_structure,
218
+ out_dir=out_dir,
219
+ images_dir=images_dir,
220
+ output_image_width=output_image_width),
221
+ images), total=len(images)))
222
+
223
+ else:
224
+
225
+ for entry in tqdm(images):
226
+
227
+ rendering_result = render_image(entry,detector_label_map,classification_label_map,
228
+ confidence_threshold,classification_confidence_threshold,
229
+ render_detections_only,preserve_path_structure,out_dir,
230
+ images_dir,output_image_width)
231
+ rendering_results.append(rendering_result)
232
+
186
233
  # ...for each image
187
234
 
235
+ failed_images = [r for r in rendering_results if r['failed_image']]
236
+ missing_images = [r for r in rendering_results if r['missing_image']]
237
+ skipped_images = [r for r in rendering_results if r['skipped_image']]
238
+
188
239
  print('Skipped {} failed images (of {})'.format(len(failed_images),len(images)))
189
240
  print('Skipped {} missing images (of {})'.format(len(missing_images),len(images)))
241
+ print('Skipped {} below-threshold images (of {})'.format(len(skipped_images),len(images)))
190
242
 
191
- print(f'Rendered detection results on {num_saved} images, '
192
- f'saved to {out_dir}')
243
+ print(f'Rendered detection results to {out_dir}')
193
244
 
245
+ annotated_image_paths = [r['annotated_image_path'] for r in rendering_results if \
246
+ r['annotated_image_path'] is not None]
247
+
194
248
  if html_output_file is not None:
195
- from md_utils import write_html_image_list
196
- write_html_image_list.write_html_image_list(html_output_file,annotated_img_paths,
249
+
250
+ html_dir = os.path.dirname(html_output_file)
251
+
252
+ html_image_info = []
253
+
254
+ for r in rendering_results:
255
+ d = {}
256
+ annotated_image_path_relative = os.path.relpath(r['annotated_image_path'],html_dir)
257
+ d['filename'] = annotated_image_path_relative
258
+ d['textStyle'] = \
259
+ 'font-family:verdana,arial,calibri;font-size:80%;' + \
260
+ 'text-align:left;margin-top:20;margin-bottom:5'
261
+ d['title'] = '{} (max conf: {})'.format(r['file'],r['max_conf'])
262
+ html_image_info.append(d)
263
+
264
+ _ = write_html_image_list.write_html_image_list(html_output_file,html_image_info,
197
265
  options=html_output_options)
198
266
 
199
- return annotated_img_paths
267
+ return annotated_image_paths
200
268
 
201
269
 
202
270
  #%% Command-line driver
@@ -221,15 +289,9 @@ def main() -> None:
221
289
  'above which to visualize bounding boxes')
222
290
  parser.add_argument(
223
291
  '-i', '--images_dir', type=str, default=None,
224
- help='Path to a local directory or a SAS URL (in double quotes) to an '
225
- 'Azure blob storage container where images are stored. This '
292
+ help='Path to a local directory where images are stored. This '
226
293
  'serves as the root directory for image paths in '
227
- 'detector_output_path. If an Azure URL, pass the -a/--is-azure '
228
- 'flag. You can use Azure Storage Explorer to obtain a SAS URL.')
229
- parser.add_argument(
230
- '-a', '--is-azure', action='store_true',
231
- help='Flag that indidcates images_dir is an Azure blob storage '
232
- 'container URL.')
294
+ 'detector_output_path.')
233
295
  parser.add_argument(
234
296
  '-n', '--sample', type=int, default=-1,
235
297
  help='Number of images to be annotated and rendered. Set to -1 '
@@ -245,6 +307,9 @@ def main() -> None:
245
307
  parser.add_argument(
246
308
  '-html', '--html_output_file', type=str, default=None,
247
309
  help='Filename to which we should write an HTML image index (off by default)')
310
+ parser.add_argument(
311
+ '--open_html_output_file', action='store_true',
312
+ help='Open the .html output file when done')
248
313
  parser.add_argument(
249
314
  '-do', '--detections_only', action='store_true',
250
315
  help='Only render images with above-threshold detections (by default, '
@@ -263,7 +328,6 @@ def main() -> None:
263
328
  out_dir=args.out_dir,
264
329
  confidence_threshold=args.confidence,
265
330
  images_dir=args.images_dir,
266
- is_azure=args.is_azure,
267
331
  sample=args.sample,
268
332
  output_image_width=args.output_image_width,
269
333
  random_seed=args.random_seed,
@@ -271,6 +335,53 @@ def main() -> None:
271
335
  preserve_path_structure=args.preserve_path_structure,
272
336
  html_output_file=args.html_output_file)
273
337
 
338
+ if args.html_output_file is not None and args.open_html_output_file:
339
+ from md_utils.path_utils import open_file
340
+ open_file(args.html_output_file)
274
341
 
275
342
  if __name__ == '__main__':
276
343
  main()
344
+
345
+
346
+ #%% Interactive driver
347
+
348
+ if False:
349
+
350
+ pass
351
+
352
+ #%%
353
+
354
+ detector_output_path = os.path.expanduser('~/postprocessing/bellevue-camera-traps/bellevue-camera-traps-2023-12-05-v5a.0.0/combined_api_outputs/bellevue-camera-traps-2023-12-05-v5a.0.0_detections.json')
355
+ out_dir = r'g:\temp\preview'
356
+ images_dir = r'g:\camera_traps\camera_trap_images'
357
+ confidence_threshold = 0.15
358
+ sample = 50
359
+ output_image_width = 700
360
+ random_seed = 1
361
+ render_detections_only = True
362
+ classification_confidence_threshold = 0.1
363
+ html_output_file = os.path.join(out_dir,'index.html')
364
+ html_output_options = None
365
+ preserve_path_structure = False
366
+ parallelize_rendering = True
367
+ parallelize_rendering_n_cores = 10
368
+ parallelize_rendering_with_threads = False
369
+
370
+ _ = visualize_detector_output(detector_output_path,
371
+ out_dir,
372
+ images_dir,
373
+ confidence_threshold,
374
+ sample,
375
+ output_image_width,
376
+ random_seed,
377
+ render_detections_only,
378
+ classification_confidence_threshold,
379
+ html_output_file,
380
+ html_output_options,
381
+ preserve_path_structure,
382
+ parallelize_rendering,
383
+ parallelize_rendering_n_cores,
384
+ parallelize_rendering_with_threads)
385
+
386
+ from md_utils.path_utils import open_file
387
+ open_file(html_output_file)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: megadetector
3
- Version: 5.0.5
3
+ Version: 5.0.7
4
4
  Summary: MegaDetector is an AI model that helps conservation folks spend less time doing boring things with camera trap images.
5
5
  Author-email: Your friendly neighborhood MegaDetector team <cameratraps@lila.science>
6
6
  Maintainer-email: Your friendly neighborhood MegaDetector team <cameratraps@lila.science>
@@ -47,10 +47,7 @@ Requires-Dist: seaborn >=0.12.2
47
47
  Requires-Dist: scikit-learn >=1.3.1
48
48
  Requires-Dist: pandas >=2.1.1
49
49
  Requires-Dist: PyYAML >=6.0.1
50
- Requires-Dist: torch >=2.0.1
51
- Requires-Dist: torchvision >=0.15.2
52
- Requires-Dist: ultralytics ==8.0.186
53
- Requires-Dist: yolov5 ==7.0.12
50
+ Requires-Dist: ultralytics-yolov5 ==0.1.1
54
51
 
55
52
  # MegaDetector
56
53
 
@@ -71,7 +68,7 @@ If you are a computer-vision-y person looking to run or fine-tune MegaDetector p
71
68
 
72
69
  ## Reasons you might want to use this package
73
70
 
74
- If you want to programatically interact with the postprocessing tools from the MegaDetector repo, or programmatically run MegaDetector in a way that produces [Timelapse](https://saul.cpsc.ucalgary.ca/timelapse)-friendly output (i.e., output in the standard [MegaDetector output format](https://github.com/agentmorris/MegaDetector/tree/main/api/batch_processing#megadetector-batch-output-format)), this package might be for you.
71
+ If you want to programmatically interact with the postprocessing tools from the MegaDetector repo, or programmatically run MegaDetector in a way that produces [Timelapse](https://saul.cpsc.ucalgary.ca/timelapse)-friendly output (i.e., output in the standard [MegaDetector output format](https://github.com/agentmorris/MegaDetector/tree/main/api/batch_processing#megadetector-batch-output-format)), this package might be for you.
75
72
 
76
73
  Although even if that describes you, you <i>still</i> might be better off cloning the MegaDetector repo. Pip-installability requires that some dependencies be newer than what was available at the time MDv5 was trained, so results are <i>very slightly</i> different than results produced in the "official" environment. These differences <i>probably</i> don't matter much, but they have not been formally characterized.
77
74