megadetector 5.0.13__tar.gz → 5.0.15__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 (207) hide show
  1. {megadetector-5.0.13/megadetector.egg-info → megadetector-5.0.15}/PKG-INFO +1 -1
  2. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/read_exif.py +11 -5
  3. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/detection/process_video.py +230 -70
  4. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/detection/pytorch_detector.py +16 -11
  5. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/detection/run_detector.py +17 -5
  6. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/detection/run_detector_batch.py +186 -67
  7. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/detection/tf_detector.py +11 -3
  8. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/detection/video_utils.py +177 -43
  9. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/postprocessing/convert_output_format.py +12 -5
  10. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/utils/md_tests.py +279 -108
  11. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/utils/path_utils.py +38 -6
  12. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/utils/process_utils.py +8 -2
  13. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/visualization/visualization_utils.py +7 -2
  14. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/visualization/visualize_detector_output.py +0 -1
  15. {megadetector-5.0.13 → megadetector-5.0.15/megadetector.egg-info}/PKG-INFO +1 -1
  16. {megadetector-5.0.13 → megadetector-5.0.15}/pyproject.toml +1 -1
  17. {megadetector-5.0.13 → megadetector-5.0.15}/LICENSE +0 -0
  18. {megadetector-5.0.13 → megadetector-5.0.15}/README-package.md +0 -0
  19. {megadetector-5.0.13 → megadetector-5.0.15}/README.md +0 -0
  20. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/__init__.py +0 -0
  21. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/batch_processing/__init__.py +0 -0
  22. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/batch_processing/api_core/__init__.py +0 -0
  23. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/batch_processing/api_core/batch_service/__init__.py +0 -0
  24. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/batch_processing/api_core/batch_service/score.py +0 -0
  25. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/batch_processing/api_core/server.py +0 -0
  26. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/batch_processing/api_core/server_api_config.py +0 -0
  27. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/batch_processing/api_core/server_app_config.py +0 -0
  28. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/batch_processing/api_core/server_batch_job_manager.py +0 -0
  29. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/batch_processing/api_core/server_job_status_table.py +0 -0
  30. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/batch_processing/api_core/server_orchestration.py +0 -0
  31. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/batch_processing/api_core/server_utils.py +0 -0
  32. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/batch_processing/api_core_support/__init__.py +0 -0
  33. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/batch_processing/api_core_support/aggregate_results_manually.py +0 -0
  34. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/batch_processing/api_support/__init__.py +0 -0
  35. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/batch_processing/api_support/summarize_daily_activity.py +0 -0
  36. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/batch_processing/data_preparation/__init__.py +0 -0
  37. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/batch_processing/integration/digiKam/setup.py +0 -0
  38. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/batch_processing/integration/digiKam/xmp_integration.py +0 -0
  39. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/batch_processing/integration/eMammal/test_scripts/config_template.py +0 -0
  40. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/batch_processing/integration/eMammal/test_scripts/push_annotations_to_emammal.py +0 -0
  41. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/batch_processing/integration/eMammal/test_scripts/select_images_for_testing.py +0 -0
  42. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/synchronous/__init__.py +0 -0
  43. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/synchronous/api_core/animal_detection_api/__init__.py +0 -0
  44. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/synchronous/api_core/animal_detection_api/api_backend.py +0 -0
  45. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/synchronous/api_core/animal_detection_api/api_frontend.py +0 -0
  46. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/synchronous/api_core/animal_detection_api/config.py +0 -0
  47. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/synchronous/api_core/tests/__init__.py +0 -0
  48. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/api/synchronous/api_core/tests/load_test.py +0 -0
  49. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/__init__.py +0 -0
  50. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/aggregate_classifier_probs.py +0 -0
  51. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/analyze_failed_images.py +0 -0
  52. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/cache_batchapi_outputs.py +0 -0
  53. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/create_classification_dataset.py +0 -0
  54. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/crop_detections.py +0 -0
  55. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/csv_to_json.py +0 -0
  56. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/detect_and_crop.py +0 -0
  57. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/efficientnet/__init__.py +0 -0
  58. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/efficientnet/model.py +0 -0
  59. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/efficientnet/utils.py +0 -0
  60. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/evaluate_model.py +0 -0
  61. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/identify_mislabeled_candidates.py +0 -0
  62. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/json_to_azcopy_list.py +0 -0
  63. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/json_validator.py +0 -0
  64. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/map_classification_categories.py +0 -0
  65. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/merge_classification_detection_output.py +0 -0
  66. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/prepare_classification_script.py +0 -0
  67. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/prepare_classification_script_mc.py +0 -0
  68. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/run_classifier.py +0 -0
  69. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/save_mislabeled.py +0 -0
  70. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/train_classifier.py +0 -0
  71. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/train_classifier_tf.py +0 -0
  72. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/classification/train_utils.py +0 -0
  73. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/__init__.py +0 -0
  74. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/annotations/__init__.py +0 -0
  75. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/annotations/annotation_constants.py +0 -0
  76. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/camtrap_dp_to_coco.py +0 -0
  77. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/cct_json_utils.py +0 -0
  78. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/cct_to_md.py +0 -0
  79. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/cct_to_wi.py +0 -0
  80. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/coco_to_labelme.py +0 -0
  81. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/coco_to_yolo.py +0 -0
  82. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/databases/__init__.py +0 -0
  83. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/databases/add_width_and_height_to_db.py +0 -0
  84. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/databases/combine_coco_camera_traps_files.py +0 -0
  85. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/databases/integrity_check_json_db.py +0 -0
  86. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/databases/subset_json_db.py +0 -0
  87. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/generate_crops_from_cct.py +0 -0
  88. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/get_image_sizes.py +0 -0
  89. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/add_nacti_sizes.py +0 -0
  90. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/add_timestamps_to_icct.py +0 -0
  91. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/animl_results_to_md_results.py +0 -0
  92. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/auckland_doc_test_to_json.py +0 -0
  93. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/auckland_doc_to_json.py +0 -0
  94. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/awc_to_json.py +0 -0
  95. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/bellevue_to_json.py +0 -0
  96. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/cacophony-thermal-importer.py +0 -0
  97. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/carrizo_shrubfree_2018.py +0 -0
  98. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/carrizo_trail_cam_2017.py +0 -0
  99. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/cct_field_adjustments.py +0 -0
  100. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/channel_islands_to_cct.py +0 -0
  101. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/eMammal/copy_and_unzip_emammal.py +0 -0
  102. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/eMammal/eMammal_helpers.py +0 -0
  103. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/eMammal/make_eMammal_json.py +0 -0
  104. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/ena24_to_json.py +0 -0
  105. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/filenames_to_json.py +0 -0
  106. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/helena_to_cct.py +0 -0
  107. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/idaho-camera-traps.py +0 -0
  108. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/idfg_iwildcam_lila_prep.py +0 -0
  109. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/jb_csv_to_json.py +0 -0
  110. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/mcgill_to_json.py +0 -0
  111. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/missouri_to_json.py +0 -0
  112. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/nacti_fieldname_adjustments.py +0 -0
  113. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/noaa_seals_2019.py +0 -0
  114. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/pc_to_json.py +0 -0
  115. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/plot_wni_giraffes.py +0 -0
  116. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/prepare-noaa-fish-data-for-lila.py +0 -0
  117. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/prepare_zsl_imerit.py +0 -0
  118. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/rspb_to_json.py +0 -0
  119. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/save_the_elephants_survey_A.py +0 -0
  120. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/save_the_elephants_survey_B.py +0 -0
  121. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/snapshot_safari_importer.py +0 -0
  122. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/snapshot_safari_importer_reprise.py +0 -0
  123. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/snapshot_serengeti_lila.py +0 -0
  124. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/snapshotserengeti/make_full_SS_json.py +0 -0
  125. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/snapshotserengeti/make_per_season_SS_json.py +0 -0
  126. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/sulross_get_exif.py +0 -0
  127. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/timelapse_csv_set_to_json.py +0 -0
  128. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/ubc_to_json.py +0 -0
  129. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/umn_to_json.py +0 -0
  130. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/wellington_to_json.py +0 -0
  131. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/wi_to_json.py +0 -0
  132. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/importers/zamba_results_to_md_results.py +0 -0
  133. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/labelme_to_coco.py +0 -0
  134. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/labelme_to_yolo.py +0 -0
  135. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/lila/__init__.py +0 -0
  136. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/lila/add_locations_to_island_camera_traps.py +0 -0
  137. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/lila/add_locations_to_nacti.py +0 -0
  138. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/lila/create_lila_blank_set.py +0 -0
  139. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/lila/create_lila_test_set.py +0 -0
  140. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/lila/create_links_to_md_results_files.py +0 -0
  141. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/lila/download_lila_subset.py +0 -0
  142. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/lila/generate_lila_per_image_labels.py +0 -0
  143. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/lila/get_lila_annotation_counts.py +0 -0
  144. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/lila/get_lila_image_counts.py +0 -0
  145. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/lila/lila_common.py +0 -0
  146. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/lila/test_lila_metadata_urls.py +0 -0
  147. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/ocr_tools.py +0 -0
  148. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/remap_coco_categories.py +0 -0
  149. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/remove_exif.py +0 -0
  150. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/rename_images.py +0 -0
  151. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/resize_coco_dataset.py +0 -0
  152. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/wi_download_csv_to_coco.py +0 -0
  153. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/yolo_output_to_md_output.py +0 -0
  154. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/data_management/yolo_to_coco.py +0 -0
  155. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/detection/__init__.py +0 -0
  156. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/detection/detector_training/__init__.py +0 -0
  157. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/detection/detector_training/model_main_tf2.py +0 -0
  158. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/detection/run_inference_with_yolov5_val.py +0 -0
  159. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/detection/run_tiled_inference.py +0 -0
  160. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/postprocessing/__init__.py +0 -0
  161. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/postprocessing/add_max_conf.py +0 -0
  162. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/postprocessing/categorize_detections_by_size.py +0 -0
  163. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/postprocessing/classification_postprocessing.py +0 -0
  164. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/postprocessing/combine_api_outputs.py +0 -0
  165. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/postprocessing/compare_batch_results.py +0 -0
  166. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/postprocessing/load_api_results.py +0 -0
  167. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/postprocessing/md_to_coco.py +0 -0
  168. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/postprocessing/md_to_labelme.py +0 -0
  169. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/postprocessing/merge_detections.py +0 -0
  170. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/postprocessing/postprocess_batch_results.py +0 -0
  171. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/postprocessing/remap_detection_categories.py +0 -0
  172. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/postprocessing/render_detection_confusion_matrix.py +0 -0
  173. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/postprocessing/repeat_detection_elimination/find_repeat_detections.py +0 -0
  174. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +0 -0
  175. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/postprocessing/repeat_detection_elimination/repeat_detections_core.py +0 -0
  176. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/postprocessing/separate_detections_into_folders.py +0 -0
  177. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/postprocessing/subset_json_detector_output.py +0 -0
  178. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/postprocessing/top_folders_to_bottom.py +0 -0
  179. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/taxonomy_mapping/__init__.py +0 -0
  180. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +0 -0
  181. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/taxonomy_mapping/map_new_lila_datasets.py +0 -0
  182. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +0 -0
  183. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/taxonomy_mapping/preview_lila_taxonomy.py +0 -0
  184. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/taxonomy_mapping/retrieve_sample_image.py +0 -0
  185. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/taxonomy_mapping/simple_image_download.py +0 -0
  186. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/taxonomy_mapping/species_lookup.py +0 -0
  187. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/taxonomy_mapping/taxonomy_csv_checker.py +0 -0
  188. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/taxonomy_mapping/taxonomy_graph.py +0 -0
  189. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/taxonomy_mapping/validate_lila_category_mappings.py +0 -0
  190. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/utils/__init__.py +0 -0
  191. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/utils/azure_utils.py +0 -0
  192. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/utils/ct_utils.py +0 -0
  193. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/utils/directory_listing.py +0 -0
  194. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/utils/sas_blob_utils.py +0 -0
  195. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/utils/split_locations_into_train_val.py +0 -0
  196. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/utils/string_utils.py +0 -0
  197. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/utils/url_utils.py +0 -0
  198. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/utils/write_html_image_list.py +0 -0
  199. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/visualization/__init__.py +0 -0
  200. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/visualization/plot_utils.py +0 -0
  201. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/visualization/render_images_with_thumbnails.py +0 -0
  202. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector/visualization/visualize_db.py +0 -0
  203. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector.egg-info/SOURCES.txt +0 -0
  204. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector.egg-info/dependency_links.txt +0 -0
  205. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector.egg-info/requires.txt +0 -0
  206. {megadetector-5.0.13 → megadetector-5.0.15}/megadetector.egg-info/top_level.txt +0 -0
  207. {megadetector-5.0.13 → megadetector-5.0.15}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: megadetector
