megadetector 5.0.21__tar.gz → 5.0.23__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.21/megadetector.egg-info → megadetector-5.0.23}/PKG-INFO +5 -4
- {megadetector-5.0.21 → megadetector-5.0.23}/README.md +1 -1
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/cct_json_utils.py +143 -7
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/cct_to_md.py +12 -5
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/databases/integrity_check_json_db.py +83 -77
- megadetector-5.0.23/megadetector/data_management/importers/raic_csv_to_md_results.py +416 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/zamba_results_to_md_results.py +1 -2
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/lila/create_lila_test_set.py +25 -11
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/lila/download_lila_subset.py +9 -2
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/lila/generate_lila_per_image_labels.py +3 -2
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/lila/test_lila_metadata_urls.py +5 -1
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/read_exif.py +10 -14
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/rename_images.py +1 -1
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/detection/process_video.py +14 -3
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/detection/pytorch_detector.py +15 -3
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/detection/run_detector.py +4 -3
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/detection/run_detector_batch.py +2 -2
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/detection/run_inference_with_yolov5_val.py +121 -13
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/detection/video_utils.py +21 -10
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/postprocessing/classification_postprocessing.py +1 -1
- megadetector-5.0.23/megadetector/postprocessing/compare_batch_results.py +1755 -0
- megadetector-5.0.23/megadetector/postprocessing/detector_calibration.py +565 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/postprocessing/md_to_coco.py +85 -20
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/postprocessing/postprocess_batch_results.py +0 -1
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/postprocessing/validate_batch_results.py +65 -15
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/taxonomy_mapping/map_new_lila_datasets.py +15 -12
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +1 -1
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/taxonomy_mapping/preview_lila_taxonomy.py +3 -1
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/utils/ct_utils.py +71 -14
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/utils/md_tests.py +9 -1
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/utils/path_utils.py +14 -7
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/utils/process_utils.py +9 -3
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/utils/write_html_image_list.py +5 -1
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/visualization/visualization_utils.py +211 -87
- {megadetector-5.0.21 → megadetector-5.0.23/megadetector.egg-info}/PKG-INFO +5 -4
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector.egg-info/SOURCES.txt +1 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector.egg-info/requires.txt +2 -1
- {megadetector-5.0.21 → megadetector-5.0.23}/pyproject.toml +12 -5
- megadetector-5.0.21/megadetector/postprocessing/compare_batch_results.py +0 -966
- megadetector-5.0.21/megadetector/postprocessing/detector_calibration.py +0 -367
- {megadetector-5.0.21 → megadetector-5.0.23}/LICENSE +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/README-package.md +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/batch_processing/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/batch_processing/api_core/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/batch_processing/api_core/batch_service/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/batch_processing/api_core/batch_service/score.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/batch_processing/api_core/server.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/batch_processing/api_core/server_api_config.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/batch_processing/api_core/server_app_config.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/batch_processing/api_core/server_batch_job_manager.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/batch_processing/api_core/server_job_status_table.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/batch_processing/api_core/server_orchestration.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/batch_processing/api_core/server_utils.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/batch_processing/api_core_support/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/batch_processing/api_core_support/aggregate_results_manually.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/batch_processing/api_support/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/batch_processing/api_support/summarize_daily_activity.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/batch_processing/data_preparation/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/batch_processing/integration/digiKam/setup.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/batch_processing/integration/digiKam/xmp_integration.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/batch_processing/integration/eMammal/test_scripts/config_template.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/batch_processing/integration/eMammal/test_scripts/push_annotations_to_emammal.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/batch_processing/integration/eMammal/test_scripts/select_images_for_testing.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/synchronous/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/synchronous/api_core/animal_detection_api/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/synchronous/api_core/animal_detection_api/api_backend.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/synchronous/api_core/animal_detection_api/api_frontend.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/synchronous/api_core/animal_detection_api/config.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/synchronous/api_core/tests/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/api/synchronous/api_core/tests/load_test.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/aggregate_classifier_probs.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/analyze_failed_images.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/cache_batchapi_outputs.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/create_classification_dataset.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/crop_detections.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/csv_to_json.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/detect_and_crop.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/efficientnet/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/efficientnet/model.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/efficientnet/utils.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/evaluate_model.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/identify_mislabeled_candidates.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/json_to_azcopy_list.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/json_validator.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/map_classification_categories.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/merge_classification_detection_output.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/prepare_classification_script.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/prepare_classification_script_mc.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/run_classifier.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/save_mislabeled.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/train_classifier.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/train_classifier_tf.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/classification/train_utils.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/annotations/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/annotations/annotation_constants.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/camtrap_dp_to_coco.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/cct_to_wi.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/coco_to_labelme.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/coco_to_yolo.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/databases/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/databases/add_width_and_height_to_db.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/databases/combine_coco_camera_traps_files.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/databases/subset_json_db.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/generate_crops_from_cct.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/get_image_sizes.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/add_nacti_sizes.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/add_timestamps_to_icct.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/animl_results_to_md_results.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/auckland_doc_test_to_json.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/auckland_doc_to_json.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/awc_to_json.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/bellevue_to_json.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/cacophony-thermal-importer.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/carrizo_shrubfree_2018.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/carrizo_trail_cam_2017.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/cct_field_adjustments.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/channel_islands_to_cct.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/eMammal/copy_and_unzip_emammal.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/eMammal/eMammal_helpers.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/eMammal/make_eMammal_json.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/ena24_to_json.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/filenames_to_json.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/helena_to_cct.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/idaho-camera-traps.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/idfg_iwildcam_lila_prep.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/import_desert_lion_conservation_camera_traps.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/jb_csv_to_json.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/mcgill_to_json.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/missouri_to_json.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/nacti_fieldname_adjustments.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/noaa_seals_2019.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/osu-small-animals-to-json.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/pc_to_json.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/plot_wni_giraffes.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/prepare_zsl_imerit.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/rspb_to_json.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/save_the_elephants_survey_A.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/save_the_elephants_survey_B.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/snapshot_safari_importer.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/snapshot_serengeti_lila.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/snapshotserengeti/make_full_SS_json.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/snapshotserengeti/make_per_season_SS_json.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/sulross_get_exif.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/timelapse_csv_set_to_json.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/ubc_to_json.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/umn_to_json.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/wellington_to_json.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/importers/wi_to_json.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/labelme_to_coco.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/labelme_to_yolo.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/lila/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/lila/add_locations_to_island_camera_traps.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/lila/add_locations_to_nacti.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/lila/create_lila_blank_set.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/lila/create_links_to_md_results_files.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/lila/get_lila_annotation_counts.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/lila/get_lila_image_counts.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/lila/lila_common.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/ocr_tools.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/remap_coco_categories.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/remove_exif.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/resize_coco_dataset.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/wi_download_csv_to_coco.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/yolo_output_to_md_output.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/data_management/yolo_to_coco.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/detection/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/detection/detector_training/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/detection/detector_training/model_main_tf2.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/detection/run_tiled_inference.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/detection/tf_detector.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/postprocessing/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/postprocessing/add_max_conf.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/postprocessing/categorize_detections_by_size.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/postprocessing/combine_api_outputs.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/postprocessing/convert_output_format.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/postprocessing/load_api_results.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/postprocessing/md_to_labelme.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/postprocessing/merge_detections.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/postprocessing/remap_detection_categories.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/postprocessing/render_detection_confusion_matrix.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/postprocessing/repeat_detection_elimination/find_repeat_detections.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/postprocessing/repeat_detection_elimination/repeat_detections_core.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/postprocessing/separate_detections_into_folders.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/postprocessing/subset_json_detector_output.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/postprocessing/top_folders_to_bottom.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/taxonomy_mapping/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/taxonomy_mapping/retrieve_sample_image.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/taxonomy_mapping/simple_image_download.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/taxonomy_mapping/species_lookup.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/taxonomy_mapping/taxonomy_csv_checker.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/taxonomy_mapping/taxonomy_graph.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/taxonomy_mapping/validate_lila_category_mappings.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/utils/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/utils/azure_utils.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/utils/directory_listing.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/utils/sas_blob_utils.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/utils/split_locations_into_train_val.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/utils/string_utils.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/utils/torch_test.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/utils/url_utils.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/visualization/__init__.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/visualization/plot_utils.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/visualization/render_images_with_thumbnails.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/visualization/visualize_db.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector/visualization/visualize_detector_output.py +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector.egg-info/dependency_links.txt +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/megadetector.egg-info/top_level.txt +0 -0
- {megadetector-5.0.21 → megadetector-5.0.23}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: megadetector
|
|
3
|
-
Version: 5.0.
|
|
3
|
+
Version: 5.0.23
|
|
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>
|
|
@@ -32,14 +32,14 @@ Keywords: camera traps,conservation,wildlife,ai,megadetector
|
|
|
32
32
|
Classifier: Development Status :: 3 - Alpha
|
|
33
33
|
Classifier: License :: OSI Approved :: MIT License
|
|
34
34
|
Classifier: Programming Language :: Python :: 3
|
|
35
|
-
Requires-Python:
|
|
35
|
+
Requires-Python: <=3.13,>=3.9
|
|
36
36
|
Description-Content-Type: text/markdown
|
|
37
37
|
License-File: LICENSE
|
|
38
38
|
Requires-Dist: Pillow>=9.5
|
|
39
39
|
Requires-Dist: tqdm>=4.64.0
|
|
40
40
|
Requires-Dist: jsonpickle>=3.0.2
|
|
41
41
|
Requires-Dist: humanfriendly>=10.0
|
|
42
|
-
Requires-Dist: numpy<
|
|
42
|
+
Requires-Dist: numpy<2.0,>=1.26.4
|
|
43
43
|
Requires-Dist: matplotlib>=3.8.0
|
|
44
44
|
Requires-Dist: opencv-python>=4.8.0
|
|
45
45
|
Requires-Dist: requests>=2.31.0
|
|
@@ -49,6 +49,7 @@ Requires-Dist: scikit-learn>=1.3.1
|
|
|
49
49
|
Requires-Dist: pandas>=2.1.1
|
|
50
50
|
Requires-Dist: PyYAML>=6.0.1
|
|
51
51
|
Requires-Dist: ultralytics-yolov5==0.1.1
|
|
52
|
+
Requires-Dist: python-dateutil
|
|
52
53
|
|
|
53
54
|
# MegaDetector
|
|
54
55
|
|
|
@@ -107,7 +107,7 @@ Here are a few of the organizations that have used MegaDetector... we're only li
|
|
|
107
107
|
* [Wildlife Coexistence Lab](https://wildlife.forestry.ubc.ca/), University of British Columbia
|
|
108
108
|
* [Wildlife Research](https://www.dfw.state.or.us/wildlife/research/index.asp), Oregon Department of Fish and Wildlife
|
|
109
109
|
* [Wildlife Division](https://www.michigan.gov/dnr/about/contact/wildlife), Michigan Department of Natural Resources
|
|
110
|
-
|
|
110
|
+
* [Kohl Wildlife Lab](https://kohlwildlifelab.com/), University of Georgia
|
|
111
111
|
* Department of Ecology, TU Berlin
|
|
112
112
|
* Ghost Cat Analytics
|
|
113
113
|
* Protected Areas Unit, Canadian Wildlife Service
|
|
@@ -12,6 +12,8 @@ https://github.com/agentmorris/MegaDetector/blob/main/megadetector/data_manageme
|
|
|
12
12
|
|
|
13
13
|
import json
|
|
14
14
|
import os
|
|
15
|
+
import datetime
|
|
16
|
+
import dateutil
|
|
15
17
|
|
|
16
18
|
from tqdm import tqdm
|
|
17
19
|
from collections import defaultdict, OrderedDict
|
|
@@ -302,6 +304,106 @@ class SequenceOptions:
|
|
|
302
304
|
|
|
303
305
|
#%% Functions
|
|
304
306
|
|
|
307
|
+
def write_object_with_serialized_datetimes(d,json_fn):
|
|
308
|
+
"""
|
|
309
|
+
Writes the object [d] to the .json file [json_fn] with a standard approach
|
|
310
|
+
to serializing Python datetime objects.
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
d (obj): the object to write, typically a dict
|
|
314
|
+
json_fn (str): the output filename
|
|
315
|
+
"""
|
|
316
|
+
|
|
317
|
+
# This writes datetimes as:
|
|
318
|
+
#
|
|
319
|
+
# 2022-12-31T09:52:50
|
|
320
|
+
def json_serialize_datetime(obj):
|
|
321
|
+
if isinstance(obj, (datetime.datetime, datetime.date)):
|
|
322
|
+
return obj.isoformat()
|
|
323
|
+
raise TypeError('Object {} (type {}) not serializable'.format(
|
|
324
|
+
str(obj),type(obj)))
|
|
325
|
+
|
|
326
|
+
with open(json_fn,'w') as f:
|
|
327
|
+
json.dump(d,f,indent=1,default=json_serialize_datetime)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def parse_datetimes_from_cct_image_list(images,conversion_failure_behavior='error'):
|
|
331
|
+
"""
|
|
332
|
+
Given the "images" field from a COCO camera traps dictionary, converts all
|
|
333
|
+
string-formatted datetime fields to Python datetimes, making reasonable assumptions
|
|
334
|
+
about datetime representations. Modifies [images] in place.
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
images (list): a list of dicts in CCT images format
|
|
338
|
+
conversion_failure_behavior (str, optional): determines what happens on a failed
|
|
339
|
+
conversion; can be "error" (raise an error), "str" (leave as a string), or
|
|
340
|
+
"none" (convert to None)
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
images: the input list, with datetimes converted (after modifying in place)
|
|
344
|
+
"""
|
|
345
|
+
|
|
346
|
+
assert isinstance(images,list)
|
|
347
|
+
|
|
348
|
+
for im in images:
|
|
349
|
+
|
|
350
|
+
if 'datetime' not in im:
|
|
351
|
+
continue
|
|
352
|
+
if isinstance(im['datetime'],datetime.datetime):
|
|
353
|
+
continue
|
|
354
|
+
try:
|
|
355
|
+
dt = dateutil.parser.parse(im['datetime'])
|
|
356
|
+
im['datetime'] = dt
|
|
357
|
+
except Exception as e:
|
|
358
|
+
s = 'could not parse datetime {}: {}'.format(str(im['datetime']),str(e))
|
|
359
|
+
if conversion_failure_behavior == 'error':
|
|
360
|
+
raise ValueError(s)
|
|
361
|
+
elif conversion_failure_behavior == 'str':
|
|
362
|
+
print('Warning: {}'.format(s))
|
|
363
|
+
pass
|
|
364
|
+
elif conversion_failure_behavior == 'none':
|
|
365
|
+
print('Warning: {}'.format(s))
|
|
366
|
+
im['datetime'] = None
|
|
367
|
+
|
|
368
|
+
# ...for each image
|
|
369
|
+
|
|
370
|
+
return images
|
|
371
|
+
|
|
372
|
+
# ...def parse_datetimes_from_cct_image_list(...)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def parse_datetimes_from_cct_dict(d,conversion_failure_behavior='error'):
|
|
376
|
+
"""
|
|
377
|
+
Given a COCO camera traps dictionary that may just have been loaded from file,
|
|
378
|
+
converts all string-formatted datetime fields to Python datetimes, making
|
|
379
|
+
reasonable assumptions about datetime representations. Modifies [d] in place
|
|
380
|
+
if [d] is supplied as a dict
|
|
381
|
+
|
|
382
|
+
Args:
|
|
383
|
+
d (dict or str): a dict in CCT format or a filename pointing to a CCT .json file
|
|
384
|
+
conversion_failure_behavior (str, optional): determines what happens on a failed
|
|
385
|
+
conversion; can be "error" (raise an error), "str" (leave as a string), or
|
|
386
|
+
"none" (convert to None)
|
|
387
|
+
|
|
388
|
+
Returns:
|
|
389
|
+
dict: the CCT dict with converted datetimes.
|
|
390
|
+
"""
|
|
391
|
+
|
|
392
|
+
if isinstance(d,str):
|
|
393
|
+
assert os.path.isfile(d), 'Could not find .json file {}'.format(d)
|
|
394
|
+
with open(d,'r') as f:
|
|
395
|
+
d = json.load(f)
|
|
396
|
+
|
|
397
|
+
images = d['images']
|
|
398
|
+
|
|
399
|
+
# Modifies in place
|
|
400
|
+
_ = parse_datetimes_from_cct_image_list(images)
|
|
401
|
+
|
|
402
|
+
return d
|
|
403
|
+
|
|
404
|
+
# ...def parse_datetimes_from_cct_dict(...)
|
|
405
|
+
|
|
406
|
+
|
|
305
407
|
def create_sequences(image_info,options=None):
|
|
306
408
|
"""
|
|
307
409
|
Synthesizes episodes/sequences/bursts for the images in [image_info].
|
|
@@ -310,21 +412,41 @@ def create_sequences(image_info,options=None):
|
|
|
310
412
|
fields for each image.
|
|
311
413
|
|
|
312
414
|
Args:
|
|
313
|
-
image_info (str, dict, or list): a dict in CCT format, a CCT .json file, or just the
|
|
314
|
-
of a CCT dataset (a list of dicts with fields 'file_name' (str),
|
|
315
|
-
'location' (str)).
|
|
415
|
+
image_info (str, dict, or list): a dict in CCT format, a CCT .json file, or just the
|
|
416
|
+
'images' component of a CCT dataset (a list of dicts with fields 'file_name' (str),
|
|
417
|
+
'datetime' (datetime), and 'location' (str)).
|
|
418
|
+
options (SequenceOptions): options parameterizing the assembly of images into sequences;
|
|
419
|
+
see the SequenceOptions class for details.
|
|
420
|
+
|
|
421
|
+
Returns:
|
|
422
|
+
image_info: if [image_info] is passed as a list, returns the list, otherwise returns
|
|
423
|
+
a CCT-formatted dict.
|
|
316
424
|
"""
|
|
317
425
|
|
|
318
426
|
if options is None:
|
|
319
427
|
options = SequenceOptions()
|
|
320
428
|
|
|
321
|
-
|
|
429
|
+
to_return = None
|
|
430
|
+
|
|
431
|
+
if isinstance(image_info,list):
|
|
432
|
+
to_return = image_info
|
|
433
|
+
|
|
434
|
+
elif isinstance(image_info,str):
|
|
322
435
|
with open(image_info,'r') as f:
|
|
323
|
-
|
|
436
|
+
d = json.load(f)
|
|
437
|
+
to_return = d
|
|
438
|
+
image_info = d['images']
|
|
324
439
|
|
|
325
|
-
|
|
440
|
+
elif isinstance(image_info,dict):
|
|
441
|
+
to_return = image_info
|
|
326
442
|
image_info = image_info['images']
|
|
443
|
+
|
|
444
|
+
else:
|
|
445
|
+
raise ValueError('Unrecognized type for [image_info]')
|
|
327
446
|
|
|
447
|
+
# Modifies the images in place
|
|
448
|
+
_ = parse_datetimes_from_cct_image_list(image_info)
|
|
449
|
+
|
|
328
450
|
# Find all unique locations
|
|
329
451
|
locations = set()
|
|
330
452
|
for im in image_info:
|
|
@@ -401,4 +523,18 @@ def create_sequences(image_info,options=None):
|
|
|
401
523
|
|
|
402
524
|
print('Created {} sequences from {} images'.format(len(all_sequences),len(image_info)))
|
|
403
525
|
|
|
404
|
-
|
|
526
|
+
return to_return
|
|
527
|
+
|
|
528
|
+
# ...def create_sequences(...)
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
#%% Test drivers
|
|
532
|
+
|
|
533
|
+
if False:
|
|
534
|
+
|
|
535
|
+
pass
|
|
536
|
+
|
|
537
|
+
#%%
|
|
538
|
+
|
|
539
|
+
fn = r'g:\temp\test.json'
|
|
540
|
+
d = parse_datetimes_from_cct_dict(fn,conversion_failure_behavior='error')
|
|
@@ -27,10 +27,13 @@ from tqdm import tqdm
|
|
|
27
27
|
def cct_to_md(input_filename,output_filename=None):
|
|
28
28
|
"""
|
|
29
29
|
"Converts" a COCO Camera Traps file to a MD results file. Currently ignores
|
|
30
|
-
non-bounding-box annotations
|
|
30
|
+
non-bounding-box annotations. If the semi-standard "score" field is present in
|
|
31
|
+
an annotation, or the totally non-standard "conf" field is present, it will be
|
|
32
|
+
transferred to the output, otherwise a confidence value of 1.0 is assumed for
|
|
33
|
+
all annotations.
|
|
31
34
|
|
|
32
|
-
The
|
|
33
|
-
CCT-formatted dataset, and you want to do that in Timelapse.
|
|
35
|
+
The main reason to run this script the scenario where you are going to add information
|
|
36
|
+
to an existing CCT-formatted dataset, and you want to do that in Timelapse.
|
|
34
37
|
|
|
35
38
|
Currently assumes that width and height are present in the input data, does not
|
|
36
39
|
read them from images.
|
|
@@ -113,8 +116,12 @@ def cct_to_md(input_filename,output_filename=None):
|
|
|
113
116
|
|
|
114
117
|
det = {}
|
|
115
118
|
det['category'] = str(ann['category_id'])
|
|
116
|
-
|
|
117
|
-
|
|
119
|
+
if 'score' in ann:
|
|
120
|
+
det['conf'] = ann['score']
|
|
121
|
+
elif 'conf' in ann:
|
|
122
|
+
det['conf'] = ann['conf']
|
|
123
|
+
else:
|
|
124
|
+
det['conf'] = 1.0
|
|
118
125
|
|
|
119
126
|
# MegaDetector: [x,y,width,height] (normalized, origin upper-left)
|
|
120
127
|
# CCT: [x,y,width,height] (absolute, origin upper-left)
|
|
@@ -28,6 +28,7 @@ from tqdm import tqdm
|
|
|
28
28
|
|
|
29
29
|
from megadetector.visualization.visualization_utils import open_image
|
|
30
30
|
from megadetector.utils import ct_utils
|
|
31
|
+
from megadetector.utils.path_utils import find_images
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
#%% Classes and environment
|
|
@@ -67,7 +68,7 @@ class IntegrityCheckOptions:
|
|
|
67
68
|
self.allowIntIDs = False
|
|
68
69
|
|
|
69
70
|
# This is used in a medium-hacky way to share modified options across threads
|
|
70
|
-
|
|
71
|
+
default_options = IntegrityCheckOptions()
|
|
71
72
|
|
|
72
73
|
|
|
73
74
|
#%% Functions
|
|
@@ -89,7 +90,7 @@ def _check_image_existence_and_size(image,options=None):
|
|
|
89
90
|
"""
|
|
90
91
|
|
|
91
92
|
if options is None:
|
|
92
|
-
options =
|
|
93
|
+
options = default_options
|
|
93
94
|
|
|
94
95
|
assert options.bCheckImageExistence
|
|
95
96
|
|
|
@@ -124,9 +125,9 @@ def integrity_check_json_db(jsonFile, options=None):
|
|
|
124
125
|
|
|
125
126
|
Returns:
|
|
126
127
|
tuple: tuple containing:
|
|
127
|
-
-
|
|
128
|
+
- sorted_categories (dict): list of categories used in [jsonFile], sorted by frequency
|
|
128
129
|
- data (dict): the data loaded from [jsonFile]
|
|
129
|
-
-
|
|
130
|
+
- error_info (dict): specific validation errors
|
|
130
131
|
"""
|
|
131
132
|
|
|
132
133
|
if options is None:
|
|
@@ -141,7 +142,7 @@ def integrity_check_json_db(jsonFile, options=None):
|
|
|
141
142
|
if options.baseDir is None:
|
|
142
143
|
options.baseDir = ''
|
|
143
144
|
|
|
144
|
-
|
|
145
|
+
base_dir = options.baseDir
|
|
145
146
|
|
|
146
147
|
|
|
147
148
|
##%% Read .json file if necessary, integrity-check fields
|
|
@@ -156,7 +157,7 @@ def integrity_check_json_db(jsonFile, options=None):
|
|
|
156
157
|
|
|
157
158
|
if options.verbose:
|
|
158
159
|
print('Reading .json {} with base dir [{}]...'.format(
|
|
159
|
-
jsonFile,
|
|
160
|
+
jsonFile,base_dir))
|
|
160
161
|
|
|
161
162
|
with open(jsonFile,'r') as f:
|
|
162
163
|
data = json.load(f)
|
|
@@ -171,17 +172,17 @@ def integrity_check_json_db(jsonFile, options=None):
|
|
|
171
172
|
# info = data['info']
|
|
172
173
|
assert 'info' in data, 'No info struct in database'
|
|
173
174
|
|
|
174
|
-
if len(
|
|
175
|
-
assert os.path.isdir(
|
|
175
|
+
if len(base_dir) > 0:
|
|
176
|
+
assert os.path.isdir(base_dir), 'Base directory {} does not exist'.format(base_dir)
|
|
176
177
|
|
|
177
178
|
|
|
178
179
|
##%% Build dictionaries, checking ID uniqueness and internal validity as we go
|
|
179
180
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
181
|
+
image_id_to_image = {}
|
|
182
|
+
ann_id_to_ann = {}
|
|
183
|
+
category_id_to_category = {}
|
|
184
|
+
category_name_to_category = {}
|
|
185
|
+
image_location_set = set()
|
|
185
186
|
|
|
186
187
|
if options.verbose:
|
|
187
188
|
print('Checking categories...')
|
|
@@ -195,16 +196,16 @@ def integrity_check_json_db(jsonFile, options=None):
|
|
|
195
196
|
assert isinstance(cat['id'],int), 'Illegal category ID type: [{}]'.format(str(cat['id']))
|
|
196
197
|
assert isinstance(cat['name'],str), 'Illegal category name type [{}]'.format(str(cat['name']))
|
|
197
198
|
|
|
198
|
-
|
|
199
|
-
|
|
199
|
+
category_id = cat['id']
|
|
200
|
+
category_name = cat['name']
|
|
200
201
|
|
|
201
202
|
# Confirm ID uniqueness
|
|
202
|
-
assert
|
|
203
|
-
|
|
203
|
+
assert category_id not in category_id_to_category, 'Category ID {} is used more than once'.format(category_id)
|
|
204
|
+
category_id_to_category[category_id] = cat
|
|
204
205
|
cat['_count'] = 0
|
|
205
206
|
|
|
206
|
-
assert
|
|
207
|
-
|
|
207
|
+
assert category_name not in category_name_to_category, 'Category name {} is used more than once'.format(category_name)
|
|
208
|
+
category_name_to_category[category_name] = cat
|
|
208
209
|
|
|
209
210
|
# ...for each category
|
|
210
211
|
|
|
@@ -217,7 +218,7 @@ def integrity_check_json_db(jsonFile, options=None):
|
|
|
217
218
|
print('Trimming image list to {}'.format(options.iMaxNumImages))
|
|
218
219
|
images = images[0:options.iMaxNumImages]
|
|
219
220
|
|
|
220
|
-
|
|
221
|
+
image_paths_in_json = set()
|
|
221
222
|
|
|
222
223
|
sequences = set()
|
|
223
224
|
|
|
@@ -230,9 +231,9 @@ def integrity_check_json_db(jsonFile, options=None):
|
|
|
230
231
|
assert 'file_name' in image
|
|
231
232
|
assert 'id' in image
|
|
232
233
|
|
|
233
|
-
image['file_name'] =
|
|
234
|
+
image['file_name'] = image['file_name'].replace('\\','/')
|
|
234
235
|
|
|
235
|
-
|
|
236
|
+
image_paths_in_json.add(image['file_name'])
|
|
236
237
|
|
|
237
238
|
assert isinstance(image['file_name'],str), 'Illegal image filename type'
|
|
238
239
|
|
|
@@ -242,12 +243,12 @@ def integrity_check_json_db(jsonFile, options=None):
|
|
|
242
243
|
else:
|
|
243
244
|
assert isinstance(image['id'],str), 'Illegal image ID type'
|
|
244
245
|
|
|
245
|
-
|
|
246
|
+
image_id = image['id']
|
|
246
247
|
|
|
247
248
|
# Confirm ID uniqueness
|
|
248
|
-
assert
|
|
249
|
+
assert image_id not in image_id_to_image, 'Duplicate image ID {}'.format(image_id)
|
|
249
250
|
|
|
250
|
-
|
|
251
|
+
image_id_to_image[image_id] = image
|
|
251
252
|
|
|
252
253
|
if 'height' in image:
|
|
253
254
|
assert 'width' in image, 'Image with height but no width: {}'.format(image['id'])
|
|
@@ -263,70 +264,74 @@ def integrity_check_json_db(jsonFile, options=None):
|
|
|
263
264
|
# assert isinstance(image['location'], str) or isinstance(image['location'], int), \
|
|
264
265
|
# 'Illegal image location type'
|
|
265
266
|
assert isinstance(image['location'], str)
|
|
266
|
-
|
|
267
|
+
image_location_set.add(image['location'])
|
|
267
268
|
|
|
268
269
|
if 'seq_id' in image:
|
|
269
270
|
sequences.add(image['seq_id'])
|
|
270
271
|
|
|
271
272
|
assert not ('sequence_id' in image or 'sequence' in image), 'Illegal sequence identifier'
|
|
272
273
|
|
|
273
|
-
|
|
274
|
+
unused_files = []
|
|
274
275
|
|
|
276
|
+
image_paths_relative = None
|
|
277
|
+
|
|
275
278
|
# Are we checking for unused images?
|
|
276
|
-
if (len(
|
|
279
|
+
if (len(base_dir) > 0) and options.bFindUnusedImages:
|
|
277
280
|
|
|
278
281
|
if options.verbose:
|
|
279
282
|
print('\nEnumerating images...')
|
|
280
283
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
for
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
relDir = os.path.relpath(root, baseDir)
|
|
287
|
-
relFile = os.path.join(relDir,file)
|
|
288
|
-
relFile = os.path.normpath(relFile)
|
|
289
|
-
if len(relFile) > 2 and \
|
|
290
|
-
(relFile[0:2] == './' or relFile[0:2] == '.\\'):
|
|
291
|
-
relFile = relFile[2:]
|
|
292
|
-
imagePaths.append(relFile)
|
|
293
|
-
|
|
294
|
-
for p in imagePaths:
|
|
295
|
-
if p not in imagePathsInJson:
|
|
296
|
-
# print('Image {} is unused'.format(p))
|
|
297
|
-
unusedFiles.append(p)
|
|
284
|
+
image_paths_relative = find_images(base_dir,return_relative_paths=True,recursive=True)
|
|
285
|
+
|
|
286
|
+
for fn_relative in image_paths_relative:
|
|
287
|
+
if fn_relative not in image_paths_in_json:
|
|
288
|
+
unused_files.append(fn_relative)
|
|
298
289
|
|
|
299
|
-
|
|
290
|
+
validation_errors = []
|
|
300
291
|
|
|
301
|
-
#
|
|
302
|
-
if options.
|
|
292
|
+
# If we're checking image existence but not image size, we don't need to read the images
|
|
293
|
+
if options.bCheckImageExistence and not options.bCheckImageSizes:
|
|
294
|
+
|
|
295
|
+
if image_paths_relative is None:
|
|
296
|
+
image_paths_relative = find_images(base_dir,return_relative_paths=True,recursive=True)
|
|
297
|
+
|
|
298
|
+
image_paths_relative_set = set(image_paths_relative)
|
|
299
|
+
|
|
300
|
+
for im in images:
|
|
301
|
+
if im['file_name'] not in image_paths_relative_set:
|
|
302
|
+
validation_errors.append(im['file_name'])
|
|
303
|
+
|
|
304
|
+
# If we're checking image size, we need to read the images
|
|
305
|
+
if options.bCheckImageSizes:
|
|
303
306
|
|
|
304
|
-
if len(
|
|
307
|
+
if len(base_dir) == 0:
|
|
305
308
|
print('Warning: checking image sizes without a base directory, assuming "."')
|
|
306
309
|
|
|
307
310
|
if options.verbose:
|
|
308
311
|
print('Checking image existence and/or image sizes...')
|
|
309
312
|
|
|
310
313
|
if options.nThreads is not None and options.nThreads > 1:
|
|
314
|
+
if options.verbose:
|
|
315
|
+
print('Starting a pool of {} workers'.format(options.nThreads))
|
|
311
316
|
pool = ThreadPool(options.nThreads)
|
|
312
317
|
# results = pool.imap_unordered(lambda x: fetch_url(x,nImages), indexedUrlList)
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
318
|
+
default_options.baseDir = options.baseDir
|
|
319
|
+
default_options.bCheckImageSizes = options.bCheckImageSizes
|
|
320
|
+
default_options.bCheckImageExistence = options.bCheckImageExistence
|
|
316
321
|
results = tqdm(pool.imap(_check_image_existence_and_size, images), total=len(images))
|
|
317
322
|
else:
|
|
318
323
|
results = []
|
|
319
324
|
for im in tqdm(images):
|
|
320
325
|
results.append(_check_image_existence_and_size(im,options))
|
|
321
326
|
|
|
322
|
-
for
|
|
323
|
-
if not
|
|
324
|
-
|
|
327
|
+
for i_image,result in enumerate(results):
|
|
328
|
+
if result is not None:
|
|
329
|
+
validation_errors.append(images[i_image]['file_name'])
|
|
325
330
|
|
|
326
331
|
# ...for each image
|
|
327
332
|
|
|
328
333
|
if options.verbose:
|
|
329
|
-
print('{} validation errors (of {})'.format(len(
|
|
334
|
+
print('{} validation errors (of {})'.format(len(validation_errors),len(images)))
|
|
330
335
|
print('Checking annotations...')
|
|
331
336
|
|
|
332
337
|
nBoxes = 0
|
|
@@ -355,22 +360,22 @@ def integrity_check_json_db(jsonFile, options=None):
|
|
|
355
360
|
annId = ann['id']
|
|
356
361
|
|
|
357
362
|
# Confirm ID uniqueness
|
|
358
|
-
assert annId not in
|
|
359
|
-
|
|
363
|
+
assert annId not in ann_id_to_ann
|
|
364
|
+
ann_id_to_ann[annId] = ann
|
|
360
365
|
|
|
361
366
|
# Confirm validity
|
|
362
|
-
assert ann['category_id'] in
|
|
367
|
+
assert ann['category_id'] in category_id_to_category, \
|
|
363
368
|
'Category {} not found in category list'.format(ann['category_id'])
|
|
364
|
-
assert ann['image_id'] in
|
|
369
|
+
assert ann['image_id'] in image_id_to_image, \
|
|
365
370
|
'Image ID {} referred to by annotation {}, not available'.format(
|
|
366
371
|
ann['image_id'],ann['id'])
|
|
367
372
|
|
|
368
|
-
|
|
369
|
-
|
|
373
|
+
image_id_to_image[ann['image_id']]['_count'] += 1
|
|
374
|
+
category_id_to_category[ann['category_id']]['_count'] +=1
|
|
370
375
|
|
|
371
376
|
# ...for each annotation
|
|
372
377
|
|
|
373
|
-
|
|
378
|
+
sorted_categories = sorted(categories, key=itemgetter('_count'), reverse=True)
|
|
374
379
|
|
|
375
380
|
|
|
376
381
|
##%% Print statistics
|
|
@@ -390,18 +395,18 @@ def integrity_check_json_db(jsonFile, options=None):
|
|
|
390
395
|
print('Found {} unannotated images, {} images with multiple annotations'.format(
|
|
391
396
|
nUnannotated,nMultiAnnotated))
|
|
392
397
|
|
|
393
|
-
if (len(
|
|
394
|
-
print('Found {} unused image files'.format(len(
|
|
398
|
+
if (len(base_dir) > 0) and options.bFindUnusedImages:
|
|
399
|
+
print('Found {} unused image files'.format(len(unused_files)))
|
|
395
400
|
|
|
396
|
-
|
|
401
|
+
n_unused_categories = 0
|
|
397
402
|
|
|
398
403
|
# Find unused categories
|
|
399
404
|
for cat in categories:
|
|
400
405
|
if cat['_count'] == 0:
|
|
401
406
|
print('Unused category: {}'.format(cat['name']))
|
|
402
|
-
|
|
407
|
+
n_unused_categories += 1
|
|
403
408
|
|
|
404
|
-
print('Found {} unused categories'.format(
|
|
409
|
+
print('Found {} unused categories'.format(n_unused_categories))
|
|
405
410
|
|
|
406
411
|
sequenceString = 'no sequence info'
|
|
407
412
|
if len(sequences) > 0:
|
|
@@ -410,21 +415,21 @@ def integrity_check_json_db(jsonFile, options=None):
|
|
|
410
415
|
print('\nDB contains {} images, {} annotations, {} bboxes, {} categories, {}\n'.format(
|
|
411
416
|
len(images),len(annotations),nBoxes,len(categories),sequenceString))
|
|
412
417
|
|
|
413
|
-
if len(
|
|
414
|
-
print('DB contains images from {} locations\n'.format(len(
|
|
418
|
+
if len(image_location_set) > 0:
|
|
419
|
+
print('DB contains images from {} locations\n'.format(len(image_location_set)))
|
|
415
420
|
|
|
416
421
|
print('Categories and annotation (not image) counts:\n')
|
|
417
422
|
|
|
418
|
-
for cat in
|
|
423
|
+
for cat in sorted_categories:
|
|
419
424
|
print('{:6} {}'.format(cat['_count'],cat['name']))
|
|
420
425
|
|
|
421
426
|
print('')
|
|
422
427
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
428
|
+
error_info = {}
|
|
429
|
+
error_info['unused_files'] = unused_files
|
|
430
|
+
error_info['validation_errors'] = validation_errors
|
|
426
431
|
|
|
427
|
-
return
|
|
432
|
+
return sorted_categories, data, error_info
|
|
428
433
|
|
|
429
434
|
# ...def integrity_check_json_db()
|
|
430
435
|
|
|
@@ -434,7 +439,8 @@ def integrity_check_json_db(jsonFile, options=None):
|
|
|
434
439
|
def main():
|
|
435
440
|
|
|
436
441
|
parser = argparse.ArgumentParser()
|
|
437
|
-
parser.add_argument('jsonFile'
|
|
442
|
+
parser.add_argument('jsonFile',type=str,
|
|
443
|
+
help='COCO-formatted .json file to validate')
|
|
438
444
|
parser.add_argument('--bCheckImageSizes', action='store_true',
|
|
439
445
|
help='Validate image size, requires baseDir to be specified. ' + \
|
|
440
446
|
'Implies existence checking.')
|
|
@@ -490,4 +496,4 @@ if False:
|
|
|
490
496
|
|
|
491
497
|
for json_file in json_files:
|
|
492
498
|
|
|
493
|
-
|
|
499
|
+
sorted_categories,data,_ = integrity_check_json_db(json_file, options)
|