megadetector 5.0.24__tar.gz → 5.0.26__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 (218) hide show
  1. {megadetector-5.0.24/megadetector.egg-info → megadetector-5.0.26}/PKG-INFO +6 -3
  2. {megadetector-5.0.24 → megadetector-5.0.26}/README-package.md +2 -0
  3. {megadetector-5.0.24 → megadetector-5.0.26}/README.md +12 -8
  4. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/cct_json_utils.py +15 -2
  5. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/coco_to_yolo.py +53 -31
  6. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/databases/combine_coco_camera_traps_files.py +7 -3
  7. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/databases/integrity_check_json_db.py +2 -2
  8. megadetector-5.0.26/megadetector/data_management/lila/add_locations_to_island_camera_traps.py +101 -0
  9. megadetector-5.0.26/megadetector/data_management/lila/add_locations_to_nacti.py +151 -0
  10. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/lila/generate_lila_per_image_labels.py +2 -2
  11. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/lila/test_lila_metadata_urls.py +21 -10
  12. megadetector-5.0.26/megadetector/data_management/remap_coco_categories.py +133 -0
  13. megadetector-5.0.24/megadetector/data_management/wi_to_md.py → megadetector-5.0.26/megadetector/data_management/speciesnet_to_md.py +2 -2
  14. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/yolo_to_coco.py +45 -15
  15. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/detection/run_detector.py +1 -0
  16. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/detection/run_detector_batch.py +5 -4
  17. megadetector-5.0.26/megadetector/postprocessing/classification_postprocessing.py +980 -0
  18. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/compare_batch_results.py +176 -9
  19. megadetector-5.0.26/megadetector/postprocessing/create_crop_folder.py +420 -0
  20. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/load_api_results.py +4 -1
  21. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/md_to_coco.py +1 -1
  22. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/postprocess_batch_results.py +158 -44
  23. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/repeat_detection_elimination/find_repeat_detections.py +3 -8
  24. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/repeat_detection_elimination/repeat_detections_core.py +2 -2
  25. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/separate_detections_into_folders.py +20 -4
  26. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/subset_json_detector_output.py +180 -15
  27. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/validate_batch_results.py +13 -5
  28. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/taxonomy_mapping/map_new_lila_datasets.py +6 -6
  29. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/taxonomy_mapping/preview_lila_taxonomy.py +3 -58
  30. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/taxonomy_mapping/species_lookup.py +45 -2
  31. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/utils/ct_utils.py +76 -3
  32. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/utils/directory_listing.py +4 -4
  33. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/utils/gpu_test.py +21 -3
  34. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/utils/md_tests.py +142 -49
  35. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/utils/path_utils.py +342 -19
  36. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/utils/wi_utils.py +1286 -212
  37. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/visualization/visualization_utils.py +16 -4
  38. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/visualization/visualize_db.py +1 -1
  39. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/visualization/visualize_detector_output.py +1 -4
  40. {megadetector-5.0.24 → megadetector-5.0.26/megadetector.egg-info}/PKG-INFO +6 -3
  41. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector.egg-info/SOURCES.txt +2 -1
  42. {megadetector-5.0.24 → megadetector-5.0.26}/pyproject.toml +2 -3
  43. megadetector-5.0.24/megadetector/data_management/lila/add_locations_to_island_camera_traps.py +0 -97
  44. megadetector-5.0.24/megadetector/data_management/lila/add_locations_to_nacti.py +0 -147
  45. megadetector-5.0.24/megadetector/data_management/remap_coco_categories.py +0 -84
  46. megadetector-5.0.24/megadetector/postprocessing/classification_postprocessing.py +0 -716
  47. {megadetector-5.0.24 → megadetector-5.0.26}/LICENSE +0 -0
  48. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/__init__.py +0 -0
  49. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/batch_processing/__init__.py +0 -0
  50. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/batch_processing/api_core/__init__.py +0 -0
  51. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/batch_processing/api_core/batch_service/__init__.py +0 -0
  52. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/batch_processing/api_core/batch_service/score.py +0 -0
  53. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/batch_processing/api_core/server.py +0 -0
  54. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/batch_processing/api_core/server_api_config.py +0 -0
  55. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/batch_processing/api_core/server_app_config.py +0 -0
  56. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/batch_processing/api_core/server_batch_job_manager.py +0 -0
  57. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/batch_processing/api_core/server_job_status_table.py +0 -0
  58. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/batch_processing/api_core/server_orchestration.py +0 -0
  59. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/batch_processing/api_core/server_utils.py +0 -0
  60. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/batch_processing/api_core_support/__init__.py +0 -0
  61. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/batch_processing/api_core_support/aggregate_results_manually.py +0 -0
  62. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/batch_processing/api_support/__init__.py +0 -0
  63. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/batch_processing/api_support/summarize_daily_activity.py +0 -0
  64. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/batch_processing/data_preparation/__init__.py +0 -0
  65. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/batch_processing/integration/digiKam/setup.py +0 -0
  66. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/batch_processing/integration/digiKam/xmp_integration.py +0 -0
  67. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/batch_processing/integration/eMammal/test_scripts/config_template.py +0 -0
  68. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/batch_processing/integration/eMammal/test_scripts/push_annotations_to_emammal.py +0 -0
  69. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/batch_processing/integration/eMammal/test_scripts/select_images_for_testing.py +0 -0
  70. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/synchronous/__init__.py +0 -0
  71. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/synchronous/api_core/animal_detection_api/__init__.py +0 -0
  72. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/synchronous/api_core/animal_detection_api/api_backend.py +0 -0
  73. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/synchronous/api_core/animal_detection_api/api_frontend.py +0 -0
  74. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/synchronous/api_core/animal_detection_api/config.py +0 -0
  75. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/synchronous/api_core/tests/__init__.py +0 -0
  76. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/api/synchronous/api_core/tests/load_test.py +0 -0
  77. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/__init__.py +0 -0
  78. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/aggregate_classifier_probs.py +0 -0
  79. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/analyze_failed_images.py +0 -0
  80. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/cache_batchapi_outputs.py +0 -0
  81. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/create_classification_dataset.py +0 -0
  82. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/crop_detections.py +0 -0
  83. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/csv_to_json.py +0 -0
  84. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/detect_and_crop.py +0 -0
  85. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/efficientnet/__init__.py +0 -0
  86. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/efficientnet/model.py +0 -0
  87. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/efficientnet/utils.py +0 -0
  88. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/evaluate_model.py +0 -0
  89. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/identify_mislabeled_candidates.py +0 -0
  90. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/json_to_azcopy_list.py +0 -0
  91. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/json_validator.py +0 -0
  92. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/map_classification_categories.py +0 -0
  93. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/merge_classification_detection_output.py +0 -0
  94. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/prepare_classification_script.py +0 -0
  95. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/prepare_classification_script_mc.py +0 -0
  96. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/run_classifier.py +0 -0
  97. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/save_mislabeled.py +0 -0
  98. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/train_classifier.py +0 -0
  99. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/train_classifier_tf.py +0 -0
  100. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/classification/train_utils.py +0 -0
  101. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/__init__.py +0 -0
  102. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/annotations/__init__.py +0 -0
  103. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/annotations/annotation_constants.py +0 -0
  104. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/camtrap_dp_to_coco.py +0 -0
  105. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/cct_to_md.py +0 -0
  106. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/cct_to_wi.py +0 -0
  107. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/coco_to_labelme.py +0 -0
  108. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/databases/__init__.py +0 -0
  109. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/databases/add_width_and_height_to_db.py +0 -0
  110. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/databases/subset_json_db.py +0 -0
  111. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/generate_crops_from_cct.py +0 -0
  112. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/get_image_sizes.py +0 -0
  113. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/add_nacti_sizes.py +0 -0
  114. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/add_timestamps_to_icct.py +0 -0
  115. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/animl_results_to_md_results.py +0 -0
  116. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/auckland_doc_test_to_json.py +0 -0
  117. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/auckland_doc_to_json.py +0 -0
  118. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/awc_to_json.py +0 -0
  119. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/bellevue_to_json.py +0 -0
  120. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/cacophony-thermal-importer.py +0 -0
  121. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/carrizo_shrubfree_2018.py +0 -0
  122. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/carrizo_trail_cam_2017.py +0 -0
  123. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/cct_field_adjustments.py +0 -0
  124. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/channel_islands_to_cct.py +0 -0
  125. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/eMammal/copy_and_unzip_emammal.py +0 -0
  126. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/eMammal/eMammal_helpers.py +0 -0
  127. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/eMammal/make_eMammal_json.py +0 -0
  128. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/ena24_to_json.py +0 -0
  129. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/filenames_to_json.py +0 -0
  130. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/helena_to_cct.py +0 -0
  131. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/idaho-camera-traps.py +0 -0
  132. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/idfg_iwildcam_lila_prep.py +0 -0
  133. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/import_desert_lion_conservation_camera_traps.py +0 -0
  134. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/jb_csv_to_json.py +0 -0
  135. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/mcgill_to_json.py +0 -0
  136. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/missouri_to_json.py +0 -0
  137. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/nacti_fieldname_adjustments.py +0 -0
  138. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/noaa_seals_2019.py +0 -0
  139. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/osu-small-animals-to-json.py +0 -0
  140. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/pc_to_json.py +0 -0
  141. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/plot_wni_giraffes.py +0 -0
  142. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/prepare_zsl_imerit.py +0 -0
  143. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/raic_csv_to_md_results.py +0 -0
  144. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/rspb_to_json.py +0 -0
  145. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/save_the_elephants_survey_A.py +0 -0
  146. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/save_the_elephants_survey_B.py +0 -0
  147. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/snapshot_safari_importer.py +0 -0
  148. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/snapshot_serengeti_lila.py +0 -0
  149. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/snapshotserengeti/make_full_SS_json.py +0 -0
  150. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/snapshotserengeti/make_per_season_SS_json.py +0 -0
  151. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/sulross_get_exif.py +0 -0
  152. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/timelapse_csv_set_to_json.py +0 -0
  153. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/ubc_to_json.py +0 -0
  154. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/umn_to_json.py +0 -0
  155. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/wellington_to_json.py +0 -0
  156. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/wi_to_json.py +0 -0
  157. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/importers/zamba_results_to_md_results.py +0 -0
  158. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/labelme_to_coco.py +0 -0
  159. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/labelme_to_yolo.py +0 -0
  160. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/lila/__init__.py +0 -0
  161. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/lila/create_lila_blank_set.py +0 -0
  162. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/lila/create_lila_test_set.py +0 -0
  163. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/lila/create_links_to_md_results_files.py +0 -0
  164. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/lila/download_lila_subset.py +0 -0
  165. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/lila/get_lila_annotation_counts.py +0 -0
  166. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/lila/get_lila_image_counts.py +0 -0
  167. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/lila/lila_common.py +0 -0
  168. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/mewc_to_md.py +0 -0
  169. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/ocr_tools.py +0 -0
  170. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/read_exif.py +0 -0
  171. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/remove_exif.py +0 -0
  172. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/rename_images.py +0 -0
  173. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/resize_coco_dataset.py +0 -0
  174. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/wi_download_csv_to_coco.py +0 -0
  175. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/data_management/yolo_output_to_md_output.py +0 -0
  176. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/detection/__init__.py +0 -0
  177. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/detection/process_video.py +0 -0
  178. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/detection/pytorch_detector.py +0 -0
  179. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/detection/run_inference_with_yolov5_val.py +0 -0
  180. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/detection/run_tiled_inference.py +0 -0
  181. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/detection/tf_detector.py +0 -0
  182. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/detection/video_utils.py +0 -0
  183. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/__init__.py +0 -0
  184. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/add_max_conf.py +0 -0
  185. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/categorize_detections_by_size.py +0 -0
  186. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/combine_batch_outputs.py +0 -0
  187. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/convert_output_format.py +0 -0
  188. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/detector_calibration.py +0 -0
  189. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/md_to_labelme.py +0 -0
  190. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/md_to_wi.py +0 -0
  191. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/merge_detections.py +0 -0
  192. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/remap_detection_categories.py +0 -0
  193. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/render_detection_confusion_matrix.py +0 -0
  194. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +0 -0
  195. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/postprocessing/top_folders_to_bottom.py +0 -0
  196. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/taxonomy_mapping/__init__.py +0 -0
  197. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +0 -0
  198. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +0 -0
  199. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/taxonomy_mapping/retrieve_sample_image.py +0 -0
  200. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/taxonomy_mapping/simple_image_download.py +0 -0
  201. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/taxonomy_mapping/taxonomy_csv_checker.py +0 -0
  202. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/taxonomy_mapping/taxonomy_graph.py +0 -0
  203. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/taxonomy_mapping/validate_lila_category_mappings.py +0 -0
  204. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/utils/__init__.py +0 -0
  205. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/utils/azure_utils.py +0 -0
  206. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/utils/process_utils.py +0 -0
  207. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/utils/sas_blob_utils.py +0 -0
  208. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/utils/split_locations_into_train_val.py +0 -0
  209. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/utils/string_utils.py +0 -0
  210. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/utils/url_utils.py +0 -0
  211. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/utils/write_html_image_list.py +0 -0
  212. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/visualization/__init__.py +0 -0
  213. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/visualization/plot_utils.py +0 -0
  214. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector/visualization/render_images_with_thumbnails.py +0 -0
  215. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector.egg-info/dependency_links.txt +0 -0
  216. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector.egg-info/requires.txt +0 -0
  217. {megadetector-5.0.24 → megadetector-5.0.26}/megadetector.egg-info/top_level.txt +0 -0
  218. {megadetector-5.0.24 → megadetector-5.0.26}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: megadetector