3
- Version: 5.0.13
3
+ Version: 5.0.15
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>
@@ -16,7 +16,7 @@ path. No attempt is made to be consistent in format across the two approaches.
16
16
  import os
17
17
  import subprocess
18
18
  import json
19
- from datetime import datetime
19
+ from datetime import date, datetime
20
20
 
21
21
  from multiprocessing.pool import ThreadPool as ThreadPool
22
22
  from multiprocessing.pool import Pool as Pool
@@ -64,7 +64,7 @@ class ReadExifOptions:
64
64
  #:
65
65
  #: A useful set of tags one might want to limit queries for:
66
66
  #:
67
- #: options.tags_to_include = ['DateTime','Model','Make','ExifImageWidth','ExifImageHeight','DateTime',
67
+ #: options.tags_to_include = ['DateTime','Model','Make','ExifImageWidth','ExifImageHeight',
68
68
  #: 'DateTimeOriginal','Orientation']
69
69
  self.tags_to_include = None
70
70
 
@@ -103,7 +103,7 @@ class ExifResultsToCCTOptions:
103
103
 
104
104
  #: Function for extracting location information, should take a string
105
105
  #: and return a string. Defaults to ct_utils.image_file_to_camera_folder. If
106
- #: this is None, uses folder names as locations.
106
+ #: this is None, location is written as "unknown".
107
107
  self.filename_to_location_function = image_file_to_camera_folder
