megadetector 5.0.28__py3-none-any.whl → 5.0.29__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/api_core/batch_service/score.py +4 -5
- megadetector/api/batch_processing/api_core_support/aggregate_results_manually.py +1 -1
- megadetector/api/batch_processing/api_support/summarize_daily_activity.py +1 -1
- 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/api/synchronous/api_core/tests/load_test.py +2 -3
- 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/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 +23 -23
- 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 -1
- megadetector/data_management/camtrap_dp_to_coco.py +45 -45
- megadetector/data_management/cct_json_utils.py +101 -101
- 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 +189 -189
- megadetector/data_management/databases/add_width_and_height_to_db.py +3 -2
- megadetector/data_management/databases/combine_coco_camera_traps_files.py +38 -38
- megadetector/data_management/databases/integrity_check_json_db.py +202 -188
- megadetector/data_management/databases/subset_json_db.py +33 -33
- megadetector/data_management/generate_crops_from_cct.py +38 -38
- megadetector/data_management/get_image_sizes.py +54 -49
- megadetector/data_management/labelme_to_coco.py +130 -124
- megadetector/data_management/labelme_to_yolo.py +78 -72
- 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 +91 -91
- megadetector/data_management/lila/get_lila_annotation_counts.py +30 -30
- megadetector/data_management/lila/get_lila_image_counts.py +22 -22
- megadetector/data_management/lila/lila_common.py +70 -70
- megadetector/data_management/lila/test_lila_metadata_urls.py +13 -14
- megadetector/data_management/mewc_to_md.py +339 -340
- megadetector/data_management/ocr_tools.py +258 -252
- megadetector/data_management/read_exif.py +231 -224
- megadetector/data_management/remap_coco_categories.py +26 -26
- megadetector/data_management/remove_exif.py +31 -20
- megadetector/data_management/rename_images.py +187 -187
- megadetector/data_management/resize_coco_dataset.py +41 -41
- 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 +117 -120
- megadetector/data_management/yolo_to_coco.py +195 -188
- megadetector/detection/change_detection.py +831 -0
- megadetector/detection/process_video.py +340 -337
- megadetector/detection/pytorch_detector.py +304 -262
- megadetector/detection/run_detector.py +177 -164
- megadetector/detection/run_detector_batch.py +364 -363
- megadetector/detection/run_inference_with_yolov5_val.py +328 -325
- megadetector/detection/run_tiled_inference.py +256 -249
- megadetector/detection/tf_detector.py +24 -24
- megadetector/detection/video_utils.py +290 -282
- megadetector/postprocessing/add_max_conf.py +15 -11
- megadetector/postprocessing/categorize_detections_by_size.py +44 -44
- megadetector/postprocessing/classification_postprocessing.py +415 -415
- megadetector/postprocessing/combine_batch_outputs.py +20 -21
- megadetector/postprocessing/compare_batch_results.py +528 -517
- megadetector/postprocessing/convert_output_format.py +97 -97
- megadetector/postprocessing/create_crop_folder.py +219 -146
- megadetector/postprocessing/detector_calibration.py +173 -168
- megadetector/postprocessing/generate_csv_report.py +508 -499
- megadetector/postprocessing/load_api_results.py +23 -20
- megadetector/postprocessing/md_to_coco.py +129 -98
- megadetector/postprocessing/md_to_labelme.py +89 -83
- megadetector/postprocessing/md_to_wi.py +40 -40
- megadetector/postprocessing/merge_detections.py +87 -114
- megadetector/postprocessing/postprocess_batch_results.py +313 -298
- megadetector/postprocessing/remap_detection_categories.py +36 -36
- megadetector/postprocessing/render_detection_confusion_matrix.py +205 -199
- 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 +702 -677
- 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 +15 -15
- megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +14 -14
- megadetector/taxonomy_mapping/preview_lila_taxonomy.py +66 -66
- megadetector/taxonomy_mapping/retrieve_sample_image.py +16 -16
- megadetector/taxonomy_mapping/simple_image_download.py +8 -8
- megadetector/taxonomy_mapping/species_lookup.py +33 -33
- 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/azure_utils.py +22 -22
- megadetector/utils/ct_utils.py +1018 -200
- megadetector/utils/directory_listing.py +21 -77
- megadetector/utils/gpu_test.py +22 -22
- megadetector/utils/md_tests.py +541 -518
- megadetector/utils/path_utils.py +1457 -398
- megadetector/utils/process_utils.py +41 -41
- megadetector/utils/sas_blob_utils.py +53 -49
- megadetector/utils/split_locations_into_train_val.py +61 -61
- megadetector/utils/string_utils.py +147 -26
- megadetector/utils/url_utils.py +463 -173
- megadetector/utils/wi_utils.py +2629 -2526
- megadetector/utils/write_html_image_list.py +137 -137
- megadetector/visualization/plot_utils.py +21 -21
- megadetector/visualization/render_images_with_thumbnails.py +37 -73
- megadetector/visualization/visualization_utils.py +401 -397
- megadetector/visualization/visualize_db.py +197 -190
- megadetector/visualization/visualize_detector_output.py +79 -73
- {megadetector-5.0.28.dist-info → megadetector-5.0.29.dist-info}/METADATA +135 -132
- megadetector-5.0.29.dist-info/RECORD +163 -0
- {megadetector-5.0.28.dist-info → megadetector-5.0.29.dist-info}/WHEEL +1 -1
- {megadetector-5.0.28.dist-info → megadetector-5.0.29.dist-info}/licenses/LICENSE +0 -0
- {megadetector-5.0.28.dist-info → megadetector-5.0.29.dist-info}/top_level.txt +0 -0
- 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-5.0.28.dist-info/RECORD +0 -209
|
@@ -1,399 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
ubc_to_json.py
|
|
4
|
-
|
|
5
|
-
Convert the .csv file provided for the UBC data set to a
|
|
6
|
-
COCO-camera-traps .json file
|
|
7
|
-
|
|
8
|
-
Images were provided in eight folders, each of which contained a .csv
|
|
9
|
-
file with annotations. Those annotations came in two slightly different
|
|
10
|
-
formats, the two formats corresponding to folders starting with "SC_" and
|
|
11
|
-
otherwise.
|
|
12
|
-
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
#%% Constants and environment
|
|
16
|
-
|
|
17
|
-
import pandas as pd
|
|
18
|
-
import os
|
|
19
|
-
import json
|
|
20
|
-
import uuid
|
|
21
|
-
import numpy as np
|
|
22
|
-
import shutil
|
|
23
|
-
|
|
24
|
-
from tqdm import tqdm
|
|
25
|
-
from PIL import Image
|
|
26
|
-
|
|
27
|
-
from megadetector.visualization import visualize_db
|
|
28
|
-
from megadetector.data_management.databases import integrity_check_json_db
|
|
29
|
-
from megadetector.utils.path_utils import find_images
|
|
30
|
-
|
|
31
|
-
input_base = r'e:\ubc'
|
|
32
|
-
assert(os.path.isdir(input_base))
|
|
33
|
-
|
|
34
|
-
output_base = r'f:\data_staging\ubc'
|
|
35
|
-
output_json_file = os.path.join(output_base,'ubc.json')
|
|
36
|
-
file_list_file = os.path.join(output_base,'all_files.txt')
|
|
37
|
-
|
|
38
|
-
os.makedirs(output_base,exist_ok=True)
|
|
39
|
-
|
|
40
|
-
# Map Excel column names - which vary a little across spreadsheets - to a common set of names
|
|
41
|
-
mapped_fields = {"Survey.Name" : "survey_name",
|
|
42
|
-
"project_id": "survey_name",
|
|
43
|
-
"Camera.Name": "camera_name",
|
|
44
|
-
"station_id": "camera_name",
|
|
45
|
-
"Media.Filename": "filename",
|
|
46
|
-
"orig_file": "filename",
|
|
47
|
-
"timestamp_pst": "datetime",
|
|
48
|
-
"Date.Time": "datetime",
|
|
49
|
-
"Species": "species",
|
|
50
|
-
"latin_name": "species",
|
|
51
|
-
"common.name": "common_name",
|
|
52
|
-
"common_names": "common_name",
|
|
53
|
-
"Sighting.Quantity": "species_count"
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
category_mappings = {
|
|
57
|
-
'bird_spp.':'unknown_bird',
|
|
58
|
-
'dog_dog':'dog',
|
|
59
|
-
'hiker_hiker':'hiker',
|
|
60
|
-
'quad_quad':'quad',
|
|
61
|
-
'skier_skier':'skier',
|
|
62
|
-
'snowshoer_snowshoer':'showshoer',
|
|
63
|
-
'quad_quad':'quad'
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
target_fields = ['species_count','group_count','behaviour']
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
#%% Enumerate images
|
|
70
|
-
|
|
71
|
-
# Load from file if we've already enumerated
|
|
72
|
-
if os.path.isfile(file_list_file):
|
|
73
|
-
with open(file_list_file,'r') as f:
|
|
74
|
-
files = f.readlines()
|
|
75
|
-
files = [s.strip() for s in files]
|
|
76
|
-
image_full_paths = files
|
|
77
|
-
print('Loaded {} images from {}'.format(len(image_full_paths),file_list_file))
|
|
78
|
-
else:
|
|
79
|
-
image_full_paths = find_images(input_base, recursive=True)
|
|
80
|
-
with open(file_list_file,'w') as f:
|
|
81
|
-
for line in image_full_paths:
|
|
82
|
-
f.write(line + '\n')
|
|
83
|
-
print('Enumerated {} images from {}'.format(len(image_full_paths),input_base))
|
|
84
|
-
|
|
85
|
-
image_full_paths_set = set(image_full_paths)
|
|
86
|
-
image_relative_paths = [os.path.relpath(fn,input_base) for fn in image_full_paths]
|
|
87
|
-
image_relative_paths_set = set(image_relative_paths)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
#%% Create CCT dictionaries
|
|
91
|
-
|
|
92
|
-
annotations = []
|
|
93
|
-
image_ids_to_images = {}
|
|
94
|
-
category_name_to_category = {}
|
|
95
|
-
|
|
96
|
-
# Force the empty category to be ID 0
|
|
97
|
-
empty_category = {}
|
|
98
|
-
empty_category['name'] = 'empty'
|
|
99
|
-
empty_category['id'] = 0
|
|
100
|
-
empty_category['common_name'] = 'empty'
|
|
101
|
-
category_name_to_category['empty'] = empty_category
|
|
102
|
-
next_category_id = 1
|
|
103
|
-
|
|
104
|
-
latin_to_common = {}
|
|
105
|
-
|
|
106
|
-
folders = os.listdir(input_base)
|
|
107
|
-
|
|
108
|
-
# To simplify debugging of the loop below
|
|
109
|
-
i_folder = 0; folder = folders[i_folder];
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
##%% Create CCT dictionaries (loop)
|
|
113
|
-
|
|
114
|
-
invalid_images = []
|
|
115
|
-
|
|
116
|
-
for i_folder,folder in enumerate(folders):
|
|
117
|
-
|
|
118
|
-
##%%
|
|
119
|
-
|
|
120
|
-
print('\nProcessing folder {} of {}: {}'.format(i_folder,len(folders),folder))
|
|
121
|
-
|
|
122
|
-
filenames_to_rows = {}
|
|
123
|
-
filenames_with_multiple_annotations = []
|
|
124
|
-
missing_images = []
|
|
125
|
-
image_directory = os.path.join(input_base, folder)
|
|
126
|
-
files = os.listdir(image_directory)
|
|
127
|
-
files = list(filter(lambda f: f.endswith('.csv'), files))
|
|
128
|
-
input_metadata_file = os.path.join(input_base, folder, files[0])
|
|
129
|
-
assert(os.path.isfile(input_metadata_file))
|
|
130
|
-
|
|
131
|
-
# Read source data for this folder
|
|
132
|
-
input_metadata = pd.read_csv(input_metadata_file)
|
|
133
|
-
|
|
134
|
-
# Rename columns
|
|
135
|
-
input_metadata.rename(columns=mapped_fields, inplace=True)
|
|
136
|
-
print('Read {} columns and {} rows from metadata file'.format(len(input_metadata.columns), len(input_metadata)))
|
|
137
|
-
|
|
138
|
-
if folder.startswith("SC_"):
|
|
139
|
-
# Folder name is the first two characters of the filename
|
|
140
|
-
#
|
|
141
|
-
# Create relative path names from the filename itself
|
|
142
|
-
input_metadata['image_relative_path'] = input_metadata['filename'].apply(
|
|
143
|
-
lambda x: os.path.join(folder, x[0:2], x.replace(".JPG", ".jpg")))
|
|
144
|
-
else:
|
|
145
|
-
# Folder name is the camera name
|
|
146
|
-
#
|
|
147
|
-
# Create relative path names from camera name and filename
|
|
148
|
-
input_metadata['image_relative_path'] = input_metadata[['camera_name', 'filename']].apply(
|
|
149
|
-
lambda x: os.path.join(folder, x[0], x[1]), axis = 1)
|
|
150
|
-
|
|
151
|
-
# Which of our images are in the spreadsheet?
|
|
152
|
-
# i_row = 0; fn = input_metadata['image_relative_path'][i_row]
|
|
153
|
-
for i_row, image_relative_path in enumerate(input_metadata['image_relative_path']):
|
|
154
|
-
|
|
155
|
-
if image_relative_path in filenames_to_rows:
|
|
156
|
-
filenames_with_multiple_annotations.append(image_relative_path)
|
|
157
|
-
filenames_to_rows[image_relative_path].append(i_row)
|
|
158
|
-
else:
|
|
159
|
-
filenames_to_rows[image_relative_path] = [i_row]
|
|
160
|
-
image_full_path = os.path.join(input_base, image_relative_path)
|
|
161
|
-
|
|
162
|
-
if not image_full_path in image_full_paths_set:
|
|
163
|
-
assert image_relative_path not in image_relative_paths_set
|
|
164
|
-
missing_images.append(image_full_path)
|
|
165
|
-
|
|
166
|
-
print('Finished verifying image existence for {} files in {} rows\nFound {} filenames with multiple labels, {} missing images'.format(
|
|
167
|
-
len(input_metadata), len(filenames_to_rows),
|
|
168
|
-
len(filenames_with_multiple_annotations), len(missing_images)))
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
##%% Check for images that aren't included in the metadata file
|
|
172
|
-
|
|
173
|
-
# Find all the images in this folder
|
|
174
|
-
image_relative_paths_this_folder = [s for s in image_relative_paths if s.startswith(folder)]
|
|
175
|
-
|
|
176
|
-
# Which of these aren't in the spreadsheet?
|
|
177
|
-
annotated_files_this_folder = list(filenames_to_rows.keys())
|
|
178
|
-
annotated_files_this_folder_set = set(annotated_files_this_folder)
|
|
179
|
-
unannotated_images = [s for s in image_relative_paths_this_folder if s not in annotated_files_this_folder_set]
|
|
180
|
-
|
|
181
|
-
print('Found {} unannotated images (of {}) in this folder'.format(
|
|
182
|
-
len(unannotated_images),len(image_relative_paths_this_folder)))
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
##%% Create entries in CCT dictionaries
|
|
186
|
-
|
|
187
|
-
image_relative_path = list(filenames_to_rows.keys())[0]
|
|
188
|
-
|
|
189
|
-
for image_relative_path in list(filenames_to_rows.keys()):
|
|
190
|
-
|
|
191
|
-
# Only process images we have on disk
|
|
192
|
-
if image_relative_path not in image_relative_paths_set:
|
|
193
|
-
continue
|
|
194
|
-
|
|
195
|
-
image_full_path = os.path.join(input_base,image_relative_path)
|
|
196
|
-
|
|
197
|
-
# This is redundant, but doing this for clarity, at basically no performance
|
|
198
|
-
# cost since we need to *read* the images below to check validity.
|
|
199
|
-
assert os.path.isfile(image_full_path)
|
|
200
|
-
|
|
201
|
-
img_id = image_relative_path.replace('\\','/').replace('/','_').replace(' ','_')
|
|
202
|
-
row_indices = filenames_to_rows[image_relative_path]
|
|
203
|
-
|
|
204
|
-
# i_row = row_indices[0]
|
|
205
|
-
for i_row in row_indices:
|
|
206
|
-
|
|
207
|
-
row = input_metadata.iloc[i_row]
|
|
208
|
-
assert(row['image_relative_path'] == image_relative_path)
|
|
209
|
-
timestamp = row['datetime']
|
|
210
|
-
location = row['survey_name'] + '_' + row['camera_name']
|
|
211
|
-
|
|
212
|
-
if img_id in image_ids_to_images:
|
|
213
|
-
im = image_ids_to_images[img_id]
|
|
214
|
-
assert im['file_name'] == image_relative_path
|
|
215
|
-
assert im['location'] == location
|
|
216
|
-
else:
|
|
217
|
-
im = {}
|
|
218
|
-
|
|
219
|
-
try:
|
|
220
|
-
pil_image = Image.open(image_full_path)
|
|
221
|
-
width, height = pil_image.size
|
|
222
|
-
im['width'] = width
|
|
223
|
-
im['height'] = height
|
|
224
|
-
except:
|
|
225
|
-
# These generally represent zero-byte images in this data set, don't try
|
|
226
|
-
# to find the very small handful that might be other kinds of failures we
|
|
227
|
-
# might want to keep around.
|
|
228
|
-
# print('Error opening image {}'.format(image_relative_path))
|
|
229
|
-
invalid_images.append(image_relative_path)
|
|
230
|
-
continue
|
|
231
|
-
|
|
232
|
-
im['id'] = img_id
|
|
233
|
-
im['file_name'] = image_relative_path
|
|
234
|
-
im['datetime'] = timestamp
|
|
235
|
-
im['location'] = location
|
|
236
|
-
|
|
237
|
-
image_ids_to_images[img_id] = im
|
|
238
|
-
|
|
239
|
-
species = row['species'].lower().strip().replace(' ','_')
|
|
240
|
-
|
|
241
|
-
if (isinstance(species,float) or \
|
|
242
|
-
(isinstance(species,str) and (len(species) == 0))):
|
|
243
|
-
category_name = 'empty'
|
|
244
|
-
else:
|
|
245
|
-
category_name = species
|
|
246
|
-
del species
|
|
247
|
-
|
|
248
|
-
category_name = category_name.strip().lower()
|
|
249
|
-
|
|
250
|
-
common_name = row['common_name']
|
|
251
|
-
if isinstance(common_name,float) and np.isnan(common_name):
|
|
252
|
-
common_name = ''
|
|
253
|
-
else:
|
|
254
|
-
common_name = str(common_name).lower().strip().replace(', ',',').replace(' ','_')
|
|
255
|
-
|
|
256
|
-
for k,v in category_mappings.items():
|
|
257
|
-
common_name = common_name.replace(k,v)
|
|
258
|
-
category_name = category_name.replace(k,v)
|
|
259
|
-
common_name = common_name.replace('.','').replace('spp','species')
|
|
260
|
-
category_name = category_name.replace('.','').replace('spp','species')
|
|
261
|
-
|
|
262
|
-
if category_name == 'passerine_species' and common_name != '' and common_name != 'passerine_species':
|
|
263
|
-
category_name = common_name
|
|
264
|
-
|
|
265
|
-
# If we've seen this category before...
|
|
266
|
-
if category_name in category_name_to_category:
|
|
267
|
-
|
|
268
|
-
category = category_name_to_category[category_name]
|
|
269
|
-
category_id = category['id']
|
|
270
|
-
|
|
271
|
-
# ...make sure it used the same latin --> common mapping
|
|
272
|
-
#
|
|
273
|
-
# If the previous instance had no mapping, use the new one.
|
|
274
|
-
if category['common_name'] == '':
|
|
275
|
-
category['common_name'] = common_name
|
|
276
|
-
else:
|
|
277
|
-
# assert common_name == category['common_name']
|
|
278
|
-
if common_name != category['common_name']:
|
|
279
|
-
print('Warning: common name {} used for species {}, previously {}'.format(
|
|
280
|
-
common_name,category_name,category['common_name']))
|
|
281
|
-
|
|
282
|
-
else:
|
|
283
|
-
|
|
284
|
-
category_id = next_category_id
|
|
285
|
-
category = {}
|
|
286
|
-
category['id'] = category_id
|
|
287
|
-
category['name'] = category_name
|
|
288
|
-
category['common_name'] = common_name
|
|
289
|
-
category_name_to_category[category_name] = category
|
|
290
|
-
next_category_id += 1
|
|
291
|
-
|
|
292
|
-
# Create an annotation
|
|
293
|
-
ann = {}
|
|
294
|
-
ann['id'] = str(uuid.uuid1())
|
|
295
|
-
ann['image_id'] = im['id']
|
|
296
|
-
ann['category_id'] = category_id
|
|
297
|
-
|
|
298
|
-
for target_field in target_fields:
|
|
299
|
-
if target_field in input_metadata.columns:
|
|
300
|
-
val = row[target_field]
|
|
301
|
-
if isinstance(val,float) and np.isnan(val):
|
|
302
|
-
val = ''
|
|
303
|
-
else:
|
|
304
|
-
val = str(val).strip()
|
|
305
|
-
ann[target_field] = val
|
|
306
|
-
|
|
307
|
-
annotations.append(ann)
|
|
308
|
-
|
|
309
|
-
# ...for each annotation we found for this image
|
|
310
|
-
|
|
311
|
-
# ...for each image
|
|
312
|
-
|
|
313
|
-
# ...for each dataset
|
|
314
|
-
|
|
315
|
-
images = list(image_ids_to_images.values())
|
|
316
|
-
categories = list(category_name_to_category.values())
|
|
317
|
-
|
|
318
|
-
# Print all of our species mappings
|
|
319
|
-
for c in categories:
|
|
320
|
-
print(c['name'].ljust(30) + c['common_name'])
|
|
321
|
-
|
|
322
|
-
print('Finished creating CCT dictionaries, loaded {} images of {} total on disk ({} invalid)'.format(
|
|
323
|
-
len(images), len(image_relative_paths_set), len(invalid_images)))
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
#%% Copy images for which we actually have annotations to a new folder, lowercase everything
|
|
327
|
-
|
|
328
|
-
# im = images[0]
|
|
329
|
-
for im in tqdm(images):
|
|
330
|
-
relative_filename = im['file_name']
|
|
331
|
-
input_filename = os.path.join(input_base,relative_filename)
|
|
332
|
-
output_filename = os.path.join(output_base,relative_filename).lower()
|
|
333
|
-
os.makedirs(os.path.dirname(output_filename),exist_ok=True)
|
|
334
|
-
|
|
335
|
-
shutil.copy(input_filename, output_filename)
|
|
336
|
-
im['file_name'] = im['file_name'].lower()
|
|
337
|
-
im['id'] = im['id'].lower()
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
#%% Create info struct
|
|
341
|
-
|
|
342
|
-
info = {}
|
|
343
|
-
info['year'] = 2020
|
|
344
|
-
info['version'] = 1
|
|
345
|
-
info['description'] = 'UBC Camera Traps'
|
|
346
|
-
info['contributor'] = 'UBC'
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
#%% Convert image IDs to lowercase in annotations, tag as sequence level
|
|
350
|
-
|
|
351
|
-
# While there isn't any sequence information, the nature of false positives
|
|
352
|
-
# here leads me to believe the images were labeled at the sequence level, so
|
|
353
|
-
# we should trust labels more when positives are verified. Overall false
|
|
354
|
-
# positive rate looks to be between 1% and 5%.
|
|
355
|
-
|
|
356
|
-
for ann in annotations:
|
|
357
|
-
ann['image_id'] = ann['image_id'].lower()
|
|
358
|
-
ann['sequence_level_annotation'] = True
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
#%% Write output
|
|
362
|
-
|
|
363
|
-
json_data = {}
|
|
364
|
-
json_data['images'] = images
|
|
365
|
-
json_data['annotations'] = annotations
|
|
366
|
-
json_data['categories'] = categories
|
|
367
|
-
json_data['info'] = info
|
|
368
|
-
json.dump(json_data, open(output_json_file, 'w'), indent=2)
|
|
369
|
-
|
|
370
|
-
print('Finished writing .json file with {} images, {} annotations, and {} categories'.format(
|
|
371
|
-
len(images), len(annotations), len(categories)))
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
#%% Validate output
|
|
375
|
-
|
|
376
|
-
options = integrity_check_json_db.IntegrityCheckOptions()
|
|
377
|
-
options.baseDir = output_base
|
|
378
|
-
options.bCheckImageSizes = False
|
|
379
|
-
options.bCheckImageExistence = False
|
|
380
|
-
options.bFindUnusedImages = True
|
|
381
|
-
|
|
382
|
-
sortedCategories, data, errors = integrity_check_json_db.integrity_check_json_db(
|
|
383
|
-
output_json_file, options)
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
#%% Preview labels
|
|
387
|
-
|
|
388
|
-
viz_options = visualize_db.DbVizOptions()
|
|
389
|
-
viz_options.num_to_visualize = 2000
|
|
390
|
-
viz_options.trim_to_images_with_bboxes = False
|
|
391
|
-
viz_options.add_search_links = True
|
|
392
|
-
viz_options.sort_by_filename = False
|
|
393
|
-
viz_options.parallelize_rendering = True
|
|
394
|
-
html_output_file, image_db = visualize_db.visualize_db(db_path=output_json_file,
|
|
395
|
-
output_dir=os.path.join(
|
|
396
|
-
output_base, 'preview'),
|
|
397
|
-
image_base_dir=output_base,
|
|
398
|
-
options=viz_options)
|
|
399
|
-
os.startfile(html_output_file)
|