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,365 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
pc_to_json.py
|
|
4
|
-
|
|
5
|
-
Convert a particular collection of .csv files from Parks Canada to CCT format.
|
|
6
|
-
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
#%% Constants and environment
|
|
10
|
-
|
|
11
|
-
import pandas as pd
|
|
12
|
-
import uuid
|
|
13
|
-
import json
|
|
14
|
-
import time
|
|
15
|
-
|
|
16
|
-
import numpy as np
|
|
17
|
-
from tqdm import tqdm
|
|
18
|
-
|
|
19
|
-
import humanfriendly
|
|
20
|
-
import os
|
|
21
|
-
import PIL
|
|
22
|
-
|
|
23
|
-
from megadetector.data_management.databases import integrity_check_json_db
|
|
24
|
-
from megadetector.data_management.cct_json_utils import IndexedJsonDb
|
|
25
|
-
from megadetector.data_management import cct_json_to_filename_json
|
|
26
|
-
from megadetector.visualization import visualize_db
|
|
27
|
-
from megadetector.utils import path_utils
|
|
28
|
-
|
|
29
|
-
input_base = r"g:\20190715"
|
|
30
|
-
output_file = r"D:\wildlife_data\parks_canada\pc_20190715.json"
|
|
31
|
-
preview_base = r"D:\wildlife_data\parks_canada\preview"
|
|
32
|
-
|
|
33
|
-
filename_replacements = {}
|
|
34
|
-
category_mappings = {'':'unlabeled'}
|
|
35
|
-
|
|
36
|
-
csv_prefix = 'ImageData_Microsoft___'
|
|
37
|
-
|
|
38
|
-
expected_columns = 'Location,DateImage,TimeImage,Species,Total,Horses,DogsOnLeash,DogsOffLeash,AdultFemale,AdultMale,AdultUnknown,Subadult,YLY,YOY,ImageName'.split(',')
|
|
39
|
-
columns_to_copy = {'Total':'count','Horses':'horses','DogsOnLeash':'dogsonleash','DogsOffLeash':'dogsoffleash',
|
|
40
|
-
'AdultFemale':'adultfemale','AdultMale':'adultmale','AdultUnknown':'adultunknown',
|
|
41
|
-
'Subadult':'subadult','YLY':'yearling','YOY':'youngofyear'}
|
|
42
|
-
|
|
43
|
-
retrieve_image_sizes = False
|
|
44
|
-
|
|
45
|
-
max_num_csvs = -1
|
|
46
|
-
|
|
47
|
-
db_sampling_scheme = 'preview' # 'labeled','all'
|
|
48
|
-
n_unlabeled_to_sample = -1
|
|
49
|
-
cap_unlabeled_to_labeled = True
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
#%% Read and concatenate source data
|
|
53
|
-
|
|
54
|
-
# List files
|
|
55
|
-
input_files = os.listdir(input_base)
|
|
56
|
-
|
|
57
|
-
# List of dataframes, one per .csv file; we'll concatenate later
|
|
58
|
-
all_input_metadata = []
|
|
59
|
-
|
|
60
|
-
# i_file = 87; fn = input_files[i_file]
|
|
61
|
-
for i_file,fn in enumerate(input_files):
|
|
62
|
-
|
|
63
|
-
if max_num_csvs > 0 and len(all_input_metadata) >= max_num_csvs:
|
|
64
|
-
break
|
|
65
|
-
|
|
66
|
-
if not fn.endswith('.csv'):
|
|
67
|
-
continue
|
|
68
|
-
if not fn.startswith(csv_prefix):
|
|
69
|
-
continue
|
|
70
|
-
dirname = fn.replace(csv_prefix,'').replace('.csv','')
|
|
71
|
-
dirfullpath = os.path.join(input_base,dirname)
|
|
72
|
-
if not os.path.isdir(dirfullpath):
|
|
73
|
-
dirname = fn.replace(csv_prefix,'').replace('.csv','').replace(' ',' ')
|
|
74
|
-
dirfullpath = os.path.join(input_base,dirname)
|
|
75
|
-
assert(os.path.isdir(dirfullpath))
|
|
76
|
-
|
|
77
|
-
metadata_fullpath = os.path.join(input_base,fn)
|
|
78
|
-
print('Reading {}'.format(metadata_fullpath))
|
|
79
|
-
df = pd.read_csv(metadata_fullpath)
|
|
80
|
-
assert list(df.columns) == expected_columns
|
|
81
|
-
df['DirName'] = dirname
|
|
82
|
-
all_input_metadata.append(df)
|
|
83
|
-
|
|
84
|
-
# Concatenate into a giant data frame
|
|
85
|
-
input_metadata = pd.concat(all_input_metadata)
|
|
86
|
-
|
|
87
|
-
print('Read {} rows total'.format(len(input_metadata)))
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
#%% List files
|
|
91
|
-
|
|
92
|
-
print('Listing images...')
|
|
93
|
-
image_full_paths = path_utils.find_images(input_base,bRecursive=True)
|
|
94
|
-
print('Finished listing {} images'.format(len(image_full_paths)))
|
|
95
|
-
|
|
96
|
-
image_relative_paths = []
|
|
97
|
-
for s in image_full_paths:
|
|
98
|
-
image_relative_paths.append(os.path.relpath(s,input_base))
|
|
99
|
-
image_relative_paths = set(image_relative_paths)
|
|
100
|
-
|
|
101
|
-
image_relative_paths_lower = set()
|
|
102
|
-
for s in image_relative_paths:
|
|
103
|
-
image_relative_paths_lower.add(s.lower())
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
#%% Main loop over labels (prep)
|
|
107
|
-
|
|
108
|
-
start_time = time.time()
|
|
109
|
-
|
|
110
|
-
relative_path_to_image = {}
|
|
111
|
-
|
|
112
|
-
images = []
|
|
113
|
-
annotations = []
|
|
114
|
-
category_name_to_category = {}
|
|
115
|
-
missing_files = []
|
|
116
|
-
|
|
117
|
-
# Force the empty category to be ID 0
|
|
118
|
-
empty_category = {}
|
|
119
|
-
empty_category['name'] = 'empty'
|
|
120
|
-
empty_category['id'] = 0
|
|
121
|
-
category_name_to_category['empty'] = empty_category
|
|
122
|
-
next_category_id = 1
|
|
123
|
-
|
|
124
|
-
labeled_images = []
|
|
125
|
-
unlabeled_images = []
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
#%% Main loop over labels (loop)
|
|
129
|
-
|
|
130
|
-
# iRow = 0; row = input_metadata.iloc[iRow]
|
|
131
|
-
for iRow,row in tqdm(input_metadata.iterrows(),total=len(input_metadata)):
|
|
132
|
-
|
|
133
|
-
# ImageID,FileName,FilePath,SpeciesID,CommonName
|
|
134
|
-
image_id = str(uuid.uuid1())
|
|
135
|
-
relative_path = os.path.normpath(row['ImageName'])
|
|
136
|
-
|
|
137
|
-
if relative_path not in image_relative_paths:
|
|
138
|
-
if relative_path.lower() in image_relative_paths_lower:
|
|
139
|
-
print('Warning: lower-case version of {} in path list'.format(relative_path))
|
|
140
|
-
else:
|
|
141
|
-
missing_files.append(relative_path)
|
|
142
|
-
continue
|
|
143
|
-
|
|
144
|
-
full_path = os.path.join(input_base,relative_path)
|
|
145
|
-
|
|
146
|
-
# assert os.path.isfile(full_path)
|
|
147
|
-
|
|
148
|
-
if relative_path in relative_path_to_image:
|
|
149
|
-
|
|
150
|
-
im = relative_path_to_image[relative_path]
|
|
151
|
-
|
|
152
|
-
else:
|
|
153
|
-
|
|
154
|
-
im = {}
|
|
155
|
-
im['id'] = image_id
|
|
156
|
-
im['file_name'] = relative_path
|
|
157
|
-
im['seq_id'] = '-1'
|
|
158
|
-
im['location'] = row['Location']
|
|
159
|
-
im['datetime'] = row['DateImage'] + ' ' + row['TimeImage']
|
|
160
|
-
|
|
161
|
-
images.append(im)
|
|
162
|
-
relative_path_to_image[relative_path] = im
|
|
163
|
-
|
|
164
|
-
if retrieve_image_sizes:
|
|
165
|
-
|
|
166
|
-
# Retrieve image width and height
|
|
167
|
-
pil_im = PIL.Image.open(full_path)
|
|
168
|
-
width, height = pil_im.size
|
|
169
|
-
im['width'] = width
|
|
170
|
-
im['height'] = height
|
|
171
|
-
|
|
172
|
-
species = row['Species']
|
|
173
|
-
if isinstance(species,float):
|
|
174
|
-
assert np.isnan(species)
|
|
175
|
-
species = 'unlabeled'
|
|
176
|
-
|
|
177
|
-
category_name = species.lower().strip()
|
|
178
|
-
if category_name in category_mappings:
|
|
179
|
-
category_name = category_mappings[category_name]
|
|
180
|
-
|
|
181
|
-
if category_name not in category_name_to_category:
|
|
182
|
-
category = {}
|
|
183
|
-
category['name'] = category_name
|
|
184
|
-
category['id'] = next_category_id
|
|
185
|
-
next_category_id += 1
|
|
186
|
-
category_name_to_category[category_name] = category
|
|
187
|
-
else:
|
|
188
|
-
category = category_name_to_category[category_name]
|
|
189
|
-
assert category['name'] == category_name
|
|
190
|
-
|
|
191
|
-
category_id = category['id']
|
|
192
|
-
|
|
193
|
-
if category_name == 'unlabeled':
|
|
194
|
-
unlabeled_images.append(im)
|
|
195
|
-
else:
|
|
196
|
-
labeled_images.append(im)
|
|
197
|
-
|
|
198
|
-
# Create an annotation
|
|
199
|
-
ann = {}
|
|
200
|
-
|
|
201
|
-
# The Internet tells me this guarantees uniqueness to a reasonable extent, even
|
|
202
|
-
# beyond the sheer improbability of collisions.
|
|
203
|
-
ann['id'] = str(uuid.uuid1())
|
|
204
|
-
ann['image_id'] = im['id']
|
|
205
|
-
ann['category_id'] = category_id
|
|
206
|
-
|
|
207
|
-
for col in columns_to_copy:
|
|
208
|
-
ann[columns_to_copy[col]] = row[col]
|
|
209
|
-
|
|
210
|
-
annotations.append(ann)
|
|
211
|
-
|
|
212
|
-
# ...for each image
|
|
213
|
-
|
|
214
|
-
categories = list(category_name_to_category.values())
|
|
215
|
-
|
|
216
|
-
elapsed = time.time() - start_time
|
|
217
|
-
print('Finished verifying file loop in {}, {} matched images, {} missing images, {} unlabeled images'.format(
|
|
218
|
-
humanfriendly.format_timespan(elapsed), len(images), len(missing_files), len(unlabeled_images)))
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
#%% See what's up with missing files
|
|
222
|
-
|
|
223
|
-
dirnames = set()
|
|
224
|
-
# s = list(image_relative_paths)[0]
|
|
225
|
-
for s in image_relative_paths:
|
|
226
|
-
image_dir = os.path.dirname(s)
|
|
227
|
-
dirnames.add(image_dir)
|
|
228
|
-
|
|
229
|
-
missing_images_with_missing_dirs = []
|
|
230
|
-
missing_images_with_non_missing_dirs = []
|
|
231
|
-
|
|
232
|
-
missing_dirs = set()
|
|
233
|
-
|
|
234
|
-
# s = missing_files[0]
|
|
235
|
-
for s in missing_files:
|
|
236
|
-
assert s not in image_relative_paths
|
|
237
|
-
dirname = os.path.dirname(s)
|
|
238
|
-
if dirname not in dirnames:
|
|
239
|
-
missing_images_with_missing_dirs.append(s)
|
|
240
|
-
missing_dirs.add(dirname)
|
|
241
|
-
else:
|
|
242
|
-
missing_images_with_non_missing_dirs.append(s)
|
|
243
|
-
|
|
244
|
-
print('Of {} missing files, {} are due to {} missing folders'.format(
|
|
245
|
-
len(missing_files),len(missing_images_with_missing_dirs),len(missing_dirs)))
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
#%% Check for images that aren't included in the metadata file
|
|
249
|
-
|
|
250
|
-
unmatched_files = []
|
|
251
|
-
|
|
252
|
-
for i_image,relative_path in tqdm(enumerate(image_relative_paths),total=len(image_relative_paths)):
|
|
253
|
-
|
|
254
|
-
if relative_path not in relative_path_to_image:
|
|
255
|
-
unmatched_files.append(relative_path)
|
|
256
|
-
|
|
257
|
-
print('Finished checking {} images to make sure they\'re in the metadata, found {} mismatches'.format(
|
|
258
|
-
len(image_relative_paths),len(unmatched_files)))
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
#%% Sample the database
|
|
262
|
-
|
|
263
|
-
images_all = images
|
|
264
|
-
annotations_all = annotations
|
|
265
|
-
|
|
266
|
-
#%%
|
|
267
|
-
|
|
268
|
-
if db_sampling_scheme == 'all':
|
|
269
|
-
|
|
270
|
-
pass
|
|
271
|
-
|
|
272
|
-
elif db_sampling_scheme == 'labeled' or db_sampling_scheme == 'preview':
|
|
273
|
-
|
|
274
|
-
json_data = {}
|
|
275
|
-
json_data['images'] = images
|
|
276
|
-
json_data['annotations'] = annotations
|
|
277
|
-
json_data['categories'] = categories
|
|
278
|
-
|
|
279
|
-
indexed_db = IndexedJsonDb(json_data)
|
|
280
|
-
|
|
281
|
-
# Collect the images we want
|
|
282
|
-
sampled_images = []
|
|
283
|
-
for im in images:
|
|
284
|
-
classes = indexed_db.get_classes_for_image(im)
|
|
285
|
-
if 'unlabeled' in classes and len(classes) == 1:
|
|
286
|
-
pass
|
|
287
|
-
else:
|
|
288
|
-
sampled_images.append(im)
|
|
289
|
-
|
|
290
|
-
if db_sampling_scheme == 'preview':
|
|
291
|
-
n_sample = n_unlabeled_to_sample
|
|
292
|
-
if n_sample == -1:
|
|
293
|
-
n_sample = len(labeled_images)
|
|
294
|
-
if n_sample > len(labeled_images) and cap_unlabeled_to_labeled:
|
|
295
|
-
n_sample = len(labeled_images)
|
|
296
|
-
if n_sample > len(unlabeled_images):
|
|
297
|
-
n_sample = len(unlabeled_images)
|
|
298
|
-
print('Sampling {} of {} unlabeled images'.format(n_sample,len(unlabeled_images)))
|
|
299
|
-
from random import sample
|
|
300
|
-
sampled_images.extend(sample(unlabeled_images,n_sample))
|
|
301
|
-
|
|
302
|
-
sampled_annotations = []
|
|
303
|
-
for im in sampled_images:
|
|
304
|
-
sampled_annotations.extend(indexed_db.get_annotations_for_image(im))
|
|
305
|
-
|
|
306
|
-
print('Sampling {} of {} images, {} of {} annotations'.format(
|
|
307
|
-
len(sampled_images),len(images),len(sampled_annotations),len(annotations)))
|
|
308
|
-
|
|
309
|
-
images = sampled_images
|
|
310
|
-
annotations = sampled_annotations
|
|
311
|
-
|
|
312
|
-
else:
|
|
313
|
-
|
|
314
|
-
raise ValueError('Unrecognized DB sampling scheme {}'.format(db_sampling_scheme))
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
#%% Create info struct
|
|
318
|
-
|
|
319
|
-
info = {}
|
|
320
|
-
info['year'] = 2019
|
|
321
|
-
info['version'] = 1
|
|
322
|
-
info['description'] = 'COCO style database'
|
|
323
|
-
info['secondary_contributor'] = 'Converted to COCO .json by Dan Morris'
|
|
324
|
-
info['contributor'] = 'Parks Canada'
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
#%% Write output
|
|
328
|
-
|
|
329
|
-
json_data = {}
|
|
330
|
-
json_data['images'] = images
|
|
331
|
-
json_data['annotations'] = annotations
|
|
332
|
-
json_data['categories'] = categories
|
|
333
|
-
json_data['info'] = info
|
|
334
|
-
json.dump(json_data, open(output_file,'w'), indent=4)
|
|
335
|
-
|
|
336
|
-
print('Finished writing .json file with {} images, {} annotations, and {} categories'.format(
|
|
337
|
-
len(images),len(annotations),len(categories)))
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
#%% Validate the database's integrity
|
|
341
|
-
|
|
342
|
-
json_data = json.load(open(output_file))
|
|
343
|
-
options = integrity_check_json_db.IntegrityCheckOptions()
|
|
344
|
-
sortedCategories,data = integrity_check_json_db.integrity_check_json_db(json_data, options)
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
#%% Render a bunch of images to make sure the labels got carried along correctly
|
|
348
|
-
|
|
349
|
-
output_dir = preview_base
|
|
350
|
-
|
|
351
|
-
options = visualize_db.DbVizOptions()
|
|
352
|
-
options.num_to_visualize = 100
|
|
353
|
-
options.sort_by_filename = False
|
|
354
|
-
# options.classes_to_exclude = ['unlabeled']
|
|
355
|
-
options.classes_to_exclude = None
|
|
356
|
-
|
|
357
|
-
htmlOutputFile,_ = visualize_db.visualize_db(json_data,output_dir,input_base,options)
|
|
358
|
-
os.startfile(htmlOutputFile)
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
#%% Write out a list of files to annotate
|
|
362
|
-
|
|
363
|
-
_,file_list = cct_json_to_filename_json.convertJsonToStringList(output_file,prepend="20190715/")
|
|
364
|
-
os.startfile(file_list)
|
|
365
|
-
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
plot_wni_giraffes.py
|
|
4
|
-
|
|
5
|
-
Plot keypoints on a random sample of images from the wni-giraffes data set.
|
|
6
|
-
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
#%% Constants and imports
|
|
10
|
-
|
|
11
|
-
import os
|
|
12
|
-
import json
|
|
13
|
-
import random
|
|
14
|
-
|
|
15
|
-
from PIL import Image
|
|
16
|
-
from PIL import ImageDraw
|
|
17
|
-
from tqdm import tqdm
|
|
18
|
-
|
|
19
|
-
input_file = r"G:\data_staging\wni-out\wni_giraffes_train.json"
|
|
20
|
-
image_base = r"G:\data_staging\wni-out\images"
|
|
21
|
-
output_base = r"G:\data_staging\wni-out\test-plots"
|
|
22
|
-
os.makedirs(output_base,exist_ok=True)
|
|
23
|
-
|
|
24
|
-
tool_colors = ['red','green','blue','magenta']
|
|
25
|
-
use_fancy_ellipses = True
|
|
26
|
-
draw_individual_samples = False
|
|
27
|
-
|
|
28
|
-
median_radius = 20
|
|
29
|
-
median_linewidth = 8
|
|
30
|
-
|
|
31
|
-
sample_radius = 10
|
|
32
|
-
|
|
33
|
-
n_images_to_plot = 100
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
#%% Load and select data
|
|
37
|
-
|
|
38
|
-
with open(input_file,'r') as f:
|
|
39
|
-
d = json.load(f)
|
|
40
|
-
annotations = d['annotations']
|
|
41
|
-
print(d['info'])
|
|
42
|
-
|
|
43
|
-
short_tool_names = list(d['info']['tool_names'].keys())
|
|
44
|
-
annotations_to_plot = random.sample(annotations,n_images_to_plot)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
#%% Support functions
|
|
48
|
-
|
|
49
|
-
# https://stackoverflow.com/questions/32504246/draw-ellipse-in-python-pil-with-line-thickness
|
|
50
|
-
def draw_fancy_ellipse(image, x, y, radius, width=1, outline='white', antialias=4):
|
|
51
|
-
|
|
52
|
-
bounds = (x-radius,y-radius,x+radius,y+radius)
|
|
53
|
-
|
|
54
|
-
# Use a single channel image (mode='L') as mask.
|
|
55
|
-
# The size of the mask can be increased relative to the input image
|
|
56
|
-
# to get smoother looking results.
|
|
57
|
-
mask = Image.new(
|
|
58
|
-
size=[int(dim * antialias) for dim in image.size],
|
|
59
|
-
mode='L', color='black')
|
|
60
|
-
draw = ImageDraw.Draw(mask)
|
|
61
|
-
|
|
62
|
-
# draw outer shape in white (color) and inner shape in black (transparent)
|
|
63
|
-
for offset, fill in (width/-2.0, 'white'), (width/2.0, 'black'):
|
|
64
|
-
left, top = [(value + offset) * antialias for value in bounds[:2]]
|
|
65
|
-
right, bottom = [(value - offset) * antialias for value in bounds[2:]]
|
|
66
|
-
draw.ellipse([left, top, right, bottom], fill=fill)
|
|
67
|
-
|
|
68
|
-
# downsample the mask using PIL.Image.LANCZOS
|
|
69
|
-
# (a high-quality downsampling filter).
|
|
70
|
-
mask = mask.resize(image.size, Image.LANCZOS)
|
|
71
|
-
|
|
72
|
-
# paste outline color to input image through the mask
|
|
73
|
-
image.paste(outline, mask=mask)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def draw_ellipse(image, x, y, radius, linewidth, color_index, use_imagedraw=False):
|
|
77
|
-
|
|
78
|
-
if use_imagedraw:
|
|
79
|
-
draw_fancy_ellipse(image, x, y, radius=radius, width=linewidth, outline=tool_colors[color_index])
|
|
80
|
-
else:
|
|
81
|
-
draw = ImageDraw.Draw(image)
|
|
82
|
-
bounds = (x-radius,y-radius,x+radius,y+radius)
|
|
83
|
-
draw.ellipse(bounds, fill=tool_colors[color_index])
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
#%% Plot some images
|
|
87
|
-
|
|
88
|
-
# ann = annotations_to_plot[0]
|
|
89
|
-
for ann in tqdm(annotations_to_plot):
|
|
90
|
-
|
|
91
|
-
input_path = os.path.join(image_base,ann['filename'])
|
|
92
|
-
output_path = os.path.join(output_base,ann['filename'].replace('/','_'))
|
|
93
|
-
|
|
94
|
-
im = None
|
|
95
|
-
im = Image.open(input_path)
|
|
96
|
-
|
|
97
|
-
# i_tool = 0; tool_name = short_tool_names[i_tool]
|
|
98
|
-
for i_tool,tool_name in enumerate(short_tool_names):
|
|
99
|
-
|
|
100
|
-
tool_keypoints = ann['keypoints'][tool_name]
|
|
101
|
-
|
|
102
|
-
# Don't plot tools that don't have a consensus annotation
|
|
103
|
-
if tool_keypoints['median_x'] is None:
|
|
104
|
-
continue
|
|
105
|
-
|
|
106
|
-
median_x = tool_keypoints['median_x']
|
|
107
|
-
median_y = tool_keypoints['median_y']
|
|
108
|
-
|
|
109
|
-
draw_ellipse(im, median_x, median_y, median_radius, median_linewidth, color_index=i_tool,
|
|
110
|
-
use_imagedraw=use_fancy_ellipses)
|
|
111
|
-
|
|
112
|
-
if draw_individual_samples:
|
|
113
|
-
for i_sample in range(0,len(tool_keypoints['x'])):
|
|
114
|
-
x = tool_keypoints['x'][i_sample]
|
|
115
|
-
y = tool_keypoints['y'][i_sample]
|
|
116
|
-
draw_ellipse(im, x, y, sample_radius, None, color_index=i_tool,
|
|
117
|
-
use_imagedraw=False)
|
|
118
|
-
|
|
119
|
-
# ...for each tool
|
|
120
|
-
|
|
121
|
-
im.save(output_path)
|
|
122
|
-
|
|
123
|
-
# ...for each annotation
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
prepare_zsl_imerit.py
|
|
4
|
-
|
|
5
|
-
Prepare ZSL Borneo data for annotation (convert input data to iMerit-friendly format).
|
|
6
|
-
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
#%% Imports and constants
|
|
10
|
-
|
|
11
|
-
import json
|
|
12
|
-
import os
|
|
13
|
-
|
|
14
|
-
from tqdm import tqdm
|
|
15
|
-
from operator import itemgetter
|
|
16
|
-
from shutil import copyfile
|
|
17
|
-
|
|
18
|
-
from megadetector.visualization import visualize_db
|
|
19
|
-
from megadetector.data_management.databases import integrity_check_json_db
|
|
20
|
-
from megadetector.data_management.cct_json_utils import IndexedJsonDb
|
|
21
|
-
|
|
22
|
-
annotation_list_filename = r'd:\wildlife_data\zsl_borneo\all_img_ids_to_bbox.json'
|
|
23
|
-
image_json = r'd:\wildlife_data\zsl_borneo\201906cameratraps\0.5\zsl_camera_traps_201906.json'
|
|
24
|
-
image_base = r'd:\wildlife_data\zsl_borneo\201906cameratraps\0.5'
|
|
25
|
-
output_base = r'd:\wildlife_data\zsl_borneo'
|
|
26
|
-
|
|
27
|
-
human_classes = ['human','hunter']
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
#%% Load data
|
|
31
|
-
|
|
32
|
-
with open(annotation_list_filename,'r') as f:
|
|
33
|
-
annotation_list = json.load(f)
|
|
34
|
-
|
|
35
|
-
# with open(image_json,'r') as f:
|
|
36
|
-
# data = json.load(f)
|
|
37
|
-
indexedData = IndexedJsonDb(image_json)
|
|
38
|
-
|
|
39
|
-
print('Done loading data')
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
#%% Validate data
|
|
43
|
-
|
|
44
|
-
options = integrity_check_json_db.IntegrityCheckOptions()
|
|
45
|
-
options.baseDir = image_base
|
|
46
|
-
options.bCheckImageSizes = False
|
|
47
|
-
options.bCheckImageExistence = True
|
|
48
|
-
options.bFindUnusedImages = False
|
|
49
|
-
|
|
50
|
-
sortedCategories = integrity_check_json_db.integrity_check_json_db(indexedData.db,options)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
#%% Label previews
|
|
54
|
-
|
|
55
|
-
viz_options = visualize_db.DbVizOptions()
|
|
56
|
-
viz_options.num_to_visualize = 500
|
|
57
|
-
viz_options.trim_to_images_with_bboxes = False
|
|
58
|
-
viz_options.add_search_links = True
|
|
59
|
-
viz_options.sort_by_filename = False
|
|
60
|
-
html_output_file,image_db = visualize_db.visualize_db(indexedData.db,
|
|
61
|
-
os.path.join(output_base,'preview'),
|
|
62
|
-
image_base,viz_options)
|
|
63
|
-
os.startfile(html_output_file)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
#%% Collect images to annotate
|
|
67
|
-
|
|
68
|
-
images_to_annotate = []
|
|
69
|
-
|
|
70
|
-
annotation_list = set(annotation_list)
|
|
71
|
-
n_humans = 0
|
|
72
|
-
|
|
73
|
-
for im in tqdm(indexedData.db['images']):
|
|
74
|
-
|
|
75
|
-
class_names = indexedData.get_classes_for_image(im)
|
|
76
|
-
b_human = False
|
|
77
|
-
for cn in class_names:
|
|
78
|
-
if cn.lower() in human_classes:
|
|
79
|
-
b_human = True
|
|
80
|
-
n_humans += 1
|
|
81
|
-
break
|
|
82
|
-
|
|
83
|
-
if b_human or im['id'] in annotation_list:
|
|
84
|
-
images_to_annotate.append(im)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
print('Found {} of {} images ({} humans)'.format(len(images_to_annotate),len(annotation_list),n_humans))
|
|
88
|
-
assert len(images_to_annotate) >= len(annotation_list)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
#%% Sort by sequence and frame
|
|
93
|
-
|
|
94
|
-
images_to_annotate = sorted(images_to_annotate, key=itemgetter('seq_id', 'frame_num'))
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
#%% Copy to a folder by GUID
|
|
98
|
-
|
|
99
|
-
# dataset[dataset_id].seq[sequence_id].frame[frame_number].img[img_id].extension
|
|
100
|
-
|
|
101
|
-
imerit_output_base = os.path.join(output_base,'imerit_batch_9')
|
|
102
|
-
os.makedirs(imerit_output_base,exist_ok=True)
|
|
103
|
-
|
|
104
|
-
# im = images_to_annotate[0]
|
|
105
|
-
for im in tqdm(images_to_annotate):
|
|
106
|
-
|
|
107
|
-
relative_path = im['file_name']
|
|
108
|
-
extension = os.path.splitext(relative_path)[1]
|
|
109
|
-
frame_num = im['frame_num']
|
|
110
|
-
seq_id = im['seq_id']
|
|
111
|
-
id = im['id']
|
|
112
|
-
assert '.' not in id
|
|
113
|
-
input_full_path = os.path.join(image_base,relative_path)
|
|
114
|
-
assert os.path.isfile(input_full_path)
|
|
115
|
-
output_filename = 'datasetzslborneo.seq' + '{0:0>8d}'.format(seq_id) + '.frame' + \
|
|
116
|
-
'{0:0>4d}'.format(frame_num) + '.img' + id + extension
|
|
117
|
-
im['imerit_filename'] = output_filename
|
|
118
|
-
output_full_path = os.path.join(imerit_output_base,output_filename)
|
|
119
|
-
assert not os.path.isfile(output_full_path)
|
|
120
|
-
copyfile(input_full_path,output_full_path)
|
|
121
|
-
|
|
122
|
-
# ...for each image
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
#%% Write out the annotation list
|
|
126
|
-
|
|
127
|
-
imerit_batch9_json_filename = os.path.join(imerit_output_base,'imerit_batch_9.json')
|
|
128
|
-
with open(imerit_batch9_json_filename,'w') as f:
|
|
129
|
-
json.dump(images_to_annotate, f, indent=2)
|
|
130
|
-
|
|
131
|
-
|