megadetector 5.0.10__py3-none-any.whl → 5.0.11__py3-none-any.whl
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.10.dist-info → megadetector-5.0.11.dist-info}/LICENSE +0 -0
- {megadetector-5.0.10.dist-info → megadetector-5.0.11.dist-info}/METADATA +12 -11
- megadetector-5.0.11.dist-info/RECORD +5 -0
- megadetector-5.0.11.dist-info/top_level.txt +1 -0
- api/__init__.py +0 -0
- api/batch_processing/__init__.py +0 -0
- api/batch_processing/api_core/__init__.py +0 -0
- api/batch_processing/api_core/batch_service/__init__.py +0 -0
- api/batch_processing/api_core/batch_service/score.py +0 -439
- api/batch_processing/api_core/server.py +0 -294
- api/batch_processing/api_core/server_api_config.py +0 -98
- api/batch_processing/api_core/server_app_config.py +0 -55
- api/batch_processing/api_core/server_batch_job_manager.py +0 -220
- api/batch_processing/api_core/server_job_status_table.py +0 -152
- api/batch_processing/api_core/server_orchestration.py +0 -360
- api/batch_processing/api_core/server_utils.py +0 -92
- api/batch_processing/api_core_support/__init__.py +0 -0
- api/batch_processing/api_core_support/aggregate_results_manually.py +0 -46
- api/batch_processing/api_support/__init__.py +0 -0
- api/batch_processing/api_support/summarize_daily_activity.py +0 -152
- api/batch_processing/data_preparation/__init__.py +0 -0
- api/batch_processing/data_preparation/manage_local_batch.py +0 -2391
- api/batch_processing/data_preparation/manage_video_batch.py +0 -327
- api/batch_processing/integration/digiKam/setup.py +0 -6
- api/batch_processing/integration/digiKam/xmp_integration.py +0 -465
- api/batch_processing/integration/eMammal/test_scripts/config_template.py +0 -5
- api/batch_processing/integration/eMammal/test_scripts/push_annotations_to_emammal.py +0 -126
- api/batch_processing/integration/eMammal/test_scripts/select_images_for_testing.py +0 -55
- api/batch_processing/postprocessing/__init__.py +0 -0
- api/batch_processing/postprocessing/add_max_conf.py +0 -64
- api/batch_processing/postprocessing/categorize_detections_by_size.py +0 -163
- api/batch_processing/postprocessing/combine_api_outputs.py +0 -249
- api/batch_processing/postprocessing/compare_batch_results.py +0 -958
- api/batch_processing/postprocessing/convert_output_format.py +0 -397
- api/batch_processing/postprocessing/load_api_results.py +0 -195
- api/batch_processing/postprocessing/md_to_coco.py +0 -310
- api/batch_processing/postprocessing/md_to_labelme.py +0 -330
- api/batch_processing/postprocessing/merge_detections.py +0 -401
- api/batch_processing/postprocessing/postprocess_batch_results.py +0 -1904
- api/batch_processing/postprocessing/remap_detection_categories.py +0 -170
- api/batch_processing/postprocessing/render_detection_confusion_matrix.py +0 -661
- api/batch_processing/postprocessing/repeat_detection_elimination/find_repeat_detections.py +0 -211
- api/batch_processing/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +0 -82
- api/batch_processing/postprocessing/repeat_detection_elimination/repeat_detections_core.py +0 -1631
- api/batch_processing/postprocessing/separate_detections_into_folders.py +0 -731
- api/batch_processing/postprocessing/subset_json_detector_output.py +0 -696
- api/batch_processing/postprocessing/top_folders_to_bottom.py +0 -223
- api/synchronous/__init__.py +0 -0
- api/synchronous/api_core/animal_detection_api/__init__.py +0 -0
- api/synchronous/api_core/animal_detection_api/api_backend.py +0 -152
- api/synchronous/api_core/animal_detection_api/api_frontend.py +0 -266
- api/synchronous/api_core/animal_detection_api/config.py +0 -35
- api/synchronous/api_core/animal_detection_api/data_management/annotations/annotation_constants.py +0 -47
- api/synchronous/api_core/animal_detection_api/detection/detector_training/copy_checkpoints.py +0 -43
- api/synchronous/api_core/animal_detection_api/detection/detector_training/model_main_tf2.py +0 -114
- api/synchronous/api_core/animal_detection_api/detection/process_video.py +0 -543
- api/synchronous/api_core/animal_detection_api/detection/pytorch_detector.py +0 -304
- api/synchronous/api_core/animal_detection_api/detection/run_detector.py +0 -627
- api/synchronous/api_core/animal_detection_api/detection/run_detector_batch.py +0 -1029
- api/synchronous/api_core/animal_detection_api/detection/run_inference_with_yolov5_val.py +0 -581
- api/synchronous/api_core/animal_detection_api/detection/run_tiled_inference.py +0 -754
- api/synchronous/api_core/animal_detection_api/detection/tf_detector.py +0 -165
- api/synchronous/api_core/animal_detection_api/detection/video_utils.py +0 -495
- api/synchronous/api_core/animal_detection_api/md_utils/azure_utils.py +0 -174
- api/synchronous/api_core/animal_detection_api/md_utils/ct_utils.py +0 -262
- api/synchronous/api_core/animal_detection_api/md_utils/directory_listing.py +0 -251
- api/synchronous/api_core/animal_detection_api/md_utils/matlab_porting_tools.py +0 -97
- api/synchronous/api_core/animal_detection_api/md_utils/path_utils.py +0 -416
- api/synchronous/api_core/animal_detection_api/md_utils/process_utils.py +0 -110
- api/synchronous/api_core/animal_detection_api/md_utils/sas_blob_utils.py +0 -509
- api/synchronous/api_core/animal_detection_api/md_utils/string_utils.py +0 -59
- api/synchronous/api_core/animal_detection_api/md_utils/url_utils.py +0 -144
- api/synchronous/api_core/animal_detection_api/md_utils/write_html_image_list.py +0 -226
- api/synchronous/api_core/animal_detection_api/md_visualization/visualization_utils.py +0 -841
- api/synchronous/api_core/tests/__init__.py +0 -0
- api/synchronous/api_core/tests/load_test.py +0 -110
- classification/__init__.py +0 -0
- classification/aggregate_classifier_probs.py +0 -108
- classification/analyze_failed_images.py +0 -227
- classification/cache_batchapi_outputs.py +0 -198
- classification/create_classification_dataset.py +0 -627
- classification/crop_detections.py +0 -516
- classification/csv_to_json.py +0 -226
- classification/detect_and_crop.py +0 -855
- classification/efficientnet/__init__.py +0 -9
- classification/efficientnet/model.py +0 -415
- classification/efficientnet/utils.py +0 -610
- classification/evaluate_model.py +0 -520
- classification/identify_mislabeled_candidates.py +0 -152
- classification/json_to_azcopy_list.py +0 -63
- classification/json_validator.py +0 -695
- classification/map_classification_categories.py +0 -276
- classification/merge_classification_detection_output.py +0 -506
- classification/prepare_classification_script.py +0 -194
- classification/prepare_classification_script_mc.py +0 -228
- classification/run_classifier.py +0 -286
- classification/save_mislabeled.py +0 -110
- classification/train_classifier.py +0 -825
- classification/train_classifier_tf.py +0 -724
- classification/train_utils.py +0 -322
- data_management/__init__.py +0 -0
- data_management/annotations/__init__.py +0 -0
- data_management/annotations/annotation_constants.py +0 -34
- data_management/camtrap_dp_to_coco.py +0 -238
- data_management/cct_json_utils.py +0 -395
- data_management/cct_to_md.py +0 -176
- data_management/cct_to_wi.py +0 -289
- data_management/coco_to_labelme.py +0 -272
- data_management/coco_to_yolo.py +0 -662
- data_management/databases/__init__.py +0 -0
- data_management/databases/add_width_and_height_to_db.py +0 -33
- data_management/databases/combine_coco_camera_traps_files.py +0 -206
- data_management/databases/integrity_check_json_db.py +0 -477
- data_management/databases/subset_json_db.py +0 -115
- data_management/generate_crops_from_cct.py +0 -149
- data_management/get_image_sizes.py +0 -188
- data_management/importers/add_nacti_sizes.py +0 -52
- data_management/importers/add_timestamps_to_icct.py +0 -79
- data_management/importers/animl_results_to_md_results.py +0 -158
- data_management/importers/auckland_doc_test_to_json.py +0 -372
- data_management/importers/auckland_doc_to_json.py +0 -200
- data_management/importers/awc_to_json.py +0 -189
- data_management/importers/bellevue_to_json.py +0 -273
- data_management/importers/cacophony-thermal-importer.py +0 -796
- data_management/importers/carrizo_shrubfree_2018.py +0 -268
- data_management/importers/carrizo_trail_cam_2017.py +0 -287
- data_management/importers/cct_field_adjustments.py +0 -57
- data_management/importers/channel_islands_to_cct.py +0 -913
- data_management/importers/eMammal/copy_and_unzip_emammal.py +0 -180
- data_management/importers/eMammal/eMammal_helpers.py +0 -249
- data_management/importers/eMammal/make_eMammal_json.py +0 -223
- data_management/importers/ena24_to_json.py +0 -275
- data_management/importers/filenames_to_json.py +0 -385
- data_management/importers/helena_to_cct.py +0 -282
- data_management/importers/idaho-camera-traps.py +0 -1407
- data_management/importers/idfg_iwildcam_lila_prep.py +0 -294
- data_management/importers/jb_csv_to_json.py +0 -150
- data_management/importers/mcgill_to_json.py +0 -250
- data_management/importers/missouri_to_json.py +0 -489
- data_management/importers/nacti_fieldname_adjustments.py +0 -79
- data_management/importers/noaa_seals_2019.py +0 -181
- data_management/importers/pc_to_json.py +0 -365
- data_management/importers/plot_wni_giraffes.py +0 -123
- data_management/importers/prepare-noaa-fish-data-for-lila.py +0 -359
- data_management/importers/prepare_zsl_imerit.py +0 -131
- data_management/importers/rspb_to_json.py +0 -356
- data_management/importers/save_the_elephants_survey_A.py +0 -320
- data_management/importers/save_the_elephants_survey_B.py +0 -332
- data_management/importers/snapshot_safari_importer.py +0 -758
- data_management/importers/snapshot_safari_importer_reprise.py +0 -665
- data_management/importers/snapshot_serengeti_lila.py +0 -1067
- data_management/importers/snapshotserengeti/make_full_SS_json.py +0 -150
- data_management/importers/snapshotserengeti/make_per_season_SS_json.py +0 -153
- data_management/importers/sulross_get_exif.py +0 -65
- data_management/importers/timelapse_csv_set_to_json.py +0 -490
- data_management/importers/ubc_to_json.py +0 -399
- data_management/importers/umn_to_json.py +0 -507
- data_management/importers/wellington_to_json.py +0 -263
- data_management/importers/wi_to_json.py +0 -441
- data_management/importers/zamba_results_to_md_results.py +0 -181
- data_management/labelme_to_coco.py +0 -548
- data_management/labelme_to_yolo.py +0 -272
- data_management/lila/__init__.py +0 -0
- data_management/lila/add_locations_to_island_camera_traps.py +0 -97
- data_management/lila/add_locations_to_nacti.py +0 -147
- data_management/lila/create_lila_blank_set.py +0 -557
- data_management/lila/create_lila_test_set.py +0 -151
- data_management/lila/create_links_to_md_results_files.py +0 -106
- data_management/lila/download_lila_subset.py +0 -177
- data_management/lila/generate_lila_per_image_labels.py +0 -515
- data_management/lila/get_lila_annotation_counts.py +0 -170
- data_management/lila/get_lila_image_counts.py +0 -111
- data_management/lila/lila_common.py +0 -300
- data_management/lila/test_lila_metadata_urls.py +0 -132
- data_management/ocr_tools.py +0 -874
- data_management/read_exif.py +0 -681
- data_management/remap_coco_categories.py +0 -84
- data_management/remove_exif.py +0 -66
- data_management/resize_coco_dataset.py +0 -189
- data_management/wi_download_csv_to_coco.py +0 -246
- data_management/yolo_output_to_md_output.py +0 -441
- data_management/yolo_to_coco.py +0 -676
- detection/__init__.py +0 -0
- detection/detector_training/__init__.py +0 -0
- detection/detector_training/model_main_tf2.py +0 -114
- detection/process_video.py +0 -703
- detection/pytorch_detector.py +0 -337
- detection/run_detector.py +0 -779
- detection/run_detector_batch.py +0 -1219
- detection/run_inference_with_yolov5_val.py +0 -917
- detection/run_tiled_inference.py +0 -935
- detection/tf_detector.py +0 -188
- detection/video_utils.py +0 -606
- docs/source/conf.py +0 -43
- md_utils/__init__.py +0 -0
- md_utils/azure_utils.py +0 -174
- md_utils/ct_utils.py +0 -612
- md_utils/directory_listing.py +0 -246
- md_utils/md_tests.py +0 -968
- md_utils/path_utils.py +0 -1044
- md_utils/process_utils.py +0 -157
- md_utils/sas_blob_utils.py +0 -509
- md_utils/split_locations_into_train_val.py +0 -228
- md_utils/string_utils.py +0 -92
- md_utils/url_utils.py +0 -323
- md_utils/write_html_image_list.py +0 -225
- md_visualization/__init__.py +0 -0
- md_visualization/plot_utils.py +0 -293
- md_visualization/render_images_with_thumbnails.py +0 -275
- md_visualization/visualization_utils.py +0 -1537
- md_visualization/visualize_db.py +0 -551
- md_visualization/visualize_detector_output.py +0 -406
- megadetector-5.0.10.dist-info/RECORD +0 -224
- megadetector-5.0.10.dist-info/top_level.txt +0 -8
- taxonomy_mapping/__init__.py +0 -0
- taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +0 -491
- taxonomy_mapping/map_new_lila_datasets.py +0 -154
- taxonomy_mapping/prepare_lila_taxonomy_release.py +0 -142
- taxonomy_mapping/preview_lila_taxonomy.py +0 -591
- taxonomy_mapping/retrieve_sample_image.py +0 -71
- taxonomy_mapping/simple_image_download.py +0 -218
- taxonomy_mapping/species_lookup.py +0 -834
- taxonomy_mapping/taxonomy_csv_checker.py +0 -159
- taxonomy_mapping/taxonomy_graph.py +0 -346
- taxonomy_mapping/validate_lila_category_mappings.py +0 -83
- {megadetector-5.0.10.dist-info → megadetector-5.0.11.dist-info}/WHEEL +0 -0
|
@@ -1,581 +0,0 @@
|
|
|
1
|
-
########
|
|
2
|
-
#
|
|
3
|
-
# run_inference_with_yolov5_val.py
|
|
4
|
-
#
|
|
5
|
-
# Runs a folder of images through MegaDetector (or another YOLOv5 model) with YOLOv5's
|
|
6
|
-
# val.py, converting the output to the standard MD format. The main goal is to leverage
|
|
7
|
-
# YOLO's test-time augmentation tools.
|
|
8
|
-
#
|
|
9
|
-
# YOLOv5's val.py uses each file's base name as a unique identifier, which doesn't work
|
|
10
|
-
# when you have typical camera trap images like:
|
|
11
|
-
#
|
|
12
|
-
# a/b/c/RECONYX0001.JPG
|
|
13
|
-
# d/e/f/RECONYX0001.JPG
|
|
14
|
-
#
|
|
15
|
-
# ...so this script jumps through a bunch of hoops to put a symlinks in a flat
|
|
16
|
-
# folder, run YOLOv5 on that folder, and map the results back to the real files.
|
|
17
|
-
#
|
|
18
|
-
# Currently requires the user to supply the path where a working YOLOv5 install lives,
|
|
19
|
-
# and assumes that the current conda environment is all set up for YOLOv5.
|
|
20
|
-
#
|
|
21
|
-
# By default, this script uses symlinks to format the input images in a way that YOLOv5's
|
|
22
|
-
# val.py likes. This requires admin privileges on Windows... actually technically this only
|
|
23
|
-
# requires permissions to create symbolic links, but I've never seen a case where someone has
|
|
24
|
-
# that permission and *doesn't* have admin privileges. If you are running this script on
|
|
25
|
-
# Windows and you don't have admin privileges, use --no_use_symlinks.
|
|
26
|
-
#
|
|
27
|
-
# TODO:
|
|
28
|
-
#
|
|
29
|
-
# * Multiple GPU support
|
|
30
|
-
#
|
|
31
|
-
# * Checkpointing
|
|
32
|
-
#
|
|
33
|
-
# * Support alternative class names at the command line (currently defaults to MD classes,
|
|
34
|
-
# though other class names can be supplied programmatically)
|
|
35
|
-
#
|
|
36
|
-
########
|
|
37
|
-
|
|
38
|
-
#%% Imports
|
|
39
|
-
|
|
40
|
-
import os
|
|
41
|
-
import uuid
|
|
42
|
-
import glob
|
|
43
|
-
import tempfile
|
|
44
|
-
import shutil
|
|
45
|
-
import json
|
|
46
|
-
|
|
47
|
-
from tqdm import tqdm
|
|
48
|
-
|
|
49
|
-
from md_utils import path_utils
|
|
50
|
-
from md_utils import process_utils
|
|
51
|
-
from data_management import yolo_output_to_md_output
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
#%% Options class
|
|
55
|
-
|
|
56
|
-
class YoloInferenceOptions:
|
|
57
|
-
|
|
58
|
-
## Required ##
|
|
59
|
-
|
|
60
|
-
input_folder = None
|
|
61
|
-
model_filename = None
|
|
62
|
-
yolo_working_folder = None
|
|
63
|
-
output_file = None
|
|
64
|
-
|
|
65
|
-
## Optional ##
|
|
66
|
-
|
|
67
|
-
image_size = 1280 * 1.3
|
|
68
|
-
conf_thres = '0.001'
|
|
69
|
-
batch_size = 1
|
|
70
|
-
device_string = '0'
|
|
71
|
-
augment = True
|
|
72
|
-
|
|
73
|
-
symlink_folder = None
|
|
74
|
-
use_symlinks = True
|
|
75
|
-
|
|
76
|
-
yolo_results_folder = None
|
|
77
|
-
|
|
78
|
-
remove_symlink_folder = True
|
|
79
|
-
remove_yolo_results_folder = True
|
|
80
|
-
|
|
81
|
-
# These are deliberately offset from the standard MD categories; YOLOv5
|
|
82
|
-
# needs categories IDs to start at 0.
|
|
83
|
-
yolo_category_id_to_name = {0:'animal',1:'person',2:'vehicle'}
|
|
84
|
-
|
|
85
|
-
# 'error','skip','overwrite'
|
|
86
|
-
overwrite_handling = 'skip'
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
#%% Main function
|
|
90
|
-
|
|
91
|
-
def run_inference_with_yolo_val(options):
|
|
92
|
-
|
|
93
|
-
##%% Path handling
|
|
94
|
-
|
|
95
|
-
assert os.path.isdir(options.input_folder) or os.path.isfile(options.input_folder), \
|
|
96
|
-
'Could not find input {}'.format(options.input_folder)
|
|
97
|
-
assert os.path.isdir(options.yolo_working_folder), \
|
|
98
|
-
'Could not find working folder {}'.format(options.yolo_working_folder)
|
|
99
|
-
assert os.path.isfile(options.model_filename), \
|
|
100
|
-
'Could not find model file {}'.format(options.model_filename)
|
|
101
|
-
|
|
102
|
-
if os.path.exists(options.output_file):
|
|
103
|
-
if options.overwrite_handling == 'skip':
|
|
104
|
-
print('Warning: output file {} exists, skipping'.format(options.output_file))
|
|
105
|
-
return
|
|
106
|
-
elif options.overwrite_handling == 'overwrite':
|
|
107
|
-
print('Warning: output file {} exists, overwriting'.format(options.output_file))
|
|
108
|
-
elif options.overwrite_handling == 'error':
|
|
109
|
-
raise ValueError('Output file {} exists'.format(options.output_file))
|
|
110
|
-
else:
|
|
111
|
-
raise ValueError('Unknown output handling method {}'.format(options.overwrite_handling))
|
|
112
|
-
|
|
113
|
-
os.makedirs(os.path.dirname(options.output_file),exist_ok=True)
|
|
114
|
-
|
|
115
|
-
temporary_folder = None
|
|
116
|
-
symlink_folder_is_temp_folder = False
|
|
117
|
-
yolo_folder_is_temp_folder = False
|
|
118
|
-
|
|
119
|
-
job_id = str(uuid.uuid1())
|
|
120
|
-
|
|
121
|
-
def get_job_temporary_folder(tf):
|
|
122
|
-
if tf is not None:
|
|
123
|
-
return tf
|
|
124
|
-
tempdir_base = tempfile.gettempdir()
|
|
125
|
-
tf = os.path.join(tempdir_base,'md_to_yolo','md_to_yolo_' + job_id)
|
|
126
|
-
os.makedirs(tf,exist_ok=True)
|
|
127
|
-
return tf
|
|
128
|
-
|
|
129
|
-
symlink_folder = options.symlink_folder
|
|
130
|
-
yolo_results_folder = options.yolo_results_folder
|
|
131
|
-
|
|
132
|
-
if symlink_folder is None:
|
|
133
|
-
temporary_folder = get_job_temporary_folder(temporary_folder)
|
|
134
|
-
symlink_folder = os.path.join(temporary_folder,'symlinks')
|
|
135
|
-
symlink_folder_is_temp_folder = True
|
|
136
|
-
|
|
137
|
-
if yolo_results_folder is None:
|
|
138
|
-
temporary_folder = get_job_temporary_folder(temporary_folder)
|
|
139
|
-
yolo_results_folder = os.path.join(temporary_folder,'yolo_results')
|
|
140
|
-
yolo_folder_is_temp_folder = True
|
|
141
|
-
|
|
142
|
-
# Attach a GUID to the symlink folder, regardless of whether we created it
|
|
143
|
-
symlink_folder_inner = os.path.join(symlink_folder,job_id)
|
|
144
|
-
|
|
145
|
-
os.makedirs(symlink_folder_inner,exist_ok=True)
|
|
146
|
-
os.makedirs(yolo_results_folder,exist_ok=True)
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
##%% Enumerate images
|
|
150
|
-
|
|
151
|
-
if os.path.isdir(options.input_folder):
|
|
152
|
-
image_files_absolute = path_utils.find_images(options.input_folder,recursive=True)
|
|
153
|
-
else:
|
|
154
|
-
assert os.path.isfile(options.input_folder)
|
|
155
|
-
with open(options.input_folder,'r') as f:
|
|
156
|
-
image_files_absolute = json.load(f)
|
|
157
|
-
assert isinstance(image_files_absolute,list)
|
|
158
|
-
for fn in image_files_absolute:
|
|
159
|
-
assert os.path.isfile(fn), 'Could not find image file {}'.format(fn)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
##%% Create symlinks to give a unique ID to each image
|
|
163
|
-
|
|
164
|
-
image_id_to_file = {}
|
|
165
|
-
image_id_to_error = {}
|
|
166
|
-
|
|
167
|
-
if options.use_symlinks:
|
|
168
|
-
print('Creating {} symlinks in {}'.format(len(image_files_absolute),symlink_folder_inner))
|
|
169
|
-
else:
|
|
170
|
-
print('Symlinks disabled, copying {} images to {}'.format(len(image_files_absolute),symlink_folder_inner))
|
|
171
|
-
|
|
172
|
-
# i_image = 0; image_fn = image_files_absolute[i_image]
|
|
173
|
-
for i_image,image_fn in tqdm(enumerate(image_files_absolute),total=len(image_files_absolute)):
|
|
174
|
-
|
|
175
|
-
ext = os.path.splitext(image_fn)[1]
|
|
176
|
-
|
|
177
|
-
image_id = str(i_image).zfill(10)
|
|
178
|
-
image_id_to_file[image_id] = image_fn
|
|
179
|
-
symlink_name = image_id + ext
|
|
180
|
-
symlink_full_path = os.path.join(symlink_folder_inner,symlink_name)
|
|
181
|
-
|
|
182
|
-
try:
|
|
183
|
-
if options.use_symlinks:
|
|
184
|
-
path_utils.safe_create_link(image_fn,symlink_full_path)
|
|
185
|
-
else:
|
|
186
|
-
shutil.copyfile(image_fn,symlink_full_path)
|
|
187
|
-
except Exception as e:
|
|
188
|
-
image_id_to_error[image_id] = str(e)
|
|
189
|
-
print('Warning: error copying/creating link for input file {}: {}'.format(
|
|
190
|
-
image_fn,str(e)))
|
|
191
|
-
continue
|
|
192
|
-
|
|
193
|
-
# ...for each image
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
##%% Create the dataset file
|
|
197
|
-
|
|
198
|
-
# Category IDs need to be continuous integers starting at 0
|
|
199
|
-
category_ids = sorted(list(options.yolo_category_id_to_name.keys()))
|
|
200
|
-
assert category_ids[0] == 0
|
|
201
|
-
assert len(category_ids) == 1 + category_ids[-1]
|
|
202
|
-
|
|
203
|
-
dataset_file = os.path.join(yolo_results_folder,'dataset.yaml')
|
|
204
|
-
|
|
205
|
-
with open(dataset_file,'w') as f:
|
|
206
|
-
f.write('path: {}\n'.format(symlink_folder_inner))
|
|
207
|
-
f.write('train: .\n')
|
|
208
|
-
f.write('val: .\n')
|
|
209
|
-
f.write('test: .\n')
|
|
210
|
-
f.write('\n')
|
|
211
|
-
f.write('nc: {}\n'.format(len(options.yolo_category_id_to_name)))
|
|
212
|
-
f.write('\n')
|
|
213
|
-
f.write('names:\n')
|
|
214
|
-
for category_id in category_ids:
|
|
215
|
-
assert isinstance(category_id,int)
|
|
216
|
-
f.write(' {}: {}\n'.format(category_id,
|
|
217
|
-
options.yolo_category_id_to_name[category_id]))
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
##%% Prepare YOLOv5 command
|
|
221
|
-
|
|
222
|
-
image_size_string = str(round(options.image_size))
|
|
223
|
-
cmd = 'python val.py --data "{}"'.format(dataset_file)
|
|
224
|
-
cmd += ' --weights "{}"'.format(options.model_filename)
|
|
225
|
-
cmd += ' --batch-size {} --imgsz {} --conf-thres {} --task test'.format(
|
|
226
|
-
options.batch_size,image_size_string,options.conf_thres)
|
|
227
|
-
cmd += ' --device "{}" --save-json'.format(options.device_string)
|
|
228
|
-
cmd += ' --project "{}" --name "{}" --exist-ok'.format(yolo_results_folder,'yolo_results')
|
|
229
|
-
|
|
230
|
-
if options.augment:
|
|
231
|
-
cmd += ' --augment'
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
##%% Run YOLOv5 command
|
|
235
|
-
|
|
236
|
-
current_dir = os.getcwd()
|
|
237
|
-
os.chdir(options.yolo_working_folder)
|
|
238
|
-
execution_result = process_utils.execute_and_print(cmd)
|
|
239
|
-
assert execution_result['status'] == 0, 'Error running YOLOv5'
|
|
240
|
-
yolo_console_output = execution_result['output']
|
|
241
|
-
|
|
242
|
-
yolo_read_failures = []
|
|
243
|
-
for line in yolo_console_output:
|
|
244
|
-
if 'cannot identify image file' in line:
|
|
245
|
-
tokens = line.split('cannot identify image file')
|
|
246
|
-
image_name = tokens[-1].strip()
|
|
247
|
-
assert image_name[0] == "'" and image_name [-1] == "'"
|
|
248
|
-
image_name = image_name[1:-1]
|
|
249
|
-
yolo_read_failures.append(image_name)
|
|
250
|
-
|
|
251
|
-
# image_file = yolo_read_failures[0]
|
|
252
|
-
for image_file in yolo_read_failures:
|
|
253
|
-
image_id = os.path.splitext(os.path.basename(image_file))[0]
|
|
254
|
-
assert image_id in image_id_to_file
|
|
255
|
-
if image_id not in image_id_to_error:
|
|
256
|
-
image_id_to_error[image_id] = 'YOLOv5 read failure'
|
|
257
|
-
|
|
258
|
-
os.chdir(current_dir)
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
##%% Convert results to MD format
|
|
262
|
-
|
|
263
|
-
json_files = glob.glob(yolo_results_folder+ '/yolo_results/*.json')
|
|
264
|
-
assert len(json_files) == 1
|
|
265
|
-
yolo_json_file = json_files[0]
|
|
266
|
-
|
|
267
|
-
image_id_to_relative_path = {}
|
|
268
|
-
for image_id in image_id_to_file:
|
|
269
|
-
fn = image_id_to_file[image_id]
|
|
270
|
-
if os.path.isdir(options.input_folder):
|
|
271
|
-
assert options.input_folder in fn
|
|
272
|
-
relative_path = os.path.relpath(fn,options.input_folder)
|
|
273
|
-
else:
|
|
274
|
-
assert os.path.isfile(options.input_folder)
|
|
275
|
-
# We'll use the absolute path as a relative path, and pass '/'
|
|
276
|
-
# as the base path in this case.
|
|
277
|
-
relative_path = fn
|
|
278
|
-
image_id_to_relative_path[image_id] = relative_path
|
|
279
|
-
|
|
280
|
-
if os.path.isdir(options.input_folder):
|
|
281
|
-
image_base = options.input_folder
|
|
282
|
-
else:
|
|
283
|
-
assert os.path.isfile(options.input_folder)
|
|
284
|
-
image_base = '/'
|
|
285
|
-
|
|
286
|
-
yolo_output_to_md_output.yolo_json_output_to_md_output(
|
|
287
|
-
yolo_json_file=yolo_json_file,
|
|
288
|
-
image_folder=image_base,
|
|
289
|
-
output_file=options.output_file,
|
|
290
|
-
yolo_category_id_to_name=options.yolo_category_id_to_name,
|
|
291
|
-
detector_name=os.path.basename(options.model_filename),
|
|
292
|
-
image_id_to_relative_path=image_id_to_relative_path,
|
|
293
|
-
image_id_to_error=image_id_to_error)
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
##%% Clean up
|
|
297
|
-
|
|
298
|
-
if options.remove_symlink_folder:
|
|
299
|
-
shutil.rmtree(symlink_folder)
|
|
300
|
-
elif symlink_folder_is_temp_folder:
|
|
301
|
-
print('Warning: using temporary symlink folder {}, but not removing it'.format(
|
|
302
|
-
symlink_folder))
|
|
303
|
-
|
|
304
|
-
if options.remove_yolo_results_folder:
|
|
305
|
-
shutil.rmtree(yolo_results_folder)
|
|
306
|
-
elif yolo_folder_is_temp_folder:
|
|
307
|
-
print('Warning: using temporary YOLO results folder {}, but not removing it'.format(
|
|
308
|
-
yolo_results_folder))
|
|
309
|
-
|
|
310
|
-
# ...def run_inference_with_yolo_val()
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
#%% Command-line driver
|
|
314
|
-
|
|
315
|
-
import argparse,sys
|
|
316
|
-
from md_utils.ct_utils import args_to_object
|
|
317
|
-
|
|
318
|
-
def main():
|
|
319
|
-
|
|
320
|
-
options = YoloInferenceOptions()
|
|
321
|
-
|
|
322
|
-
parser = argparse.ArgumentParser()
|
|
323
|
-
parser.add_argument(
|
|
324
|
-
'model_filename',type=str,
|
|
325
|
-
help='model file name')
|
|
326
|
-
parser.add_argument(
|
|
327
|
-
'input_folder',type=str,
|
|
328
|
-
help='folder on which to recursively run the model, or a .json list of filenames')
|
|
329
|
-
parser.add_argument(
|
|
330
|
-
'output_file',type=str,
|
|
331
|
-
help='.json file where output will be written')
|
|
332
|
-
parser.add_argument(
|
|
333
|
-
'yolo_working_folder',type=str,
|
|
334
|
-
help='folder in which to execute val.py')
|
|
335
|
-
|
|
336
|
-
parser.add_argument(
|
|
337
|
-
'--image_size', default=options.image_size, type=int,
|
|
338
|
-
help='image size for model execution (default {})'.format(
|
|
339
|
-
options.image_size))
|
|
340
|
-
parser.add_argument(
|
|
341
|
-
'--conf_thres', default=options.conf_thres, type=float,
|
|
342
|
-
help='confidence threshold for including detections in the output file (default {})'.format(
|
|
343
|
-
options.conf_thres))
|
|
344
|
-
parser.add_argument(
|
|
345
|
-
'--batch_size', default=options.batch_size, type=int,
|
|
346
|
-
help='inference batch size (default {})'.format(options.batch_size))
|
|
347
|
-
parser.add_argument(
|
|
348
|
-
'--device_string', default=options.device_string, type=str,
|
|
349
|
-
help='CUDA device specifier, e.g. "0" or "cpu" (default {})'.format(options.device_string))
|
|
350
|
-
parser.add_argument(
|
|
351
|
-
'--overwrite_handling', default=options.overwrite_handling, type=str,
|
|
352
|
-
help='action to take if the output file exists (skip, error, overwrite) (default {})'.format(
|
|
353
|
-
options.overwrite_handling) )
|
|
354
|
-
|
|
355
|
-
parser.add_argument(
|
|
356
|
-
'--symlink_folder', type=str,
|
|
357
|
-
help='temporary folder for symlinks (defaults to a folder in the system temp dir)')
|
|
358
|
-
parser.add_argument(
|
|
359
|
-
'--yolo_results_folder', type=str,
|
|
360
|
-
help='temporary folder for YOLO intermediate output (defaults to a folder in the system temp dir)')
|
|
361
|
-
parser.add_argument(
|
|
362
|
-
'--no_use_symlinks', action='store_true',
|
|
363
|
-
help='copy files instead of creating symlinks when preparing the yolo input folder')
|
|
364
|
-
parser.add_argument(
|
|
365
|
-
'--no_remove_symlink_folder', action='store_true',
|
|
366
|
-
help='don\'t remove the temporary folder full of symlinks')
|
|
367
|
-
parser.add_argument(
|
|
368
|
-
'--no_remove_yolo_results_folder', action='store_true',
|
|
369
|
-
help='don\'t remove the temporary folder full of YOLO intermediate files')
|
|
370
|
-
|
|
371
|
-
if options.augment:
|
|
372
|
-
default_augment_enabled = 1
|
|
373
|
-
else:
|
|
374
|
-
default_augment_enabled = 0
|
|
375
|
-
parser.add_argument(
|
|
376
|
-
'--augment_enabled', default=default_augment_enabled, type=int,
|
|
377
|
-
help='enable/disable augmentation (default {})'.format(default_augment_enabled))
|
|
378
|
-
|
|
379
|
-
if len(sys.argv[1:]) == 0:
|
|
380
|
-
parser.print_help()
|
|
381
|
-
parser.exit()
|
|
382
|
-
|
|
383
|
-
args = parser.parse_args()
|
|
384
|
-
|
|
385
|
-
args_to_object(args, options)
|
|
386
|
-
options.remove_symlink_folder = (not options.no_remove_symlink_folder)
|
|
387
|
-
options.remove_yolo_results_folder = (not options.no_remove_yolo_results_folder)
|
|
388
|
-
options.use_symlinks = (not options.no_use_symlinks)
|
|
389
|
-
options.augment = (options.augment_enabled > 0)
|
|
390
|
-
|
|
391
|
-
print(options.__dict__)
|
|
392
|
-
|
|
393
|
-
run_inference_with_yolo_val(options)
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
if __name__ == '__main__':
|
|
397
|
-
main()
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
#%% Scrap
|
|
401
|
-
|
|
402
|
-
if False:
|
|
403
|
-
|
|
404
|
-
#%% Run from a set of options
|
|
405
|
-
|
|
406
|
-
options = YoloInferenceOptions()
|
|
407
|
-
|
|
408
|
-
args = {'augment': 1, 'batch_size': 1, 'conf_thres': 0.005, 'device_string': '1', 'image_size': 1664.0, 'input_folder': '/home/user/postprocessing/usgs-kissel/usgs-kissel-2023-09-11-aug-v5a.0.0/chunk031.json', 'model_filename': '/home/user/models/camera_traps/megadetector/md_v5.0.0/md_v5a.0.0.pt', 'output_file': '/home/user/postprocessing/usgs-kissel/usgs-kissel-2023-09-11-aug-v5a.0.0/chunk031_results.json', 'overwrite_handling': 'skip', 'symlink_folder': '/home/user/postprocessing/usgs-kissel/usgs-kissel-2023-09-11-aug-v5a.0.0/symlinks/symlinks_031', 'yolo_results_folder': '/home/user/postprocessing/usgs-kissel/usgs-kissel-2023-09-11-aug-v5a.0.0/yolo_results/yolo_results_031', 'yolo_working_folder': '/home/user/git/yolov5', 'remove_symlink_folder': True, 'remove_yolo_results_folder': True, 'use_symlinks': False, 'augment': True}
|
|
409
|
-
|
|
410
|
-
for k in args:
|
|
411
|
-
setattr(options, k, args[k])
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
#%% Test driver (folder)
|
|
415
|
-
|
|
416
|
-
project_name = 'KRU-test-corrupted'
|
|
417
|
-
input_folder = os.path.expanduser(f'~/data/{project_name}')
|
|
418
|
-
output_folder = os.path.expanduser(f'~/tmp/{project_name}')
|
|
419
|
-
model_filename = os.path.expanduser('~/models/camera_traps/megadetector/md_v5.0.0/md_v5a.0.0.pt')
|
|
420
|
-
yolo_working_folder = os.path.expanduser('~/git/yolov5')
|
|
421
|
-
model_name = os.path.splitext(os.path.basename(model_filename))[0]
|
|
422
|
-
|
|
423
|
-
symlink_folder = os.path.join(output_folder,'symlinks')
|
|
424
|
-
yolo_results_folder = os.path.join(output_folder,'yolo_results')
|
|
425
|
-
|
|
426
|
-
output_file = os.path.join(output_folder,'{}_{}-md_format.json'.format(
|
|
427
|
-
project_name,model_name))
|
|
428
|
-
|
|
429
|
-
options = YoloInferenceOptions()
|
|
430
|
-
|
|
431
|
-
options.yolo_working_folder = yolo_working_folder
|
|
432
|
-
|
|
433
|
-
options.output_file = output_file
|
|
434
|
-
|
|
435
|
-
options.augment = False
|
|
436
|
-
options.conf_thres = '0.001'
|
|
437
|
-
options.batch_size = 1
|
|
438
|
-
options.device_string = '0'
|
|
439
|
-
|
|
440
|
-
if options.augment:
|
|
441
|
-
options.image_size = round(1280 * 1.3)
|
|
442
|
-
else:
|
|
443
|
-
options.image_size = 1280
|
|
444
|
-
|
|
445
|
-
options.input_folder = input_folder
|
|
446
|
-
options.model_filename = model_filename
|
|
447
|
-
|
|
448
|
-
options.yolo_results_folder = yolo_results_folder # os.path.join(output_folder + 'yolo_results')
|
|
449
|
-
options.symlink_folder = symlink_folder # os.path.join(output_folder,'symlinks')
|
|
450
|
-
options.use_symlinks = False
|
|
451
|
-
|
|
452
|
-
options.remove_temporary_symlink_folder = False
|
|
453
|
-
options.remove_yolo_results_file = False
|
|
454
|
-
|
|
455
|
-
cmd = f'python run_inference_with_yolov5_val.py {model_filename} {input_folder} {output_file} {yolo_working_folder} ' + \
|
|
456
|
-
f' --image_size {options.image_size} --conf_thres {options.conf_thres} --batch_size {options.batch_size} ' + \
|
|
457
|
-
f' --symlink_folder {options.symlink_folder} --yolo_results_folder {options.yolo_results_folder} ' + \
|
|
458
|
-
' --no_remove_symlink_folder --no_remove_yolo_results_folder'
|
|
459
|
-
|
|
460
|
-
if not options.use_symlinks:
|
|
461
|
-
cmd += ' --no_use_symlinks'
|
|
462
|
-
if not options.augment:
|
|
463
|
-
cmd += ' --augment_enabled 0'
|
|
464
|
-
|
|
465
|
-
print(cmd)
|
|
466
|
-
execute_in_python = False
|
|
467
|
-
if execute_in_python:
|
|
468
|
-
run_inference_with_yolo_val(options)
|
|
469
|
-
else:
|
|
470
|
-
import clipboard; clipboard.copy(cmd)
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
#%% Preview results
|
|
474
|
-
|
|
475
|
-
postprocessing_output_folder = os.path.join(output_folder,'yolo-val-preview')
|
|
476
|
-
md_json_file = options.output_file
|
|
477
|
-
|
|
478
|
-
from api.batch_processing.postprocessing.postprocess_batch_results import (
|
|
479
|
-
PostProcessingOptions, process_batch_results)
|
|
480
|
-
|
|
481
|
-
with open(md_json_file,'r') as f:
|
|
482
|
-
d = json.load(f)
|
|
483
|
-
|
|
484
|
-
base_task_name = os.path.basename(md_json_file)
|
|
485
|
-
|
|
486
|
-
pp_options = PostProcessingOptions()
|
|
487
|
-
pp_options.image_base_dir = input_folder
|
|
488
|
-
pp_options.include_almost_detections = True
|
|
489
|
-
pp_options.num_images_to_sample = None
|
|
490
|
-
pp_options.confidence_threshold = 0.1
|
|
491
|
-
pp_options.almost_detection_confidence_threshold = pp_options.confidence_threshold - 0.025
|
|
492
|
-
pp_options.ground_truth_json_file = None
|
|
493
|
-
pp_options.separate_detections_by_category = True
|
|
494
|
-
# pp_options.sample_seed = 0
|
|
495
|
-
|
|
496
|
-
pp_options.parallelize_rendering = True
|
|
497
|
-
pp_options.parallelize_rendering_n_cores = 16
|
|
498
|
-
pp_options.parallelize_rendering_with_threads = False
|
|
499
|
-
|
|
500
|
-
output_base = os.path.join(postprocessing_output_folder,
|
|
501
|
-
base_task_name + '_{:.3f}'.format(pp_options.confidence_threshold))
|
|
502
|
-
|
|
503
|
-
os.makedirs(output_base, exist_ok=True)
|
|
504
|
-
print('Processing to {}'.format(output_base))
|
|
505
|
-
|
|
506
|
-
pp_options.api_output_file = md_json_file
|
|
507
|
-
pp_options.output_dir = output_base
|
|
508
|
-
ppresults = process_batch_results(pp_options)
|
|
509
|
-
html_output_file = ppresults.output_html_file
|
|
510
|
-
|
|
511
|
-
path_utils.open_file(html_output_file)
|
|
512
|
-
|
|
513
|
-
# ...for each prediction file
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
#%% Compare results
|
|
517
|
-
|
|
518
|
-
import itertools
|
|
519
|
-
|
|
520
|
-
from api.batch_processing.postprocessing.compare_batch_results import (
|
|
521
|
-
BatchComparisonOptions,PairwiseBatchComparisonOptions,compare_batch_results)
|
|
522
|
-
|
|
523
|
-
options = BatchComparisonOptions()
|
|
524
|
-
|
|
525
|
-
organization_name = ''
|
|
526
|
-
project_name = ''
|
|
527
|
-
|
|
528
|
-
options.job_name = f'{organization_name}-comparison'
|
|
529
|
-
options.output_folder = os.path.join(output_folder,'model_comparison')
|
|
530
|
-
options.image_folder = input_folder
|
|
531
|
-
|
|
532
|
-
options.pairwise_options = []
|
|
533
|
-
|
|
534
|
-
filenames = [
|
|
535
|
-
f'/home/user/tmp/{project_name}/{project_name}_md_v5a.0.0-md_format.json',
|
|
536
|
-
f'/home/user/postprocessing/{organization_name}/{organization_name}-2023-04-06-v5a.0.0/combined_api_outputs/{organization_name}-2023-04-06-v5a.0.0_detections.json',
|
|
537
|
-
f'/home/user/postprocessing/{organization_name}/{organization_name}-2023-04-06-v5b.0.0/combined_api_outputs/{organization_name}-2023-04-06-v5b.0.0_detections.json'
|
|
538
|
-
]
|
|
539
|
-
|
|
540
|
-
descriptions = ['YOLO w/augment','MDv5a','MDv5b']
|
|
541
|
-
|
|
542
|
-
if False:
|
|
543
|
-
results = []
|
|
544
|
-
|
|
545
|
-
for fn in filenames:
|
|
546
|
-
with open(fn,'r') as f:
|
|
547
|
-
d = json.load(f)
|
|
548
|
-
results.append(d)
|
|
549
|
-
|
|
550
|
-
detection_thresholds = [0.1,0.1,0.1]
|
|
551
|
-
|
|
552
|
-
assert len(detection_thresholds) == len(filenames)
|
|
553
|
-
|
|
554
|
-
rendering_thresholds = [(x*0.6666) for x in detection_thresholds]
|
|
555
|
-
|
|
556
|
-
# Choose all pairwise combinations of the files in [filenames]
|
|
557
|
-
for i, j in itertools.combinations(list(range(0,len(filenames))),2):
|
|
558
|
-
|
|
559
|
-
pairwise_options = PairwiseBatchComparisonOptions()
|
|
560
|
-
|
|
561
|
-
pairwise_options.results_filename_a = filenames[i]
|
|
562
|
-
pairwise_options.results_filename_b = filenames[j]
|
|
563
|
-
|
|
564
|
-
pairwise_options.results_description_a = descriptions[i]
|
|
565
|
-
pairwise_options.results_description_b = descriptions[j]
|
|
566
|
-
|
|
567
|
-
pairwise_options.rendering_confidence_threshold_a = rendering_thresholds[i]
|
|
568
|
-
pairwise_options.rendering_confidence_threshold_b = rendering_thresholds[j]
|
|
569
|
-
|
|
570
|
-
pairwise_options.detection_thresholds_a = {'animal':detection_thresholds[i],
|
|
571
|
-
'person':detection_thresholds[i],
|
|
572
|
-
'vehicle':detection_thresholds[i]}
|
|
573
|
-
pairwise_options.detection_thresholds_b = {'animal':detection_thresholds[j],
|
|
574
|
-
'person':detection_thresholds[j],
|
|
575
|
-
'vehicle':detection_thresholds[j]}
|
|
576
|
-
options.pairwise_options.append(pairwise_options)
|
|
577
|
-
|
|
578
|
-
results = compare_batch_results(options)
|
|
579
|
-
|
|
580
|
-
from md_utils.path_utils import open_file
|
|
581
|
-
open_file(results.html_output_file)
|