108
108
 
109
109
 
@@ -689,7 +689,7 @@ def exif_results_to_cct(exif_results,cct_output_file=None,options=None):
689
689
 
690
690
  # By default we assume that each leaf-node folder is a location
691
691
  if options.filename_to_location_function is None:
692
- im['location'] = os.path.dirname(exif_result['file_name'])
692
+ im['location'] = 'unknown'
693
693
  else:
694
694
  im['location'] = options.filename_to_location_function(exif_result['file_name'])
695
695
 
@@ -738,9 +738,15 @@ def exif_results_to_cct(exif_results,cct_output_file=None,options=None):
738
738
  d['annotations'] = []
739
739
  d['categories'] = []
740
740
 
741
+ def json_serialize_datetime(obj):
742
+ if isinstance(obj, (datetime, date)):
743
+ return obj.isoformat()
744
+ raise TypeError('Object {} (type {}) not serializable'.format(
745
+ str(obj),type(obj)))
746
+
741
747
  if cct_output_file is not None:
742
748
  with open(cct_output_file,'w') as f:
743
- json.dump(d,indent=1)
749
+ json.dump(d,f,indent=1,default=json_serialize_datetime)
744
750
 
745
751
  return d
746
752
 
@@ -12,8 +12,6 @@ writing them to disk. The upside, though, is that this approach allows you to r
12
12
  detection elimination after running MegaDetector, and it allows allows more efficient re-use
13
13
  of frames if you end up running MD more than once, or running multiple versions of MD.
14
14
 
