megadetector 5.0.2__tar.gz → 5.0.4__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.
- megadetector-5.0.4/PKG-INFO +141 -0
- megadetector-5.0.4/README-package.md +88 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/README.md +1 -1
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/postprocessing/md_to_labelme.py +67 -38
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/merge_classification_detection_output.py +1 -1
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/cacophony-thermal-importer.py +1 -1
- {megadetector-5.0.2 → megadetector-5.0.4}/detection/process_video.py +1 -1
- {megadetector-5.0.2 → megadetector-5.0.4}/detection/pytorch_detector.py +25 -7
- {megadetector-5.0.2 → megadetector-5.0.4}/detection/run_detector.py +69 -1
- {megadetector-5.0.2 → megadetector-5.0.4}/detection/run_detector_batch.py +8 -1
- {megadetector-5.0.2 → megadetector-5.0.4}/md_utils/url_utils.py +4 -3
- {megadetector-5.0.2 → megadetector-5.0.4}/md_visualization/visualization_utils.py +1 -1
- megadetector-5.0.4/megadetector.egg-info/PKG-INFO +141 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/megadetector.egg-info/SOURCES.txt +1 -0
- megadetector-5.0.4/megadetector.egg-info/requires.txt +16 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/pyproject.toml +18 -18
- megadetector-5.0.2/PKG-INFO +0 -295
- megadetector-5.0.2/megadetector.egg-info/PKG-INFO +0 -295
- megadetector-5.0.2/megadetector.egg-info/requires.txt +0 -16
- {megadetector-5.0.2 → megadetector-5.0.4}/LICENSE +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/api_core/batch_service/score.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/api_core/server.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/api_core/server_api_config.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/api_core/server_app_config.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/api_core/server_batch_job_manager.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/api_core/server_job_status_table.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/api_core/server_orchestration.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/api_core/server_utils.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/api_core_support/aggregate_results_manually.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/api_support/summarize_daily_activity.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/data_preparation/manage_local_batch.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/data_preparation/manage_video_batch.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/integration/digiKam/setup.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/integration/digiKam/xmp_integration.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/integration/eMammal/test_scripts/config_template.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/integration/eMammal/test_scripts/push_annotations_to_emammal.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/integration/eMammal/test_scripts/select_images_for_testing.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/postprocessing/add_max_conf.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/postprocessing/categorize_detections_by_size.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/postprocessing/combine_api_outputs.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/postprocessing/compare_batch_results.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/postprocessing/convert_output_format.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/postprocessing/load_api_results.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/postprocessing/md_to_coco_starter_code.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/postprocessing/merge_detections.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/postprocessing/postprocess_batch_results.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/postprocessing/repeat_detection_elimination/find_repeat_detections.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/postprocessing/repeat_detection_elimination/repeat_detections_core.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/postprocessing/separate_detections_into_folders.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/postprocessing/subset_json_detector_output.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/postprocessing/top_folders_to_bottom.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/synchronous/api_core/animal_detection_api/api_backend.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/synchronous/api_core/animal_detection_api/api_frontend.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/synchronous/api_core/animal_detection_api/config.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/api/synchronous/api_core/tests/load_test.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/aggregate_classifier_probs.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/analyze_failed_images.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/cache_batchapi_outputs.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/create_classification_dataset.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/crop_detections.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/csv_to_json.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/detect_and_crop.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/efficientnet/__init__.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/efficientnet/model.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/efficientnet/utils.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/evaluate_model.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/identify_mislabeled_candidates.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/json_to_azcopy_list.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/json_validator.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/map_classification_categories.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/prepare_classification_script.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/prepare_classification_script_mc.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/run_classifier.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/save_mislabeled.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/train_classifier.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/train_classifier_tf.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/classification/train_utils.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/annotations/annotation_constants.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/cct_json_to_filename_json.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/cct_json_utils.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/cct_to_csv.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/cct_to_md.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/cct_to_wi.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/coco_to_yolo.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/databases/add_width_and_height_to_db.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/databases/combine_coco_camera_traps_files.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/databases/integrity_check_json_db.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/databases/remove_corrupted_images_from_db.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/databases/subset_json_db.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/generate_crops_from_cct.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/get_image_sizes.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/add_nacti_sizes.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/auckland_doc_test_to_json.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/auckland_doc_to_json.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/awc_to_json.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/bellevue_to_json.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/carrizo_shrubfree_2018.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/carrizo_trail_cam_2017.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/cct_field_adjustments.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/channel_islands_to_cct.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/eMammal/copy_and_unzip_emammal.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/eMammal/eMammal_helpers.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/eMammal/make_eMammal_json.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/ena24_to_json.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/filenames_to_json.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/helena_to_cct.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/idaho-camera-traps.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/idfg_iwildcam_lila_prep.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/jb_csv_to_json.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/mcgill_to_json.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/missouri_to_json.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/nacti_fieldname_adjustments.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/noaa_seals_2019.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/pc_to_json.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/plot_wni_giraffes.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/prepare-noaa-fish-data-for-lila.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/prepare_zsl_imerit.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/rspb_to_json.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/save_the_elephants_survey_A.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/save_the_elephants_survey_B.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/snapshot_safari_importer.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/snapshot_safari_importer_reprise.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/snapshot_serengeti_lila.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/snapshotserengeti/make_full_SS_json.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/snapshotserengeti/make_per_season_SS_json.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/sulross_get_exif.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/timelapse_csv_set_to_json.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/ubc_to_json.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/umn_to_json.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/wellington_to_json.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/wi_to_json.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/lila/add_locations_to_island_camera_traps.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/lila/create_lila_test_set.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/lila/download_lila_subset.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/lila/generate_lila_per_image_labels.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/lila/get_lila_annotation_counts.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/lila/get_lila_image_counts.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/lila/lila_common.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/read_exif.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/remove_exif.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/yolo_output_to_md_output.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/data_management/yolo_to_coco.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/detection/detector_training/copy_checkpoints.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/detection/detector_training/model_main_tf2.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/detection/run_inference_with_yolov5_val.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/detection/run_tiled_inference.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/detection/tf_detector.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/detection/video_utils.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/md_utils/azure_utils.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/md_utils/ct_utils.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/md_utils/directory_listing.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/md_utils/matlab_porting_tools.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/md_utils/path_utils.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/md_utils/process_utils.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/md_utils/sas_blob_utils.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/md_utils/string_utils.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/md_utils/write_html_image_list.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/md_visualization/plot_utils.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/md_visualization/render_images_with_thumbnails.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/md_visualization/visualize_db.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/md_visualization/visualize_detector_output.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/md_visualization/visualize_incoming_annotations.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/md_visualization/visualize_megadb.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/megadetector.egg-info/dependency_links.txt +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/megadetector.egg-info/top_level.txt +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/setup.cfg +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/taxonomy_mapping/map_new_lila_datasets.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/taxonomy_mapping/prepare_lila_taxonomy_release.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/taxonomy_mapping/preview_lila_taxonomy.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/taxonomy_mapping/retrieve_sample_image.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/taxonomy_mapping/simple_image_download.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/taxonomy_mapping/species_lookup.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/taxonomy_mapping/taxonomy_csv_checker.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/taxonomy_mapping/taxonomy_graph.py +0 -0
- {megadetector-5.0.2 → megadetector-5.0.4}/taxonomy_mapping/validate_lila_category_mappings.py +0 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: megadetector
|
|
3
|
+
Version: 5.0.4
|
|
4
|
+
Summary: MegaDetector is an AI model that helps conservation folks spend less time doing boring things with camera trap images.
|
|
5
|
+
Author-email: Your friendly neighborhood MegaDetector team <cameratraps@lila.science>
|
|
6
|
+
Maintainer-email: Your friendly neighborhood MegaDetector team <cameratraps@lila.science>
|
|
7
|
+
License: MIT License
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
26
|
+
|
|
27
|
+
Project-URL: Homepage, https://github.com/agentmorris/MegaDetector
|
|
28
|
+
Project-URL: Bug Reports, https://github.com/agentmorris/MegaDetector/issues
|
|
29
|
+
Project-URL: Source, https://github.com/agentmorris/MegaDetector
|
|
30
|
+
Keywords: camera traps,conservation,wildlife,ai
|
|
31
|
+
Classifier: Development Status :: 3 - Alpha
|
|
32
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
33
|
+
Classifier: Programming Language :: Python :: 3
|
|
34
|
+
Requires-Python: >=3.9
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
License-File: LICENSE
|
|
37
|
+
Requires-Dist: Pillow>=9.5
|
|
38
|
+
Requires-Dist: tqdm>=4.64.0
|
|
39
|
+
Requires-Dist: jsonpickle>=3.0.2
|
|
40
|
+
Requires-Dist: humanfriendly>=10.0
|
|
41
|
+
Requires-Dist: numpy>=1.26.0
|
|
42
|
+
Requires-Dist: matplotlib>=3.8.0
|
|
43
|
+
Requires-Dist: opencv-python>=4.8.0
|
|
44
|
+
Requires-Dist: requests>=2.31.0
|
|
45
|
+
Requires-Dist: pyqtree>=1.0.0
|
|
46
|
+
Requires-Dist: seaborn>=0.12.2
|
|
47
|
+
Requires-Dist: scikit-learn>=1.3.1
|
|
48
|
+
Requires-Dist: pandas>=2.1.1
|
|
49
|
+
Requires-Dist: PyYAML>=6.0.1
|
|
50
|
+
Requires-Dist: torch>=2.0.1
|
|
51
|
+
Requires-Dist: torchvision>=0.15.2
|
|
52
|
+
Requires-Dist: yolov5>=7.0.12
|
|
53
|
+
|
|
54
|
+
# MegaDetector
|
|
55
|
+
|
|
56
|
+
This package is a pip-installable version of the support/inference code for [MegaDetector](https://github.com/agentmorris/MegaDetector), an object detection model that helps conservation biologists spend less time doing boring things with camera trap images.
|
|
57
|
+
|
|
58
|
+
If you want to learn more about what MegaDetector is all about, head over to the [MegaDetector repo](https://github.com/agentmorris/MegaDetector).
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
## Reasons you probably aren't looking for this package
|
|
62
|
+
|
|
63
|
+
### If you are an ecologist...
|
|
64
|
+
|
|
65
|
+
If you are an ecologist looking to use MegaDetector to help you get through your camera trap images, you probably don't want this package. We recommend starting with our "[Getting started with MegaDetector](https://github.com/agentmorris/MegaDetector/blob/main/collaborations.md)" page, then digging in to the [MegaDetector User Guide](https://github.com/agentmorris/MegaDetector/blob/main/megadetector.md), which will walk you through the process of using MegaDetector. That journey will <i>not</i> involve this package.
|
|
66
|
+
|
|
67
|
+
### If you are a computer-vision-y type...
|
|
68
|
+
|
|
69
|
+
If you are a computer-vision-y person looking to run or fine-tune MegaDetector programmatically, you still probably don't want this package. MegaDetector is just a fine-tuned version of [YOLOv5](https://github.com/ultralytics/yolov5), and the [ultralytics](https://github.com/ultralytics/ultralytics/) package (from the developers of YOLOv5) has a zillion bells and whistles for both inference and fine-tuning that this package doesn't.
|
|
70
|
+
|
|
71
|
+
## Reasons you might want to use this package
|
|
72
|
+
|
|
73
|
+
If you want to programatically interact with the postprocessing tools from the MegaDetector repo, or programmatically run MegaDetector in a way that produces [Timelapse](https://saul.cpsc.ucalgary.ca/timelapse)-friendly output (i.e., output in the standard [MegaDetector output format](https://github.com/agentmorris/MegaDetector/tree/main/api/batch_processing#megadetector-batch-output-format)), this package might be for you.
|
|
74
|
+
|
|
75
|
+
Although even if that describes you, you <i>still</i> might be better off cloning the MegaDetector repo. Pip-installability requires that some dependencies be newer than what was available at the time MDv5 was trained, so results are <i>very slightly</i> different than results produced in the "official" environment. These differences <i>probably</i> don't matter much, but they have not been formally characterized.
|
|
76
|
+
|
|
77
|
+
## If I haven't talked you out of using this package...
|
|
78
|
+
|
|
79
|
+
To install:
|
|
80
|
+
|
|
81
|
+
`pip install megadetector`
|
|
82
|
+
|
|
83
|
+
MegaDetector model weights aren't downloaded at pip-install time, but they will be (optionally) automatically downloaded the first time you run the model.
|
|
84
|
+
|
|
85
|
+
### Examples of things you can do with this package
|
|
86
|
+
|
|
87
|
+
#### Run MegaDetector on one image and count the number of detections
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
from md_utils import url_utils
|
|
91
|
+
from md_visualization import visualization_utils as vis_utils
|
|
92
|
+
from detection import run_detector
|
|
93
|
+
|
|
94
|
+
# This is the image at the bottom of this page, it has one animal in it
|
|
95
|
+
image_url = 'https://github.com/agentmorris/MegaDetector/raw/main/images/orinoquia-thumb-web.jpg'
|
|
96
|
+
temporary_filename = url_utils.download_url(image_url)
|
|
97
|
+
|
|
98
|
+
image = vis_utils.load_image(temporary_filename)
|
|
99
|
+
|
|
100
|
+
# This will automatically download MDv5a to the system temp folder;
|
|
101
|
+
# you can also specify a filename explicitly, or set the $MDV5A
|
|
102
|
+
# environment variable to point to the model file.
|
|
103
|
+
model = run_detector.load_detector('MDV5A')
|
|
104
|
+
|
|
105
|
+
result = model.generate_detections_one_image(image)
|
|
106
|
+
|
|
107
|
+
detections_above_threshold = [d for d in result['detections'] if d['conf'] > 0.2]
|
|
108
|
+
print('Found {} detection above threshold'.format(len(detections_above_threshold)))
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### Run MegaDetector on a folder of images
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
from detection.run_detector_batch import load_and_run_detector_batch,write_results_to_file
|
|
115
|
+
from md_utils import path_utils
|
|
116
|
+
import os
|
|
117
|
+
|
|
118
|
+
# Pick a folder to run MD on recursively, and an output file
|
|
119
|
+
image_folder = os.path.expanduser('~/megadetector_test_images')
|
|
120
|
+
output_file = os.path.expanduser('~/megadetector_output_test.json')
|
|
121
|
+
|
|
122
|
+
# Recursively find images
|
|
123
|
+
image_file_names = path_utils.find_images(image_folder,recursive=True)
|
|
124
|
+
|
|
125
|
+
# This will automatically download MDv5a to the system temp folder;
|
|
126
|
+
# you can also specify a filename explicitly, or set the $MDV5A
|
|
127
|
+
# environment variable to point to the model file.
|
|
128
|
+
results = load_and_run_detector_batch('MDV5A', image_file_names)
|
|
129
|
+
|
|
130
|
+
# Write results as relative filenames, this is what Timelapse
|
|
131
|
+
# and other downstream tools expect.
|
|
132
|
+
write_results_to_file(results,output_file,relative_path_base=image_folder)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Contact
|
|
136
|
+
|
|
137
|
+
Contact <a href="cameratraps@lila.science">cameratraps@lila.science</a> with questions.
|
|
138
|
+
|
|
139
|
+
## Gratuitous animal picture
|
|
140
|
+
|
|
141
|
+
<img src="https://github.com/agentmorris/MegaDetector/raw/main/images/orinoquia-thumb-web_detections.jpg"><br/>Image credit University of Minnesota, from the [Orinoquía Camera Traps](http://lila.science/datasets/orinoquia-camera-traps/) data set.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# MegaDetector
|
|
2
|
+
|
|
3
|
+
This package is a pip-installable version of the support/inference code for [MegaDetector](https://github.com/agentmorris/MegaDetector), an object detection model that helps conservation biologists spend less time doing boring things with camera trap images.
|
|
4
|
+
|
|
5
|
+
If you want to learn more about what MegaDetector is all about, head over to the [MegaDetector repo](https://github.com/agentmorris/MegaDetector).
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Reasons you probably aren't looking for this package
|
|
9
|
+
|
|
10
|
+
### If you are an ecologist...
|
|
11
|
+
|
|
12
|
+
If you are an ecologist looking to use MegaDetector to help you get through your camera trap images, you probably don't want this package. We recommend starting with our "[Getting started with MegaDetector](https://github.com/agentmorris/MegaDetector/blob/main/collaborations.md)" page, then digging in to the [MegaDetector User Guide](https://github.com/agentmorris/MegaDetector/blob/main/megadetector.md), which will walk you through the process of using MegaDetector. That journey will <i>not</i> involve this package.
|
|
13
|
+
|
|
14
|
+
### If you are a computer-vision-y type...
|
|
15
|
+
|
|
16
|
+
If you are a computer-vision-y person looking to run or fine-tune MegaDetector programmatically, you still probably don't want this package. MegaDetector is just a fine-tuned version of [YOLOv5](https://github.com/ultralytics/yolov5), and the [ultralytics](https://github.com/ultralytics/ultralytics/) package (from the developers of YOLOv5) has a zillion bells and whistles for both inference and fine-tuning that this package doesn't.
|
|
17
|
+
|
|
18
|
+
## Reasons you might want to use this package
|
|
19
|
+
|
|
20
|
+
If you want to programatically interact with the postprocessing tools from the MegaDetector repo, or programmatically run MegaDetector in a way that produces [Timelapse](https://saul.cpsc.ucalgary.ca/timelapse)-friendly output (i.e., output in the standard [MegaDetector output format](https://github.com/agentmorris/MegaDetector/tree/main/api/batch_processing#megadetector-batch-output-format)), this package might be for you.
|
|
21
|
+
|
|
22
|
+
Although even if that describes you, you <i>still</i> might be better off cloning the MegaDetector repo. Pip-installability requires that some dependencies be newer than what was available at the time MDv5 was trained, so results are <i>very slightly</i> different than results produced in the "official" environment. These differences <i>probably</i> don't matter much, but they have not been formally characterized.
|
|
23
|
+
|
|
24
|
+
## If I haven't talked you out of using this package...
|
|
25
|
+
|
|
26
|
+
To install:
|
|
27
|
+
|
|
28
|
+
`pip install megadetector`
|
|
29
|
+
|
|
30
|
+
MegaDetector model weights aren't downloaded at pip-install time, but they will be (optionally) automatically downloaded the first time you run the model.
|
|
31
|
+
|
|
32
|
+
### Examples of things you can do with this package
|
|
33
|
+
|
|
34
|
+
#### Run MegaDetector on one image and count the number of detections
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
from md_utils import url_utils
|
|
38
|
+
from md_visualization import visualization_utils as vis_utils
|
|
39
|
+
from detection import run_detector
|
|
40
|
+
|
|
41
|
+
# This is the image at the bottom of this page, it has one animal in it
|
|
42
|
+
image_url = 'https://github.com/agentmorris/MegaDetector/raw/main/images/orinoquia-thumb-web.jpg'
|
|
43
|
+
temporary_filename = url_utils.download_url(image_url)
|
|
44
|
+
|
|
45
|
+
image = vis_utils.load_image(temporary_filename)
|
|
46
|
+
|
|
47
|
+
# This will automatically download MDv5a to the system temp folder;
|
|
48
|
+
# you can also specify a filename explicitly, or set the $MDV5A
|
|
49
|
+
# environment variable to point to the model file.
|
|
50
|
+
model = run_detector.load_detector('MDV5A')
|
|
51
|
+
|
|
52
|
+
result = model.generate_detections_one_image(image)
|
|
53
|
+
|
|
54
|
+
detections_above_threshold = [d for d in result['detections'] if d['conf'] > 0.2]
|
|
55
|
+
print('Found {} detection above threshold'.format(len(detections_above_threshold)))
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
#### Run MegaDetector on a folder of images
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
from detection.run_detector_batch import load_and_run_detector_batch,write_results_to_file
|
|
62
|
+
from md_utils import path_utils
|
|
63
|
+
import os
|
|
64
|
+
|
|
65
|
+
# Pick a folder to run MD on recursively, and an output file
|
|
66
|
+
image_folder = os.path.expanduser('~/megadetector_test_images')
|
|
67
|
+
output_file = os.path.expanduser('~/megadetector_output_test.json')
|
|
68
|
+
|
|
69
|
+
# Recursively find images
|
|
70
|
+
image_file_names = path_utils.find_images(image_folder,recursive=True)
|
|
71
|
+
|
|
72
|
+
# This will automatically download MDv5a to the system temp folder;
|
|
73
|
+
# you can also specify a filename explicitly, or set the $MDV5A
|
|
74
|
+
# environment variable to point to the model file.
|
|
75
|
+
results = load_and_run_detector_batch('MDV5A', image_file_names)
|
|
76
|
+
|
|
77
|
+
# Write results as relative filenames, this is what Timelapse
|
|
78
|
+
# and other downstream tools expect.
|
|
79
|
+
write_results_to_file(results,output_file,relative_path_base=image_folder)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Contact
|
|
83
|
+
|
|
84
|
+
Contact <a href="cameratraps@lila.science">cameratraps@lila.science</a> with questions.
|
|
85
|
+
|
|
86
|
+
## Gratuitous animal picture
|
|
87
|
+
|
|
88
|
+
<img src="https://github.com/agentmorris/MegaDetector/raw/main/images/orinoquia-thumb-web_detections.jpg"><br/>Image credit University of Minnesota, from the [Orinoquía Camera Traps](http://lila.science/datasets/orinoquia-camera-traps/) data set.
|
|
@@ -122,7 +122,7 @@ Here are a few of the organizations that have used MegaDetector... we're only li
|
|
|
122
122
|
* [Bavarian Forest National Park](https://www.nationalpark-bayerischer-wald.bayern.de/english/index.htm) ([story](https://customers.microsoft.com/en-au/story/1667539539271247797-nationalparkbayerischerwald-azure-en))
|
|
123
123
|
* [Felidae Conservation Fund](https://felidaefund.org/) ([WildePod platform](https://wildepod.org/)) ([blog post](https://abhaykashyap.com/blog/ai-powered-camera-trap-image-annotation-system/))
|
|
124
124
|
* [Alberta Biodiversity Monitoring Institute (ABMI)](https://www.abmi.ca/home.html) ([WildTrax platform](https://www.wildtrax.ca/)) (blog posts [1](https://wildcams.ca/blog/the-abmi-visits-the-zoo/),[2](http://blog.abmi.ca/2023/06/14/making-wildtrax-its-not-a-kind-of-magic-behind-the-screen/))
|
|
125
|
-
* [Shan Shui Conservation Center](http://en.shanshui.org/) ([blog post](https://mp.weixin.qq.com/s/iOIQF3ckj0-rEG4yJgerYw?fbclid=IwAR0alwiWbe3udIcFvqqwm7y5qgr9hZpjr871FZIa-ErGUukZ7yJ3ZhgCevs)) ([translated blog post](https://mp-weixin-qq-com.translate.goog/s/iOIQF3ckj0-rEG4yJgerYw?fbclid=IwAR0alwiWbe3udIcFvqqwm7y5qgr9hZpjr871FZIa-ErGUukZ7yJ3ZhgCevs&_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=en&_x_tr_pto=wapp))
|
|
125
|
+
* [Shan Shui Conservation Center](http://en.shanshui.org/) ([blog post](https://mp.weixin.qq.com/s/iOIQF3ckj0-rEG4yJgerYw?fbclid=IwAR0alwiWbe3udIcFvqqwm7y5qgr9hZpjr871FZIa-ErGUukZ7yJ3ZhgCevs)) ([translated blog post](https://mp-weixin-qq-com.translate.goog/s/iOIQF3ckj0-rEG4yJgerYw?fbclid=IwAR0alwiWbe3udIcFvqqwm7y5qgr9hZpjr871FZIa-ErGUukZ7yJ3ZhgCevs&_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=en&_x_tr_pto=wapp)) ([Web demo](https://cameratrap-ai.hinature.cn/home))
|
|
126
126
|
* [Irvine Ranch Conservancy](http://www.irconservancy.org/) ([story](https://www.ocregister.com/2022/03/30/ai-software-is-helping-researchers-focus-on-learning-about-ocs-wild-animals/))
|
|
127
127
|
* [Wildlife Protection Solutions](https://wildlifeprotectionsolutions.org/) ([story](https://customers.microsoft.com/en-us/story/1384184517929343083-wildlife-protection-solutions-nonprofit-ai-for-earth), [story](https://www.enterpriseai.news/2023/02/20/ai-helps-wildlife-protection-solutions-safeguard-endangered-species/))
|
|
128
128
|
* [Q42](https://www.q42.nl/en) ([blog post](https://engineering.q42.nl/ai-bear-repeller/))
|
{megadetector-5.0.2 → megadetector-5.0.4}/api/batch_processing/postprocessing/md_to_labelme.py
RENAMED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
# md_to_labelme.py
|
|
4
4
|
#
|
|
5
5
|
# "Converts" a MegaDetector output .json file to labelme format (one .json per image
|
|
6
|
-
# file). "Convert" is in quotes because this is an opinionated transformation
|
|
7
|
-
# confidence threshold.
|
|
6
|
+
# file). "Convert" is in quotes because this is an opinionated transformation that
|
|
7
|
+
# requires a confidence threshold.
|
|
8
8
|
#
|
|
9
9
|
# TODO:
|
|
10
10
|
#
|
|
@@ -24,13 +24,65 @@ from md_visualization.visualization_utils import open_image
|
|
|
24
24
|
from md_utils.ct_utils import truncate_float
|
|
25
25
|
|
|
26
26
|
output_precision = 3
|
|
27
|
+
default_confidence_threshold = 0.15
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
#%% Functions
|
|
30
31
|
|
|
31
|
-
def
|
|
32
|
+
def get_labelme_dict_for_image(im,image_base_name,category_id_to_name,info=None,confidence_threshold=None):
|
|
33
|
+
"""
|
|
34
|
+
For the given image struct in MD results format, reformat the detections into
|
|
35
|
+
labelme format. Returns a dict.
|
|
36
|
+
"""
|
|
32
37
|
|
|
33
|
-
|
|
38
|
+
if confidence_threshold is None:
|
|
39
|
+
confidence_threshold = -1.0
|
|
40
|
+
|
|
41
|
+
output_dict = {}
|
|
42
|
+
if info is not None:
|
|
43
|
+
output_dict['md_info'] = info
|
|
44
|
+
output_dict['version'] = '5.3.0a0'
|
|
45
|
+
output_dict['flags'] = {}
|
|
46
|
+
output_dict['shapes'] = []
|
|
47
|
+
output_dict['imagePath'] = image_base_name
|
|
48
|
+
output_dict['imageHeight'] = im['height']
|
|
49
|
+
output_dict['imageWidth'] = im['width']
|
|
50
|
+
output_dict['imageData'] = None
|
|
51
|
+
|
|
52
|
+
for det in im['detections']:
|
|
53
|
+
|
|
54
|
+
if det['conf'] < confidence_threshold:
|
|
55
|
+
continue
|
|
56
|
+
|
|
57
|
+
shape = {}
|
|
58
|
+
shape['conf'] = det['conf']
|
|
59
|
+
shape['label'] = category_id_to_name[det['category']]
|
|
60
|
+
shape['shape_type'] = 'rectangle'
|
|
61
|
+
shape['description'] = ''
|
|
62
|
+
shape['group_id'] = None
|
|
63
|
+
|
|
64
|
+
# MD boxes are [x_min, y_min, width_of_box, height_of_box] (relative)
|
|
65
|
+
#
|
|
66
|
+
# labelme boxes are [[x0,y0],[x1,y1]] (absolute)
|
|
67
|
+
x0 = truncate_float(det['bbox'][0] * im['width'],output_precision)
|
|
68
|
+
y0 = truncate_float(det['bbox'][1] * im['height'],output_precision)
|
|
69
|
+
x1 = truncate_float(x0 + det['bbox'][2] * im['width'],output_precision)
|
|
70
|
+
y1 = truncate_float(y0 + det['bbox'][3] * im['height'],output_precision)
|
|
71
|
+
shape['points'] = [[x0,y0],[x1,y1]]
|
|
72
|
+
output_dict['shapes'].append(shape)
|
|
73
|
+
|
|
74
|
+
# ...for each detection
|
|
75
|
+
|
|
76
|
+
return output_dict
|
|
77
|
+
|
|
78
|
+
# ...def get_labelme_dict_for_image()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def md_to_labelme(results_file,image_base,confidence_threshold=None,overwrite=False):
|
|
82
|
+
"""
|
|
83
|
+
For all the images in [results_file], write a .json file in labelme format alongside the
|
|
84
|
+
corresponding relative path within image_base.
|
|
85
|
+
"""
|
|
34
86
|
|
|
35
87
|
# Load MD results
|
|
36
88
|
with open(results_file,'r') as f:
|
|
@@ -57,45 +109,20 @@ def md_to_labelme(results_file,image_base,confidence_threshold,overwrite=False):
|
|
|
57
109
|
print('Skipping existing file {}'.format(json_path))
|
|
58
110
|
continue
|
|
59
111
|
|
|
60
|
-
output_dict =
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
output_dict['imagePath'] = os.path.basename(im_full_path)
|
|
66
|
-
output_dict['imageHeight'] = im['height']
|
|
67
|
-
output_dict['imageWidth'] = im['width']
|
|
68
|
-
output_dict['imageData'] = None
|
|
69
|
-
|
|
70
|
-
for det in im['detections']:
|
|
71
|
-
|
|
72
|
-
if det['conf'] < confidence_threshold:
|
|
73
|
-
continue
|
|
74
|
-
|
|
75
|
-
shape = {}
|
|
76
|
-
shape['conf'] = det['conf']
|
|
77
|
-
shape['label'] = md_results['detection_categories'][det['category']]
|
|
78
|
-
shape['shape_type'] = 'rectangle'
|
|
79
|
-
shape['description'] = ''
|
|
80
|
-
shape['group_id'] = None
|
|
81
|
-
|
|
82
|
-
# MD boxes are [x_min, y_min, width_of_box, height_of_box] (relative)
|
|
83
|
-
#
|
|
84
|
-
# labelme boxes are [[x0,y0],[x1,y1]] (absolute)
|
|
85
|
-
x0 = truncate_float(det['bbox'][0] * im['width'],output_precision)
|
|
86
|
-
y0 = truncate_float(det['bbox'][1] * im['height'],output_precision)
|
|
87
|
-
x1 = truncate_float(x0 + det['bbox'][2] * im['width'],output_precision)
|
|
88
|
-
y1 = truncate_float(y0 + det['bbox'][3] * im['height'],output_precision)
|
|
89
|
-
shape['points'] = [[x0,y0],[x1,y1]]
|
|
90
|
-
output_dict['shapes'].append(shape)
|
|
91
|
-
|
|
92
|
-
# ...for each detection
|
|
112
|
+
output_dict = get_labelme_dict_for_image(im,
|
|
113
|
+
image_base_name=os.path.basename(im_full_path),
|
|
114
|
+
category_id_to_name=md_results['detection_categories'],
|
|
115
|
+
info=md_results['info'],
|
|
116
|
+
confidence_threshold=confidence_threshold)
|
|
93
117
|
|
|
94
118
|
with open(json_path,'w') as f:
|
|
95
119
|
json.dump(output_dict,f,indent=1)
|
|
96
120
|
|
|
97
121
|
# ...for each image
|
|
98
122
|
|
|
123
|
+
# ...def md_to_labelme()
|
|
124
|
+
|
|
125
|
+
|
|
99
126
|
#%% Interactive driver
|
|
100
127
|
|
|
101
128
|
if False:
|
|
@@ -131,7 +158,9 @@ def main():
|
|
|
131
158
|
parser.add_argument(
|
|
132
159
|
'confidence_threshold',
|
|
133
160
|
type=float,
|
|
134
|
-
|
|
161
|
+
default=default_confidence_threshold,
|
|
162
|
+
help='Confidence threshold (default {})'.format(default_confidence_threshold)
|
|
163
|
+
)
|
|
135
164
|
|
|
136
165
|
parser.add_argument(
|
|
137
166
|
'--overwrite',
|
{megadetector-5.0.2 → megadetector-5.0.4}/data_management/importers/cacophony-thermal-importer.py
RENAMED
|
@@ -484,7 +484,7 @@ def process_file(fn_relative,verbose=False):
|
|
|
484
484
|
if tag['label'] in tag_mappings:
|
|
485
485
|
tag['label'] = tag_mappings[tag['label']]
|
|
486
486
|
|
|
487
|
-
# Discard tags below the
|
|
487
|
+
# Discard tags below the minimum confidence
|
|
488
488
|
if tag['confidence'] >= confidence_threshold:
|
|
489
489
|
valid_tags.append(tag)
|
|
490
490
|
else:
|
|
@@ -103,7 +103,7 @@ def process_video(options):
|
|
|
103
103
|
# TODO:
|
|
104
104
|
#
|
|
105
105
|
# This is a lazy fix to an issue... if multiple users run this script, the
|
|
106
|
-
# "process_camera_trap_video" is owned by the first person who creates it, and others
|
|
106
|
+
# "process_camera_trap_video" folder is owned by the first person who creates it, and others
|
|
107
107
|
# can't write to it. I could create uniquely-named folders, but I philosophically prefer
|
|
108
108
|
# to put all the individual UUID-named folders within a larger folder, so as to be a
|
|
109
109
|
# good tempdir citizen. So, the lazy fix is to make this world-writable.
|
|
@@ -40,7 +40,8 @@ if try_yolov5_import and not utils_imported:
|
|
|
40
40
|
utils_imported = True
|
|
41
41
|
print('Imported YOLOv5 from YOLOv5 package')
|
|
42
42
|
except Exception:
|
|
43
|
-
print('YOLOv5 module import failed, falling back to path-based import')
|
|
43
|
+
# print('YOLOv5 module import failed, falling back to path-based import')
|
|
44
|
+
pass
|
|
44
45
|
|
|
45
46
|
# If we haven't succeeded yet, import from the ultralytics package
|
|
46
47
|
if try_ultralytics_import and not utils_imported:
|
|
@@ -59,7 +60,8 @@ if try_ultralytics_import and not utils_imported:
|
|
|
59
60
|
utils_imported = True
|
|
60
61
|
print('Imported YOLOv5 from ultralytics package')
|
|
61
62
|
except Exception:
|
|
62
|
-
print('Ultralytics module import failed, falling back to yolov5 import')
|
|
63
|
+
# print('Ultralytics module import failed, falling back to yolov5 import')
|
|
64
|
+
pass
|
|
63
65
|
|
|
64
66
|
# If we haven't succeeded yet, import from the YOLOv5 repo
|
|
65
67
|
if not utils_imported:
|
|
@@ -83,7 +85,7 @@ assert utils_imported, 'YOLOv5 import error'
|
|
|
83
85
|
|
|
84
86
|
print(f'Using PyTorch version {torch.__version__}')
|
|
85
87
|
|
|
86
|
-
|
|
88
|
+
|
|
87
89
|
#%% Classes
|
|
88
90
|
|
|
89
91
|
class PTDetector:
|
|
@@ -114,7 +116,19 @@ class PTDetector:
|
|
|
114
116
|
|
|
115
117
|
@staticmethod
|
|
116
118
|
def _load_model(model_pt_path, device):
|
|
117
|
-
|
|
119
|
+
|
|
120
|
+
# There are two very slightly different ways to load the model, (1) using the
|
|
121
|
+
# map_location=device parameter to torch.load and (2) calling .to(device) after
|
|
122
|
+
# loading the model. The former is what we did for a zillion years, but is not
|
|
123
|
+
# supported on Apple silicon at of 2029.09. Switching to the latter causes
|
|
124
|
+
# very slight changes to the output, which always make me nervous, so I'm not
|
|
125
|
+
# doing a wholesale swap just yet. Instead, we'll just do this on M1 hardware.
|
|
126
|
+
use_map_location = (device != 'mps')
|
|
127
|
+
|
|
128
|
+
if use_map_location:
|
|
129
|
+
checkpoint = torch.load(model_pt_path, map_location=device)
|
|
130
|
+
else:
|
|
131
|
+
checkpoint = torch.load(model_pt_path)
|
|
118
132
|
|
|
119
133
|
# Compatibility fix that allows us to load older YOLOv5 models with
|
|
120
134
|
# newer versions of YOLOv5/PT
|
|
@@ -122,12 +136,16 @@ class PTDetector:
|
|
|
122
136
|
t = type(m)
|
|
123
137
|
if t is torch.nn.Upsample and not hasattr(m, 'recompute_scale_factor'):
|
|
124
138
|
m.recompute_scale_factor = None
|
|
139
|
+
|
|
140
|
+
if use_map_location:
|
|
141
|
+
model = checkpoint['model'].float().fuse().eval()
|
|
142
|
+
else:
|
|
143
|
+
model = checkpoint['model'].float().fuse().eval().to(device)
|
|
125
144
|
|
|
126
|
-
model = checkpoint['model'].float().fuse().eval() # FP32 model
|
|
127
145
|
return model
|
|
128
146
|
|
|
129
|
-
def generate_detections_one_image(self, img_original, image_id,
|
|
130
|
-
detection_threshold, image_size=None,
|
|
147
|
+
def generate_detections_one_image(self, img_original, image_id='unknown',
|
|
148
|
+
detection_threshold=0.00001, image_size=None,
|
|
131
149
|
skip_image_resizing=False):
|
|
132
150
|
"""
|
|
133
151
|
Apply the detector to an image.
|
|
@@ -127,6 +127,15 @@ DEFAULT_BOX_EXPANSION = 0
|
|
|
127
127
|
DEFAULT_LABEL_FONT_SIZE = 16
|
|
128
128
|
DETECTION_FILENAME_INSERT = '_detections'
|
|
129
129
|
|
|
130
|
+
# The model filenames "MDV5A", "MDV5B", and "MDV4" are special; they will trigger an
|
|
131
|
+
# automatic model download to the system temp folder, or they will use the paths specified in the
|
|
132
|
+
# $MDV4, $MDV5A, or $MDV5B environment variables if they exist.
|
|
133
|
+
downloadable_models = {
|
|
134
|
+
'MDV4':'https://github.com/agentmorris/MegaDetector/releases/download/v4.1/md_v4.1.0.pb',
|
|
135
|
+
'MDV5A':'https://github.com/agentmorris/MegaDetector/releases/download/v5.0/md_v5a.0.0.pt',
|
|
136
|
+
'MDV5B':'https://github.com/agentmorris/MegaDetector/releases/download/v5.0/md_v5b.0.0.pt'
|
|
137
|
+
}
|
|
138
|
+
|
|
130
139
|
|
|
131
140
|
#%% Utility functions
|
|
132
141
|
|
|
@@ -257,6 +266,9 @@ def load_detector(model_file, force_cpu=False):
|
|
|
257
266
|
Load a TF or PT detector, depending on the extension of model_file.
|
|
258
267
|
"""
|
|
259
268
|
|
|
269
|
+
# Possibly automatically download the model
|
|
270
|
+
model_file = try_download_known_detector(model_file)
|
|
271
|
+
|
|
260
272
|
start_time = time.time()
|
|
261
273
|
if model_file.endswith('.pb'):
|
|
262
274
|
from detection.tf_detector import TFDetector
|
|
@@ -290,6 +302,9 @@ def load_and_run_detector(model_file, image_file_names, output_dir,
|
|
|
290
302
|
print('Warning: no files available')
|
|
291
303
|
return
|
|
292
304
|
|
|
305
|
+
# Possibly automatically download the model
|
|
306
|
+
model_file = try_download_known_detector(model_file)
|
|
307
|
+
|
|
293
308
|
print('GPU available: {}'.format(is_gpu_available(model_file)))
|
|
294
309
|
|
|
295
310
|
detector = load_detector(model_file)
|
|
@@ -429,6 +444,55 @@ def load_and_run_detector(model_file, image_file_names, output_dir,
|
|
|
429
444
|
# ...def load_and_run_detector()
|
|
430
445
|
|
|
431
446
|
|
|
447
|
+
def download_model(model_name,force_download=False):
|
|
448
|
+
"""
|
|
449
|
+
Download one of the known models to local temp space if it hasn't already been downloaded
|
|
450
|
+
"""
|
|
451
|
+
|
|
452
|
+
import tempfile
|
|
453
|
+
from md_utils.url_utils import download_url
|
|
454
|
+
model_tempdir = os.path.join(tempfile.gettempdir(), 'megadetector_models')
|
|
455
|
+
os.makedirs(model_tempdir,exist_ok=True)
|
|
456
|
+
|
|
457
|
+
# This is a lazy fix to an issue... if multiple users run this script, the
|
|
458
|
+
# "megadetector_models" folder is owned by the first person who creates it, and others
|
|
459
|
+
# can't write to it. I could create uniquely-named folders, but I philosophically prefer
|
|
460
|
+
# to put all the individual UUID-named folders within a larger folder, so as to be a
|
|
461
|
+
# good tempdir citizen. So, the lazy fix is to make this world-writable.
|
|
462
|
+
try:
|
|
463
|
+
os.chmod(model_tempdir,0o777)
|
|
464
|
+
except Exception:
|
|
465
|
+
pass
|
|
466
|
+
if model_name not in downloadable_models:
|
|
467
|
+
print('Unrecognized downloadable model {}'.format(model_name))
|
|
468
|
+
return None
|
|
469
|
+
url = downloadable_models[model_name]
|
|
470
|
+
destination_filename = os.path.join(model_tempdir,url.split('/')[-1])
|
|
471
|
+
local_file = download_url(url, destination_filename=destination_filename, progress_updater=None,
|
|
472
|
+
force_download=force_download, verbose=True)
|
|
473
|
+
return local_file
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def try_download_known_detector(detector_file):
|
|
477
|
+
"""
|
|
478
|
+
Check whether detector_file is really the name of a known model, in which case we will
|
|
479
|
+
either read the actual filename from the corresponding environment variable or download
|
|
480
|
+
(if necessary) to local temp space. Otherwise just returns the input string.
|
|
481
|
+
"""
|
|
482
|
+
|
|
483
|
+
if detector_file in downloadable_models:
|
|
484
|
+
if detector_file in os.environ:
|
|
485
|
+
fn = os.environ[detector_file]
|
|
486
|
+
print('Reading MD location from environment variable {}: {}'.format(
|
|
487
|
+
detector_file,fn))
|
|
488
|
+
detector_file = fn
|
|
489
|
+
else:
|
|
490
|
+
print('Downloading model {}'.format(detector_file))
|
|
491
|
+
detector_file = download_model(detector_file)
|
|
492
|
+
return detector_file
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
|
|
432
496
|
#%% Command-line driver
|
|
433
497
|
|
|
434
498
|
def main():
|
|
@@ -438,7 +502,7 @@ def main():
|
|
|
438
502
|
|
|
439
503
|
parser.add_argument(
|
|
440
504
|
'detector_file',
|
|
441
|
-
help='Path
|
|
505
|
+
help='Path detector model file (.pb or .pt). Can also be MDV4, MDV5A, or MDV5B to request automatic download.')
|
|
442
506
|
|
|
443
507
|
# Must specify either an image file or a directory
|
|
444
508
|
group = parser.add_mutually_exclusive_group(required=True)
|
|
@@ -506,6 +570,10 @@ def main():
|
|
|
506
570
|
|
|
507
571
|
args = parser.parse_args()
|
|
508
572
|
|
|
573
|
+
# If the specified detector file is really the name of a known model, find
|
|
574
|
+
# (and possibly download) that model
|
|
575
|
+
args.detector_file = try_download_known_detector(args.detector_file)
|
|
576
|
+
|
|
509
577
|
assert os.path.exists(args.detector_file), 'detector file {} does not exist'.format(
|
|
510
578
|
args.detector_file)
|
|
511
579
|
assert 0.0 < args.threshold <= 1.0, 'Confidence threshold needs to be between 0 and 1'
|
|
@@ -64,6 +64,7 @@ from multiprocessing.pool import Pool as workerpool
|
|
|
64
64
|
import detection.run_detector as run_detector
|
|
65
65
|
from detection.run_detector import is_gpu_available,\
|
|
66
66
|
load_detector,\
|
|
67
|
+
try_download_known_detector,\
|
|
67
68
|
get_detector_version_from_filename,\
|
|
68
69
|
get_detector_metadata_from_version_string
|
|
69
70
|
|
|
@@ -413,6 +414,8 @@ def load_and_run_detector_batch(model_file, image_file_names, checkpoint_path=No
|
|
|
413
414
|
|
|
414
415
|
already_processed = set([i['file'] for i in results])
|
|
415
416
|
|
|
417
|
+
model_file = try_download_known_detector(model_file)
|
|
418
|
+
|
|
416
419
|
print('GPU available: {}'.format(is_gpu_available(model_file)))
|
|
417
420
|
|
|
418
421
|
if n_cores > 1 and is_gpu_available(model_file):
|
|
@@ -743,7 +746,7 @@ def main():
|
|
|
743
746
|
description='Module to run a TF/PT animal detection model on lots of images')
|
|
744
747
|
parser.add_argument(
|
|
745
748
|
'detector_file',
|
|
746
|
-
help='Path to detector model file (.pb or .pt)')
|
|
749
|
+
help='Path to detector model file (.pb or .pt). Can also be MDV4, MDV5A, or MDV5B to request automatic download.')
|
|
747
750
|
parser.add_argument(
|
|
748
751
|
'image_file',
|
|
749
752
|
help='Path to a single image file, a JSON file containing a list of paths to images, or a directory')
|
|
@@ -838,6 +841,10 @@ def main():
|
|
|
838
841
|
|
|
839
842
|
args = parser.parse_args()
|
|
840
843
|
|
|
844
|
+
# If the specified detector file is really the name of a known model, find
|
|
845
|
+
# (and possibly download) that model
|
|
846
|
+
args.detector_file = try_download_known_detector(args.detector_file)
|
|
847
|
+
|
|
841
848
|
assert os.path.exists(args.detector_file), \
|
|
842
849
|
'detector file {} does not exist'.format(args.detector_file)
|
|
843
850
|
assert 0.0 < args.threshold <= 1.0, 'Confidence threshold needs to be between 0 and 1'
|
|
@@ -17,9 +17,6 @@ import requests
|
|
|
17
17
|
from tqdm import tqdm
|
|
18
18
|
from urllib.parse import urlparse
|
|
19
19
|
|
|
20
|
-
# pip install progressbar2
|
|
21
|
-
import progressbar
|
|
22
|
-
|
|
23
20
|
url_utils_temp_dir = None
|
|
24
21
|
max_path_len = 255
|
|
25
22
|
|
|
@@ -36,6 +33,10 @@ class DownloadProgressBar():
|
|
|
36
33
|
|
|
37
34
|
def __call__(self, block_num, block_size, total_size):
|
|
38
35
|
if not self.pbar:
|
|
36
|
+
# This is a pretty random import I'd rather not depend on outside of the
|
|
37
|
+
# rare case where it's used, so importing locally
|
|
38
|
+
# pip install progressbar2
|
|
39
|
+
import progressbar
|
|
39
40
|
self.pbar = progressbar.ProgressBar(max_value=total_size)
|
|
40
41
|
self.pbar.start()
|
|
41
42
|
|
|
@@ -160,7 +160,7 @@ def resize_image(image, target_width, target_height=-1):
|
|
|
160
160
|
# w = ar * h
|
|
161
161
|
target_width = int(aspect_ratio * target_height)
|
|
162
162
|
|
|
163
|
-
# This parameter changed between Pillow
|
|
163
|
+
# This parameter changed between Pillow versions 9 and 10, and for a bit, I'd like to
|
|
164
164
|
# support both.
|
|
165
165
|
try:
|
|
166
166
|
resized_image = image.resize((target_width, target_height), Image.ANTIALIAS)
|