megadetector 5.0.11__py3-none-any.whl → 5.0.13__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 (203) hide show
  1. megadetector/api/__init__.py +0 -0
  2. megadetector/api/batch_processing/__init__.py +0 -0
  3. megadetector/api/batch_processing/api_core/__init__.py +0 -0
  4. megadetector/api/batch_processing/api_core/batch_service/__init__.py +0 -0
  5. megadetector/api/batch_processing/api_core/batch_service/score.py +439 -0
  6. megadetector/api/batch_processing/api_core/server.py +294 -0
  7. megadetector/api/batch_processing/api_core/server_api_config.py +97 -0
  8. megadetector/api/batch_processing/api_core/server_app_config.py +55 -0
  9. megadetector/api/batch_processing/api_core/server_batch_job_manager.py +220 -0
  10. megadetector/api/batch_processing/api_core/server_job_status_table.py +149 -0
  11. megadetector/api/batch_processing/api_core/server_orchestration.py +360 -0
  12. megadetector/api/batch_processing/api_core/server_utils.py +88 -0
  13. megadetector/api/batch_processing/api_core_support/__init__.py +0 -0
  14. megadetector/api/batch_processing/api_core_support/aggregate_results_manually.py +46 -0
  15. megadetector/api/batch_processing/api_support/__init__.py +0 -0
  16. megadetector/api/batch_processing/api_support/summarize_daily_activity.py +152 -0
  17. megadetector/api/batch_processing/data_preparation/__init__.py +0 -0
  18. megadetector/api/batch_processing/integration/digiKam/setup.py +6 -0
  19. megadetector/api/batch_processing/integration/digiKam/xmp_integration.py +465 -0
  20. megadetector/api/batch_processing/integration/eMammal/test_scripts/config_template.py +5 -0
  21. megadetector/api/batch_processing/integration/eMammal/test_scripts/push_annotations_to_emammal.py +125 -0
  22. megadetector/api/batch_processing/integration/eMammal/test_scripts/select_images_for_testing.py +55 -0
  23. megadetector/api/synchronous/__init__.py +0 -0
  24. megadetector/api/synchronous/api_core/animal_detection_api/__init__.py +0 -0
  25. megadetector/api/synchronous/api_core/animal_detection_api/api_backend.py +152 -0
  26. megadetector/api/synchronous/api_core/animal_detection_api/api_frontend.py +263 -0
  27. megadetector/api/synchronous/api_core/animal_detection_api/config.py +35 -0
  28. megadetector/api/synchronous/api_core/tests/__init__.py +0 -0
  29. megadetector/api/synchronous/api_core/tests/load_test.py +110 -0
  30. megadetector/classification/__init__.py +0 -0
  31. megadetector/classification/aggregate_classifier_probs.py +108 -0
  32. megadetector/classification/analyze_failed_images.py +227 -0
  33. megadetector/classification/cache_batchapi_outputs.py +198 -0
  34. megadetector/classification/create_classification_dataset.py +627 -0
  35. megadetector/classification/crop_detections.py +516 -0
  36. megadetector/classification/csv_to_json.py +226 -0
  37. megadetector/classification/detect_and_crop.py +855 -0
  38. megadetector/classification/efficientnet/__init__.py +9 -0
  39. megadetector/classification/efficientnet/model.py +415 -0
  40. megadetector/classification/efficientnet/utils.py +607 -0
  41. megadetector/classification/evaluate_model.py +520 -0
  42. megadetector/classification/identify_mislabeled_candidates.py +152 -0
  43. megadetector/classification/json_to_azcopy_list.py +63 -0
  44. megadetector/classification/json_validator.py +699 -0
  45. megadetector/classification/map_classification_categories.py +276 -0
  46. megadetector/classification/merge_classification_detection_output.py +506 -0
  47. megadetector/classification/prepare_classification_script.py +194 -0
  48. megadetector/classification/prepare_classification_script_mc.py +228 -0
  49. megadetector/classification/run_classifier.py +287 -0
  50. megadetector/classification/save_mislabeled.py +110 -0
  51. megadetector/classification/train_classifier.py +827 -0
  52. megadetector/classification/train_classifier_tf.py +725 -0
  53. megadetector/classification/train_utils.py +323 -0
  54. megadetector/data_management/__init__.py +0 -0
  55. megadetector/data_management/annotations/__init__.py +0 -0
  56. megadetector/data_management/annotations/annotation_constants.py +34 -0
  57. megadetector/data_management/camtrap_dp_to_coco.py +237 -0
  58. megadetector/data_management/cct_json_utils.py +404 -0
  59. megadetector/data_management/cct_to_md.py +176 -0
  60. megadetector/data_management/cct_to_wi.py +289 -0
  61. megadetector/data_management/coco_to_labelme.py +283 -0
  62. megadetector/data_management/coco_to_yolo.py +662 -0
  63. megadetector/data_management/databases/__init__.py +0 -0
  64. megadetector/data_management/databases/add_width_and_height_to_db.py +33 -0
  65. megadetector/data_management/databases/combine_coco_camera_traps_files.py +206 -0
  66. megadetector/data_management/databases/integrity_check_json_db.py +493 -0
  67. megadetector/data_management/databases/subset_json_db.py +115 -0
  68. megadetector/data_management/generate_crops_from_cct.py +149 -0
  69. megadetector/data_management/get_image_sizes.py +189 -0
  70. megadetector/data_management/importers/add_nacti_sizes.py +52 -0
  71. megadetector/data_management/importers/add_timestamps_to_icct.py +79 -0
  72. megadetector/data_management/importers/animl_results_to_md_results.py +158 -0
  73. megadetector/data_management/importers/auckland_doc_test_to_json.py +373 -0
  74. megadetector/data_management/importers/auckland_doc_to_json.py +201 -0
  75. megadetector/data_management/importers/awc_to_json.py +191 -0
  76. megadetector/data_management/importers/bellevue_to_json.py +273 -0
  77. megadetector/data_management/importers/cacophony-thermal-importer.py +793 -0
  78. megadetector/data_management/importers/carrizo_shrubfree_2018.py +269 -0
  79. megadetector/data_management/importers/carrizo_trail_cam_2017.py +289 -0
  80. megadetector/data_management/importers/cct_field_adjustments.py +58 -0
  81. megadetector/data_management/importers/channel_islands_to_cct.py +913 -0
  82. megadetector/data_management/importers/eMammal/copy_and_unzip_emammal.py +180 -0
  83. megadetector/data_management/importers/eMammal/eMammal_helpers.py +249 -0
  84. megadetector/data_management/importers/eMammal/make_eMammal_json.py +223 -0
  85. megadetector/data_management/importers/ena24_to_json.py +276 -0
  86. megadetector/data_management/importers/filenames_to_json.py +386 -0
  87. megadetector/data_management/importers/helena_to_cct.py +283 -0
  88. megadetector/data_management/importers/idaho-camera-traps.py +1407 -0
  89. megadetector/data_management/importers/idfg_iwildcam_lila_prep.py +294 -0
  90. megadetector/data_management/importers/jb_csv_to_json.py +150 -0
  91. megadetector/data_management/importers/mcgill_to_json.py +250 -0
  92. megadetector/data_management/importers/missouri_to_json.py +490 -0
  93. megadetector/data_management/importers/nacti_fieldname_adjustments.py +79 -0
  94. megadetector/data_management/importers/noaa_seals_2019.py +181 -0
  95. megadetector/data_management/importers/pc_to_json.py +365 -0
  96. megadetector/data_management/importers/plot_wni_giraffes.py +123 -0
  97. megadetector/data_management/importers/prepare-noaa-fish-data-for-lila.py +359 -0
  98. megadetector/data_management/importers/prepare_zsl_imerit.py +131 -0
  99. megadetector/data_management/importers/rspb_to_json.py +356 -0
  100. megadetector/data_management/importers/save_the_elephants_survey_A.py +320 -0
  101. megadetector/data_management/importers/save_the_elephants_survey_B.py +329 -0
  102. megadetector/data_management/importers/snapshot_safari_importer.py +758 -0
  103. megadetector/data_management/importers/snapshot_safari_importer_reprise.py +665 -0
  104. megadetector/data_management/importers/snapshot_serengeti_lila.py +1067 -0
  105. megadetector/data_management/importers/snapshotserengeti/make_full_SS_json.py +150 -0
  106. megadetector/data_management/importers/snapshotserengeti/make_per_season_SS_json.py +153 -0
  107. megadetector/data_management/importers/sulross_get_exif.py +65 -0
  108. megadetector/data_management/importers/timelapse_csv_set_to_json.py +490 -0
  109. megadetector/data_management/importers/ubc_to_json.py +399 -0
  110. megadetector/data_management/importers/umn_to_json.py +507 -0
  111. megadetector/data_management/importers/wellington_to_json.py +263 -0
  112. megadetector/data_management/importers/wi_to_json.py +442 -0
  113. megadetector/data_management/importers/zamba_results_to_md_results.py +181 -0
  114. megadetector/data_management/labelme_to_coco.py +547 -0
  115. megadetector/data_management/labelme_to_yolo.py +272 -0
  116. megadetector/data_management/lila/__init__.py +0 -0
  117. megadetector/data_management/lila/add_locations_to_island_camera_traps.py +97 -0
  118. megadetector/data_management/lila/add_locations_to_nacti.py +147 -0
  119. megadetector/data_management/lila/create_lila_blank_set.py +558 -0
  120. megadetector/data_management/lila/create_lila_test_set.py +152 -0
  121. megadetector/data_management/lila/create_links_to_md_results_files.py +106 -0
  122. megadetector/data_management/lila/download_lila_subset.py +178 -0
  123. megadetector/data_management/lila/generate_lila_per_image_labels.py +516 -0
  124. megadetector/data_management/lila/get_lila_annotation_counts.py +170 -0
  125. megadetector/data_management/lila/get_lila_image_counts.py +112 -0
  126. megadetector/data_management/lila/lila_common.py +300 -0
  127. megadetector/data_management/lila/test_lila_metadata_urls.py +132 -0
  128. megadetector/data_management/ocr_tools.py +870 -0
  129. megadetector/data_management/read_exif.py +809 -0
  130. megadetector/data_management/remap_coco_categories.py +84 -0
  131. megadetector/data_management/remove_exif.py +66 -0
  132. megadetector/data_management/rename_images.py +187 -0
  133. megadetector/data_management/resize_coco_dataset.py +189 -0
  134. megadetector/data_management/wi_download_csv_to_coco.py +247 -0
  135. megadetector/data_management/yolo_output_to_md_output.py +446 -0
  136. megadetector/data_management/yolo_to_coco.py +676 -0
  137. megadetector/detection/__init__.py +0 -0
  138. megadetector/detection/detector_training/__init__.py +0 -0
  139. megadetector/detection/detector_training/model_main_tf2.py +114 -0
  140. megadetector/detection/process_video.py +846 -0
  141. megadetector/detection/pytorch_detector.py +355 -0
  142. megadetector/detection/run_detector.py +779 -0
  143. megadetector/detection/run_detector_batch.py +1219 -0
  144. megadetector/detection/run_inference_with_yolov5_val.py +1087 -0
  145. megadetector/detection/run_tiled_inference.py +934 -0
  146. megadetector/detection/tf_detector.py +192 -0
  147. megadetector/detection/video_utils.py +698 -0
  148. megadetector/postprocessing/__init__.py +0 -0
  149. megadetector/postprocessing/add_max_conf.py +64 -0
  150. megadetector/postprocessing/categorize_detections_by_size.py +165 -0
  151. megadetector/postprocessing/classification_postprocessing.py +716 -0
  152. megadetector/postprocessing/combine_api_outputs.py +249 -0
  153. megadetector/postprocessing/compare_batch_results.py +966 -0
  154. megadetector/postprocessing/convert_output_format.py +396 -0
  155. megadetector/postprocessing/load_api_results.py +195 -0
  156. megadetector/postprocessing/md_to_coco.py +310 -0
  157. megadetector/postprocessing/md_to_labelme.py +330 -0
  158. megadetector/postprocessing/merge_detections.py +412 -0
  159. megadetector/postprocessing/postprocess_batch_results.py +1908 -0
  160. megadetector/postprocessing/remap_detection_categories.py +170 -0
  161. megadetector/postprocessing/render_detection_confusion_matrix.py +660 -0
  162. megadetector/postprocessing/repeat_detection_elimination/find_repeat_detections.py +211 -0
  163. megadetector/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +83 -0
  164. megadetector/postprocessing/repeat_detection_elimination/repeat_detections_core.py +1635 -0
  165. megadetector/postprocessing/separate_detections_into_folders.py +730 -0
  166. megadetector/postprocessing/subset_json_detector_output.py +700 -0
  167. megadetector/postprocessing/top_folders_to_bottom.py +223 -0
  168. megadetector/taxonomy_mapping/__init__.py +0 -0
  169. megadetector/taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +491 -0
  170. megadetector/taxonomy_mapping/map_new_lila_datasets.py +150 -0
  171. megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +142 -0
  172. megadetector/taxonomy_mapping/preview_lila_taxonomy.py +588 -0
  173. megadetector/taxonomy_mapping/retrieve_sample_image.py +71 -0
  174. megadetector/taxonomy_mapping/simple_image_download.py +219 -0
  175. megadetector/taxonomy_mapping/species_lookup.py +834 -0
  176. megadetector/taxonomy_mapping/taxonomy_csv_checker.py +159 -0
  177. megadetector/taxonomy_mapping/taxonomy_graph.py +346 -0
  178. megadetector/taxonomy_mapping/validate_lila_category_mappings.py +83 -0
  179. megadetector/utils/__init__.py +0 -0
  180. megadetector/utils/azure_utils.py +178 -0
  181. megadetector/utils/ct_utils.py +613 -0
  182. megadetector/utils/directory_listing.py +246 -0
  183. megadetector/utils/md_tests.py +1164 -0
  184. megadetector/utils/path_utils.py +1045 -0
  185. megadetector/utils/process_utils.py +160 -0
  186. megadetector/utils/sas_blob_utils.py +509 -0
  187. megadetector/utils/split_locations_into_train_val.py +228 -0
  188. megadetector/utils/string_utils.py +92 -0
  189. megadetector/utils/url_utils.py +323 -0
  190. megadetector/utils/write_html_image_list.py +225 -0
  191. megadetector/visualization/__init__.py +0 -0
  192. megadetector/visualization/plot_utils.py +293 -0
  193. megadetector/visualization/render_images_with_thumbnails.py +275 -0
  194. megadetector/visualization/visualization_utils.py +1536 -0
  195. megadetector/visualization/visualize_db.py +552 -0
  196. megadetector/visualization/visualize_detector_output.py +405 -0
  197. {megadetector-5.0.11.dist-info → megadetector-5.0.13.dist-info}/LICENSE +0 -0
  198. {megadetector-5.0.11.dist-info → megadetector-5.0.13.dist-info}/METADATA +2 -2
  199. megadetector-5.0.13.dist-info/RECORD +201 -0
  200. megadetector-5.0.13.dist-info/top_level.txt +1 -0
  201. megadetector-5.0.11.dist-info/RECORD +0 -5
  202. megadetector-5.0.11.dist-info/top_level.txt +0 -1
  203. {megadetector-5.0.11.dist-info → megadetector-5.0.13.dist-info}/WHEEL +0 -0
