megadetector 5.0.28__py3-none-any.whl → 10.0.0__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/api/batch_processing/integration/digiKam/xmp_integration.py +2 -2
- megadetector/api/batch_processing/integration/eMammal/test_scripts/push_annotations_to_emammal.py +1 -1
- megadetector/api/batch_processing/integration/eMammal/test_scripts/select_images_for_testing.py +1 -1
- megadetector/classification/aggregate_classifier_probs.py +3 -3
- megadetector/classification/analyze_failed_images.py +5 -5
- megadetector/classification/cache_batchapi_outputs.py +5 -5
- megadetector/classification/create_classification_dataset.py +11 -12
- megadetector/classification/crop_detections.py +10 -10
- megadetector/classification/csv_to_json.py +8 -8
- megadetector/classification/detect_and_crop.py +13 -15
- megadetector/classification/efficientnet/model.py +8 -8
- megadetector/classification/efficientnet/utils.py +6 -5
- megadetector/classification/evaluate_model.py +7 -7
- megadetector/classification/identify_mislabeled_candidates.py +6 -6
- megadetector/classification/json_to_azcopy_list.py +1 -1
- megadetector/classification/json_validator.py +29 -32
- megadetector/classification/map_classification_categories.py +9 -9
- megadetector/classification/merge_classification_detection_output.py +12 -9
- megadetector/classification/prepare_classification_script.py +19 -19
- megadetector/classification/prepare_classification_script_mc.py +26 -26
- megadetector/classification/run_classifier.py +4 -4
- megadetector/classification/save_mislabeled.py +6 -6
- megadetector/classification/train_classifier.py +1 -1
- megadetector/classification/train_classifier_tf.py +9 -9
- megadetector/classification/train_utils.py +10 -10
- megadetector/data_management/annotations/annotation_constants.py +1 -2
- megadetector/data_management/camtrap_dp_to_coco.py +79 -46
- megadetector/data_management/cct_json_utils.py +103 -103
- megadetector/data_management/cct_to_md.py +49 -49
- megadetector/data_management/cct_to_wi.py +33 -33
- megadetector/data_management/coco_to_labelme.py +75 -75
- megadetector/data_management/coco_to_yolo.py +210 -193
- megadetector/data_management/databases/add_width_and_height_to_db.py +86 -12
- megadetector/data_management/databases/combine_coco_camera_traps_files.py +40 -40
- megadetector/data_management/databases/integrity_check_json_db.py +228 -200
- megadetector/data_management/databases/subset_json_db.py +33 -33
- megadetector/data_management/generate_crops_from_cct.py +88 -39
- megadetector/data_management/get_image_sizes.py +54 -49
- megadetector/data_management/labelme_to_coco.py +133 -125
- megadetector/data_management/labelme_to_yolo.py +159 -73
- megadetector/data_management/lila/create_lila_blank_set.py +81 -83
- megadetector/data_management/lila/create_lila_test_set.py +32 -31
- megadetector/data_management/lila/create_links_to_md_results_files.py +18 -18
- megadetector/data_management/lila/download_lila_subset.py +21 -24
- megadetector/data_management/lila/generate_lila_per_image_labels.py +365 -107
- megadetector/data_management/lila/get_lila_annotation_counts.py +35 -33
- megadetector/data_management/lila/get_lila_image_counts.py +22 -22
- megadetector/data_management/lila/lila_common.py +73 -70
- megadetector/data_management/lila/test_lila_metadata_urls.py +28 -19
- megadetector/data_management/mewc_to_md.py +344 -340
- megadetector/data_management/ocr_tools.py +262 -255
- megadetector/data_management/read_exif.py +249 -227
- megadetector/data_management/remap_coco_categories.py +90 -28
- megadetector/data_management/remove_exif.py +81 -21
- megadetector/data_management/rename_images.py +187 -187
- megadetector/data_management/resize_coco_dataset.py +588 -120
- megadetector/data_management/speciesnet_to_md.py +41 -41
- megadetector/data_management/wi_download_csv_to_coco.py +55 -55
- megadetector/data_management/yolo_output_to_md_output.py +248 -122
- megadetector/data_management/yolo_to_coco.py +333 -191
- megadetector/detection/change_detection.py +832 -0
- megadetector/detection/process_video.py +340 -337
- megadetector/detection/pytorch_detector.py +358 -278
- megadetector/detection/run_detector.py +399 -186
- megadetector/detection/run_detector_batch.py +404 -377
- megadetector/detection/run_inference_with_yolov5_val.py +340 -327
- megadetector/detection/run_tiled_inference.py +257 -249
- megadetector/detection/tf_detector.py +24 -24
- megadetector/detection/video_utils.py +332 -295
- megadetector/postprocessing/add_max_conf.py +19 -11
- megadetector/postprocessing/categorize_detections_by_size.py +45 -45
- megadetector/postprocessing/classification_postprocessing.py +468 -433
- megadetector/postprocessing/combine_batch_outputs.py +23 -23
- megadetector/postprocessing/compare_batch_results.py +590 -525
- megadetector/postprocessing/convert_output_format.py +106 -102
- megadetector/postprocessing/create_crop_folder.py +347 -147
- megadetector/postprocessing/detector_calibration.py +173 -168
- megadetector/postprocessing/generate_csv_report.py +508 -499
- megadetector/postprocessing/load_api_results.py +48 -27
- megadetector/postprocessing/md_to_coco.py +133 -102
- megadetector/postprocessing/md_to_labelme.py +107 -90
- megadetector/postprocessing/md_to_wi.py +40 -40
- megadetector/postprocessing/merge_detections.py +92 -114
- megadetector/postprocessing/postprocess_batch_results.py +319 -301
- megadetector/postprocessing/remap_detection_categories.py +91 -38
- megadetector/postprocessing/render_detection_confusion_matrix.py +214 -205
- megadetector/postprocessing/repeat_detection_elimination/find_repeat_detections.py +57 -57
- megadetector/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +27 -28
- megadetector/postprocessing/repeat_detection_elimination/repeat_detections_core.py +704 -679
- megadetector/postprocessing/separate_detections_into_folders.py +226 -211
- megadetector/postprocessing/subset_json_detector_output.py +265 -262
- megadetector/postprocessing/top_folders_to_bottom.py +45 -45
- megadetector/postprocessing/validate_batch_results.py +70 -70
- megadetector/taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +52 -52
- megadetector/taxonomy_mapping/map_new_lila_datasets.py +18 -19
- megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +54 -33
- megadetector/taxonomy_mapping/preview_lila_taxonomy.py +67 -67
- megadetector/taxonomy_mapping/retrieve_sample_image.py +16 -16
- megadetector/taxonomy_mapping/simple_image_download.py +8 -8
- megadetector/taxonomy_mapping/species_lookup.py +156 -74
- megadetector/taxonomy_mapping/taxonomy_csv_checker.py +14 -14
- megadetector/taxonomy_mapping/taxonomy_graph.py +10 -10
- megadetector/taxonomy_mapping/validate_lila_category_mappings.py +13 -13
- megadetector/utils/ct_utils.py +1049 -211
- megadetector/utils/directory_listing.py +21 -77
- megadetector/utils/gpu_test.py +22 -22
- megadetector/utils/md_tests.py +632 -529
- megadetector/utils/path_utils.py +1520 -431
- megadetector/utils/process_utils.py +41 -41
- megadetector/utils/split_locations_into_train_val.py +62 -62
- megadetector/utils/string_utils.py +148 -27
- megadetector/utils/url_utils.py +489 -176
- megadetector/utils/wi_utils.py +2658 -2526
- megadetector/utils/write_html_image_list.py +137 -137
- megadetector/visualization/plot_utils.py +34 -30
- megadetector/visualization/render_images_with_thumbnails.py +39 -74
- megadetector/visualization/visualization_utils.py +487 -435
- megadetector/visualization/visualize_db.py +232 -198
- megadetector/visualization/visualize_detector_output.py +82 -76
- {megadetector-5.0.28.dist-info → megadetector-10.0.0.dist-info}/METADATA +5 -2
- megadetector-10.0.0.dist-info/RECORD +139 -0
- {megadetector-5.0.28.dist-info → megadetector-10.0.0.dist-info}/WHEEL +1 -1
- megadetector/api/batch_processing/api_core/__init__.py +0 -0
- megadetector/api/batch_processing/api_core/batch_service/__init__.py +0 -0
- megadetector/api/batch_processing/api_core/batch_service/score.py +0 -439
- megadetector/api/batch_processing/api_core/server.py +0 -294
- megadetector/api/batch_processing/api_core/server_api_config.py +0 -97
- megadetector/api/batch_processing/api_core/server_app_config.py +0 -55
- megadetector/api/batch_processing/api_core/server_batch_job_manager.py +0 -220
- megadetector/api/batch_processing/api_core/server_job_status_table.py +0 -149
- megadetector/api/batch_processing/api_core/server_orchestration.py +0 -360
- megadetector/api/batch_processing/api_core/server_utils.py +0 -88
- megadetector/api/batch_processing/api_core_support/__init__.py +0 -0
- megadetector/api/batch_processing/api_core_support/aggregate_results_manually.py +0 -46
- megadetector/api/batch_processing/api_support/__init__.py +0 -0
- megadetector/api/batch_processing/api_support/summarize_daily_activity.py +0 -152
- megadetector/api/batch_processing/data_preparation/__init__.py +0 -0
- megadetector/api/synchronous/__init__.py +0 -0
- megadetector/api/synchronous/api_core/animal_detection_api/__init__.py +0 -0
- megadetector/api/synchronous/api_core/animal_detection_api/api_backend.py +0 -151
- megadetector/api/synchronous/api_core/animal_detection_api/api_frontend.py +0 -263
- megadetector/api/synchronous/api_core/animal_detection_api/config.py +0 -35
- megadetector/api/synchronous/api_core/tests/__init__.py +0 -0
- megadetector/api/synchronous/api_core/tests/load_test.py +0 -110
- megadetector/data_management/importers/add_nacti_sizes.py +0 -52
- megadetector/data_management/importers/add_timestamps_to_icct.py +0 -79
- megadetector/data_management/importers/animl_results_to_md_results.py +0 -158
- megadetector/data_management/importers/auckland_doc_test_to_json.py +0 -373
- megadetector/data_management/importers/auckland_doc_to_json.py +0 -201
- megadetector/data_management/importers/awc_to_json.py +0 -191
- megadetector/data_management/importers/bellevue_to_json.py +0 -272
- megadetector/data_management/importers/cacophony-thermal-importer.py +0 -793
- megadetector/data_management/importers/carrizo_shrubfree_2018.py +0 -269
- megadetector/data_management/importers/carrizo_trail_cam_2017.py +0 -289
- megadetector/data_management/importers/cct_field_adjustments.py +0 -58
- megadetector/data_management/importers/channel_islands_to_cct.py +0 -913
- megadetector/data_management/importers/eMammal/copy_and_unzip_emammal.py +0 -180
- megadetector/data_management/importers/eMammal/eMammal_helpers.py +0 -249
- megadetector/data_management/importers/eMammal/make_eMammal_json.py +0 -223
- megadetector/data_management/importers/ena24_to_json.py +0 -276
- megadetector/data_management/importers/filenames_to_json.py +0 -386
- megadetector/data_management/importers/helena_to_cct.py +0 -283
- megadetector/data_management/importers/idaho-camera-traps.py +0 -1407
- megadetector/data_management/importers/idfg_iwildcam_lila_prep.py +0 -294
- megadetector/data_management/importers/import_desert_lion_conservation_camera_traps.py +0 -387
- megadetector/data_management/importers/jb_csv_to_json.py +0 -150
- megadetector/data_management/importers/mcgill_to_json.py +0 -250
- megadetector/data_management/importers/missouri_to_json.py +0 -490
- megadetector/data_management/importers/nacti_fieldname_adjustments.py +0 -79
- megadetector/data_management/importers/noaa_seals_2019.py +0 -181
- megadetector/data_management/importers/osu-small-animals-to-json.py +0 -364
- megadetector/data_management/importers/pc_to_json.py +0 -365
- megadetector/data_management/importers/plot_wni_giraffes.py +0 -123
- megadetector/data_management/importers/prepare_zsl_imerit.py +0 -131
- megadetector/data_management/importers/raic_csv_to_md_results.py +0 -416
- megadetector/data_management/importers/rspb_to_json.py +0 -356
- megadetector/data_management/importers/save_the_elephants_survey_A.py +0 -320
- megadetector/data_management/importers/save_the_elephants_survey_B.py +0 -329
- megadetector/data_management/importers/snapshot_safari_importer.py +0 -758
- megadetector/data_management/importers/snapshot_serengeti_lila.py +0 -1067
- megadetector/data_management/importers/snapshotserengeti/make_full_SS_json.py +0 -150
- megadetector/data_management/importers/snapshotserengeti/make_per_season_SS_json.py +0 -153
- megadetector/data_management/importers/sulross_get_exif.py +0 -65
- megadetector/data_management/importers/timelapse_csv_set_to_json.py +0 -490
- megadetector/data_management/importers/ubc_to_json.py +0 -399
- megadetector/data_management/importers/umn_to_json.py +0 -507
- megadetector/data_management/importers/wellington_to_json.py +0 -263
- megadetector/data_management/importers/wi_to_json.py +0 -442
- megadetector/data_management/importers/zamba_results_to_md_results.py +0 -180
- megadetector/data_management/lila/add_locations_to_island_camera_traps.py +0 -101
- megadetector/data_management/lila/add_locations_to_nacti.py +0 -151
- megadetector/utils/azure_utils.py +0 -178
- megadetector/utils/sas_blob_utils.py +0 -509
- megadetector-5.0.28.dist-info/RECORD +0 -209
- /megadetector/{api/batch_processing/__init__.py → __init__.py} +0 -0
- {megadetector-5.0.28.dist-info → megadetector-10.0.0.dist-info}/licenses/LICENSE +0 -0
- {megadetector-5.0.28.dist-info → megadetector-10.0.0.dist-info}/top_level.txt +0 -0
|
@@ -1,187 +1,187 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
rename_images.py
|
|
4
|
-
|
|
5
|
-
Copies images from a possibly-nested folder structure to a flat folder structure, including EXIF
|
|
6
|
-
timestamps in each filename. Loosely equivalent to camtrapR's imageRename() function.
|
|
7
|
-
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
#%% Imports and constants
|
|
11
|
-
|
|
12
|
-
import os
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
from megadetector.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
read_exif_options.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
read_exif_options.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
def main():
|
|
157
|
-
|
|
158
|
-
parser = argparse.ArgumentParser(
|
|
159
|
-
description='Copies images from a possibly-nested folder structure to a flat folder structure, ' + \
|
|
160
|
-
'adding datetime information from EXIF to each filename')
|
|
161
|
-
|
|
162
|
-
parser.add_argument(
|
|
163
|
-
'input_folder',
|
|
164
|
-
type=str,
|
|
165
|
-
help='The folder to search for images, always recursive')
|
|
166
|
-
|
|
167
|
-
parser.add_argument(
|
|
168
|
-
'output_folder',
|
|
169
|
-
type=str,
|
|
170
|
-
help='The folder to which we should write the flattened image structure')
|
|
171
|
-
|
|
172
|
-
parser.add_argument(
|
|
173
|
-
'--dry_run',
|
|
174
|
-
action='store_true',
|
|
175
|
-
help="Only map images, don't actually copy")
|
|
176
|
-
|
|
177
|
-
if len(sys.argv[1:]) == 0:
|
|
178
|
-
parser.print_help()
|
|
179
|
-
parser.exit()
|
|
180
|
-
|
|
181
|
-
args = parser.parse_args()
|
|
182
|
-
|
|
183
|
-
rename_images(args.input_folder,args.output_folder,dry_run=args.dry_run,
|
|
184
|
-
verbose=True,read_exif_options=None)
|
|
185
|
-
|
|
186
|
-
if __name__ == '__main__':
|
|
187
|
-
main()
|
|
1
|
+
"""
|
|
2
|
+
|
|
3
|
+
rename_images.py
|
|
4
|
+
|
|
5
|
+
Copies images from a possibly-nested folder structure to a flat folder structure, including EXIF
|
|
6
|
+
timestamps in each filename. Loosely equivalent to camtrapR's imageRename() function.
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
#%% Imports and constants
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
import argparse
|
|
15
|
+
|
|
16
|
+
from megadetector.utils.path_utils import \
|
|
17
|
+
find_images, insert_before_extension, parallel_copy_files
|
|
18
|
+
from megadetector.data_management.read_exif import \
|
|
19
|
+
ReadExifOptions, read_exif_from_folder
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
#%% Functions
|
|
23
|
+
|
|
24
|
+
def rename_images(input_folder,
|
|
25
|
+
output_folder,
|
|
26
|
+
dry_run=False,
|
|
27
|
+
verbose=False,
|
|
28
|
+
read_exif_options=None,
|
|
29
|
+
n_copy_workers=8):
|
|
30
|
+
"""
|
|
31
|
+
For the given image struct in COCO format and associated list of annotations, reformats the
|
|
32
|
+
detections into labelme format.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
input_folder (str): the folder to search for images, always recursive
|
|
36
|
+
output_folder (str): the folder to which we will copy images; cannot be the
|
|
37
|
+
same as [input_folder]
|
|
38
|
+
dry_run (bool, optional): only map images, don't actually copy
|
|
39
|
+
verbose (bool, optional): enable additional debug output
|
|
40
|
+
read_exif_options (ReadExifOptions, optional): parameters controlling the reading of
|
|
41
|
+
EXIF information
|
|
42
|
+
n_copy_workers (int, optional): number of parallel threads to use for copying
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
dict: a dict mapping relative filenames in the input folder to relative filenames in the output
|
|
46
|
+
folder
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
assert os.path.isdir(input_folder), 'Input folder {} does not exist'.format(
|
|
50
|
+
input_folder)
|
|
51
|
+
|
|
52
|
+
if not dry_run:
|
|
53
|
+
os.makedirs(output_folder,exist_ok=True)
|
|
54
|
+
|
|
55
|
+
# Read exif information
|
|
56
|
+
if read_exif_options is None:
|
|
57
|
+
read_exif_options = ReadExifOptions()
|
|
58
|
+
|
|
59
|
+
read_exif_options.tags_to_include = ['DateTime','Model','Make','ExifImageWidth','ExifImageHeight','DateTime',
|
|
60
|
+
'DateTimeOriginal']
|
|
61
|
+
read_exif_options.verbose = False
|
|
62
|
+
|
|
63
|
+
exif_info = read_exif_from_folder(input_folder=input_folder,
|
|
64
|
+
output_file=None,
|
|
65
|
+
options=read_exif_options,
|
|
66
|
+
filenames=None,recursive=True)
|
|
67
|
+
|
|
68
|
+
print('Read EXIF information for {} images'.format(len(exif_info)))
|
|
69
|
+
|
|
70
|
+
filename_to_exif_info = {info['file_name']:info for info in exif_info}
|
|
71
|
+
|
|
72
|
+
image_files = find_images(input_folder,return_relative_paths=True,convert_slashes=True,recursive=True)
|
|
73
|
+
|
|
74
|
+
for fn in image_files:
|
|
75
|
+
assert fn in filename_to_exif_info, 'No EXIF info available for {}'.format(fn)
|
|
76
|
+
|
|
77
|
+
input_fn_relative_to_output_fn_relative = {}
|
|
78
|
+
|
|
79
|
+
# fn_relative = image_files[0]
|
|
80
|
+
for fn_relative in image_files:
|
|
81
|
+
|
|
82
|
+
input_fn_abs = os.path.join(input_folder,fn_relative)
|
|
83
|
+
image_exif_info = filename_to_exif_info[fn_relative]
|
|
84
|
+
if 'exif_tags' in image_exif_info:
|
|
85
|
+
image_exif_info = image_exif_info['exif_tags']
|
|
86
|
+
|
|
87
|
+
if image_exif_info is None or \
|
|
88
|
+
'DateTimeOriginal' not in image_exif_info or \
|
|
89
|
+
image_exif_info['DateTimeOriginal'] is None:
|
|
90
|
+
|
|
91
|
+
dt_tag = 'unknown_datetime'
|
|
92
|
+
print('Warning: no datetime for {}'.format(fn_relative))
|
|
93
|
+
|
|
94
|
+
else:
|
|
95
|
+
|
|
96
|
+
dt_tag = str(image_exif_info['DateTimeOriginal']).replace(':','-').replace(' ','_').strip()
|
|
97
|
+
|
|
98
|
+
flat_filename = fn_relative.replace('\\','/').replace('/','_')
|
|
99
|
+
|
|
100
|
+
output_fn_relative = insert_before_extension(flat_filename,dt_tag)
|
|
101
|
+
|
|
102
|
+
input_fn_relative_to_output_fn_relative[fn_relative] = output_fn_relative
|
|
103
|
+
|
|
104
|
+
if not dry_run:
|
|
105
|
+
|
|
106
|
+
input_fn_abs_to_output_fn_abs = {}
|
|
107
|
+
for input_fn_relative in input_fn_relative_to_output_fn_relative:
|
|
108
|
+
output_fn_relative = input_fn_relative_to_output_fn_relative[input_fn_relative]
|
|
109
|
+
input_fn_abs = os.path.join(input_folder,input_fn_relative)
|
|
110
|
+
output_fn_abs = os.path.join(output_folder,output_fn_relative)
|
|
111
|
+
input_fn_abs_to_output_fn_abs[input_fn_abs] = output_fn_abs
|
|
112
|
+
|
|
113
|
+
parallel_copy_files(input_file_to_output_file=input_fn_abs_to_output_fn_abs,
|
|
114
|
+
max_workers=n_copy_workers,
|
|
115
|
+
use_threads=True,
|
|
116
|
+
overwrite=True,
|
|
117
|
+
verbose=verbose)
|
|
118
|
+
|
|
119
|
+
return input_fn_relative_to_output_fn_relative
|
|
120
|
+
|
|
121
|
+
# ...def rename_images()
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
#%% Interactive driver
|
|
125
|
+
|
|
126
|
+
if False:
|
|
127
|
+
|
|
128
|
+
pass
|
|
129
|
+
|
|
130
|
+
#%% Configure options
|
|
131
|
+
|
|
132
|
+
input_folder = r'G:\camera_traps\camera_trap_videos\2024.05.25\cam3'
|
|
133
|
+
output_folder = r'G:\camera_traps\camera_trap_videos\2024.05.25\cam3_flat'
|
|
134
|
+
dry_run = False
|
|
135
|
+
verbose = True
|
|
136
|
+
read_exif_options = ReadExifOptions()
|
|
137
|
+
read_exif_options.tags_to_include = ['DateTime','Model','Make','ExifImageWidth','ExifImageHeight','DateTime',
|
|
138
|
+
'DateTimeOriginal']
|
|
139
|
+
read_exif_options.n_workers = 8
|
|
140
|
+
read_exif_options.verbose = verbose
|
|
141
|
+
n_copy_workers = 8
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
#%% Programmatic execution
|
|
145
|
+
|
|
146
|
+
input_fn_relative_to_output_fn_relative = rename_images(input_folder,
|
|
147
|
+
output_folder,
|
|
148
|
+
dry_run=dry_run,
|
|
149
|
+
verbose=verbose,
|
|
150
|
+
read_exif_options=read_exif_options,
|
|
151
|
+
n_copy_workers=n_copy_workers)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
#%% Command-line driver
|
|
155
|
+
|
|
156
|
+
def main(): # noqa
|
|
157
|
+
|
|
158
|
+
parser = argparse.ArgumentParser(
|
|
159
|
+
description='Copies images from a possibly-nested folder structure to a flat folder structure, ' + \
|
|
160
|
+
'adding datetime information from EXIF to each filename')
|
|
161
|
+
|
|
162
|
+
parser.add_argument(
|
|
163
|
+
'input_folder',
|
|
164
|
+
type=str,
|
|
165
|
+
help='The folder to search for images, always recursive')
|
|
166
|
+
|
|
167
|
+
parser.add_argument(
|
|
168
|
+
'output_folder',
|
|
169
|
+
type=str,
|
|
170
|
+
help='The folder to which we should write the flattened image structure')
|
|
171
|
+
|
|
172
|
+
parser.add_argument(
|
|
173
|
+
'--dry_run',
|
|
174
|
+
action='store_true',
|
|
175
|
+
help="Only map images, don't actually copy")
|
|
176
|
+
|
|
177
|
+
if len(sys.argv[1:]) == 0:
|
|
178
|
+
parser.print_help()
|
|
179
|
+
parser.exit()
|
|
180
|
+
|
|
181
|
+
args = parser.parse_args()
|
|
182
|
+
|
|
183
|
+
rename_images(args.input_folder,args.output_folder,dry_run=args.dry_run,
|
|
184
|
+
verbose=True,read_exif_options=None)
|
|
185
|
+
|
|
186
|
+
if __name__ == '__main__':
|
|
187
|
+
main()
|