15
- TODO: optionally skip writing frames to disk, and process frames in memory.
16
-
17
15
  """
18
16
 
19
17
  #%% Imports
@@ -36,6 +34,7 @@ from megadetector.utils.path_utils import insert_before_extension, clean_path
36
34
  from megadetector.detection.video_utils import video_to_frames
37
35
  from megadetector.detection.video_utils import frames_to_video
38
36
  from megadetector.detection.video_utils import frame_results_to_video_results
37
+ from megadetector.detection.video_utils import _add_frame_numbers_to_results
39
38
  from megadetector.detection.video_utils import video_folder_to_frames
40
39
  from megadetector.detection.video_utils import default_fourcc
41
40
 
@@ -50,6 +49,11 @@ class ProcessVideoOptions:
50
49
  def __init__(self):
51
50
 
52
51
  #: Can be a model filename (.pt or .pb) or a model name (e.g. "MDV5A")
52
+ #:
53
+ #: Use the string "no_detection" to indicate that you only want to extract frames,
54
+ #: not run a model. If you do this, you almost definitely want to set
55
+ #: keep_extracted_frames to "True", otherwise everything in this module is a no-op.
56
+ #: I.e., there's no reason to extract frames, do nothing with them, then delete them.
53
57
  self.model_file = 'MDV5A'
54
58
 
55
59
  #: Video (of folder of videos) to process
@@ -66,7 +70,7 @@ class ProcessVideoOptions:
66
70
  #: if this is None
67
71
  self.frame_folder = None
68
72
 
69
- # Folder to use for rendered frames (if rendering output video); will use a folder
73
+ #: Folder to use for rendered frames (if rendering output video); will use a folder
70
74
  #: in system temp space if this is None
71
75
  self.frame_rendering_folder = None
72
76
 
@@ -111,6 +115,10 @@ class ProcessVideoOptions:
111
115
  #: fourcc code to use for writing videos; only relevant if render_output_video is True
112
116
  self.fourcc = None
113
117
 
118
+ #: force a specific frame rate for output videos; only relevant if render_output_video
119
+ #: is True
120
+ self.rendering_fs = None
121
+
114
122
  #: Confidence threshold to use for writing videos with boxes, only relevant if
115
123
  #: if render_output_video is True. Defaults to choosing a reasonable threshold
116
124
  #: based on the model version.
@@ -121,9 +129,13 @@ class ProcessVideoOptions:
121
129
 
122
130
  #: Sample every Nth frame; set to None (default) or 1 to sample every frame. Typically
123
131
  #: we sample down to around 3 fps, so for typical 30 fps videos, frame_sample=10 is a
124
- #: typical value.
132
+ #: typical value. Mutually exclusive with [frames_to_extract].
125
133
  self.frame_sample = None
126
134
 
135
+ #: Extract a specific set of frames (list of ints, or a single int). Mutually exclusive with
136
+ #: [frame_sample].
137
+ self.frames_to_extract = None
138
+
127
139
  #: Number of workers to use for parallelization; set to <= 1 to disable parallelization
128
140
  self.n_cores = 1
129
141
 
@@ -134,11 +146,18 @@ class ProcessVideoOptions:
134
146
  #: detector.
135
147
  self.class_mapping_filename = None
136
148
 
137
- #: JPEG quality for frame output, from 0-100. Defaults to the opencv default (typically 95)
149
+ #: JPEG quality for frame output, from 0-100. Use None or -1 to let opencv decide.
138
150
  self.quality = 90
139
151
 
140
152
  #: Resize frames so they're at most this wide
141
- self.max_width = 1600
153
+ self.max_width = None
154
+
155
+ #: Run the model at this image size (don't mess with this unless you know what you're
156
+ #: getting into)
157
+ self.image_size = None
158
+
159
+ #: Enable image augmentation
160
+ self.augment = False
142
161
 
143
162
  # ...class ProcessVideoOptions
144
163
 
@@ -278,7 +297,8 @@ def _clean_up_extracted_frames(options,frame_output_folder,frame_filenames):
278
297
 
279
298
  def process_video(options):
280
299
  """
281
- Process a single video through MD, optionally writing a new video with boxes
300
+ Process a single video through MD, optionally writing a new video with boxes.
301
+ Can also be used just to split a video into frames, without running a model.
282
302
 
283
303
  Args:
284
304
  options (ProcessVideoOptions): all the parameters used to control this process,
@@ -294,6 +314,10 @@ def process_video(options):
294
314
  if options.render_output_video and (options.output_video_file is None):
295
315
  options.output_video_file = options.input_video_file + '.detections.mp4'
296
316
 
317
+ if options.model_file == 'no_detection' and not options.keep_extracted_frames:
318
+ print('Warning: you asked for no detection, but did not specify keep_extracted_frames, this is a no-op')
319
+ return
320
+
297
321
  # Track whether frame and rendering folders were created by this script
298
322
  caller_provided_frame_output_folder = (options.frame_folder is not None)
299
323
  caller_provided_rendering_output_folder = (options.frame_rendering_folder is not None)
@@ -309,15 +333,31 @@ def process_video(options):
309
333
 
310
334
  os.makedirs(frame_output_folder, exist_ok=True)
311
335
 
336
+
337
+ ## Extract frames
338
+
312
339
  frame_filenames, Fs = video_to_frames(
313
- options.input_video_file, frame_output_folder,
314
- every_n_frames=options.frame_sample, overwrite=(not options.reuse_frames_if_available),
315
- quality=options.quality, max_width=options.max_width, verbose=options.verbose)
340
+ options.input_video_file,
341
+ frame_output_folder,
342
+ every_n_frames=options.frame_sample,
343
+ overwrite=(not options.reuse_frames_if_available),
344
+ quality=options.quality,
345
+ max_width=options.max_width,
346
+ verbose=options.verbose,
347
+ frames_to_extract=options.frames_to_extract)
316
348
 