@@ -0,0 +1,412 @@
1
+ """
2
+
3
+ merge_detections.py
4
+
5
+ Merge high-confidence detections from one or more results files into another
6
+ file. Typically used to combine results from MDv5b and/or MDv4 into a "primary"
7
+ results file from MDv5a.
8
+
9
+ Detection categories must be the same in both files; if you want to first remap
10
+ one file's category mapping to be the same as another's, see remap_detection_categories.
11
+
12
+ If you want to literally merge two .json files, see combine_api_outputs.py.
13
+
14
+ """
15
+
16
+ #%% Constants and imports
17
+
18
+ import argparse
19
+ import sys
20
+ import json
21
+ import os
22
+
23
+ from tqdm import tqdm
24
+
25
+ from megadetector.utils.ct_utils import get_iou
26
+
27
+
28
+ #%% Structs
29
+
30
+ class MergeDetectionsOptions:
31
+
32
+ def __init__(self):
33
+
34
+ #: Maximum detection size to include in the merged output
35
+ self.max_detection_size = 1.01
36
+
37
+ #: Minimum detection size to include in the merged output
38
+ self.min_detection_size = 0
39
+
40
+ #: Exclude detections whose confidence in the source file(s) is less
41
+ #: than this. Should have the same length as the number of source files.
42
+ self.source_confidence_thresholds = [0.05]
43
+
44
+ #: Don't bother merging into target images if there is a similar detection
45
+ #: above this threshold (or if there is *any* detection above this threshold,
46
+ #: and merge_empty_only is True)
47
+ self.target_confidence_threshold = 0.2
48
+
49
+ #: If you want to merge only certain categories, specify one
50
+ #: (but not both) of these. These are category IDs, not names.
51
+ self.categories_to_include = None
52
+
53
+ #: If you want to merge only certain categories, specify one
54
+ #: (but not both) of these. These are category IDs, not names.
55
+ self.categories_to_exclude = None
56
+
57
+ #: Only merge detections into images that have *no* detections in the
58
+ #: target results file.
59
+ self.merge_empty_only = False
60
+
61
+ #: IoU threshold above which two detections are considered the same
62
+ self.iou_threshold = 0.65
63
+
64
+ #: Error if this is False and the output file exists
65
+ self.overwrite = False
66
+
67
+
68
+ #%% Main function
69
+
70
+ def merge_detections(source_files,target_file,output_file,options=None):
71
+ """
72
+ Merge high-confidence detections from one or more results files into another
73
+ file. Typically used to combine results from MDv5b and/or MDv4 into a "primary"
74
+ results file from MDv5a.
75
+
76
+ [source_files] (a list of files or a single filename) specifies the set of
77
+ results files that will be merged into [target_file]. The difference between a
78
+ "source file" and the "target file" is that if no merging is necessary, either because
79
+ two boxes are nearly identical or because merge_only_empty is True and the target
80
+ file already has above-threshold detection for an image+category, the output file gets
81
+ the results of the "target" file. I.e., the "target" file wins all ties.
82
+
83
+ The results are written to [output_file].
84
+
85
+ """
86
+
87
+ if isinstance(source_files,str):
88
+ source_files = [source_files]
89
+
90
+ if options is None:
91
+ options = MergeDetectionsOptions()
92
+
93
+ if (not options.overwrite) and (os.path.isfile(output_file)):
94
+ print('File {} exists, bypassing merge'.format(output_file))
95
+ return
96
+
97
+ assert not ((options.categories_to_exclude is not None) and \
98
+ (options.categories_to_include is not None)), \
99
+ 'categories_to_include and categories_to_exclude are mutually exclusive'
100
+
101
+ if options.categories_to_exclude is not None:
102
+ options.categories_to_exclude = [int(c) for c in options.categories_to_exclude]
103
+
104
+ if options.categories_to_include is not None:
105
+ options.categories_to_include = [int(c) for c in options.categories_to_include]
106
+
107
+ assert len(source_files) == len(options.source_confidence_thresholds), \
108
+ '{} source files provided, but {} source confidence thresholds provided'.format(
109
+ len(source_files),len(options.source_confidence_thresholds))
110
+
111
+ for fn in source_files:
112
+ assert os.path.isfile(fn), 'Could not find source file {}'.format(fn)
113
+
114
+ assert os.path.isfile(target_file)
115
+
116
+ os.makedirs(os.path.dirname(output_file),exist_ok=True)
117
+
118
+ with open(target_file,'r') as f:
119
+ output_data = json.load(f)
120
+
121
+ print('Loaded results for {} images'.format(len(output_data['images'])))
122
+
123
+ fn_to_image = {}
124
+
125
+ # im = output_data['images'][0]
126
+ for im in output_data['images']:
127
+ fn_to_image[im['file']] = im
128
+
129
+ if 'detections_transferred_from' not in output_data['info']:
130
+ output_data['info']['detections_transferred_from'] = []
131
+
132
+ if 'detector' not in output_data['info']:
133
+ output_data['info']['detector'] = 'MDv4 (assumed)'
134
+
135
+ detection_categories_raw = output_data['detection_categories'].keys()
136
+
137
+ # Determine whether we should be processing all categories, or just a subset
138
+ # of categories.
139
+ detection_categories = []
140
+
141
+ if options.categories_to_exclude is not None:
142
+ for c in detection_categories_raw:
143
+ if int(c) not in options.categories_to_exclude:
144
+ detection_categories.append(c)
145
+ else:
146
+ print('Excluding category {}'.format(c))
147
+ elif options.categories_to_include is not None:
148
+ for c in detection_categories_raw:
149
+ if int(c) in options.categories_to_include:
150
+ print('Including category {}'.format(c))
151
+ detection_categories.append(c)
152
+ else:
153
+ detection_categories = detection_categories_raw
154
+
155
+ # i_source_file = 0; source_file = source_files[i_source_file]
156
+ for i_source_file,source_file in enumerate(source_files):
157
+
158
+ print('Processing detections from file {}'.format(source_file))
159
+
160
+ with open(source_file,'r') as f:
161
+ source_data = json.load(f)
162
+
163
+ if 'detector' in source_data['info']:
164
+ source_detector_name = source_data['info']['detector']
165
+ else:
166
+ source_detector_name = os.path.basename(source_file)
167
+
168
+ output_data['info']['detections_transferred_from'].append(os.path.basename(source_file))
169
+ output_data['info']['detector'] = output_data['info']['detector'] + ' + ' + source_detector_name
170
+
171
+ assert source_data['detection_categories'] == output_data['detection_categories'], \
172
+ 'Cannot merge files with different detection category maps'
173
+
174
+ source_confidence_threshold = options.source_confidence_thresholds[i_source_file]
175
+
176
+ # source_im = source_data['images'][0]
177
+ for source_im in tqdm(source_data['images']):
178
+
179
+ image_filename = source_im['file']
180
+
181
+ assert image_filename in fn_to_image, 'Image {} not in target image set'.format(image_filename)
182
+ target_im = fn_to_image[image_filename]
183
+
184
+ if 'detections' not in source_im or source_im['detections'] is None:
185
+ continue
186
+
187
+ if 'detections' not in target_im or target_im['detections'] is None:
188
+ continue
189
+
190
+ source_detections_this_image = source_im['detections']
191
+ target_detections_this_image = target_im['detections']
192
+
193
+ detections_to_transfer = []
194
+
195
+ # detection_category = list(detection_categories)[0]
196
+ for detection_category in detection_categories:
197
+
198
+ target_detections_this_category = \
199
+ [det for det in target_detections_this_image if det['category'] == \
200
+ detection_category]
201
+
202
+ max_target_confidence_this_category = 0.0
203
+
204
+ if len(target_detections_this_category) > 0:
205
+ max_target_confidence_this_category = max([det['conf'] for \
206
+ det in target_detections_this_category])
207
+
208
+ # If we have a valid detection in the target file, and we're only merging
209
+ # into images that have no detections at all, we don't need to review the individual
210
+ # detections in the source file.
211
+ if options.merge_empty_only and \
212
+ (max_target_confidence_this_category >= options.target_confidence_threshold):
213
+ continue
214
+
215
+ source_detections_this_category_raw = [det for det in \
216
+ source_detections_this_image if det['category'] == detection_category]
217
+
218
+ # Boxes are x/y/w/h
219
+ # source_sizes = [det['bbox'][2]*det['bbox'][3] for det in source_detections_this_category_raw]
220
+
221
+ # Only look at source boxes within the size range
222
+ source_detections_this_category_filtered = [
223
+ det for det in source_detections_this_category_raw if \
224
+ (det['bbox'][2]*det['bbox'][3] <= options.max_detection_size) and \
225
+ (det['bbox'][2]*det['bbox'][3] >= options.min_detection_size) \
226
+ ]
227
+
228
+ # det = source_detections_this_category_filtered[0]
229
+ for det in source_detections_this_category_filtered:
230
+
231
+ if det['conf'] >= source_confidence_threshold:
232
+
233
+ # Check only whole images
234
+ if options.merge_empty_only:
235
+
236
+ # We verified this above, asserting here for clarity
237
+ assert max_target_confidence_this_category < options.target_confidence_threshold
238
+ det['transferred_from'] = source_detector_name
239
+ detections_to_transfer.append(det)
240
+
241
+ # Check individual detections
242
+ else:
243
+
244
+ # Does this source detection match any existing above-threshold
245
+ # target category detections?
246
+ matches_existing_box = False
247
+
248
+ # target_detection = target_detections_this_category[0]
249
+ for target_detection in target_detections_this_category:
250
+
251
+ if (target_detection['conf'] >= options.target_confidence_threshold) \
252
+ and \
253
+ (get_iou(det['bbox'],target_detection['bbox']) >= options.iou_threshold):
254
+ matches_existing_box = True
255
+ break
256
+
257
+ if (not matches_existing_box):
258
+ det['transferred_from'] = source_detector_name
259
+ detections_to_transfer.append(det)
260
+
261
+ # ...if this source detection is above the confidence threshold
262
+
263
+ # ...for each source detection within category
264
+
265
+ # ...for each detection category
266
+
267
+ if len(detections_to_transfer) > 0:
268
+
269
+ # print('Adding {} detections to image {}'.format(len(detections_to_transfer),image_filename))
270
+ detections = fn_to_image[image_filename]['detections']
271
+ detections.extend(detections_to_transfer)
272
+
273
+ # Update the max_detection_conf field (if present)
274
+ if 'max_detection_conf' in fn_to_image[image_filename]:
275
+ fn_to_image[image_filename]['max_detection_conf'] = \
276
+ max([d['conf'] for d in detections])
277
+
278
+ # ...if we have any detections to transfer
279
+
280
+ # ...for each image
281
+
282
+ # ...for each source file
283
+
284
+ with open(output_file,'w') as f:
285
+ json.dump(output_data,f,indent=1)
286
+
287
+ print('Saved merged results to {}'.format(output_file))
288
+
289
+
290
+ #%% Command-line driver
291
+
292
+ def main():
293
+
294
+ default_options = MergeDetectionsOptions()
295
+
296
+ parser = argparse.ArgumentParser(
297
+ description='Merge detections from one or more MegaDetector results files into an existing reuslts file')
298
+ parser.add_argument(
299
+ 'source_files',
300
+ nargs="+",
301
+ help='Path to source .json file(s) to merge from')
302
+ parser.add_argument(
303
+ 'target_file',
304
+ help='Path to a .json file to merge detections into')
305
+ parser.add_argument(
306
+ 'output_file',
307
+ help='Path to output .json results file')
308
+ parser.add_argument(
309
+ '--max_detection_size',
310
+ type=float,
311
+ default=default_options.max_detection_size,
312
+ help='Ignore detections with an area larger than this (as a fraction of ' + \
313
+ 'image size) (default {})'.format(
314
+ default_options.max_detection_size))
315
+ parser.add_argument(
316
+ '--min_detection_size',
317
+ default=default_options.min_detection_size,
318
+ type=float,
319
+ help='Ignore detections with an area smaller than this (as a fraction of ' + \
320
+ 'image size) (default {})'.format(
321
+ default_options.min_detection_size))
322
+ parser.add_argument(
323
+ '--source_confidence_thresholds',
324
+ nargs="+",
325
+ type=float,
326
+ default=default_options.source_confidence_thresholds,
327
+ help='List of thresholds for each source file (default {}). '.format(
328
+ default_options.source_confidence_thresholds) + \
329
+ 'Merge only if the source file\'s detection confidence is higher than its ' + \
330
+ 'corresponding threshold. Should be the same length as the number of source files.')
331
+ parser.add_argument(
332
+ '--target_confidence_threshold',
333
+ type=float,
334
+ default=default_options.target_confidence_threshold,
335
+ help='Don\'t merge if target file\'s detection confidence is already higher ' + \
336
+ 'than this (default {}). '.format(
337
+ default_options.target_confidence_threshold))
338
+ parser.add_argument(
339
+ '--categories_to_include',
340
+ type=int,
341
+ nargs="+",
342
+ default=None,
343
+ help='List of numeric detection category IDs to include')
344
+ parser.add_argument(
345
+ '--categories_to_exclude',
346
+ type=int,
347
+ nargs="+",
348
+ default=None,
349
+ help='List of numeric detection categories to include')
350
+ parser.add_argument(
351
+ '--merge_empty_only',
352
+ action='store_true',
353
+ help='Ignore individual detections and only merge images for which the target ' + \
354
+ 'file contains no detections')
355
+ parser.add_argument(
356
+ '--iou_threshold',
357
+ type=float,
358
+ default=default_options.iou_threshold,
359
+ help='Sets the minimum IoU for a source detection to be considered the same as ' + \
360
+ 'a target detection (default {})'.format(default_options.iou_threshold))
361
+
362
+ if len(sys.argv[1:]) == 0:
363
+ parser.print_help()
364
+ parser.exit()
365
+
366
+ args = parser.parse_args()
367
+
368
+ options = MergeDetectionsOptions()
369
+ options.max_detection_size = args.max_detection_size
370
+ options.min_detection_size = args.min_detection_size
371
+ options.source_confidence_thresholds = args.source_confidence_thresholds
372
+ options.target_confidence_threshold = args.target_confidence_threshold
373
+ options.categories_to_include = args.categories_to_include
374
+ options.categories_to_exclude = args.categories_to_exclude
375
+ options.merge_empty_only = args.merge_empty_only
376
+ options.iou_threshold = args.iou_threshold
377
+
378
+ merge_detections(args.source_files, args.target_file, args.output_file, options)
379
+
380
+
381
+ #%% Test driver
382
+
383
+ if False:
384
+
385
+ #%%
386
+
387
+ options = MergeDetectionsOptions()
388
+ options.max_detection_size = 0.1
389
+ options.target_confidence_threshold = 0.3
390
+ options.categories_to_include = [1]
391
+ source_files = ['/home/user/postprocessing/iwildcam/iwildcam-mdv4-2022-05-01/combined_api_outputs/iwildcam-mdv4-2022-05-01_detections.json']
392
+ options.source_confidence_thresholds = [0.8]
393
+ target_file = '/home/user/postprocessing/iwildcam/iwildcam-mdv5-camcocoinat-2022-05-02/combined_api_outputs/iwildcam-mdv5-camcocoinat-2022-05-02_detections.json'
394
+ output_file = '/home/user/postprocessing/iwildcam/merged-detections/mdv4_mdv5-camcocoinat-2022-05-02.json'
395
+ merge_detections(source_files, target_file, output_file, options)
396
+
397
+ options = MergeDetectionsOptions()
398
+ options.max_detection_size = 0.1
399
+ options.target_confidence_threshold = 0.3
400
+ options.categories_to_include = [1]
401
+ source_files = [
402
+ '/home/user/postprocessing/iwildcam/iwildcam-mdv4-2022-05-01/combined_api_outputs/iwildcam-mdv4-2022-05-01_detections.json',
403
+ '/home/user/postprocessing/iwildcam/iwildcam-mdv5-camonly-2022-05-02/combined_api_outputs/iwildcam-mdv5-camonly-2022-05-02_detections.json',
404
+ ]
405
+ options.source_confidence_thresholds = [0.8,0.5]
406
+ target_file = '/home/user/postprocessing/iwildcam/iwildcam-mdv5-camcocoinat-2022-05-02/combined_api_outputs/iwildcam-mdv5-camcocoinat-2022-05-02_detections.json'
407
+ output_file = '/home/user/postprocessing/iwildcam/merged-detections/mdv4_mdv5-camonly_mdv5-camcocoinat-2022-05-02.json'
408
+ merge_detections(source_files, target_file, output_file, options)
409
+
410
+ if __name__ == '__main__':
411
+ main()
412
+