3
- Version: 5.0.24
3
+ Version: 5.0.26
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>
@@ -30,7 +30,7 @@ Project-URL: Bug Reports, https://github.com/agentmorris/MegaDetector/issues
30
30
  Project-URL: Source, https://github.com/agentmorris/MegaDetector
31
31
  Keywords: camera traps,conservation,wildlife,ai,megadetector
32
32
  Classifier: Programming Language :: Python :: 3
33
- Requires-Python: <=3.13,>=3.9
33
+ Requires-Python: <3.14,>=3.9
34
34
  Description-Content-Type: text/markdown
35
35
  License-File: LICENSE
36
36
  Requires-Dist: mkl==2024.0; sys_platform != "darwin"
@@ -51,6 +51,7 @@ Requires-Dist: dill
51
51
  Requires-Dist: ultralytics-yolov5==0.1.1
52
52
  Requires-Dist: yolov9pip==0.0.4
53
53
  Requires-Dist: python-dateutil
54
+ Dynamic: license-file
54
55
 
55
56
  # MegaDetector
56
57
 
@@ -58,6 +59,8 @@ This package is a pip-installable version of the support/inference code for [Meg
58
59
 
59
60
  If you aren't looking for the Python package specifically, and you just want to learn more about what MegaDetector is all about, head over to the [MegaDetector repo](https://github.com/agentmorris/MegaDetector/?tab=readme-ov-file#megadetector).
60
61
 
62
+ If you don't want to run MegaDetector, and you just want to use the utilities in this package - postprocessing, manipulating large volumes of camera trap images, etc. - you may want to check out the [megadetector-utils](https://pypi.org/project/megadetector-utils/) package, which is identical to this one, but excludes all of the PyTorch/YOLO dependencies, and is thus approximately one zillion times smaller.
63
+
61
64
  ## Installation
62
65
 
63
66
  Install with:
@@ -4,6 +4,8 @@ This package is a pip-installable version of the support/inference code for [Meg
4
4
 
5
5
  If you aren't looking for the Python package specifically, and you just want to learn more about what MegaDetector is all about, head over to the [MegaDetector repo](https://github.com/agentmorris/MegaDetector/?tab=readme-ov-file#megadetector).
6
6
 
7
+ If you don't want to run MegaDetector, and you just want to use the utilities in this package - postprocessing, manipulating large volumes of camera trap images, etc. - you may want to check out the [megadetector-utils](https://pypi.org/project/megadetector-utils/) package, which is identical to this one, but excludes all of the PyTorch/YOLO dependencies, and is thus approximately one zillion times smaller.
8
+
7
9
  ## Installation
8
10
 
9
11
  Install with:
@@ -10,12 +10,16 @@
10
10
  5. [Repo contents](#repo-contents)
11
11
  6. [Contact](#contact)
12
12
  7. [Gratuitous camera trap picture](#gratuitous-camera-trap-picture)
13
+ 8. [License](#license)
14
+ 9. [Contributing](#contributing)
13
15
 
14
16
 
15
17
  ## What's MegaDetector all about?
16
18
 
17
19
  [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.
18
20
 
21
+ MegaDetector only finds animals, it doesn't identify them to species level. If you're looking for a species classifier, check out [SpeciesNet](https://github.com/google/cameratrapai), a species classifier that plays nicely with MegaDetector.
22
+
19
23
  Here's a &ldquo;teaser&rdquo; image of what MegaDetector output looks like:
20
24
 
21
25
  ![Red bounding box on fox](images/detector_example.jpg)<br/>Image credit University of Washington.
@@ -27,7 +31,7 @@ Here's a &ldquo;teaser&rdquo; image of what MegaDetector output looks like:
27
31
  * 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.
28
32
  * 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.
29
33
  * 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").
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>!
34
+ * If you have any questions, or you want to tell us that MegaDetector was amazing/terrible on your images, or you have a zillion images and you want some help digging out of that backlog, <a href="mailto:cameratraps@lila.science">email us</a>!
31
35
 
32
36
  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;.
33
37
 
@@ -45,9 +49,9 @@ Here are a few of the organizations that have used MegaDetector... we're only li
45
49
  * [Canadian Parks and Wilderness Society (CPAWS) Northern Alberta Chapter](https://cpawsnab.org/)
46
50
  * [Conservation X Labs](https://conservationxlabs.com/)
47
51
  * [Czech University of Life Sciences Prague](https://www.czu.cz/en)
48
- * [Dudek Camera Trap AI Image Toolkit (AIT)](https://dudek.com/services/wildlife-camera-trap-ai-image-processing-and-management/)
52
+ * [Dudek Camera Trap AI Image Toolkit (AIT)](https://ait.dudek.com/)
49
53
  * [EcoLogic Consultants Ltd.](https://www.consult-ecologic.com/)
50
- * [Estación Biológica de Doñana](http://www.ebd.csic.es/inicio)
54
+ * [Estación Biológica de Doñana](https://www.ebd.csic.es/)
51
55
  * [Indigenous Desert Alliance](https://www.indigenousdesertalliance.com/)
52
56
  * [Myall Lakes Dingo Project](https://carnivorecoexistence.info/myall-lakes-dingo-project/)
53
57
  * [Norwegian Institute for Nature Research](https://www.nina.no/english/Home)
@@ -84,7 +88,6 @@ Here are a few of the organizations that have used MegaDetector... we're only li
84
88
  * [Department of Fish and Wildlife Sciences](https://www.uidaho.edu/cnr/departments/fish-and-wildlife-sciences), University of Idaho
85
89
  * [Department of Society & Conservation](https://www.umt.edu/environment/about/departments/socon/), W.A. Franke College of Forestry & Conservation, University of Montana
86
90
  * [Department of Wildlife Ecology and Conservation](https://wec.ifas.ufl.edu/), University of Florida
87
- * [Ecology and Conservation of Amazonian Vertebrates Research Group](https://www.researchgate.net/lab/Fernanda-Michalski-Lab-4), Federal University of Amapá
88
91
  * [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)
89
92
  * [Graeme Shannon's Research Group](https://wildliferesearch.co.uk/group-1), Bangor University
90
93
  * [Grizzly Bear Recovery Program](https://www.fws.gov/office/grizzly-bear-recovery-program), U.S. Fish & Wildlife Service
@@ -102,12 +105,13 @@ Here are a few of the organizations that have used MegaDetector... we're only li
102
105
  * [Santa Monica Mountains Recreation Area](https://www.nps.gov/samo/index.htm), National Park Service
103
106
  * [Seattle Urban Carnivore Project](https://www.zoo.org/seattlecarnivores), Woodland Park Zoo
104
107
  * [Serra dos Órgãos National Park](https://www.icmbio.gov.br/parnaserradosorgaos/), ICMBio
105
- * [Snapshot USA](https://emammal.si.edu/snapshot-usa), Smithsonian
108
+ * [Snapshot USA](https://www.snapshot-usa.org/), Smithsonian
106
109
  * [TROPECOLNET project](https://www.anabenitezlopez.com/research/global-change-biology/tropecolnet/), Museo Nacional de Ciencias Naturales
107
110
  * [Wildlife Coexistence Lab](https://wildlife.forestry.ubc.ca/), University of British Columbia
108
111
  * [Wildlife Research](https://www.dfw.state.or.us/wildlife/research/index.asp), Oregon Department of Fish and Wildlife
109
112
  * [Wildlife Division](https://www.michigan.gov/dnr/about/contact/wildlife), Michigan Department of Natural Resources
110
113
  * [Kohl Wildlife Lab](https://kohlwildlifelab.com/), University of Georgia
114
+ * Ecology and Conservation of Amazonian Vertebrates Research Group, Federal University of Amapá
111
115
  * Department of Ecology, TU Berlin
112
116
  * Ghost Cat Analytics
113
117
  * Protected Areas Unit, Canadian Wildlife Service
@@ -132,7 +136,7 @@ Here are a few of the organizations that have used MegaDetector... we're only li
132
136
  * [BirdLife Malta](https://birdlifemalta.org/) ([tweet](https://x.com/BirdLife_Malta/status/1817456839862173783?t=S-KRiZ5R1-CoW8-tbYNjqQ&s=03)) ([LI post](https://www.linkedin.com/posts/birdlifemalta_worldnatureconservationday-shearwater-colony-activity-7223220656589463553-X2Mc/?utm_source=share&utm_medium=member_android))
133
137
  * [Endangered Landscapes and Seascapes Programme](https://www.endangeredlandscapes.org/), Cambridge Conservation Initiative ([blog post](https://www.endangeredlandscapes.org/news/ai-for-wildlife-monitoring-a-real-time-alert-system-for-bears-and-wild-boars-in-romanias-carpathian-mountains/))
134
138
 
135
- * [Road Ecology Center](https://roadecology.ucdavis.edu/), University of California, Davis ([Wildlife Observer Network platform](https://wildlifeobserver.net/))
139
+ * [Road Ecology Center](https://roadecology.ucdavis.edu/), University of California, Davis ([Wildlife Observer Network platform](https://roadecology.ucdavis.edu/research/projects/wildlife-observer-network))
136
140
  * [The Nature Conservancy in California](https://www.nature.org/en-us/about-us/where-we-work/united-states/california/) ([Animl platform](https://github.com/tnc-ca-geo/animl-frontend)) ([story](https://www.vision-systems.com/non-factory/environment-agriculture/article/14304433/the-nature-conservancy-brings-cameras-ai-to-invasive-species-prevention))
137
141
  * [San Diego Zoo Wildlife Alliance](https://science.sandiegozoo.org/) ([Animl R package](https://github.com/conservationtechlab/animl))
138
142
  * [TerrOïko](https://www.terroiko.fr/) ([OCAPI platform](https://www.terroiko.fr/ocapi))
@@ -202,9 +206,9 @@ Code for hosting our models as an API, either for synchronous operation (i.e., f
202
206
 
203
207
  #### megadetector/classification
204
208
 
205
- 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.
209
+ This folder is largely deprecated thanks to the release of [SpeciesNet](https://github.com/google/cameratrapai), a species classifier that is better than any of the classifiers we ever trained with the stuff in this folder. That said, this folder contains code for training species classifiers on new data sets, generally trained on MegaDetector crops.
206
210
 
207
- All that said, here's another "teaser image" of what you get at the end of training and running a classifier:
211
+ Here's another "teaser image" of what you get at the end of training and running a classifier:
208
212
 
209
213
  <img src="images/warthog_classifications.jpg" width="700"><br/>Image credit University of Minnesota, from the Snapshot Safari program.
210
214
 
@@ -300,7 +300,10 @@ class SequenceOptions:
300
300
  def __init__(self):
301
301
  #: Images separated by <= this duration will be grouped into the same sequence.
302
302
  self.episode_interval_seconds = 60.0
303
-
303
+
304
+ #: How to handle invalid datetimes: 'error' or 'none'
305
+ self.datetime_conversion_failure_behavior = 'none'
306
+
304
307
 
305
308
  #%% Functions
306
309
 
@@ -445,7 +448,17 @@ def create_sequences(image_info,options=None):
445
448
  raise ValueError('Unrecognized type for [image_info]')
446
449
 
447
450
  # Modifies the images in place
448
- _ = parse_datetimes_from_cct_image_list(image_info)
451
+ _ = parse_datetimes_from_cct_image_list(image_info,
452
+ conversion_failure_behavior=options.datetime_conversion_failure_behavior)
453
+
454
+ n_invalid_datetimes = 0
455
+ for im in image_info:
456
+ if not isinstance(im['datetime'],datetime.datetime):
457
+ assert im['datetime'] is None, 'At this point, datetimes should be valid or None'
458
+ n_invalid_datetimes += 1
459
+ if n_invalid_datetimes > 0:
460
+ print('Warning: {} of {} images have invalid datetimes'.format(
461
+ n_invalid_datetimes,len(image_info)))
449
462
 
450
463
  # Find all unique locations
451
464
  locations = set()
@@ -47,6 +47,9 @@ def write_yolo_dataset_file(yolo_dataset_file,
47
47
  class_list (list or str): an ordered list of class names (the first item will be class 0,
48
48
  etc.), or the name of a text file containing an ordered list of class names (one per
49
49
  line, starting from class zero).
50
+ train_folder_relative (str, optional): train folder name, used only to populate dataset.yaml
51
+ val_folder_relative (str, optional): val folder name, used only to populate dataset.yaml
52
+ test_folder_relative (str, optional): test folder name, used only to populate dataset.yaml
50
53
  """
51
54
 
52
55
  # Read class names
@@ -97,7 +100,7 @@ def coco_to_yolo(input_image_folder,
97
100
  category_names_to_exclude=None,
98
101
  category_names_to_include=None,
99
102
  write_output=True,
100
- flatten_paths=True):
103
+ flatten_paths=False):
101
104
  """
102
105
  Converts a COCO-formatted dataset to a YOLO-formatted dataset, optionally flattening the
103
106
  dataset to a single folder in the process.
@@ -116,17 +119,21 @@ def coco_to_yolo(input_image_folder,
116
119
  images are left alone.
117
120
  source_format (str, optional): can be 'coco' (default) or 'coco_camera_traps'. The only difference
118
121
  is that when source_format is 'coco_camera_traps', we treat an image with a non-bbox
119
- annotation with a category id of 0 as a special case, i.e. that's how an empty image
120
- is indicated. The original COCO standard is a little ambiguous on this issue. If
121
- source_format is 'coco', we either treat images as empty or error, depending on the value
122
- of [allow_empty_annotations]. [allow_empty_annotations] has no effect if source_format is
123
- 'coco_camera_traps'.
122
+ annotation as a special case, i.e. that's how an empty image is indicated. The original
123
+ COCO standard is a little ambiguous on this issue. If source_format is 'coco', we
124
+ either treat images as empty or error, depending on the value of [allow_empty_annotations].
125
+ [allow_empty_annotations] has no effect if source_format is 'coco_camera_traps'.
126
+ overwrite_images (bool, optional): over-write images in the output folder if they exist
124
127
  create_image_and_label_folder (bool, optional): whether to create separate folders called 'images' and
125
128
  'labels' in the YOLO output folder. If create_image_and_label_folders is False,
126
129
  a/b/c/image001.jpg will become a#b#c#image001.jpg, and the corresponding text file will
127
130
  be a#b#c#image001.txt. If create_image_and_label_folders is True, a/b/c/image001.jpg will become
128
131
  images/a#b#c#image001.jpg, and the corresponding text file will be
129
132
  labels/a#b#c#image001.txt.
133
+ class_file_name (str, optional): .txt file (relative to the output folder) that we should
134
+ populate with a list of classes (or None to omit)
135
+ allow_empty_annotations (bool, optional): if this is False and [source_format] is 'coco',
136
+ we'll error on annotations that have no 'bbox' field
130
137
  clip_boxes (bool, optional): whether to clip bounding box coordinates to the range [0,1] before
131
138
  converting to YOLO xywh format
132
139
  image_id_to_output_image_json_file (str, optional): an optional *output* file, to which we will write
@@ -139,12 +146,14 @@ def coco_to_yolo(input_image_folder,
139
146
  category_names_to_exclude (str, optional): category names that should not be represented in the
140
147
  YOLO output; only impacts annotations, does not prevent copying images. There's almost no reason
141
148
  you would want to specify this and [category_names_to_include].
142
- category_names_to_include (str, optional): allow-list of category names that should be represented in the
143
- YOLO output; only impacts annotations, does not prevent copying images. There's almost no reason
144
- you would want to specify this and [category_names_to_exclude].
149
+ category_names_to_include (str, optional): allow-list of category names that should be represented
150
+ in the YOLO output; only impacts annotations, does not prevent copying images. There's almost
151
+ no reason you would want to specify this and [category_names_to_exclude].
145
152
  write_output (bool, optional): determines whether we actually copy images and write annotations;
146
153
  setting this to False mostly puts this function in "dry run" "mode. The class list
147
154
  file is written regardless of the value of write_output.
155
+ flatten_paths (bool, optional): replace /'s in image filenames with [path_replacement_char],
156
+ which ensures that the output folder is a single flat folder.
148
157
 
149
158
  Returns:
150
159
  dict: information about the coco --> yolo mapping, containing at least the fields:
@@ -313,9 +322,9 @@ def coco_to_yolo(input_image_folder,
313
322
 
314
323
  elif source_format == 'coco_camera_traps':
315
324
 
316
- # We allow empty bbox lists in COCO camera traps; this is typically a negative
317
- # example in a dataset that has bounding boxes, and 0 is typically the empty
318
- # category.
325
+ # We allow empty bbox lists in COCO camera traps files; this is typically a
326
+ # negative example in a dataset that has bounding boxes, and 0 is typically
327
+ # the empty category, which is typically 0.
319
328
  if ann['category_id'] != 0:
320
329
  if not printed_empty_annotation_warning:
321
330
  printed_empty_annotation_warning = True
@@ -429,13 +438,14 @@ def coco_to_yolo(input_image_folder,
429
438
 
430
439
  print('Generating class list')
431
440
 
432
- class_list_filename = os.path.join(output_folder,class_file_name)
433
- with open(class_list_filename, 'w') as f:
434
- print('Writing class list to {}'.format(class_list_filename))
435
- for i_class in range(0,len(yolo_id_to_name)):
436
- # Category IDs should range from 0..N-1
437
- assert i_class in yolo_id_to_name
438
- f.write(yolo_id_to_name[i_class] + '\n')
441
+ if class_file_name is not None:
442
+ class_list_filename = os.path.join(output_folder,class_file_name)
443
+ with open(class_list_filename, 'w') as f:
444
+ print('Writing class list to {}'.format(class_list_filename))
445
+ for i_class in range(0,len(yolo_id_to_name)):
446
+ # Category IDs should range from 0..N-1
447
+ assert i_class in yolo_id_to_name
448
+ f.write(yolo_id_to_name[i_class] + '\n')
439
449
 
440
450
  if image_id_to_output_image_json_file is not None:
441
451
  print('Writing image ID mapping to {}'.format(image_id_to_output_image_json_file))
@@ -457,6 +467,9 @@ def coco_to_yolo(input_image_folder,
457
467
 
458
468
  source_image_to_dest_image = {}
459
469
 
470
+ label_files_written = []
471
+ n_boxes_written = 0
472
+
460
473
  # TODO: parallelize this loop
461
474
  #
462
475
  # output_info = images_to_copy[0]
@@ -471,6 +484,7 @@ def coco_to_yolo(input_image_folder,
471
484
 
472
485
  source_image_to_dest_image[source_image] = dest_image
473
486
 
487
+ # Copy the image if necessary
474
488
  if write_output:
475
489
 
476
490
  os.makedirs(os.path.dirname(dest_image),exist_ok=True)
@@ -482,17 +496,24 @@ def coco_to_yolo(input_image_folder,
482
496
  if (not os.path.isfile(dest_image)) or (overwrite_images):
483
497
  shutil.copyfile(source_image,dest_image)
484
498
 
485
- bboxes = output_info['bboxes']
499
+ bboxes = output_info['bboxes']
500
+
501
+ # Write the annotation file if necessary
502
+ #
503
+ # Only write an annotation file if there are bounding boxes. Images with
504
+ # no .txt files are treated as hard negatives, at least by YOLOv5:
505
+ #
506
+ # https://github.com/ultralytics/yolov5/issues/3218
507
+ #
508
+ # I think this is also true for images with empty .txt files, but
509
+ # I'm using the convention suggested on that issue, i.e. hard
510
+ # negatives are expressed as images without .txt files.
511
+ if len(bboxes) > 0:
486
512
 
487
- # Only write an annotation file if there are bounding boxes. Images with
488
- # no .txt files are treated as hard negatives, at least by YOLOv5:
489
- #
490
- # https://github.com/ultralytics/yolov5/issues/3218
491
- #
492
- # I think this is also true for images with empty .txt files, but
493
- # I'm using the convention suggested on that issue, i.e. hard
494
- # negatives are expressed as images without .txt files.
495
- if len(bboxes) > 0:
513
+ n_boxes_written += len(bboxes)
514
+ label_files_written.append(dest_txt)
515
+
516
+ if write_output:
496
517
 
497
518
  with open(dest_txt,'w') as f:
498
519
 
@@ -501,8 +522,7 @@ def coco_to_yolo(input_image_folder,
501
522
  assert len(bbox) == 5
502
523
  s = '{} {} {} {} {}'.format(bbox[0],bbox[1],bbox[2],bbox[3],bbox[4])
503
524
  f.write(s + '\n')
504
-
505
- # ...if we're actually writing output
525
+
506
526
 
507
527
  # ...for each image
508
528
 
@@ -510,6 +530,8 @@ def coco_to_yolo(input_image_folder,
510
530
  coco_to_yolo_info['class_list_filename'] = class_list_filename
511
531
  coco_to_yolo_info['source_image_to_dest_image'] = source_image_to_dest_image
512
532
  coco_to_yolo_info['coco_id_to_yolo_id'] = coco_id_to_yolo_id
533
+ coco_to_yolo_info['label_files_written'] = label_files_written
534
+ coco_to_yolo_info['n_boxes_written'] = n_boxes_written
513
535
 
514
536
  return coco_to_yolo_info
515
537
 
@@ -24,8 +24,10 @@ import sys
24
24
 
25
25
  #%% Merge functions
26
26
 
27
- def combine_cct_files(input_files, output_file=None, require_uniqueness=True,
28
- filename_prefixes=None):
27
+ def combine_cct_files(input_files,
28
+ output_file=None,
29
+ require_uniqueness=True,
30
+ filename_prefixes=None):
29
31
  """
30
32
  Merges the list of COCO Camera Traps files [input_files] into a single
31
33
  dictionary, optionally writing the result to [output_file].
@@ -33,8 +35,10 @@ def combine_cct_files(input_files, output_file=None, require_uniqueness=True,
33
35
  Args:
34
36
  input_files (list): paths to CCT .json files
35
37
  output_file (str, optional): path to write merged .json file
36
- require_uniqueness (bool): whether to require that the images in
38
+ require_uniqueness (bool, optional): whether to require that the images in
37
39
  each input_dict be unique
40
+ filename_prefixes (dict, optional): dict mapping input filenames to strings
41
+ that should be prepended to image filenames from that source
38
42
 
39
43
  Returns:
40
44
  dict: the merged COCO-formatted .json dict
@@ -327,7 +327,7 @@ def integrity_check_json_db(jsonFile, options=None):
327
327
 
328
328
  for i_image,result in enumerate(results):
329
329
  if result is not None:
330
- validation_errors.append(images[i_image]['file_name'],result)
330
+ validation_errors.append((images[i_image]['file_name'],result))
331
331
 
332
332
  # ...for each image
333
333
 
@@ -393,7 +393,7 @@ def integrity_check_json_db(jsonFile, options=None):
393
393
  elif image['_count'] > 1:
394
394
  nMultiAnnotated += 1
395
395
 
396
- print('Found {} unannotated images, {} images with multiple annotations'.format(
396
+ print('\nFound {} unannotated images, {} images with multiple annotations'.format(
397
397
  nUnannotated,nMultiAnnotated))
398
398
 
399
399
  if (len(base_dir) > 0) and options.bFindUnusedImages:
@@ -0,0 +1,101 @@
1
+ """
2
+
3
+ add_locations_to_island_camera_traps.py
4
+
5
+ The Island Conservation Camera Traps dataset had unique camera identifiers embedded
6
+ in filenames, but not in the proper metadata fields. This script copies that information
7
+ to metadata.
8
+
9
+ """
10
+
11
+ #%% Imports and constants
12
+
13
+ import os
14
+ import json
15
+ from tqdm import tqdm
16
+
17
+ input_fn = os.path.expanduser('~/lila/metadata/island_conservation.json')
18
+ output_fn = os.path.expanduser('~/tmp/island_conservation.json')
19
+ preview_folder = os.path.expanduser('~/tmp/island_conservation_preview')
20
+ image_directory = os.path.expanduser('~/data/icct/public/')
21
+
22
+
23
+ #%% Prevent imports during testing
24
+
25
+ if False:
26
+
27
+ #%% Read input file
28
+
29
+ with open(input_fn,'r') as f:
30
+ d = json.load(f)
31
+
32
+ d['info']
33
+ d['info']['version'] = '1.01'
34
+
35
+
36
+ #%% Find locations
37
+
38
+ images = d['images']
39
+
40
+ locations = set()
41
+
42
+ for i_image,im in tqdm(enumerate(images),total=len(images)):
43
+ tokens_fn = im['file_name'].split('/')
44
+ tokens_id = im['id'].split('_')
45
+ assert tokens_fn[0] == tokens_id[0]
46
+ assert tokens_fn[1] == tokens_id[1]
47
+ location = tokens_fn[0] + '_' + tokens_fn[1]
48
+ im['location'] = location
49
+ locations.add(location)
50
+
51
+ locations = sorted(list(locations))
52
+
53
+ for s in locations:
54
+ print(s)
55
+
56
+
57
+ #%% Write output file
58
+
59
+ with open(output_fn,'w') as f:
60
+ json.dump(d,f,indent=1)
61
+
62
+
63
+ #%% Validate .json files
64
+
65
+ from megadetector.data_management.databases import integrity_check_json_db
66
+
67
+ options = integrity_check_json_db.IntegrityCheckOptions()
68
+ options.baseDir = image_directory
69
+ options.bCheckImageSizes = False
70
+ options.bCheckImageExistence = True
71
+ options.bFindUnusedImages = True
72
+
73
+ sorted_categories, data, error_info = integrity_check_json_db.integrity_check_json_db(output_fn, options)
74
+
75
+
76
+ #%% Preview labels
77
+
78
+ from megadetector.visualization import visualize_db
79
+
80
+ viz_options = visualize_db.DbVizOptions()
81
+ viz_options.num_to_visualize = 2000
82
+ viz_options.trim_to_images_with_bboxes = False
83
+ viz_options.add_search_links = False
84
+ viz_options.sort_by_filename = False
85
+ viz_options.parallelize_rendering = True
86
+ viz_options.classes_to_exclude = ['test']
87
+ html_output_file, image_db = visualize_db.visualize_db(db_path=output_fn,
88
+ output_dir=preview_folder,
89
+ image_base_dir=image_directory,
90
+ options=viz_options)
91
+
92
+ from megadetector.utils import path_utils
93
+ path_utils.open_file(html_output_file)
94
+
95
+
96
+ #%% Zip output file
97
+
98
+ from megadetector.utils.path_utils import zip_file
99
+
100
+ zip_file(output_fn, verbose=True)
101
+ assert os.path.isfile(output_fn + '.zip')
@@ -0,0 +1,151 @@
1
+ """
2
+
3
+ add_locations_to_nacti.py
4
+
5
+ As of 10.2023, NACTI metadata only has very coarse location information (e.g. "Florida"),
6
+ but camera IDs are embedded in filenames. This script pulls that information from filenames
7
+ and adds it to metadata.
8
+
9
+ """
10
+
11
+ #%% Imports and constants
12
+
13
+ import os
14
+ import json
15
+ import shutil
16
+
17
+ from tqdm import tqdm
18
+ from collections import defaultdict
19
+
20
+ input_file = r'd:\lila\nacti\nacti_metadata.json.1.13\nacti_metadata.json'
21
+ output_file = r'g:\temp\nacti_metadata.1.14.json'
22
+
23
+
24
+ #%% Prevent execution during testing
25
+
26
+ if False:
27
+
28
+ #%% Read metadata
29
+
30
+ with open(input_file,'r') as f:
31
+ d = json.load(f)
32
+
33
+ assert d['info']['version'] == 1.13
34
+
35
+
36
+ #%% Map images to locations (according to the metadata)
37
+
38
+ file_name_to_original_location = {}
39
+
40
+ # im = dataset_labels['images'][0]
41
+ for im in tqdm(d['images']):
42
+ file_name_to_original_location[im['file_name']] = im['location']
43
+
44
+ original_locations = set(file_name_to_original_location.values())
45
+
46
+ print('Found {} locations in the original metadata:'.format(len(original_locations)))
47
+ for loc in original_locations:
48
+ print('[{}]'.format(loc))
49
+
50
+
51
+ #%% Map images to new locations
52
+
53
+ def path_to_location(relative_path):
54
+
55
+ relative_path = relative_path.replace('\\','/')
56
+ if relative_path in file_name_to_original_location:
57
+ location_name = file_name_to_original_location[relative_path]
58
+ if location_name == 'San Juan Mntns, Colorado':
59
+ # "part0/sub000/2010_Unit150_Ivan097_img0003.jpg"
60
+ tokens = relative_path.split('/')[-1].split('_')
61
+ assert tokens[1].startswith('Unit')
62
+ location_name = 'sanjuan_{}_{}_{}'.format(tokens[0],tokens[1],tokens[2])
63
+ elif location_name == 'Lebec, California':
64
+ # "part0/sub035/CA-03_08_13_2015_CA-03_0009738.jpg"
65
+ tokens = relative_path.split('/')[-1].split('_')
66
+ assert tokens[0].startswith('CA-') or tokens[0].startswith('TAG-')
67
+ location_name = 'lebec_{}'.format(tokens[0])
68
+ elif location_name == 'Archbold, FL':
69
+ # "part1/sub110/FL-01_01_25_2016_FL-01_0040421.jpg"
70
+ tokens = relative_path.split('/')[-1].split('_')
71
+ assert tokens[0].startswith('FL-')
72
+ location_name = 'archbold_{}'.format(tokens[0])
73
+ else:
74
+ assert location_name == ''
75
+ tokens = relative_path.split('/')[-1].split('_')
76
+ if tokens[0].startswith('CA-') or tokens[0].startswith('TAG-') or tokens[0].startswith('FL-'):
77
+ location_name = '{}'.format(tokens[0])
78
+
79
+ else:
80
+
81
+ location_name = 'unknown'
82
+
83
+ # print('Returning location {} for file {}'.format(location_name,relative_path))
84
+
85
+ return location_name
86
+
87
+ file_name_to_updated_location = {}
88
+ updated_location_to_count = defaultdict(int)
89
+ for im in tqdm(d['images']):
90
+
91
+ updated_location = path_to_location(im['file_name'])
92
+ file_name_to_updated_location[im['file_name']] = updated_location
93
+ updated_location_to_count[updated_location] += 1
94
+
95
+ updated_location_to_count = {k: v for k, v in sorted(updated_location_to_count.items(),
96
+ key=lambda item: item[1],
97
+ reverse=True)}
98
+
99
+ updated_locations = set(file_name_to_updated_location.values())
100
+
101
+ print('Found {} updated locations in the original metadata:'.format(len(updated_locations)))
102
+ for loc in updated_location_to_count:
103
+ print('{}: {}'.format(loc,updated_location_to_count[loc]))
104
+
105
+
106
+ #%% Re-write metadata
107
+
108
+ for im in d['images']:
109
+ im['location'] = file_name_to_updated_location[im['file_name']]
110
+ d['info']['version'] = 1.14
111
+
112
+ with open(output_file,'w') as f:
113
+ json.dump(d,f,indent=1)
114
+
115
+
116
+ #%% For each location, sample some random images to make sure they look consistent
117
+
118
+ input_base = r'd:\lila\nacti-unzipped'
119
+ assert os.path.isdir(input_base)
120
+
121
+ location_to_images = defaultdict(list)
122
+
123
+ for im in d['images']:
124
+ location_to_images[im['location']].append(im)
125
+
126
+ n_to_sample = 10
127
+ import random
128
+ random.seed(0)
129
+ sampling_folder_base = r'g:\temp\nacti_samples'
130
+
131
+ for location in tqdm(location_to_images):
132
+
133
+ images_this_location = location_to_images[location]
134
+ if len(images_this_location) > n_to_sample:
135
+ images_this_location = random.sample(images_this_location,n_to_sample)
136
+
137
+ for i_image,im in enumerate(images_this_location):
138
+
139
+ fn_relative = im['file_name']
140
+ source_fn_abs = os.path.join(input_base,fn_relative)
141
+ assert os.path.isfile(source_fn_abs)
142
+ ext = os.path.splitext(fn_relative)[1]
143
+ target_fn_abs = os.path.join(sampling_folder_base,'{}/{}'.format(
144
+ location,'image_{}{}'.format(str(i_image).zfill(2),ext)))
145
+ os.makedirs(os.path.dirname(target_fn_abs),exist_ok=True)
146
+ shutil.copyfile(source_fn_abs,target_fn_abs)
147
+
148
+ # ...for each image
149
+
150
+ # ...for each location
151
+
@@ -349,7 +349,7 @@ with open(output_file,'w',encoding='utf-8',newline='') as f:
349
349
 
350
350
  # ...with open()
351
351
 
352
- print('Processed {} datasets'.format(len(metadata_table)))
352
+ print('\nProcessed {} datasets'.format(len(metadata_table)))
353
353
 
354
354
 
355
355
  #%% Read the .csv back
@@ -393,7 +393,7 @@ def check_row(row):
393
393
  dataset_name_to_locations[ds_name].add(row['location_id'])
394
394
 
395
395
  # Faster, but more annoying to debug
396
- if False:
396
+ if True:
397
397
 
398
398
  df.progress_apply(check_row, axis=1)
399
399