317
349
  image_file_names = frame_filenames
318
350
  if options.debug_max_frames > 0:
319
351
  image_file_names = image_file_names[0:options.debug_max_frames]
320
-
352
+
353
+ if options.model_file == 'no_detection':
354
+ assert options.keep_extracted_frames, \
355
+ 'Internal error: keep_extracted_frames not set, but no model specified'
356
+ return
357
+
358
+
359
+ ## Run MegaDetector
360
+
321
361
  if options.reuse_results_if_available and \
322
362
  os.path.isfile(options.output_json_file):
323
363
  print('Loading results from {}'.format(options.output_json_file))
@@ -325,12 +365,17 @@ def process_video(options):
325
365
  results = json.load(f)
326
366
  else:
327
367
  results = run_detector_batch.load_and_run_detector_batch(
328
- options.model_file, image_file_names,
368
+ options.model_file,
369
+ image_file_names,
329
370
  confidence_threshold=options.json_confidence_threshold,
330
371
  n_cores=options.n_cores,
331
372
  class_mapping_filename=options.class_mapping_filename,
332
- quiet=True)
333
-
373
+ quiet=True,
374
+ augment=options.augment,
375
+ image_size=options.image_size)
376
+
377
+ _add_frame_numbers_to_results(results)
378
+
334
379
  run_detector_batch.write_results_to_file(
335
380
  results, options.output_json_file,
336
381
  relative_path_base=frame_output_folder,
@@ -357,14 +402,20 @@ def process_video(options):
357
402
  confidence_threshold=options.rendering_confidence_threshold)
358
403
 
359
404
  # Combine into a video
360
- if options.frame_sample is None:
405
+ if options.rendering_fs is not None:
406
+ rendering_fs = options.rendering_fs
407
+ elif options.frame_sample is None:
361
408
  rendering_fs = Fs
362
409
  else:
410
+ # If the original video was 30fps and we sampled every 10th frame,
411
+ # render at 3fps
363
412
  rendering_fs = Fs / options.frame_sample
364
413
 
365
414
  print('Rendering {} frames to {} at {} fps (original video {} fps)'.format(
366
415
  len(detected_frame_files), options.output_video_file,rendering_fs,Fs))
