megadetector 5.0.9__py3-none-any.whl → 5.0.11__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 (226) hide show
  1. {megadetector-5.0.9.dist-info → megadetector-5.0.11.dist-info}/LICENSE +0 -0
  2. {megadetector-5.0.9.dist-info → megadetector-5.0.11.dist-info}/METADATA +12 -11
  3. megadetector-5.0.11.dist-info/RECORD +5 -0
  4. megadetector-5.0.11.dist-info/top_level.txt +1 -0
  5. api/__init__.py +0 -0
  6. api/batch_processing/__init__.py +0 -0
  7. api/batch_processing/api_core/__init__.py +0 -0
  8. api/batch_processing/api_core/batch_service/__init__.py +0 -0
  9. api/batch_processing/api_core/batch_service/score.py +0 -439
  10. api/batch_processing/api_core/server.py +0 -294
  11. api/batch_processing/api_core/server_api_config.py +0 -98
  12. api/batch_processing/api_core/server_app_config.py +0 -55
  13. api/batch_processing/api_core/server_batch_job_manager.py +0 -220
  14. api/batch_processing/api_core/server_job_status_table.py +0 -152
  15. api/batch_processing/api_core/server_orchestration.py +0 -360
  16. api/batch_processing/api_core/server_utils.py +0 -92
  17. api/batch_processing/api_core_support/__init__.py +0 -0
  18. api/batch_processing/api_core_support/aggregate_results_manually.py +0 -46
  19. api/batch_processing/api_support/__init__.py +0 -0
  20. api/batch_processing/api_support/summarize_daily_activity.py +0 -152
  21. api/batch_processing/data_preparation/__init__.py +0 -0
  22. api/batch_processing/data_preparation/manage_local_batch.py +0 -2391
  23. api/batch_processing/data_preparation/manage_video_batch.py +0 -327
  24. api/batch_processing/integration/digiKam/setup.py +0 -6
  25. api/batch_processing/integration/digiKam/xmp_integration.py +0 -465
  26. api/batch_processing/integration/eMammal/test_scripts/config_template.py +0 -5
  27. api/batch_processing/integration/eMammal/test_scripts/push_annotations_to_emammal.py +0 -126
  28. api/batch_processing/integration/eMammal/test_scripts/select_images_for_testing.py +0 -55
  29. api/batch_processing/postprocessing/__init__.py +0 -0
  30. api/batch_processing/postprocessing/add_max_conf.py +0 -64
  31. api/batch_processing/postprocessing/categorize_detections_by_size.py +0 -163
  32. api/batch_processing/postprocessing/combine_api_outputs.py +0 -249
  33. api/batch_processing/postprocessing/compare_batch_results.py +0 -958
  34. api/batch_processing/postprocessing/convert_output_format.py +0 -397
  35. api/batch_processing/postprocessing/load_api_results.py +0 -195
  36. api/batch_processing/postprocessing/md_to_coco.py +0 -310
  37. api/batch_processing/postprocessing/md_to_labelme.py +0 -330
  38. api/batch_processing/postprocessing/merge_detections.py +0 -401
  39. api/batch_processing/postprocessing/postprocess_batch_results.py +0 -1904
  40. api/batch_processing/postprocessing/remap_detection_categories.py +0 -170
  41. api/batch_processing/postprocessing/render_detection_confusion_matrix.py +0 -661
  42. api/batch_processing/postprocessing/repeat_detection_elimination/find_repeat_detections.py +0 -211
  43. api/batch_processing/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +0 -82
  44. api/batch_processing/postprocessing/repeat_detection_elimination/repeat_detections_core.py +0 -1631
  45. api/batch_processing/postprocessing/separate_detections_into_folders.py +0 -731
  46. api/batch_processing/postprocessing/subset_json_detector_output.py +0 -696
  47. api/batch_processing/postprocessing/top_folders_to_bottom.py +0 -223
  48. api/synchronous/__init__.py +0 -0
  49. api/synchronous/api_core/animal_detection_api/__init__.py +0 -0
  50. api/synchronous/api_core/animal_detection_api/api_backend.py +0 -152
  51. api/synchronous/api_core/animal_detection_api/api_frontend.py +0 -266
  52. api/synchronous/api_core/animal_detection_api/config.py +0 -35
  53. api/synchronous/api_core/animal_detection_api/data_management/annotations/annotation_constants.py +0 -47
  54. api/synchronous/api_core/animal_detection_api/detection/detector_training/copy_checkpoints.py +0 -43
  55. api/synchronous/api_core/animal_detection_api/detection/detector_training/model_main_tf2.py +0 -114
  56. api/synchronous/api_core/animal_detection_api/detection/process_video.py +0 -543
  57. api/synchronous/api_core/animal_detection_api/detection/pytorch_detector.py +0 -304
  58. api/synchronous/api_core/animal_detection_api/detection/run_detector.py +0 -627
  59. api/synchronous/api_core/animal_detection_api/detection/run_detector_batch.py +0 -1029
  60. api/synchronous/api_core/animal_detection_api/detection/run_inference_with_yolov5_val.py +0 -581
  61. api/synchronous/api_core/animal_detection_api/detection/run_tiled_inference.py +0 -754
  62. api/synchronous/api_core/animal_detection_api/detection/tf_detector.py +0 -165
  63. api/synchronous/api_core/animal_detection_api/detection/video_utils.py +0 -495
  64. api/synchronous/api_core/animal_detection_api/md_utils/azure_utils.py +0 -174
  65. api/synchronous/api_core/animal_detection_api/md_utils/ct_utils.py +0 -262
  66. api/synchronous/api_core/animal_detection_api/md_utils/directory_listing.py +0 -251
  67. api/synchronous/api_core/animal_detection_api/md_utils/matlab_porting_tools.py +0 -97
  68. api/synchronous/api_core/animal_detection_api/md_utils/path_utils.py +0 -416
  69. api/synchronous/api_core/animal_detection_api/md_utils/process_utils.py +0 -110
  70. api/synchronous/api_core/animal_detection_api/md_utils/sas_blob_utils.py +0 -509
  71. api/synchronous/api_core/animal_detection_api/md_utils/string_utils.py +0 -59
  72. api/synchronous/api_core/animal_detection_api/md_utils/url_utils.py +0 -144
  73. api/synchronous/api_core/animal_detection_api/md_utils/write_html_image_list.py +0 -226
  74. api/synchronous/api_core/animal_detection_api/md_visualization/visualization_utils.py +0 -841
  75. api/synchronous/api_core/tests/__init__.py +0 -0
  76. api/synchronous/api_core/tests/load_test.py +0 -110
  77. classification/__init__.py +0 -0
  78. classification/aggregate_classifier_probs.py +0 -108
  79. classification/analyze_failed_images.py +0 -227
  80. classification/cache_batchapi_outputs.py +0 -198
  81. classification/create_classification_dataset.py +0 -627
  82. classification/crop_detections.py +0 -516
  83. classification/csv_to_json.py +0 -226
  84. classification/detect_and_crop.py +0 -855
  85. classification/efficientnet/__init__.py +0 -9
  86. classification/efficientnet/model.py +0 -415
  87. classification/efficientnet/utils.py +0 -610
  88. classification/evaluate_model.py +0 -520
  89. classification/identify_mislabeled_candidates.py +0 -152
  90. classification/json_to_azcopy_list.py +0 -63
  91. classification/json_validator.py +0 -695
  92. classification/map_classification_categories.py +0 -276
  93. classification/merge_classification_detection_output.py +0 -506
  94. classification/prepare_classification_script.py +0 -194
  95. classification/prepare_classification_script_mc.py +0 -228
  96. classification/run_classifier.py +0 -286
  97. classification/save_mislabeled.py +0 -110
  98. classification/train_classifier.py +0 -825
  99. classification/train_classifier_tf.py +0 -724
  100. classification/train_utils.py +0 -322
  101. data_management/__init__.py +0 -0
  102. data_management/annotations/__init__.py +0 -0
  103. data_management/annotations/annotation_constants.py +0 -34
  104. data_management/camtrap_dp_to_coco.py +0 -238
  105. data_management/cct_json_utils.py +0 -395
  106. data_management/cct_to_md.py +0 -176
  107. data_management/cct_to_wi.py +0 -289
  108. data_management/coco_to_labelme.py +0 -272
  109. data_management/coco_to_yolo.py +0 -662
  110. data_management/databases/__init__.py +0 -0
  111. data_management/databases/add_width_and_height_to_db.py +0 -33
  112. data_management/databases/combine_coco_camera_traps_files.py +0 -206
  113. data_management/databases/integrity_check_json_db.py +0 -477
  114. data_management/databases/subset_json_db.py +0 -115
  115. data_management/generate_crops_from_cct.py +0 -149
  116. data_management/get_image_sizes.py +0 -188
  117. data_management/importers/add_nacti_sizes.py +0 -52
  118. data_management/importers/add_timestamps_to_icct.py +0 -79
  119. data_management/importers/animl_results_to_md_results.py +0 -158
  120. data_management/importers/auckland_doc_test_to_json.py +0 -372
  121. data_management/importers/auckland_doc_to_json.py +0 -200
  122. data_management/importers/awc_to_json.py +0 -189
  123. data_management/importers/bellevue_to_json.py +0 -273
  124. data_management/importers/cacophony-thermal-importer.py +0 -796
  125. data_management/importers/carrizo_shrubfree_2018.py +0 -268
  126. data_management/importers/carrizo_trail_cam_2017.py +0 -287
  127. data_management/importers/cct_field_adjustments.py +0 -57
  128. data_management/importers/channel_islands_to_cct.py +0 -913
  129. data_management/importers/eMammal/copy_and_unzip_emammal.py +0 -180
  130. data_management/importers/eMammal/eMammal_helpers.py +0 -249
  131. data_management/importers/eMammal/make_eMammal_json.py +0 -223
  132. data_management/importers/ena24_to_json.py +0 -275
  133. data_management/importers/filenames_to_json.py +0 -385
  134. data_management/importers/helena_to_cct.py +0 -282
  135. data_management/importers/idaho-camera-traps.py +0 -1407
  136. data_management/importers/idfg_iwildcam_lila_prep.py +0 -294
  137. data_management/importers/jb_csv_to_json.py +0 -150
  138. data_management/importers/mcgill_to_json.py +0 -250
  139. data_management/importers/missouri_to_json.py +0 -489
  140. data_management/importers/nacti_fieldname_adjustments.py +0 -79
  141. data_management/importers/noaa_seals_2019.py +0 -181
  142. data_management/importers/pc_to_json.py +0 -365
  143. data_management/importers/plot_wni_giraffes.py +0 -123
  144. data_management/importers/prepare-noaa-fish-data-for-lila.py +0 -359
  145. data_management/importers/prepare_zsl_imerit.py +0 -131
  146. data_management/importers/rspb_to_json.py +0 -356
  147. data_management/importers/save_the_elephants_survey_A.py +0 -320
  148. data_management/importers/save_the_elephants_survey_B.py +0 -332
  149. data_management/importers/snapshot_safari_importer.py +0 -758
  150. data_management/importers/snapshot_safari_importer_reprise.py +0 -665
  151. data_management/importers/snapshot_serengeti_lila.py +0 -1067
  152. data_management/importers/snapshotserengeti/make_full_SS_json.py +0 -150
  153. data_management/importers/snapshotserengeti/make_per_season_SS_json.py +0 -153
  154. data_management/importers/sulross_get_exif.py +0 -65
  155. data_management/importers/timelapse_csv_set_to_json.py +0 -490
  156. data_management/importers/ubc_to_json.py +0 -399
  157. data_management/importers/umn_to_json.py +0 -507
  158. data_management/importers/wellington_to_json.py +0 -263
  159. data_management/importers/wi_to_json.py +0 -441
  160. data_management/importers/zamba_results_to_md_results.py +0 -181
  161. data_management/labelme_to_coco.py +0 -548
  162. data_management/labelme_to_yolo.py +0 -272
  163. data_management/lila/__init__.py +0 -0
  164. data_management/lila/add_locations_to_island_camera_traps.py +0 -97
  165. data_management/lila/add_locations_to_nacti.py +0 -147
  166. data_management/lila/create_lila_blank_set.py +0 -557
  167. data_management/lila/create_lila_test_set.py +0 -151
  168. data_management/lila/create_links_to_md_results_files.py +0 -106
  169. data_management/lila/download_lila_subset.py +0 -177
  170. data_management/lila/generate_lila_per_image_labels.py +0 -515
  171. data_management/lila/get_lila_annotation_counts.py +0 -170
  172. data_management/lila/get_lila_image_counts.py +0 -111
  173. data_management/lila/lila_common.py +0 -300
  174. data_management/lila/test_lila_metadata_urls.py +0 -132
  175. data_management/ocr_tools.py +0 -874
  176. data_management/read_exif.py +0 -681
  177. data_management/remap_coco_categories.py +0 -84
  178. data_management/remove_exif.py +0 -66
  179. data_management/resize_coco_dataset.py +0 -189
  180. data_management/wi_download_csv_to_coco.py +0 -246
  181. data_management/yolo_output_to_md_output.py +0 -441
  182. data_management/yolo_to_coco.py +0 -676
  183. detection/__init__.py +0 -0
  184. detection/detector_training/__init__.py +0 -0
  185. detection/detector_training/model_main_tf2.py +0 -114
  186. detection/process_video.py +0 -703
  187. detection/pytorch_detector.py +0 -337
  188. detection/run_detector.py +0 -779
  189. detection/run_detector_batch.py +0 -1219
  190. detection/run_inference_with_yolov5_val.py +0 -917
  191. detection/run_tiled_inference.py +0 -935
  192. detection/tf_detector.py +0 -188
  193. detection/video_utils.py +0 -606
  194. docs/source/conf.py +0 -43
  195. md_utils/__init__.py +0 -0
  196. md_utils/azure_utils.py +0 -174
  197. md_utils/ct_utils.py +0 -612
  198. md_utils/directory_listing.py +0 -246
  199. md_utils/md_tests.py +0 -968
  200. md_utils/path_utils.py +0 -1044
  201. md_utils/process_utils.py +0 -157
  202. md_utils/sas_blob_utils.py +0 -509
  203. md_utils/split_locations_into_train_val.py +0 -228
  204. md_utils/string_utils.py +0 -92
  205. md_utils/url_utils.py +0 -323
  206. md_utils/write_html_image_list.py +0 -225
  207. md_visualization/__init__.py +0 -0
  208. md_visualization/plot_utils.py +0 -293
  209. md_visualization/render_images_with_thumbnails.py +0 -275
  210. md_visualization/visualization_utils.py +0 -1537
  211. md_visualization/visualize_db.py +0 -551
  212. md_visualization/visualize_detector_output.py +0 -406
  213. megadetector-5.0.9.dist-info/RECORD +0 -224
  214. megadetector-5.0.9.dist-info/top_level.txt +0 -8
  215. taxonomy_mapping/__init__.py +0 -0
  216. taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +0 -491
  217. taxonomy_mapping/map_new_lila_datasets.py +0 -154
  218. taxonomy_mapping/prepare_lila_taxonomy_release.py +0 -142
  219. taxonomy_mapping/preview_lila_taxonomy.py +0 -591
  220. taxonomy_mapping/retrieve_sample_image.py +0 -71
  221. taxonomy_mapping/simple_image_download.py +0 -218
  222. taxonomy_mapping/species_lookup.py +0 -834
  223. taxonomy_mapping/taxonomy_csv_checker.py +0 -159
  224. taxonomy_mapping/taxonomy_graph.py +0 -346
  225. taxonomy_mapping/validate_lila_category_mappings.py +0 -83
  226. {megadetector-5.0.9.dist-info → megadetector-5.0.11.dist-info}/WHEEL +0 -0
