megadetector 5.0.11__tar.gz → 5.0.13__tar.gz

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 (208) hide show
  1. {megadetector-5.0.11 → megadetector-5.0.13}/LICENSE +0 -0
  2. {megadetector-5.0.11/megadetector.egg-info → megadetector-5.0.13}/PKG-INFO +2 -2
  3. {megadetector-5.0.11 → megadetector-5.0.13}/README-package.md +1 -1
  4. {megadetector-5.0.11 → megadetector-5.0.13}/README.md +38 -34
  5. megadetector-5.0.13/megadetector/api/__init__.py +0 -0
  6. megadetector-5.0.13/megadetector/api/batch_processing/__init__.py +0 -0
  7. megadetector-5.0.13/megadetector/api/batch_processing/api_core/__init__.py +0 -0
  8. megadetector-5.0.13/megadetector/api/batch_processing/api_core/batch_service/__init__.py +0 -0
  9. megadetector-5.0.13/megadetector/api/batch_processing/api_core/batch_service/score.py +439 -0
  10. megadetector-5.0.13/megadetector/api/batch_processing/api_core/server.py +294 -0
  11. megadetector-5.0.13/megadetector/api/batch_processing/api_core/server_api_config.py +97 -0
  12. megadetector-5.0.13/megadetector/api/batch_processing/api_core/server_app_config.py +55 -0
  13. megadetector-5.0.13/megadetector/api/batch_processing/api_core/server_batch_job_manager.py +220 -0
  14. megadetector-5.0.13/megadetector/api/batch_processing/api_core/server_job_status_table.py +149 -0
  15. megadetector-5.0.13/megadetector/api/batch_processing/api_core/server_orchestration.py +360 -0
  16. megadetector-5.0.13/megadetector/api/batch_processing/api_core/server_utils.py +88 -0
  17. megadetector-5.0.13/megadetector/api/batch_processing/api_core_support/__init__.py +0 -0
  18. megadetector-5.0.13/megadetector/api/batch_processing/api_core_support/aggregate_results_manually.py +46 -0
  19. megadetector-5.0.13/megadetector/api/batch_processing/api_support/__init__.py +0 -0
  20. megadetector-5.0.13/megadetector/api/batch_processing/api_support/summarize_daily_activity.py +152 -0
  21. megadetector-5.0.13/megadetector/api/batch_processing/data_preparation/__init__.py +0 -0
  22. megadetector-5.0.13/megadetector/api/batch_processing/integration/digiKam/setup.py +6 -0
  23. megadetector-5.0.13/megadetector/api/batch_processing/integration/digiKam/xmp_integration.py +465 -0
  24. megadetector-5.0.13/megadetector/api/batch_processing/integration/eMammal/test_scripts/config_template.py +5 -0
  25. megadetector-5.0.13/megadetector/api/batch_processing/integration/eMammal/test_scripts/push_annotations_to_emammal.py +125 -0
  26. megadetector-5.0.13/megadetector/api/batch_processing/integration/eMammal/test_scripts/select_images_for_testing.py +55 -0
  27. megadetector-5.0.13/megadetector/api/synchronous/__init__.py +0 -0
  28. megadetector-5.0.13/megadetector/api/synchronous/api_core/animal_detection_api/__init__.py +0 -0
  29. megadetector-5.0.13/megadetector/api/synchronous/api_core/animal_detection_api/api_backend.py +152 -0
  30. megadetector-5.0.13/megadetector/api/synchronous/api_core/animal_detection_api/api_frontend.py +263 -0
  31. megadetector-5.0.13/megadetector/api/synchronous/api_core/animal_detection_api/config.py +35 -0
  32. megadetector-5.0.13/megadetector/api/synchronous/api_core/tests/__init__.py +0 -0
  33. megadetector-5.0.13/megadetector/api/synchronous/api_core/tests/load_test.py +110 -0
  34. megadetector-5.0.13/megadetector/classification/__init__.py +0 -0
  35. megadetector-5.0.13/megadetector/classification/aggregate_classifier_probs.py +108 -0
  36. megadetector-5.0.13/megadetector/classification/analyze_failed_images.py +227 -0
  37. megadetector-5.0.13/megadetector/classification/cache_batchapi_outputs.py +198 -0
  38. megadetector-5.0.13/megadetector/classification/create_classification_dataset.py +627 -0
  39. megadetector-5.0.13/megadetector/classification/crop_detections.py +516 -0
  40. megadetector-5.0.13/megadetector/classification/csv_to_json.py +226 -0
  41. megadetector-5.0.13/megadetector/classification/detect_and_crop.py +855 -0
  42. megadetector-5.0.13/megadetector/classification/efficientnet/__init__.py +9 -0
  43. megadetector-5.0.13/megadetector/classification/efficientnet/model.py +415 -0
  44. megadetector-5.0.13/megadetector/classification/efficientnet/utils.py +607 -0
  45. megadetector-5.0.13/megadetector/classification/evaluate_model.py +520 -0
  46. megadetector-5.0.13/megadetector/classification/identify_mislabeled_candidates.py +152 -0
  47. megadetector-5.0.13/megadetector/classification/json_to_azcopy_list.py +63 -0
  48. megadetector-5.0.13/megadetector/classification/json_validator.py +699 -0
  49. megadetector-5.0.13/megadetector/classification/map_classification_categories.py +276 -0
  50. megadetector-5.0.13/megadetector/classification/merge_classification_detection_output.py +506 -0
  51. megadetector-5.0.13/megadetector/classification/prepare_classification_script.py +194 -0
  52. megadetector-5.0.13/megadetector/classification/prepare_classification_script_mc.py +228 -0
  53. megadetector-5.0.13/megadetector/classification/run_classifier.py +287 -0
  54. megadetector-5.0.13/megadetector/classification/save_mislabeled.py +110 -0
  55. megadetector-5.0.13/megadetector/classification/train_classifier.py +827 -0
  56. megadetector-5.0.13/megadetector/classification/train_classifier_tf.py +725 -0
  57. megadetector-5.0.13/megadetector/classification/train_utils.py +323 -0
  58. megadetector-5.0.13/megadetector/data_management/__init__.py +0 -0
  59. megadetector-5.0.13/megadetector/data_management/annotations/__init__.py +0 -0
  60. megadetector-5.0.13/megadetector/data_management/annotations/annotation_constants.py +34 -0
  61. megadetector-5.0.13/megadetector/data_management/camtrap_dp_to_coco.py +237 -0
  62. megadetector-5.0.13/megadetector/data_management/cct_json_utils.py +404 -0
  63. megadetector-5.0.13/megadetector/data_management/cct_to_md.py +176 -0
  64. megadetector-5.0.13/megadetector/data_management/cct_to_wi.py +289 -0
  65. megadetector-5.0.13/megadetector/data_management/coco_to_labelme.py +283 -0
  66. megadetector-5.0.13/megadetector/data_management/coco_to_yolo.py +662 -0
  67. megadetector-5.0.13/megadetector/data_management/databases/__init__.py +0 -0
  68. megadetector-5.0.13/megadetector/data_management/databases/add_width_and_height_to_db.py +33 -0
  69. megadetector-5.0.13/megadetector/data_management/databases/combine_coco_camera_traps_files.py +206 -0
  70. megadetector-5.0.13/megadetector/data_management/databases/integrity_check_json_db.py +493 -0
  71. megadetector-5.0.13/megadetector/data_management/databases/subset_json_db.py +115 -0
  72. megadetector-5.0.13/megadetector/data_management/generate_crops_from_cct.py +149 -0
  73. megadetector-5.0.13/megadetector/data_management/get_image_sizes.py +189 -0
  74. megadetector-5.0.13/megadetector/data_management/importers/add_nacti_sizes.py +52 -0
  75. megadetector-5.0.13/megadetector/data_management/importers/add_timestamps_to_icct.py +79 -0
  76. megadetector-5.0.13/megadetector/data_management/importers/animl_results_to_md_results.py +158 -0
  77. megadetector-5.0.13/megadetector/data_management/importers/auckland_doc_test_to_json.py +373 -0
  78. megadetector-5.0.13/megadetector/data_management/importers/auckland_doc_to_json.py +201 -0
  79. megadetector-5.0.13/megadetector/data_management/importers/awc_to_json.py +191 -0
  80. megadetector-5.0.13/megadetector/data_management/importers/bellevue_to_json.py +273 -0
  81. megadetector-5.0.13/megadetector/data_management/importers/cacophony-thermal-importer.py +793 -0
  82. megadetector-5.0.13/megadetector/data_management/importers/carrizo_shrubfree_2018.py +269 -0
  83. megadetector-5.0.13/megadetector/data_management/importers/carrizo_trail_cam_2017.py +289 -0
  84. megadetector-5.0.13/megadetector/data_management/importers/cct_field_adjustments.py +58 -0
  85. megadetector-5.0.13/megadetector/data_management/importers/channel_islands_to_cct.py +913 -0
  86. megadetector-5.0.13/megadetector/data_management/importers/eMammal/copy_and_unzip_emammal.py +180 -0
  87. megadetector-5.0.13/megadetector/data_management/importers/eMammal/eMammal_helpers.py +249 -0
  88. megadetector-5.0.13/megadetector/data_management/importers/eMammal/make_eMammal_json.py +223 -0
  89. megadetector-5.0.13/megadetector/data_management/importers/ena24_to_json.py +276 -0
  90. megadetector-5.0.13/megadetector/data_management/importers/filenames_to_json.py +386 -0
  91. megadetector-5.0.13/megadetector/data_management/importers/helena_to_cct.py +283 -0
  92. megadetector-5.0.13/megadetector/data_management/importers/idaho-camera-traps.py +1407 -0
  93. megadetector-5.0.13/megadetector/data_management/importers/idfg_iwildcam_lila_prep.py +294 -0
  94. megadetector-5.0.13/megadetector/data_management/importers/jb_csv_to_json.py +150 -0
  95. megadetector-5.0.13/megadetector/data_management/importers/mcgill_to_json.py +250 -0
  96. megadetector-5.0.13/megadetector/data_management/importers/missouri_to_json.py +490 -0
  97. megadetector-5.0.13/megadetector/data_management/importers/nacti_fieldname_adjustments.py +79 -0
  98. megadetector-5.0.13/megadetector/data_management/importers/noaa_seals_2019.py +181 -0
  99. megadetector-5.0.13/megadetector/data_management/importers/pc_to_json.py +365 -0
  100. megadetector-5.0.13/megadetector/data_management/importers/plot_wni_giraffes.py +123 -0
  101. megadetector-5.0.13/megadetector/data_management/importers/prepare-noaa-fish-data-for-lila.py +359 -0
  102. megadetector-5.0.13/megadetector/data_management/importers/prepare_zsl_imerit.py +131 -0
  103. megadetector-5.0.13/megadetector/data_management/importers/rspb_to_json.py +356 -0
  104. megadetector-5.0.13/megadetector/data_management/importers/save_the_elephants_survey_A.py +320 -0
  105. megadetector-5.0.13/megadetector/data_management/importers/save_the_elephants_survey_B.py +329 -0
  106. megadetector-5.0.13/megadetector/data_management/importers/snapshot_safari_importer.py +758 -0
  107. megadetector-5.0.13/megadetector/data_management/importers/snapshot_safari_importer_reprise.py +665 -0
  108. megadetector-5.0.13/megadetector/data_management/importers/snapshot_serengeti_lila.py +1067 -0
  109. megadetector-5.0.13/megadetector/data_management/importers/snapshotserengeti/make_full_SS_json.py +150 -0
  110. megadetector-5.0.13/megadetector/data_management/importers/snapshotserengeti/make_per_season_SS_json.py +153 -0
  111. megadetector-5.0.13/megadetector/data_management/importers/sulross_get_exif.py +65 -0
  112. megadetector-5.0.13/megadetector/data_management/importers/timelapse_csv_set_to_json.py +490 -0
  113. megadetector-5.0.13/megadetector/data_management/importers/ubc_to_json.py +399 -0
  114. megadetector-5.0.13/megadetector/data_management/importers/umn_to_json.py +507 -0
  115. megadetector-5.0.13/megadetector/data_management/importers/wellington_to_json.py +263 -0
  116. megadetector-5.0.13/megadetector/data_management/importers/wi_to_json.py +442 -0
  117. megadetector-5.0.13/megadetector/data_management/importers/zamba_results_to_md_results.py +181 -0
  118. megadetector-5.0.13/megadetector/data_management/labelme_to_coco.py +547 -0
  119. megadetector-5.0.13/megadetector/data_management/labelme_to_yolo.py +272 -0
  120. megadetector-5.0.13/megadetector/data_management/lila/__init__.py +0 -0
  121. megadetector-5.0.13/megadetector/data_management/lila/add_locations_to_island_camera_traps.py +97 -0
  122. megadetector-5.0.13/megadetector/data_management/lila/add_locations_to_nacti.py +147 -0
  123. megadetector-5.0.13/megadetector/data_management/lila/create_lila_blank_set.py +558 -0
  124. megadetector-5.0.13/megadetector/data_management/lila/create_lila_test_set.py +152 -0
  125. megadetector-5.0.13/megadetector/data_management/lila/create_links_to_md_results_files.py +106 -0
  126. megadetector-5.0.13/megadetector/data_management/lila/download_lila_subset.py +178 -0
  127. megadetector-5.0.13/megadetector/data_management/lila/generate_lila_per_image_labels.py +516 -0
  128. megadetector-5.0.13/megadetector/data_management/lila/get_lila_annotation_counts.py +170 -0
  129. megadetector-5.0.13/megadetector/data_management/lila/get_lila_image_counts.py +112 -0
  130. megadetector-5.0.13/megadetector/data_management/lila/lila_common.py +300 -0
  131. megadetector-5.0.13/megadetector/data_management/lila/test_lila_metadata_urls.py +132 -0
  132. megadetector-5.0.13/megadetector/data_management/ocr_tools.py +870 -0
  133. megadetector-5.0.13/megadetector/data_management/read_exif.py +809 -0
  134. megadetector-5.0.13/megadetector/data_management/remap_coco_categories.py +84 -0
  135. megadetector-5.0.13/megadetector/data_management/remove_exif.py +66 -0
  136. megadetector-5.0.13/megadetector/data_management/rename_images.py +187 -0
  137. megadetector-5.0.13/megadetector/data_management/resize_coco_dataset.py +189 -0
  138. megadetector-5.0.13/megadetector/data_management/wi_download_csv_to_coco.py +247 -0
  139. megadetector-5.0.13/megadetector/data_management/yolo_output_to_md_output.py +446 -0
  140. megadetector-5.0.13/megadetector/data_management/yolo_to_coco.py +676 -0
  141. megadetector-5.0.13/megadetector/detection/__init__.py +0 -0
  142. megadetector-5.0.13/megadetector/detection/detector_training/__init__.py +0 -0
  143. megadetector-5.0.13/megadetector/detection/detector_training/model_main_tf2.py +114 -0
  144. megadetector-5.0.13/megadetector/detection/process_video.py +846 -0
  145. megadetector-5.0.13/megadetector/detection/pytorch_detector.py +355 -0
  146. megadetector-5.0.13/megadetector/detection/run_detector.py +779 -0
  147. megadetector-5.0.13/megadetector/detection/run_detector_batch.py +1219 -0
  148. megadetector-5.0.13/megadetector/detection/run_inference_with_yolov5_val.py +1087 -0
  149. megadetector-5.0.13/megadetector/detection/run_tiled_inference.py +934 -0
  150. megadetector-5.0.13/megadetector/detection/tf_detector.py +192 -0
  151. megadetector-5.0.13/megadetector/detection/video_utils.py +698 -0
  152. megadetector-5.0.13/megadetector/postprocessing/__init__.py +0 -0
  153. megadetector-5.0.13/megadetector/postprocessing/add_max_conf.py +64 -0
  154. megadetector-5.0.13/megadetector/postprocessing/categorize_detections_by_size.py +165 -0
  155. megadetector-5.0.13/megadetector/postprocessing/classification_postprocessing.py +716 -0
  156. megadetector-5.0.13/megadetector/postprocessing/combine_api_outputs.py +249 -0
  157. megadetector-5.0.13/megadetector/postprocessing/compare_batch_results.py +966 -0
  158. megadetector-5.0.13/megadetector/postprocessing/convert_output_format.py +396 -0
  159. megadetector-5.0.13/megadetector/postprocessing/load_api_results.py +195 -0
  160. megadetector-5.0.13/megadetector/postprocessing/md_to_coco.py +310 -0
  161. megadetector-5.0.13/megadetector/postprocessing/md_to_labelme.py +330 -0
  162. megadetector-5.0.13/megadetector/postprocessing/merge_detections.py +412 -0
  163. megadetector-5.0.13/megadetector/postprocessing/postprocess_batch_results.py +1908 -0
  164. megadetector-5.0.13/megadetector/postprocessing/remap_detection_categories.py +170 -0
  165. megadetector-5.0.13/megadetector/postprocessing/render_detection_confusion_matrix.py +660 -0
  166. megadetector-5.0.13/megadetector/postprocessing/repeat_detection_elimination/find_repeat_detections.py +211 -0
  167. megadetector-5.0.13/megadetector/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +83 -0
  168. megadetector-5.0.13/megadetector/postprocessing/repeat_detection_elimination/repeat_detections_core.py +1635 -0
  169. megadetector-5.0.13/megadetector/postprocessing/separate_detections_into_folders.py +730 -0
  170. megadetector-5.0.13/megadetector/postprocessing/subset_json_detector_output.py +700 -0
  171. megadetector-5.0.13/megadetector/postprocessing/top_folders_to_bottom.py +223 -0
  172. megadetector-5.0.13/megadetector/taxonomy_mapping/__init__.py +0 -0
  173. megadetector-5.0.13/megadetector/taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +491 -0
  174. megadetector-5.0.13/megadetector/taxonomy_mapping/map_new_lila_datasets.py +150 -0
  175. megadetector-5.0.13/megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +142 -0
  176. megadetector-5.0.13/megadetector/taxonomy_mapping/preview_lila_taxonomy.py +588 -0
  177. megadetector-5.0.13/megadetector/taxonomy_mapping/retrieve_sample_image.py +71 -0
  178. megadetector-5.0.13/megadetector/taxonomy_mapping/simple_image_download.py +219 -0
  179. megadetector-5.0.13/megadetector/taxonomy_mapping/species_lookup.py +834 -0
  180. megadetector-5.0.13/megadetector/taxonomy_mapping/taxonomy_csv_checker.py +159 -0
  181. megadetector-5.0.13/megadetector/taxonomy_mapping/taxonomy_graph.py +346 -0
  182. megadetector-5.0.13/megadetector/taxonomy_mapping/validate_lila_category_mappings.py +83 -0
  183. megadetector-5.0.13/megadetector/utils/__init__.py +0 -0
  184. megadetector-5.0.13/megadetector/utils/azure_utils.py +178 -0
  185. megadetector-5.0.13/megadetector/utils/ct_utils.py +613 -0
  186. megadetector-5.0.13/megadetector/utils/directory_listing.py +246 -0
  187. megadetector-5.0.13/megadetector/utils/md_tests.py +1164 -0
  188. megadetector-5.0.13/megadetector/utils/path_utils.py +1045 -0
  189. megadetector-5.0.13/megadetector/utils/process_utils.py +160 -0
  190. megadetector-5.0.13/megadetector/utils/sas_blob_utils.py +509 -0
  191. megadetector-5.0.13/megadetector/utils/split_locations_into_train_val.py +228 -0
  192. megadetector-5.0.13/megadetector/utils/string_utils.py +92 -0
  193. megadetector-5.0.13/megadetector/utils/url_utils.py +323 -0
  194. megadetector-5.0.13/megadetector/utils/write_html_image_list.py +225 -0
  195. megadetector-5.0.13/megadetector/visualization/__init__.py +0 -0
  196. megadetector-5.0.13/megadetector/visualization/plot_utils.py +293 -0
  197. megadetector-5.0.13/megadetector/visualization/render_images_with_thumbnails.py +275 -0
  198. megadetector-5.0.13/megadetector/visualization/visualization_utils.py +1536 -0
  199. megadetector-5.0.13/megadetector/visualization/visualize_db.py +552 -0
  200. megadetector-5.0.13/megadetector/visualization/visualize_detector_output.py +405 -0
  201. {megadetector-5.0.11 → megadetector-5.0.13/megadetector.egg-info}/PKG-INFO +2 -2
  202. megadetector-5.0.13/megadetector.egg-info/SOURCES.txt +205 -0
  203. {megadetector-5.0.11 → megadetector-5.0.13}/megadetector.egg-info/dependency_links.txt +0 -0
  204. {megadetector-5.0.11 → megadetector-5.0.13}/megadetector.egg-info/requires.txt +0 -0
  205. {megadetector-5.0.11 → megadetector-5.0.13}/megadetector.egg-info/top_level.txt +0 -0
  206. {megadetector-5.0.11 → megadetector-5.0.13}/pyproject.toml +2 -7
  207. {megadetector-5.0.11 → megadetector-5.0.13}/setup.cfg +0 -0
  208. megadetector-5.0.11/megadetector.egg-info/SOURCES.txt +0 -9