367
- frames_to_video(detected_frame_files, rendering_fs, options.output_video_file,
416
+ frames_to_video(detected_frame_files,
417
+ rendering_fs,
418
+ options.output_video_file,
368
419
  codec_spec=options.fourcc)
369
420
 
370
421
  # Possibly clean up rendered frames
@@ -381,7 +432,13 @@ def process_video(options):
381
432
 
382
433
  def process_video_folder(options):
383
434
  """
384
- Process a folder of videos through MD
435
+ Process a folder of videos through MD. Can also be used just to split a folder of
436
+ videos into frames, without running a model.
437
+
438
+ When this function is used to run MD, two .json files will get written, one with
439
+ an entry for each *frame* (identical to what's created by process_video()), and
440
+ one with an entry for each *video* (which is more suitable for, e.g., reading into
441
+ Timelapse).
385
442
 
386
443
  Args:
387
444
  options (ProcessVideoOptions): all the parameters used to control this process,
@@ -393,13 +450,17 @@ def process_video_folder(options):
393
450
  assert os.path.isdir(options.input_video_file), \
394
451
  '{} is not a folder'.format(options.input_video_file)
395
452
 
396
- assert options.output_json_file is not None, \
397
- 'When processing a folder, you must specify an output .json file'
398
-
399
- assert options.output_json_file.endswith('.json')
400
- video_json = options.output_json_file
401
- frames_json = options.output_json_file.replace('.json','.frames.json')
402
- os.makedirs(os.path.dirname(video_json),exist_ok=True)
453
+ if options.model_file == 'no_detection' and not options.keep_extracted_frames:
454
+ print('Warning: you asked for no detection, but did not specify keep_extracted_frames, this is a no-op')
455
+ return
456
+
457
+ if options.model_file != 'no_detection':
458
+ assert options.output_json_file is not None, \
459
+ 'When processing a folder, you must specify an output .json file'
460
+ assert options.output_json_file.endswith('.json')
461
+ video_json = options.output_json_file
462
+ frames_json = options.output_json_file.replace('.json','.frames.json')
463
+ os.makedirs(os.path.dirname(video_json),exist_ok=True)
403
464
 
404
465
  # Track whether frame and rendering folders were created by this script
405
466
  caller_provided_frame_output_folder = (options.frame_folder is not None)
@@ -420,6 +481,7 @@ def process_video_folder(options):
420
481
  os.makedirs(frame_output_folder, exist_ok=True)
421
482
 
422
483
  print('Extracting frames')
484
+
423
485
  frame_filenames, Fs, video_filenames = \
424
486
  video_folder_to_frames(input_folder=options.input_video_file,
425
487
  output_folder_base=frame_output_folder,
@@ -429,8 +491,10 @@ def process_video_folder(options):
429
491
  every_n_frames=options.frame_sample,
430
492
  verbose=options.verbose,
431
493
  quality=options.quality,
432
- max_width=options.max_width)
494
+ max_width=options.max_width,
495
+ frames_to_extract=options.frames_to_extract)
433
496
 
497
+ print('Extracted frames for {} videos'.format(len(set(video_filenames))))
434
498
  image_file_names = list(itertools.chain.from_iterable(frame_filenames))
435
499
 
436
500
  if len(image_file_names) == 0:
@@ -443,6 +507,11 @@ def process_video_folder(options):
443
507
 
444
508
  if options.debug_max_frames is not None and options.debug_max_frames > 0:
445
509
  image_file_names = image_file_names[0:options.debug_max_frames]
510
+
511
+ if options.model_file == 'no_detection':
512
+ assert options.keep_extracted_frames, \
513
+ 'Internal error: keep_extracted_frames not set, but no model specified'
514
+ return
446
515
 
447
516
 
448
517
  ## Run MegaDetector on the extracted frames
@@ -454,12 +523,17 @@ def process_video_folder(options):
454
523
  else:
455
524
  print('Running MegaDetector')
456
525
  results = run_detector_batch.load_and_run_detector_batch(
457
- options.model_file, image_file_names,
526
+ options.model_file,
527
+ image_file_names,
458
528
  confidence_threshold=options.json_confidence_threshold,
459
529
  n_cores=options.n_cores,
460
530
  class_mapping_filename=options.class_mapping_filename,
461
- quiet=True)
531
+ quiet=True,
532
+ augment=options.augment,
533
+ image_size=options.image_size)
462
534
 
535
+ _add_frame_numbers_to_results(results)
536
+
463
537
  run_detector_batch.write_results_to_file(
464
538
  results, frames_json,
465
539
  relative_path_base=frame_output_folder,
@@ -518,9 +592,13 @@ def process_video_folder(options):
518
592
 
519
593
  video_fs = Fs[i_video]
520
594
 
521
- if options.frame_sample is None:
595
+ if options.rendering_fs is not None:
596
+ rendering_fs = options.rendering_fs
597
+ elif options.frame_sample is None:
522
598
  rendering_fs = video_fs
523
599
  else:
600
+ # If the original video was 30fps and we sampled every 10th frame,
601
+ # render at 3fps
524
602
  rendering_fs = video_fs / options.frame_sample
525
603
 
526
604
  input_video_file_relative = os.path.relpath(input_video_file_abs,options.input_video_file)
@@ -547,7 +625,10 @@ def process_video_folder(options):
547
625
  # Create the output video
548
626
  print('Rendering detections for video {} to {} at {} fps (original video {} fps)'.format(
549
627
  input_video_file_relative,video_output_file,rendering_fs,video_fs))
550
- frames_to_video(video_frame_files, rendering_fs, video_output_file, codec_spec=options.fourcc)
628
+ frames_to_video(video_frame_files,
629
+ rendering_fs,
630
+ video_output_file,
631
+ codec_spec=options.fourcc)
551
632
 
552
633
  # ...for each video
553
634
 
@@ -607,6 +688,14 @@ def options_to_command(options):
607
688
  cmd += ' --n_cores ' + str(options.n_cores)
608
689
  if options.frame_sample is not None:
609
690
  cmd += ' --frame_sample ' + str(options.frame_sample)
691
+ if options.frames_to_extract is not None:
692
+ cmd += ' --frames_to_extract '
693
+ if isinstance(options.frames_to_extract,int):
694
+ frames_to_extract = [options.frames_to_extract]
695
+ else:
696
+ frames_to_extract = options.frames_to_extract
697
+ for frame_number in frames_to_extract:
698
+ cmd += ' {}'.format(frame_number)
610
699
  if options.debug_max_frames is not None:
611
700
  cmd += ' --debug_max_frames ' + str(options.debug_max_frames)
612
701
  if options.class_mapping_filename is not None:
@@ -631,16 +720,20 @@ def options_to_command(options):
631
720
 
632
721
  if False:
633
722
 
723
+ pass
724
+
634
725
  #%% Process a folder of videos
635
726
 
636
727
  model_file = 'MDV5A'
637
- input_dir = r'g:\temp\test-videos'
728
+ # input_dir = r'g:\temp\test-videos'
729
+ input_dir = r'G:\temp\md-test-package\md-test-images\video-samples'
638
730
  output_base = r'g:\temp\video_test'
639
731
  frame_folder = os.path.join(output_base,'frames')
640
732
  rendering_folder = os.path.join(output_base,'rendered-frames')
641
733
  output_json_file = os.path.join(output_base,'video-test.json')
642
734
  output_video_folder = os.path.join(output_base,'output_videos')
643
735
 
736
+
644
737
  print('Processing folder {}'.format(input_dir))
645
738
 
646
739
  options = ProcessVideoOptions()
@@ -654,32 +747,28 @@ if False:
654
747
  options.quality = 90
655
748
  options.frame_sample = 10
656
749
  options.max_width = 1280
657
- options.n_cores = 5
750
+ options.n_cores = 4
658
751
  options.verbose = True
659
- options.render_output_video = True
660
-
661
- options.frame_folder = None # frame_folder
662
- options.frame_rendering_folder = None # rendering_folder
663
-
664
- options.keep_extracted_frames = False
665
- options.keep_rendered_frames = False
666
- options.force_extracted_frame_folder_deletion = True
667
- options.force_rendered_frame_folder_deletion = True
668
-
669
- # options.confidence_threshold = 0.15
752
+ options.render_output_video = True
753
+ options.frame_folder = frame_folder
754
+ options.frame_rendering_folder = rendering_folder
755
+ options.keep_extracted_frames = True
756
+ options.keep_rendered_frames = True
757
+ options.force_extracted_frame_folder_deletion = False
758
+ options.force_rendered_frame_folder_deletion = False
670
759
  options.fourcc = 'mp4v'
760
+ # options.rendering_confidence_threshold = 0.15
671
761
 
672
762
  cmd = options_to_command(options); print(cmd)
673
763
 
674
- import clipboard; clipboard.copy(cmd)
675
-
676
- if False:
677
- process_video_folder(options)
764
+ # import clipboard; clipboard.copy(cmd)
765
+ # process_video_folder(options)
678
766
 
679
767
 
680
768
  #%% Process a single video
681
769
 
682
770
  fn = r'g:\temp\test-videos\person_and_dog\DSCF0056.AVI'
771
+ assert os.path.isfile(fn)
683
772
  model_file = 'MDV5A'
684
773
  input_video_file = fn
685
774
 
@@ -687,39 +776,89 @@ if False:
687
776
  frame_folder = os.path.join(output_base,'frames')
688
777
  rendering_folder = os.path.join(output_base,'rendered-frames')
689
778
  output_json_file = os.path.join(output_base,'video-test.json')
690
- output_video_file = os.path.join(output_base,'output_videos.mp4')
779
+ output_video_file = os.path.join(output_base,'output_video.mp4')
691
780
 
692
781
  options = ProcessVideoOptions()
693
782
  options.model_file = model_file
694
783
  options.input_video_file = input_video_file
695
784
  options.render_output_video = True
696
785
  options.output_video_file = output_video_file
697
-
698
- options.verbose = True
699
-
786
+ options.output_json_file = output_json_file
787
+ options.verbose = True
700
788
  options.quality = 75
701
- options.frame_sample = None # 10
702
- options.max_width = 600
703
-
704
- options.frame_folder = None # frame_folder
705
- options.frame_rendering_folder = None # rendering_folder
706
-
789
+ options.frame_sample = 10
790
+ options.max_width = 1600
791
+ options.frame_folder = frame_folder
792
+ options.frame_rendering_folder = rendering_folder
707
793
  options.keep_extracted_frames = False
708
794
  options.keep_rendered_frames = False
709
795
  options.force_extracted_frame_folder_deletion = True
710
- options.force_rendered_frame_folder_deletion = True
711
-
712
- # options.confidence_threshold = 0.15
796
+ options.force_rendered_frame_folder_deletion = True
713
797
  options.fourcc = 'mp4v'
798
+ # options.rendering_confidence_threshold = 0.15
714
799
 
715
800
  cmd = options_to_command(options); print(cmd)
716
801
 
717
- import clipboard; clipboard.copy(cmd)
718
-
719
- if False:
720
- process_video(options)
802
+ # import clipboard; clipboard.copy(cmd)
803
+ process_video(options)
721
804
 
722
805
 
806
+ #%% Extract specific frames from a single video, no detection
807
+
808
+ fn = r'g:\temp\test-videos\person_and_dog\DSCF0064.AVI'
809
+ assert os.path.isfile(fn)
810
+ model_file = 'no_detection'
811
+ input_video_file = fn
812
+
813
+ output_base = r'g:\temp\video_test'
814
+ frame_folder = os.path.join(output_base,'frames')
815
+ output_video_file = os.path.join(output_base,'output_videos.mp4')
816
+
817
+ options = ProcessVideoOptions()
818
+ options.model_file = model_file
819
+ options.input_video_file = input_video_file
820
+ options.verbose = True
821
+ options.quality = 90
822
+ options.frame_sample = None
823
+ options.frames_to_extract = [0,100]
824
+ options.max_width = None
825
+ options.frame_folder = frame_folder
826
+ options.keep_extracted_frames = True
827
+
828
+ cmd = options_to_command(options); print(cmd)
829
+
830
+ # import clipboard; clipboard.copy(cmd)
831
+ process_video(options)
832
+
833
+
834
+ #%% Extract specific frames from a folder, no detection
835
+
836
+ fn = r'g:\temp\test-videos\person_and_dog'
837
+ assert os.path.isdir(fn)
838
+ model_file = 'no_detection'
839
+ input_video_file = fn
840
+
841
+ output_base = r'g:\temp\video_test'
842
+ frame_folder = os.path.join(output_base,'frames')
843
+ output_video_file = os.path.join(output_base,'output_videos.mp4')
844
+
845
+ options = ProcessVideoOptions()
846
+ options.model_file = model_file
847
+ options.input_video_file = input_video_file
848
+ options.verbose = True
849
+ options.quality = 90
850
+ options.frame_sample = None
851
+ options.frames_to_extract = [0,100]
852
+ options.max_width = None
853
+ options.frame_folder = frame_folder
854
+ options.keep_extracted_frames = True
855
+
856
+ cmd = options_to_command(options); print(cmd)
857
+
858
+ # import clipboard; clipboard.copy(cmd)
859
+ process_video(options)
860
+
861
+
723
862
  #%% Command-line driver
724
863
 
725
864
  def main():
@@ -731,7 +870,8 @@ def main():
731
870
  'producing a new video with detections annotated'))
732
871
 
733
872
  parser.add_argument('model_file', type=str,
734
- help='MegaDetector model file (.pt or .pb) or model name (e.g. "MDV5A")')
873
+ help='MegaDetector model file (.pt or .pb) or model name (e.g. "MDV5A"), '\
874
+ 'or the string "no_detection" to run just frame extraction')
735
875
 
736
876
  parser.add_argument('input_video_file', type=str,
737
877
  help='video file (or folder) to process')
@@ -788,25 +928,36 @@ def main():
788
928
  'whether other files were present in the folder.')
789
929
 
790
930
  parser.add_argument('--rendering_confidence_threshold', type=float,
791
- default=None, help="don't render boxes with confidence below this threshold (defaults to choosing based on the MD version)")
931
+ default=None,
932
+ help="don't render boxes with confidence below this threshold (defaults to choosing based on the MD version)")
933
+
934
+ parser.add_argument('--rendering_fs', type=float,
935
+ default=None,
936
+ help='force a specific frame rate for output videos (only relevant when using '\
937
+ '--render_output_video) (defaults to the original frame rate)')
792
938
 
793
939
  parser.add_argument('--json_confidence_threshold', type=float,
794
- default=0.0, help="don't include boxes in the .json file with confidence "\
940
+ default=default_options.json_confidence_threshold,
941
+ help="don't include boxes in the .json file with confidence "\
795
942
  'below this threshold (default {})'.format(
796
943
  default_options.json_confidence_threshold))
797
944
 
798
945
  parser.add_argument('--n_cores', type=int,
799
- default=1, help='Number of cores to use for frame separation and detection. '\
946
+ default=default_options.n_cores,
947
+ help='Number of cores to use for frame separation and detection. '\
800
948
  'If using a GPU, this option will be respected for frame separation but '\
801
949
  'ignored for detection. Only relevant to frame separation when processing '\
802
- 'a folder.')
950
+ 'a folder. Default {}.'.format(default_options.n_cores))
803
951
 
804
952
  parser.add_argument('--frame_sample', type=int,
805
953
  default=None, help='process every Nth frame (defaults to every frame)')
806
954
 
955
+ parser.add_argument('--frames_to_extract', nargs='+', type=int,
956
+ default=None, help='extract specific frames (one or more ints)')
957
+
807
958
  parser.add_argument('--quality', type=int,
808
959
  default=default_options.quality,
809
- help='JPEG quality for extracted frames (defaults to {})'.format(
960
+ help='JPEG quality for extracted frames (defaults to {}), use -1 to force no quality setting'.format(
810
961
  default_options.quality))
811
962
 
812
963
  parser.add_argument('--max_width', type=int,
@@ -828,7 +979,16 @@ def main():
828
979
  parser.add_argument('--verbose', action='store_true',
829
980
  help='Enable additional debug output')
830
981
 
831
-
982
+ parser.add_argument('--image_size',
983
+ type=int,
984
+ default=None,
985
+ help=('Force image resizing to a specific integer size on the long '\
986
+ 'axis (not recommended to change this)'))
987
+
988
+ parser.add_argument('--augment',
989
+ action='store_true',
990
+ help='Enable image augmentation')
991
+
832
992
  if len(sys.argv[1:]) == 0:
833
993
  parser.print_help()
834
994
  parser.exit()
@@ -180,9 +180,12 @@ class PTDetector:
180
180
 
181
181
  return model
182
182
 
183
- def generate_detections_one_image(self, img_original, image_id='unknown',
184
- detection_threshold=0.00001, image_size=None,
185
- skip_image_resizing=False):
183
+ def generate_detections_one_image(self, img_original,
184
+ image_id='unknown',
185
+ detection_threshold=0.00001,
186
+ image_size=None,
187
+ skip_image_resizing=False,
188
+ augment=False):
186
189
  """
187
190
  Applies the detector to an image.
188
191
 
@@ -192,11 +195,11 @@ class PTDetector:
192
195
  of the output object
193
196
  detection_threshold (float, optional): only detections above this confidence threshold
194
197
  will be included in the return value
195
- image_size (tuple, optional): image size to use for inference, only mess with this
196
- if (a) you're using a model other than MegaDetector or (b) you know what you're
197
- doing
198
- skip_image_resizing (bool, optional): whether to skip internal image resizing (and rely on external
199
- resizing)
198
+ image_size (tuple, optional): image size to use for inference, only mess with this if
199
+ (a) you're using a model other than MegaDetector or (b) you know what you're getting into
200
+ skip_image_resizing (bool, optional): whether to skip internal image resizing (and rely on
201
+ external resizing)
202
+ augment (bool, optional): enable (implementation-specific) image augmentation
200
203
 
201
204
  Returns:
202
205
  dict: a dictionary with the following fields:
@@ -242,8 +245,10 @@ class PTDetector:
242
245
  if skip_image_resizing:
243
246
  img = img_original
244
247
  else:
245
- letterbox_result = letterbox(img_original, new_shape=target_size,
246
- stride=PTDetector.STRIDE, auto=True)
248
+ letterbox_result = letterbox(img_original,
249
+ new_shape=target_size,
250
+ stride=PTDetector.STRIDE,
251
+ auto=True)
247
252
  img = letterbox_result[0]
248
253
 
249
254
  # HWC to CHW; PIL Image is RGB already
@@ -258,7 +263,7 @@ class PTDetector:
258
263
  if len(img.shape) == 3:
259
264
  img = torch.unsqueeze(img, 0)
260
265
 
261
- pred: list = self.model(img)[0]
266
+ pred = self.model(img,augment=augment)[0]
262
267
 
263
268
  # NMS
264
269
  if self.device == 'mps':