@@ -1,64 +0,0 @@
1
- """
2
-
3
- add_max_conf.py
4
-
5
- The MD output format included a "max_detection_conf" field with each image
6
- up to and including version 1.2; it was removed as of version 1.3 (it's
7
- redundant with the individual detection confidence values).
8
-
9
- Just in case someone took a dependency on that field, this script allows you
10
- to add it back to an existing .json file.
11
-
12
- """
13
-
14
- #%% Imports and constants
15
-
16
- import os
17
- import json
18
- from md_utils import ct_utils
19
-
20
-
21
- #%% Main function
22
-
23
- def add_max_conf(input_file,output_file):
24
-
25
- assert os.path.isfile(input_file), "Can't find input file {}".format(input_file)
26
-
27
- with open(input_file,'r') as f:
28
- d = json.load(f)
29
-
30
- for im in d['images']:
31
-
32
- max_conf = ct_utils.get_max_conf(im)
33
-
34
- if 'max_detection_conf' in im:
35
- assert abs(max_conf - im['max_detection_conf']) < 0.00001
36
- else:
37
- im['max_detection_conf'] = max_conf
38
-
39
- with open(output_file,'w') as f:
40
- json.dump(d,f,indent=1)
41
-
42
-
43
- #%% Driver
44
-
45
- import sys,argparse
46
-
47
- def main():
48
-
49
- parser = argparse.ArgumentParser()
50
- parser.add_argument('input_file',type=str,
51
- help='Input .json file')
52
- parser.add_argument('output_file',type=str,
53
- help='Output .json file')
54
-
55
- if len(sys.argv[1:]) == 0:
56
- parser.print_help()
57
- parser.exit()
58
-
59
- args = parser.parse_args()
60
- add_max_conf(args.input_file, args.output_file)
61
-
62
- if __name__ == '__main__':
63
- main()
64
-
@@ -1,163 +0,0 @@
1
- """
2
-
3
- categorize_detections_by_size.py
4
-
5
- Given a MegaDetector .json file, creates a separate category for bounding boxes
6
- above one or more size thresholds.
7
-
8
- """
9
-
10
- #%% Constants and imports
11
-
12
- import json
13
-
14
- from collections import defaultdict
15
- from tqdm import tqdm
16
-
17
-
18
- #%% Support classes
19
-
20
- class SizeCategorizationOptions:
21
- """
22
- Options used to parameterize categorize_detections_by_size().
23
- """
24
-
25
- #: Thresholds to use for separation, as a fraction of the image size.
26
- #:
27
- #: Should be sorted from smallest to largest.
28
- size_thresholds = [0.95]
29
-
30
- #: List of category numbers to use in separation; uses all categories if None
31
- categories_to_separate = None
32
-
33
- #: Dimension to use for thresholding; can be "size", "width", or "height"
34
- measurement = 'size'
35
-
36
- #: Categories to assign to thresholded ranges; should have the same length as
37
- #: "size_thresholds".
38
- size_category_names = ['large_detection']
39
-
40
-
41
- #%% Main functions
42
-
43
- def categorize_detections_by_size(input_file,output_file=None,options=None):
44
- """
45
- Given a MegaDetector .json file, creates a separate category for bounding boxes
46
- above one or more size thresholds, optionally writing results to [output_file].
47
-
48
- Args:
49
- input_file (str): file to process
50
- output_file (str, optional): optional output file
51
- options (SizeCategorizationOptions): categorization parameters
52
-
53
- Returns:
54
- dict: data loaded from [input_file], with the new size-based categories.
55
- Identical to what's written to [output_file], if [output_file] is not None.
56
- """
57
- if options is None:
58
- options = SizeCategorizationOptions()
59
-
60
- if options.categories_to_separate is not None:
61
- options.categories_to_separate = \
62
- [str(c) for c in options.categories_to_separate]
63
-
64
- assert len(options.size_thresholds) == len(options.size_category_names), \
65
- 'Options struct should have the same number of category names and size thresholds'
66
-
67
- # Sort size thresholds and names from largest to smallest
68
- options.size_category_names = [x for _,x in sorted(zip(options.size_thresholds,
69
- options.size_category_names),reverse=True)]
70
- options.size_thresholds = sorted(options.size_thresholds,reverse=True)
71
-
72
- with open(input_file) as f:
73
- data = json.load(f)
74
-
75
- detection_categories = data['detection_categories']
76
- category_keys = list(detection_categories.keys())
77
- category_keys = [int(k) for k in category_keys]
78
- max_key = max(category_keys)
79
-
80
- threshold_to_category_id = {}
81
- for i_threshold,threshold in enumerate(options.size_thresholds):
82
-
83
- category_id = str(max_key+1)
84
- max_key += 1
85
- detection_categories[category_id] = options.size_category_names[i_threshold]
86
- threshold_to_category_id[i_threshold] = category_id
87
-
88
- print('Creating category for {} with ID {}'.format(
89
- options.size_category_names[i_threshold],category_id))
90
-
91
- images = data['images']
92
-
93
- print('Loaded {} images'.format(len(images)))
94
-
95
- # For each image...
96
- #
97
- # im = images[0]
98
-
99
- category_id_to_count = defaultdict(int)
100
-
101
- for im in tqdm(images):
102
-
103
- if im['detections'] is None:
104
- assert im['failure'] is not None and len(im['failure']) > 0
105
- continue
106
-
107
- # d = im['detections'][0]
108
- for d in im['detections']:
109
-
110
- # Are there really any detections here?
111
- if (d is None) or ('bbox' not in d) or (d['bbox'] is None):
112
- continue
113
-
114
- # Is this a category we're supposed to process?
115
- if (options.categories_to_separate is not None) and \
116
- (d['category'] not in options.categories_to_separate):
117
- continue
118
-
119
- # https://github.com/agentmorris/MegaDetector/tree/master/api/batch_processing#detector-outputs
120
- w = d['bbox'][2]
121
- h = d['bbox'][3]
122
- detection_size = w*h
123
-
124
- metric = None
125
-
126
- if options.measurement == 'size':
127
- metric = detection_size
128
- elif options.measurement == 'width':
129
- metric = w
130
- else:
131
- assert options.measurement == 'height', 'Unrecognized measurement metric'
132
- metric = h
133
- assert metric is not None
134
-
135
- for i_threshold,threshold in enumerate(options.size_thresholds):
136
-
137
- if metric >= threshold:
138
-
139
- category_id = threshold_to_category_id[i_threshold]
140
-
141
- category_id_to_count[category_id] += 1
142
- d['category'] = category_id
143
-
144
- break
145
-
146
- # ...for each threshold
147
- # ...for each detection
148
-
149
- # ...for each image
150
-
151
- for i_threshold in range(0,len(options.size_thresholds)):
152
- category_name = options.size_category_names[i_threshold]
153
- category_id = threshold_to_category_id[i_threshold]
154
- category_count = category_id_to_count[category_id]
155
- print('Found {} detections in category {}'.format(category_count,category_name))
156
-
157
- if output_file is not None:
158
- with open(output_file,'w') as f:
159
- json.dump(data,f,indent=1)
160
-
161
- return data
162
-
163
- # ...def categorize_detections_by_size()
@@ -1,249 +0,0 @@
1
- """
2
-
3
- combine_api_outputs.py
4
-
5
- Merges two or more .json files in batch API output format, optionally
6
- writing the results to another .json file.
7
-
8
- * Concatenates image lists, erroring if images are not unique.
9
- * Errors if class lists are conflicting; errors on unrecognized fields.
10
- * Checks compatibility in info structs, within reason.
11
-
12
- File format:
13
-
14
- https://github.com/agentmorris/MegaDetector/tree/master/api/batch_processing#batch-processing-api-output-format
15
-
16
- Command-line use:
17
-
18
- combine_api_outputs input1.json input2.json ... inputN.json output.json
19
-
20
- Also see combine_api_shard_files() (not exposed via the command line yet) to
21
- combine the intermediate files created by the API.
22
-
23
- This does no checking for redundancy; if you are looking to ensemble
24
- the results of multiple model versions, see merge_detections.py.
25
-
26
- """
27
-
28
- #%% Constants and imports
29
-
30
- import argparse
31
- import sys
32
- import json
33
-
34
-
35
- #%% Merge functions
36
-
37
- def combine_api_output_files(input_files,
38
- output_file=None,
39
- require_uniqueness=True,
40
- verbose=True):
41
- """
42
- Merges the list of MD results files [input_files] into a single
43
- dictionary, optionally writing the result to [output_file].
44
-
45
- Args:
46
- input_files (list of str): paths to JSON detection files
47
- output_file (str, optional): path to write merged JSON
48
- require_uniqueness (bool): whether to require that the images in
49
- each list of images be unique
50
-
51
- Returns:
52
- dict: merged dictionaries loaded from [input_files], identical to what's
53
- written to [output_file] if [output_file] is not None
54
- """
55
-
56
- def print_if_verbose(s):
57
- if verbose:
58
- print(s)
59
-
60
- input_dicts = []
61
- for fn in input_files:
62
- print_if_verbose('Loading results from {}'.format(fn))
63
- with open(fn, 'r', encoding='utf-8') as f:
64
- input_dicts.append(json.load(f))
65
-
66
- print_if_verbose('Merging results')
67
- merged_dict = combine_api_output_dictionaries(
68
- input_dicts, require_uniqueness=require_uniqueness)
69
-
70
- print_if_verbose('Writing output to {}'.format(output_file))
71
- if output_file is not None:
72
- with open(output_file, 'w') as f:
73
- json.dump(merged_dict, f, indent=1)
74
-
75
- return merged_dict
76
-
77
-
78
- def combine_api_output_dictionaries(input_dicts, require_uniqueness=True):
79
- """
80
- Merges the list of MD results dictionaries [input_dicts] into a single dict.
81
- See module header comment for details on merge rules.
82
-
83
- Args:
84
- input_dicts (list of dicts): list of dicts in which each dict represents the
85
- contents of a MD output file
86
- require_uniqueness (bool): whether to require that the images in
87
- each input dict be unique; if this is True and image filenames are
88
- not unique, an error is raised.
89
-
90
- Returns
91
- dict: merged MD results
92
- """
93
-
94
- # Map image filenames to detections, we'll convert to a list later
95
- images = {}
96
- info = {}
97
- detection_categories = {}
98
- classification_categories = {}
99
- n_redundant_images = 0
100
- n_images = 0
101
-
102
- known_fields = ['info', 'detection_categories', 'classification_categories',
103
- 'images']
104
-
105
- for input_dict in input_dicts:
106
-
107
- for k in input_dict:
108
- if k not in known_fields:
109
- raise ValueError(f'Unrecognized API output field: {k}')
110
-
111
- # Check compatibility of detection categories
112
- for cat_id in input_dict['detection_categories']:
113
- cat_name = input_dict['detection_categories'][cat_id]
114
- if cat_id in detection_categories:
115
- assert detection_categories[cat_id] == cat_name, (
116
- 'Detection category mismatch')
117
- else:
118
- detection_categories[cat_id] = cat_name
119
-
120
- # Check compatibility of classification categories
121
- if 'classification_categories' in input_dict:
122
- for cat_id in input_dict['classification_categories']:
123
- cat_name = input_dict['classification_categories'][cat_id]
124
- if cat_id in classification_categories:
125
- assert classification_categories[cat_id] == cat_name, (
126
- 'Classification category mismatch')
127
- else:
128
- classification_categories[cat_id] = cat_name
129
-
130
- # Merge image lists, checking uniqueness
131
- for im in input_dict['images']:
132
- # Normalize path separators so we don't treat images as different if they
133
- # were processed on different OS's
134
- im['file'] = im['file'].replace('\\','/')
135
- im_file = im['file']
136
- if require_uniqueness:
137
- assert im_file not in images, f'Duplicate image: {im_file}'
138
- images[im_file] = im
139
- n_images += 1
140
- else:
141
- if im_file in images:
142
- n_redundant_images += 1
143
- previous_im = images[im_file]
144
- # Replace a previous failure with a success
145
- if ('detections' in im) and ('detections' not in previous_im):
146
- images[im_file] = im
147
- print(f'Replacing previous failure for image: {im_file}')
148
- else:
149
- images[im_file] = im
150
- n_images += 1
151
-
152
- # Merge info dicts, don't check completion time fields
153
- if len(info) == 0:
154
- info = input_dict['info']
155
- else:
156
- info_compare = input_dict['info']
157
- assert info_compare['detector'] == info['detector'], (
158
- 'Incompatible detection versions in merging')
159
- assert info_compare['format_version'] == info['format_version'], (
160
- 'Incompatible API output versions in merging')
161
- if 'classifier' in info_compare:
162
- if 'classifier' in info:
163
- assert info['classifier'] == info_compare['classifier']
164
- else:
165
- info['classifier'] = info_compare['classifier']
166
-
167
- # ...for each dictionary
168
-
169
- if n_redundant_images > 0:
170
- print(f'Warning: found {n_redundant_images} redundant images '
171
- f'(out of {n_images} total) during merge')
172
-
173
- # Convert merged image dictionaries to a sorted list
174
- sorted_images = sorted(images.values(), key=lambda im: im['file'])
175
-
176
- merged_dict = {'info': info,
177
- 'detection_categories': detection_categories,
178
- 'classification_categories': classification_categories,
179
- 'images': sorted_images}
180
- return merged_dict
181
-
182
- # ...combine_api_output_files()
183
-
184
-
185
- def combine_api_shard_files(input_files, output_file=None):
186
- """
187
- Merges the list of .json-formatted API shard files [input_files] into a single
188
- list of dictionaries, optionally writing the result to [output_file].
189
-
190
- This operates on mostly-deprecated API shard files, not MegaDetector results files.
191
- If you don't know what an API shard file is, you don't want this function.
192
-
193
- Args:
194
- input_files (list of str): files to merge
195
- output_file (str, optiona): file to which we should write merged results
196
-
197
- Returns:
198
- dict: merged results
199
-
200
- :meta private:
201
- """
202
-
203
- input_lists = []
204
- print('Loading input files')
205
- for fn in input_files:
206
- input_lists.append(json.load(open(fn)))
207
-
208
- detections = []
209
- # detection_list = input_lists[0]
210
- for detection_list in input_lists:
211
- assert isinstance(detection_list, list)
212
- # d = detection_list[0]
213
- for d in detection_list:
214
- assert 'file' in d
215
- assert 'max_detection_conf' in d
216
- assert 'detections' in d
217
- detections.extend([d])
218
-
219
- print('Writing output')
220
- if output_file is not None:
221
- with open(output_file, 'w') as f:
222
- json.dump(detections, f, indent=1)
223
-
224
- return detections
225
-
226
- # ...combine_api_shard_files()
227
-
228
-
229
- #%% Command-line driver
230
-
231
- def main():
232
-
233
- parser = argparse.ArgumentParser()
234
- parser.add_argument(
235
- 'input_paths', nargs='+',
236
- help='List of input .json files')
237
- parser.add_argument(
238
- 'output_path',
239
- help='Output .json file')
240
-
241
- if len(sys.argv[1:]) == 0:
242
- parser.print_help()
243
- parser.exit()
244
-
245
- args = parser.parse_args()
246
- combine_api_output_files(args.input_paths, args.output_path)
247
-
248
- if __name__ == '__main__':
249
- main()