File without changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: megadetector
3
- Version: 5.0.11
3
+ Version: 5.0.13
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>
@@ -114,7 +114,7 @@ print('Found {} detections above threshold'.format(len(detections_above_threshol
114
114
 
115
115
  ```
116
116
  from megadetector.detection.run_detector_batch import \
117
- load_and_run_detector_batch,write_results_to_file
117
+ load_and_run_detector_batch, write_results_to_file
118
118
  from megadetector.utils import path_utils
119
119
  import os
120
120
 
@@ -62,7 +62,7 @@ print('Found {} detections above threshold'.format(len(detections_above_threshol
62
62
 
63
63
  ```
64
64
  from megadetector.detection.run_detector_batch import \
65
- load_and_run_detector_batch,write_results_to_file
65
+ load_and_run_detector_batch, write_results_to_file
66
66
  from megadetector.utils import path_utils
67
67
  import os
68
68
 
@@ -11,6 +11,7 @@
11
11
  6. [Contact](#contact)
12
12
  7. [Gratuitous camera trap picture](#gratuitous-camera-trap-picture)
13
13
 
14
+
14
15
  ## What's MegaDetector all about?
15
16
 
16
17
  [MegaDetector](megadetector.md) is an AI model that identifies animals, people, and vehicles in camera trap images (which also makes it useful for eliminating blank images). This model is trained on several million images from a variety of ecosystems.
@@ -25,7 +26,7 @@ Here's a &ldquo;teaser&rdquo; image of what MegaDetector output looks like:
25
26
  * If you are looking for a convenient tool to run MegaDetector, you don't need anything from this repository: check out [EcoAssist](https://github.com/PetervanLunteren/EcoAssist?tab=readme-ov-file).
26
27
  * If you're just <i>considering</i> the use of AI in your workflow, and you aren't even sure yet whether MegaDetector would be useful to you, we recommend reading the "[getting started with MegaDetector](getting-started.md)" page.
27
28
  * If you're already familiar with MegaDetector and you're ready to run it on your data, see the [MegaDetector User Guide](megadetector.md) for instructions on running MegaDetector.
28
- * If you're a programmer-type looking to use tools from this repo, check out the [Python package](https://pypi.org/project/megadetector/) that provides access to everything in this repo (yes, you guessed it, "pip install megadetector").
29
+ * If you're a programmer-type looking to use tools from this repo, check out the [MegaDetector Python package](https://pypi.org/project/megadetector/) that provides access to everything in this repo (yes, you guessed it, "pip install megadetector").
29
30
  * If you have any questions, or you want to tell us that MegaDetector was amazing/terrible on your images, <a href="mailto:cameratraps@lila.science">email us</a>!
30
31
 
31
32
  MegaDetector is just one of many tools that aims to make conservation biologists more efficient with AI. If you want to learn about other ways to use AI to accelerate camera trap workflows, check out our of the field, affectionately titled &ldquo;[Everything I know about machine learning and camera traps](https://agentmorris.github.io/camera-trap-ml-survey/)&rdquo;.
@@ -65,12 +66,14 @@ Here are a few of the organizations that have used MegaDetector... we're only li
65
66
  * [Parc national du Mont-Tremblant](https://www.sepaq.com/pq/mot/index.dot?language_id=1)
66
67
  * [The Land Banking Group](https://thelandbankinggroup.com/)
67
68
  * [New Zealand Department of Conservation](https://www.doc.govt.nz)
69
+ * [Habitat NZ](https://habitatnz.co.nz/)
68
70
 
69
71
  * [Applied Conservation Macro Ecology Lab](http://www.acmelab.ca/), University of Victoria
70
72
  * [Banff National Park Resource Conservation](https://www.pc.gc.ca/en/pn-np/ab/banff/nature/conservation), Parks Canada
71
73
  * [Blumstein Lab](https://blumsteinlab.eeb.ucla.edu/), UCLA
72
74
  * [Borderlands Research Institute](https://bri.sulross.edu/), Sul Ross State University
73
75
  * [Capitol Reef National Park](https://www.nps.gov/care/index.htm) / Utah Valley University
76
+ * [Canyon Critters Project](https://www.zooniverse.org/projects/arw36/canyon-critters), University of Georgia
74
77
  * [Center for Biodiversity and Conservation](https://www.amnh.org/research/center-for-biodiversity-conservation), American Museum of Natural History
75
78
  * [Centre for Ecosystem Science](https://www.unsw.edu.au/research/), UNSW Sydney
76
79
  * [Cross-Cultural Ecology Lab](https://crossculturalecology.net/), Macquarie University
@@ -79,7 +82,7 @@ Here are a few of the organizations that have used MegaDetector... we're only li
79
82
  * [Department of Society & Conservation](https://www.umt.edu/environment/about/departments/socon/), W.A. Franke College of Forestry & Conservation, University of Montana
80
83
  * [Department of Wildlife Ecology and Conservation](https://wec.ifas.ufl.edu/), University of Florida
81
84
  * [Ecology and Conservation of Amazonian Vertebrates Research Group](https://www.researchgate.net/lab/Fernanda-Michalski-Lab-4), Federal University of Amapá
82
- * [Gola Forest Programma](https://www.rspb.org.uk/our-work/conservation/projects/scientific-support-for-the-gola-forest-programme/), Royal Society for the Protection of Birds (RSPB)
85
+ * [Gola Forest Programme](https://www.rspb.org.uk/our-work/conservation/projects/scientific-support-for-the-gola-forest-programme/), Royal Society for the Protection of Birds (RSPB)
83
86
  * [Graeme Shannon's Research Group](https://wildliferesearch.co.uk/group-1), Bangor University
84
87
  * [Grizzly Bear Recovery Program](https://www.fws.gov/office/grizzly-bear-recovery-program), U.S. Fish & Wildlife Service
85
88
  * [Hamaarag](https://hamaarag.org.il/), The Steinhardt Museum of Natural History, Tel Aviv University
@@ -138,49 +141,59 @@ Also see:
138
141
 
139
142
  ## Repo contents
140
143
 
141
- This repo is organized into the following folders...
144
+ ### Repo history
145
+
146
+ MegaDetector was initially developed by the [Microsoft AI for Earth program](https://www.microsoft.com/en-us/ai/ai-for-earth); this repo was forked from the microsoft/cameratraps repo and is maintained by the original MegaDetector developers (who are no longer at Microsoft, but are absolutely fantastically eternally grateful to Microsoft for the investment and commitment that made MegaDetector happen). If you're interested in MD's history, see the [downloading the model](https://github.com/agentmorris/MegaDetector/blob/main/megadetector.md#downloading-the-model) section in the MegaDetector User Guide to learn about the history of MegaDetector releases, and the [can you share the training data?](https://github.com/agentmorris/MegaDetector/blob/main/megadetector.md#can-you-share-the-training-data) section to learn about the training data used in each of those releases.
142
147
 
143
- This repo contains the tools for training and running [MegaDetector](megadetector.md), an object detection model that does a pretty good job finding animals, people, and vehicles (and therefore is pretty good at finding empty images) in camera trap images in a variety of terrestrial ecosystems.
148
+ ### What the code in this repo does
144
149
 
145
- The core functionality provided is:
150
+ The core functionality provided in this repo is:
146
151
 
147
- - Training and running [MegaDetector](megadetector.md).
148
- - Training and running some [species classifiers](classification) that are used in conjunction with MegaDetector.
149
- - Tools to [convert](data_management) frequently-used camera trap metadata formats into a common format.
150
- - A [batch processing API](https://github.com/agentmorris/MegaDetector/tree/main/megadetector/api/batch_processing) that runs MegaDetector on large image collections, to accelerate population surveys.
151
- - A [real-time API](https://github.com/agentmorris/MegaDetector/tree/main/megadetector/api/synchronous) that runs MegaDetector (and some species classifiers) synchronously, primarily to support biosecurity applications.
152
+ - Tools for training and running [MegaDetector](megadetector.md).
153
+ - Tools for working with MegaDetector output, e.g. for reviewing the results of a large processing batch.
154
+ - Tools to convert among frequently-used camera trap metadata formats.
152
155
 
153
156
  This repo does not host the data used to train MegaDetector, but we work with our collaborators to make data and annotations available whenever possible on [lila.science](http://lila.science). See the [MegaDetector training data](megadetector.md#can-you-share-the-training-data) section to learn more about the data used to train MegaDetector.
154
157
 
155
- MegaDetector was initially developed by the [Microsoft AI for Earth program](https://www.microsoft.com/en-us/ai/ai-for-earth); this repo was forked from the microsoft/cameratraps repo and is maintained by the original MegaDetector developers (who are no longer at Microsoft, but are absolutely fantastically eternally grateful to Microsoft for the investment and commitment that made MegaDetector happen).
158
+ ### Repo organization
156
159
 
160
+ This repo is organized into the following folders...
157
161
 
158
- ### megadetector/detection
162
+ #### megadetector/detection
159
163
 
160
164
  Code for running models, especially MegaDetector.
161
165
 
162
166
 
163
- ### megadetector/postprocessing
167
+ ##### megadetector/postprocessing
164
168
 
165
169
  Code for common operations one might do after running MegaDetector, e.g. [generating preview pages to summarize your results](https://github.com/agentmorris/MegaDetector/blob/main/megadetector/postprocessing/postprocess_batch_results.py), [separating images into different folders based on AI results](https://github.com/agentmorris/MegaDetector/blob/main/megadetector/postprocessing/separate_detections_into_folders.py), or [converting results to a different format](https://github.com/agentmorris/MegaDetector/blob/main/megadetector/postprocessing/convert_output_format.py).
166
170
 
167
171
 
168
- ### megadetector/utils
172
+ #### megadetector/utils
173
+
174
+ Small utility functions for string manipulation, filename manipulation, downloading files from URLs, etc.
169
175
 
170
- Small utility functions for string manipulation, filename manipulation, downloading files from URLs, etc. Mostly adapted from the [ai4eutils](https://github.com/microsoft/ai4eutils) repo.
171
176
 
177
+ #### megadetector/visualization
172
178
 
173
- ### megadetector/visualization
179
+ Tools for visualizing images with ground truth and/or predicted bounding boxes.
174
180
 
175
- Tools for visualizing images with ground truth and/or predicted annotations.
181
+
182
+ #### megadetector/data_management
183
+
184
+ Code for:
185
+
186
+ * Converting frequently-used metadata formats to [COCO Camera Traps](https://github.com/agentmorris/MegaDetector/blob/main/megadetector/data_management/README.md#coco-cameratraps-format) format
187
+ * Converting the output of AI models (especially YOLOv5) to the format used for AI results throughout this repo
188
+ * Creating, visualizing, and editing COCO Camera Traps .json databases
176
189
 
177
190
 
178
- ### megadetector/api
191
+ #### megadetector/api
179
192
 
180
193
  Code for hosting our models as an API, either for synchronous operation (i.e., for real-time inference) or as a batch process (for large biodiversity surveys).
181
194
 
182
195
 
183
- ### megadetector/classification
196
+ #### megadetector/classification
184
197
 
185
198
  Experimental code for training species classifiers on new data sets, generally trained on MegaDetector crops. Currently the main pipeline described in this folder relies on a large database of labeled images that is not publicly available; therefore, this folder is not yet set up to facilitate training of your own classifiers. However, it is useful for <i>users</i> of the classifiers that we train, and contains some useful starting points if you are going to take a "DIY" approach to training classifiers on cropped images.
186
199
 
@@ -189,41 +202,32 @@ All that said, here's another "teaser image" of what you get at the end of train
189
202
  <img src="images/warthog_classifications.jpg" width="700"><br/>Image credit University of Minnesota, from the Snapshot Safari program.
190
203
 
191
204
 
192
- ### megadetector/taxonomy_mapping
205
+ #### megadetector/taxonomy_mapping
193
206
 
194
207
  Code to facilitate mapping data-set-specific category names (e.g. "lion", which means very different things in Idaho vs. South Africa) to a standard taxonomy.
195
208
 
196
209
 
197
- ### megadetector/data_management
198
-
199
- Code for:
200
-
201
- * Converting frequently-used metadata formats to [COCO Camera Traps](https://github.com/agentmorris/MegaDetector/blob/main/megadetector/data_management/README.md#coco-cameratraps-format) format
202
- * Converting the output of AI models (especially YOLOv5) to the format used for AI results throughout this repo
203
- * Creating, visualizing, and editing COCO Camera Traps .json databases
204
-
205
-
206
- ### envs
210
+ #### envs
207
211
 
208
212
  Environment files... specifically .yml files for mamba/conda environments (these are what we recommend in our [MegaDetector User Guide](megadetector.md)), and a requirements.txt for the pip-inclined.
209
213
 
210
214
 
211
- ### images
215
+ #### images
212
216
 
213
217
  Media used in documentation.
214
218
 
215
219
 
216
- ### archive
220
+ #### archive
217
221
 
218
222
  Old code that we didn't <i>quite</i> want to delete, but is basically obsolete.
219
223
 
220
224
 
221
- ### sandbox
225
+ #### sandbox
222
226
 
223
227
  Random things that don't fit in any other directory, but aren't quite deprecated. Mostly postprocessing scripts that were built for a single use case but could potentially be useful in the future.
224
228
 
225
229
 
226
- ### test_images
230
+ #### test_images
227
231
 
228
232
  A handful of images from [LILA](https://lila.science) that facilitate testing and debugging.
229
233
 
File without changes
@@ -0,0 +1,439 @@
1
+ import io
2
+ import json
3
+ import math
4
+ import os
5
+ import sys
6
+ from datetime import datetime
7
+ from io import BytesIO
8
+ from typing import Union
9
+
10
+ from PIL import Image
11
+ import numpy as np
12
+ import requests
13
+ import tensorflow as tf
14
+ from azure.storage.blob import ContainerClient
15
+
16
+ print('score.py, tensorflow version:', tf.__version__)
17
+ print('score.py, tf.test.is_gpu_available:', tf.test.is_gpu_available())
18
+
19
+ PRINT_EVERY = 500
20
+
21
+
22
+ #%% Helper functions *copied* from ct_utils.py and visualization/visualization_utils.py
23
+
24
+ IMAGE_ROTATIONS = {
25
+ 3: 180,
26
+ 6: 270,
27
+ 8: 90
28
+ }
29
+
30
+ def truncate_float(x, precision=3):
31
+ """
32
+ Function for truncating a float scalar to the defined precision.
33
+ For example: truncate_float(0.0003214884) --> 0.000321
34
+ This function is primarily used to achieve a certain float representation
35
+ before exporting to JSON
36
+ Args:
37
+ x (float) Scalar to truncate
38
+ precision (int) The number of significant digits to preserve, should be
39
+ greater or equal 1
40
+ """
41
+
42
+ assert precision > 0
43
+
44
+ if np.isclose(x, 0):
45
+ return 0
46
+ else:
47
+ # Determine the factor, which shifts the decimal point of x
48
+ # just behind the last significant digit
49
+ factor = math.pow(10, precision - 1 - math.floor(math.log10(abs(x))))
50
+ # Shift decimal point by multiplicatipon with factor, flooring, and
51
+ # division by factor
52
+ return math.floor(x * factor)/factor
53
+
54
+
55
+ def open_image(input_file: Union[str, BytesIO]) -> Image:
56
+ """Opens an image in binary format using PIL.Image and converts to RGB mode.
57
+
58
+ This operation is lazy; image will not be actually loaded until the first
59
+ operation that needs to load it (for example, resizing), so file opening
60
+ errors can show up later.
61
+
62
+ Args:
63
+ input_file: str or BytesIO, either a path to an image file (anything
64
+ that PIL can open), or an image as a stream of bytes
65
+
66
+ Returns:
67
+ an PIL image object in RGB mode
68
+ """
69
+ if (isinstance(input_file, str)
70
+ and input_file.startswith(('http://', 'https://'))):
71
+ response = requests.get(input_file)
72
+ image = Image.open(BytesIO(response.content))
73
+ try:
74
+ response = requests.get(input_file)
75
+ image = Image.open(BytesIO(response.content))
76
+ except Exception as e:
77
+ print(f'Error opening image {input_file}: {e}')
78
+ raise
79
+ else:
80
+ image = Image.open(input_file)
81
+ if image.mode not in ('RGBA', 'RGB', 'L'):
82
+ raise AttributeError(f'Image {input_file} uses unsupported mode {image.mode}')
83
+ if image.mode == 'RGBA' or image.mode == 'L':
84
+ # PIL.Image.convert() returns a converted copy of this image
85
+ image = image.convert(mode='RGB')
86
+
87
+ # alter orientation as needed according to EXIF tag 0x112 (274) for Orientation
88
+ # https://gist.github.com/dangtrinhnt/a577ece4cbe5364aad28
89
+ # https://www.media.mit.edu/pia/Research/deepview/exif.html
90
+ try:
91
+ exif = image._getexif()
92
+ orientation: int = exif.get(274, None) # 274 is the key for the Orientation field
93
+ if orientation is not None and orientation in IMAGE_ROTATIONS:
94
+ image = image.rotate(IMAGE_ROTATIONS[orientation], expand=True) # returns a rotated copy
95
+ except Exception:
96
+ pass
97
+
98
+ return image
99
+
100
+
101
+ def load_image(input_file: Union[str, BytesIO]) -> Image.Image:
102
+ """Loads the image at input_file as a PIL Image into memory.
103
+ Image.open() used in open_image() is lazy and errors will occur downstream
104
+ if not explicitly loaded.
105
+ Args:
106
+ input_file: str or BytesIO, either a path to an image file (anything
107
+ that PIL can open), or an image as a stream of bytes
108
+ Returns: PIL.Image.Image, in RGB mode
109
+ """
110
+ image = open_image(input_file)
111
+ image.load()
112
+ return image
113
+
114
+
115
+ #%% TFDetector class, an unmodified *copy* of the class in detection/tf_detector.py,
116
+ # so we do not have to import the packages required by run_detector.py
117
+
118
+ class TFDetector:
119
+ """
120
+ A detector model loaded at the time of initialization. It is intended to be used with
121
+ MegaDetector (TF). The inference batch size is set to 1; code needs to be modified
122
+ to support larger batch sizes, including resizing appropriately.
123
+ """
124
+
125
+ # Number of decimal places to round to for confidence and bbox coordinates
126
+ CONF_DIGITS = 3
127
+ COORD_DIGITS = 4
128
+
129
+ # MegaDetector was trained with batch size of 1, and the resizing function is a part
130
+ # of the inference graph
131
+ BATCH_SIZE = 1
132
+
133
+ # An enumeration of failure reasons
134
+ FAILURE_TF_INFER = 'Failure TF inference'
135
+ FAILURE_IMAGE_OPEN = 'Failure image access'
136
+
137
+ DEFAULT_RENDERING_CONFIDENCE_THRESHOLD = 0.85 # to render bounding boxes
138
+ DEFAULT_OUTPUT_CONFIDENCE_THRESHOLD = 0.1 # to include in the output json file
139
+
140
+ DEFAULT_DETECTOR_LABEL_MAP = {
141
+ '1': 'animal',
142
+ '2': 'person',
143
+ '3': 'vehicle' # available in megadetector v4+
144
+ }
145
+
146
+ NUM_DETECTOR_CATEGORIES = 4 # animal, person, group, vehicle - for color assignment
147
+
148
+ def __init__(self, model_path):
149
+ """Loads model from model_path and starts a tf.Session with this graph. Obtains
150
+ input and output tensor handles."""
151
+ detection_graph = TFDetector.__load_model(model_path)
152
+ self.tf_session = tf.Session(graph=detection_graph)
153
+
154
+ self.image_tensor = detection_graph.get_tensor_by_name('image_tensor:0')
155
+ self.box_tensor = detection_graph.get_tensor_by_name('detection_boxes:0')
156
+ self.score_tensor = detection_graph.get_tensor_by_name('detection_scores:0')
157
+ self.class_tensor = detection_graph.get_tensor_by_name('detection_classes:0')
158
+
159
+ @staticmethod
160
+ def round_and_make_float(d, precision=4):
161
+ return truncate_float(float(d), precision=precision)
162
+
163
+ @staticmethod
164
+ def __convert_coords(tf_coords):
165
+ """Converts coordinates from the model's output format [y1, x1, y2, x2] to the
166
+ format used by our API and MegaDB: [x1, y1, width, height]. All coordinates
167
+ (including model outputs) are normalized in the range [0, 1].
168
+ Args:
169
+ tf_coords: np.array of predicted bounding box coordinates from the TF detector,
170
+ has format [y1, x1, y2, x2]
171
+ Returns: list of Python float, predicted bounding box coordinates [x1, y1, width, height]
172
+ """
173
+ # change from [y1, x1, y2, x2] to [x1, y1, width, height]
174
+ width = tf_coords[3] - tf_coords[1]
175
+ height = tf_coords[2] - tf_coords[0]
176
+
177
+ new = [tf_coords[1], tf_coords[0], width, height] # must be a list instead of np.array
178
+
179
+ # convert numpy floats to Python floats
180
+ for i, d in enumerate(new):
181
+ new[i] = TFDetector.round_and_make_float(d, precision=TFDetector.COORD_DIGITS)
182
+ return new
183
+
184
+ @staticmethod
185
+ def convert_to_tf_coords(array):
186
+ """From [x1, y1, width, height] to [y1, x1, y2, x2], where x1 is x_min, x2 is x_max
187
+ This is an extraneous step as the model outputs [y1, x1, y2, x2] but were converted to the API
188
+ output format - only to keep the interface of the sync API.
189
+ """
190
+ x1 = array[0]
191
+ y1 = array[1]
192
+ width = array[2]
193
+ height = array[3]
194
+ x2 = x1 + width
195
+ y2 = y1 + height
196
+ return [y1, x1, y2, x2]
197
+
198
+ @staticmethod
199
+ def __load_model(model_path):
200
+ """Loads a detection model (i.e., create a graph) from a .pb file.
201
+ Args:
202
+ model_path: .pb file of the model.
203
+ Returns: the loaded graph.
204
+ """
205
+ print('TFDetector: Loading graph...')
206
+ detection_graph = tf.Graph()
207
+ with detection_graph.as_default():
208
+ od_graph_def = tf.GraphDef()
209
+ with tf.gfile.GFile(model_path, 'rb') as fid:
210
+ serialized_graph = fid.read()
211
+ od_graph_def.ParseFromString(serialized_graph)
212
+ tf.import_graph_def(od_graph_def, name='')
213
+ print('TFDetector: Detection graph loaded.')
214
+
215
+ return detection_graph
216
+
217
+ def _generate_detections_one_image(self, image):
218
+ np_im = np.asarray(image, np.uint8)
219
+ im_w_batch_dim = np.expand_dims(np_im, axis=0)
220
+
221
+ # need to change the above line to the following if supporting a batch size > 1 and resizing to the same size
222
+ # np_images = [np.asarray(image, np.uint8) for image in images]
223
+ # images_stacked = np.stack(np_images, axis=0) if len(images) > 1 else np.expand_dims(np_images[0], axis=0)
224
+
225
+ # performs inference
226
+ (box_tensor_out, score_tensor_out, class_tensor_out) = self.tf_session.run(
227
+ [self.box_tensor, self.score_tensor, self.class_tensor],
228
+ feed_dict={self.image_tensor: im_w_batch_dim})
229
+
230
+ return box_tensor_out, score_tensor_out, class_tensor_out
231
+
232
+ def generate_detections_one_image(self, image, image_id,
233
+ detection_threshold=DEFAULT_OUTPUT_CONFIDENCE_THRESHOLD):
234
+ """Apply the detector to an image.
235
+ Args:
236
+ image: the PIL Image object
237
+ image_id: a path to identify the image; will be in the "file" field of the output object
238
+ detection_threshold: confidence above which to include the detection proposal
239
+ Returns:
240
+ A dict with the following fields, see the 'images' key in https://github.com/agentmorris/MegaDetector/tree/main/megadetector/api/batch_processing#batch-processing-api-output-format
241
+ - 'file' (always present)
242
+ - 'max_detection_conf'
243
+ - 'detections', which is a list of detection objects containing keys 'category', 'conf' and 'bbox'
244
+ - 'failure'
245
+ """
246
+ result = {
247
+ 'file': image_id
248
+ }
249
+ try:
250
+ b_box, b_score, b_class = self._generate_detections_one_image(image)
251
+
252
+ # our batch size is 1; need to loop the batch dim if supporting batch size > 1
253
+ boxes, scores, classes = b_box[0], b_score[0], b_class[0]
254
+
255
+ detections_cur_image = [] # will be empty for an image with no confident detections
256
+ max_detection_conf = 0.0
257
+ for b, s, c in zip(boxes, scores, classes):
258
+ if s > detection_threshold:
259
+ detection_entry = {
260
+ 'category': str(int(c)), # use string type for the numerical class label, not int
261
+ 'conf': truncate_float(float(s), # cast to float for json serialization
262
+ precision=TFDetector.CONF_DIGITS),
263
+ 'bbox': TFDetector.__convert_coords(b)
264
+ }
265
+ detections_cur_image.append(detection_entry)
266
+ if s > max_detection_conf:
267
+ max_detection_conf = s
268
+
269
+ result['max_detection_conf'] = truncate_float(float(max_detection_conf),
270
+ precision=TFDetector.CONF_DIGITS)
271
+ result['detections'] = detections_cur_image
272
+
273
+ except Exception as e:
274
+ result['failure'] = TFDetector.FAILURE_TF_INFER
275
+ print('TFDetector: image {} failed during inference: {}'.format(image_id, str(e)))
276
+
277
+ return result
278
+
279
+
280
+ #%% Scoring script
281
+
282
+ class BatchScorer:
283
+ """
284
+ Coordinates scoring images in this Task.
285
+
286
+ 1. have a synchronized queue that download tasks enqueue and scoring function dequeues - but need to be able to
287
+ limit the size of the queue. We do not want to write the image to disk and then load it in the scoring func.
288
+ """
289
+ def __init__(self, **kwargs):
290
+ print('score.py BatchScorer, __init__()')
291
+
292
+ detector_path = kwargs.get('detector_path')
293
+ self.detector = TFDetector(detector_path)
294
+
295
+ self.use_url = kwargs.get('use_url')
296
+ if not self.use_url:
297
+ input_container_sas = kwargs.get('input_container_sas')
298
+ self.input_container_client = ContainerClient.from_container_url(input_container_sas)
299
+
300
+ self.detection_threshold = kwargs.get('detection_threshold')
301
+
302
+ self.image_ids_to_score = kwargs.get('image_ids_to_score')
303
+
304
+ # determine if there is metadata attached to each image_id
305
+ self.metadata_available = True if isinstance(self.image_ids_to_score[0], list) else False
306
+
307
+ def _download_image(self, image_file) -> Image:
308
+ """
309
+ Args:
310
+ image_file: Public URL if use_url, else the full path from container root
311
+
312
+ Returns:
313
+ PIL image loaded
314
+ """
315
+ if not self.use_url:
316
+ downloader = self.input_container_client.download_blob(image_file)
317
+ image_file = io.BytesIO()
318
+ blob_props = downloader.download_to_stream(image_file)
319
+
320
+ image = open_image(image_file)
321
+ return image
322
+
323
+ def score_images(self) -> list:
324
+ detections = []
325
+
326
+ for i in self.image_ids_to_score:
327
+
328
+ if self.metadata_available:
329
+ image_id = i[0]
330
+ image_metadata = i[1]
331
+ else:
332
+ image_id = i
333
+
334
+ try:
335
+ image = self._download_image(image_id)
336
+ except Exception as e:
337
+ print(f'score.py BatchScorer, score_images, download_image exception: {e}')
338
+ result = {
339
+ 'file': image_id,
340
+ 'failure': TFDetector.FAILURE_IMAGE_OPEN
341
+ }
342
+ else:
343
+ result = self.detector.generate_detections_one_image(
344
+ image, image_id, detection_threshold=self.detection_threshold)
345
+
346
+ if self.metadata_available:
347
+ result['meta'] = image_metadata
348
+
349
+ detections.append(result)
350
+ if len(detections) % PRINT_EVERY == 0:
351
+ print(f'scored {len(detections)} images')
352
+
353
+ return detections
354
+
355
+
356
+ def main():
357
+ print('score.py, main()')
358
+
359
+ # information to determine input and output locations
360
+ api_instance_name = os.environ['API_INSTANCE_NAME']
361
+ job_id = os.environ['AZ_BATCH_JOB_ID']
362
+ task_id = os.environ['AZ_BATCH_TASK_ID']
363
+ mount_point = os.environ['AZ_BATCH_NODE_MOUNTS_DIR']
364
+
365
+ # other parameters for the task
366
+ begin_index = int(os.environ['TASK_BEGIN_INDEX'])
367
+ end_index = int(os.environ['TASK_END_INDEX'])
368
+
369
+ input_container_sas = os.environ.get('JOB_CONTAINER_SAS', None) # could be None if use_url
370
+ use_url = os.environ.get('JOB_USE_URL', None)
371
+
372
+ if use_url and use_url.lower() == 'true': # bool of any non-empty string is True
373
+ use_url = True
374
+ else:
375
+ use_url = False
376
+
377
+ detection_threshold = float(os.environ['DETECTION_CONF_THRESHOLD'])
378
+
379
+ print(f'score.py, main(), api_instance_name: {api_instance_name}, job_id: {job_id}, task_id: {task_id}, '
380
+ f'mount_point: {mount_point}, begin_index: {begin_index}, end_index: {end_index}, '
381
+ f'input_container_sas: {input_container_sas}, use_url (parsed): {use_url}'
382
+ f'detection_threshold: {detection_threshold}')
383
+
384
+ job_folder_mounted = os.path.join(mount_point, 'batch-api', f'api_{api_instance_name}', f'job_{job_id}')
385
+ task_out_dir = os.path.join(job_folder_mounted, 'task_outputs')
386
+ os.makedirs(task_out_dir, exist_ok=True)
387
+ task_output_path = os.path.join(task_out_dir, f'job_{job_id}_task_{task_id}.json')
388
+
389
+ # test that we can write to output path; also in case there is no image to process
390
+ with open(task_output_path, 'w') as f:
391
+ json.dump([], f)
392
+
393
+ # list images to process
394
+ list_images_path = os.path.join(job_folder_mounted, f'{job_id}_images.json')
395
+ with open(list_images_path) as f:
396
+ list_images = json.load(f)
397
+ print(f'score.py, main(), length of list_images: {len(list_images)}')
398
+
399
+ if (not isinstance(list_images, list)) or len(list_images) == 0:
400
+ print('score.py, main(), zero images in specified overall list, exiting...')
401
+ sys.exit(0)
402
+
403
+ # items in this list can be strings or [image_id, metadata]
404
+ list_images = list_images[begin_index: end_index]
405
+ if len(list_images) == 0:
406
+ print('score.py, main(), zero images in the shard, exiting')
407
+ sys.exit(0)
408
+
409
+ print(f'score.py, main(), processing {len(list_images)} images in this Task')
410
+
411
+ # model path
412
+ # Path to .pb TensorFlow detector model file, relative to the
413
+ # models/megadetector_copies folder in mounted container
414
+ detector_model_rel_path = os.environ['DETECTOR_REL_PATH']
415
+ detector_path = os.path.join(mount_point, 'models', 'megadetector_copies', detector_model_rel_path)
416
+ assert os.path.exists(detector_path), f'detector is not found at the specified path: {detector_path}'
417
+
418
+ # score the images
419
+ scorer = BatchScorer(
420
+ detector_path=detector_path,
421
+ use_url=use_url,
422
+ input_container_sas=input_container_sas,
423
+ detection_threshold=detection_threshold,
424
+ image_ids_to_score=list_images
425
+ )
426
+
427
+ try:
428
+ tick = datetime.now()
429
+ detections = scorer.score_images()
430
+ duration = datetime.now() - tick
431
+ print(f'score.py, main(), score_images() duration: {duration}')
432
+ except Exception as e:
433
+ raise RuntimeError(f'score.py, main(), exception in score_images(): {e}')
434
+
435
+ with open(task_output_path, 'w', encoding='utf-8') as f:
436
+ json.dump(detections, f, ensure_ascii=False)
437
+
438
+ if __name__ == '__main__':
439
+ main()