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
|
@@ -14,7 +14,7 @@ no detections are present.
|
|
|
14
14
|
|
|
15
15
|
YOLOv5 output has one text file per image, like so:
|
|
16
16
|
|
|
17
|
-
0 0.0141693 0.469758 0.0283385 0.131552 0.761428
|
|
17
|
+
0 0.0141693 0.469758 0.0283385 0.131552 0.761428
|
|
18
18
|
|
|
19
19
|
That's [class, x_center, y_center, width_of_box, height_of_box, confidence]
|
|
20
20
|
|
|
@@ -57,39 +57,39 @@ def read_classes_from_yolo_dataset_file(fn):
|
|
|
57
57
|
Reads a dictionary mapping integer class IDs to class names from a YOLOv5/YOLOv8
|
|
58
58
|
dataset.yaml file or a .json file. A .json file should contain a dictionary mapping
|
|
59
59
|
integer category IDs to string category names.
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
Args:
|
|
62
|
-
fn (str): YOLOv5/YOLOv8 dataset file with a .yml or .yaml extension, a .json file
|
|
62
|
+
fn (str): YOLOv5/YOLOv8 dataset file with a .yml or .yaml extension, a .json file
|
|
63
63
|
mapping integer category IDs to category names, or a .txt file with a flat
|
|
64
64
|
list of classes.
|
|
65
|
-
|
|
65
|
+
|
|
66
66
|
Returns:
|
|
67
67
|
dict: a mapping from integer category IDs to category names
|
|
68
68
|
"""
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
category_id_to_name = {}
|
|
71
|
-
|
|
71
|
+
|
|
72
72
|
if fn.endswith('.yml') or fn.endswith('.yaml'):
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
with open(fn,'r') as f:
|
|
75
75
|
lines = f.readlines()
|
|
76
|
-
|
|
76
|
+
|
|
77
77
|
pat = r'\d+:.+'
|
|
78
78
|
for s in lines:
|
|
79
79
|
if re.search(pat,s) is not None:
|
|
80
80
|
tokens = s.split(':')
|
|
81
81
|
assert len(tokens) == 2, 'Invalid token in category file {}'.format(fn)
|
|
82
82
|
category_id_to_name[int(tokens[0].strip())] = tokens[1].strip()
|
|
83
|
-
|
|
83
|
+
|
|
84
84
|
elif fn.endswith('.json'):
|
|
85
|
-
|
|
85
|
+
|
|
86
86
|
with open(fn,'r') as f:
|
|
87
87
|
d_in = json.load(f)
|
|
88
88
|
for k in d_in.keys():
|
|
89
89
|
category_id_to_name[int(k)] = d_in[k]
|
|
90
|
-
|
|
90
|
+
|
|
91
91
|
elif fn.endswith('.txt'):
|
|
92
|
-
|
|
92
|
+
|
|
93
93
|
with open(fn,'r') as f:
|
|
94
94
|
lines = f.readlines()
|
|
95
95
|
next_category_id = 0
|
|
@@ -99,20 +99,20 @@ def read_classes_from_yolo_dataset_file(fn):
|
|
|
99
99
|
continue
|
|
100
100
|
category_id_to_name[next_category_id] = s
|
|
101
101
|
next_category_id += 1
|
|
102
|
-
|
|
102
|
+
|
|
103
103
|
else:
|
|
104
|
-
|
|
104
|
+
|
|
105
105
|
raise ValueError('Unrecognized category file type: {}'.format(fn))
|
|
106
|
-
|
|
106
|
+
|
|
107
107
|
assert len(category_id_to_name) > 0, 'Failed to read class mappings from {}'.format(fn)
|
|
108
|
-
|
|
108
|
+
|
|
109
109
|
return category_id_to_name
|
|
110
|
-
|
|
111
110
|
|
|
112
|
-
|
|
111
|
+
|
|
112
|
+
def yolo_json_output_to_md_output(yolo_json_file,
|
|
113
113
|
image_folder,
|
|
114
|
-
output_file,
|
|
115
|
-
yolo_category_id_to_name,
|
|
114
|
+
output_file,
|
|
115
|
+
yolo_category_id_to_name,
|
|
116
116
|
detector_name='unknown',
|
|
117
117
|
image_id_to_relative_path=None,
|
|
118
118
|
offset_yolo_class_ids=True,
|
|
@@ -121,56 +121,55 @@ def yolo_json_output_to_md_output(yolo_json_file,
|
|
|
121
121
|
convert_slashes=True):
|
|
122
122
|
"""
|
|
123
123
|
Converts a YOLOv5/YOLOv8 .json file to MD .json format.
|
|
124
|
-
|
|
124
|
+
|
|
125
125
|
Args:
|
|
126
|
-
|
|
127
126
|
yolo_json_file (str): the .json file to convert from YOLOv5 format to MD output format
|
|
128
127
|
image_folder (str): the .json file contains relative path names, this is the path base
|
|
129
|
-
yolo_category_id_to_name (str or dict): the .json results file contains only numeric
|
|
130
|
-
identifiers for categories, but we want names and numbers for the output format;
|
|
131
|
-
yolo_category_id_to_name provides that mapping either as a dict or as a YOLOv5
|
|
128
|
+
yolo_category_id_to_name (str or dict): the .json results file contains only numeric
|
|
129
|
+
identifiers for categories, but we want names and numbers for the output format;
|
|
130
|
+
yolo_category_id_to_name provides that mapping either as a dict or as a YOLOv5
|
|
132
131
|
dataset.yaml file.
|
|
133
|
-
detector_name (str, optional): a string that gets put in the output file, not otherwise
|
|
132
|
+
detector_name (str, optional): a string that gets put in the output file, not otherwise
|
|
134
133
|
used within this function
|
|
135
|
-
image_id_to_relative_path (dict, optional): YOLOv5 .json uses only basenames (e.g.
|
|
134
|
+
image_id_to_relative_path (dict, optional): YOLOv5 .json uses only basenames (e.g.
|
|
136
135
|
abc1234.JPG); by default these will be appended to the input path to create pathnames.
|
|
137
136
|
If you have a flat folder, this is fine. If you want to map base names to relative paths in
|
|
138
|
-
a more complicated way, use this parameter.
|
|
139
|
-
offset_yolo_class_ids (bool, optional): YOLOv5 class IDs always start at zero; if you want to
|
|
140
|
-
make the output classes start at 1, set offset_yolo_class_ids to True.
|
|
141
|
-
truncate_to_standard_md_precision (bool, optional): YOLOv5 .json includes lots of
|
|
137
|
+
a more complicated way, use this parameter.
|
|
138
|
+
offset_yolo_class_ids (bool, optional): YOLOv5 class IDs always start at zero; if you want to
|
|
139
|
+
make the output classes start at 1, set offset_yolo_class_ids to True.
|
|
140
|
+
truncate_to_standard_md_precision (bool, optional): YOLOv5 .json includes lots of
|
|
142
141
|
(not-super-meaningful) precision, set this to truncate to COORD_DIGITS and CONF_DIGITS.
|
|
143
|
-
image_id_to_error (dict, optional): if you want to include image IDs in the output file for which
|
|
142
|
+
image_id_to_error (dict, optional): if you want to include image IDs in the output file for which
|
|
144
143
|
you couldn't prepare the input file in the first place due to errors, include them here.
|
|
145
144
|
convert_slashes (bool, optional): force all slashes to be forward slashes in the output file
|
|
146
|
-
"""
|
|
147
|
-
|
|
145
|
+
"""
|
|
146
|
+
|
|
148
147
|
assert os.path.isfile(yolo_json_file), \
|
|
149
148
|
'Could not find YOLO .json file {}'.format(yolo_json_file)
|
|
150
149
|
assert os.path.isdir(image_folder), \
|
|
151
150
|
'Could not find image folder {}'.format(image_folder)
|
|
152
|
-
|
|
151
|
+
|
|
153
152
|
if image_id_to_error is None:
|
|
154
153
|
image_id_to_error = {}
|
|
155
|
-
|
|
154
|
+
|
|
156
155
|
print('Converting {} to MD format and writing results to {}'.format(
|
|
157
156
|
yolo_json_file,output_file))
|
|
158
|
-
|
|
157
|
+
|
|
159
158
|
if isinstance(yolo_category_id_to_name,str):
|
|
160
159
|
assert os.path.isfile(yolo_category_id_to_name), \
|
|
161
160
|
'YOLO category mapping specified as a string, but file does not exist: {}'.format(
|
|
162
161
|
yolo_category_id_to_name)
|
|
163
162
|
yolo_category_id_to_name = read_classes_from_yolo_dataset_file(yolo_category_id_to_name)
|
|
164
|
-
|
|
163
|
+
|
|
165
164
|
if image_id_to_relative_path is None:
|
|
166
|
-
|
|
165
|
+
|
|
167
166
|
image_files = path_utils.find_images(image_folder,recursive=True)
|
|
168
167
|
image_files = [os.path.relpath(fn,image_folder) for fn in image_files]
|
|
169
|
-
|
|
168
|
+
|
|
170
169
|
# YOLOv5 identifies images in .json output by ID, which is the filename without
|
|
171
170
|
# extension. If a mapping is not provided, these need to be unique.
|
|
172
171
|
image_id_to_relative_path = {}
|
|
173
|
-
|
|
172
|
+
|
|
174
173
|
for fn in image_files:
|
|
175
174
|
image_id = os.path.splitext(os.path.basename(fn))[0]
|
|
176
175
|
if image_id in image_id_to_relative_path:
|
|
@@ -180,49 +179,49 @@ def yolo_json_output_to_md_output(yolo_json_file,
|
|
|
180
179
|
image_id_to_relative_path[image_id] = fn
|
|
181
180
|
|
|
182
181
|
image_files_relative = sorted(list(image_id_to_relative_path.values()))
|
|
183
|
-
|
|
182
|
+
|
|
184
183
|
image_file_relative_to_image_id = {}
|
|
185
184
|
for image_id in image_id_to_relative_path:
|
|
186
185
|
relative_path = image_id_to_relative_path[image_id]
|
|
187
186
|
assert relative_path not in image_file_relative_to_image_id, \
|
|
188
187
|
'Duplication image IDs in YOLO output conversion for image {}'.format(relative_path)
|
|
189
188
|
image_file_relative_to_image_id[relative_path] = image_id
|
|
190
|
-
|
|
189
|
+
|
|
191
190
|
with open(yolo_json_file,'r') as f:
|
|
192
191
|
detections = json.load(f)
|
|
193
192
|
assert isinstance(detections,list)
|
|
194
|
-
|
|
193
|
+
|
|
195
194
|
image_id_to_detections = defaultdict(list)
|
|
196
|
-
|
|
195
|
+
|
|
197
196
|
int_formatted_image_ids = False
|
|
198
|
-
|
|
197
|
+
|
|
199
198
|
# det = detections[0]
|
|
200
199
|
for det in detections:
|
|
201
|
-
|
|
200
|
+
|
|
202
201
|
# This could be a string, but if the YOLOv5 inference script sees that the strings
|
|
203
202
|
# are really ints, it converts to ints.
|
|
204
203
|
image_id = det['image_id']
|
|
205
204
|
image_id_to_detections[image_id].append(det)
|
|
206
205
|
if isinstance(image_id,int):
|
|
207
206
|
int_formatted_image_ids = True
|
|
208
|
-
|
|
207
|
+
|
|
209
208
|
# If there are any ints present, everything should be ints
|
|
210
209
|
if int_formatted_image_ids:
|
|
211
210
|
for det in detections:
|
|
212
211
|
assert isinstance(det['image_id'],int), \
|
|
213
212
|
'Found mixed int and string image IDs'
|
|
214
|
-
|
|
213
|
+
|
|
215
214
|
# Convert the keys in image_id_to_error to ints
|
|
216
215
|
#
|
|
217
|
-
# This should error if we're given non-int-friendly IDs
|
|
218
|
-
int_formatted_image_id_to_error = {}
|
|
216
|
+
# This should error if we're given non-int-friendly IDs
|
|
217
|
+
int_formatted_image_id_to_error = {}
|
|
219
218
|
for image_id in image_id_to_error:
|
|
220
219
|
int_formatted_image_id_to_error[int(image_id)] = \
|
|
221
220
|
image_id_to_error[image_id]
|
|
222
|
-
image_id_to_error = int_formatted_image_id_to_error
|
|
223
|
-
|
|
221
|
+
image_id_to_error = int_formatted_image_id_to_error
|
|
222
|
+
|
|
224
223
|
# ...if image IDs are formatted as integers in YOLO output
|
|
225
|
-
|
|
224
|
+
|
|
226
225
|
# In a modified version of val.py, we use negative category IDs to indicate an error
|
|
227
226
|
# that happened during inference (typically truncated images with valid headers,
|
|
228
227
|
# so corruption was not detected during val.py's initial corruption check pass.
|
|
@@ -232,17 +231,17 @@ def yolo_json_output_to_md_output(yolo_json_file,
|
|
|
232
231
|
error_string = det['error']
|
|
233
232
|
print('Caught inference-time failure {} for image {}'.format(error_string,det['image_id']))
|
|
234
233
|
image_id_to_error[det['image_id']] = error_string
|
|
235
|
-
|
|
234
|
+
|
|
236
235
|
output_images = []
|
|
237
|
-
|
|
236
|
+
|
|
238
237
|
# image_file_relative = image_files_relative[10]
|
|
239
238
|
for image_file_relative in tqdm(image_files_relative):
|
|
240
|
-
|
|
239
|
+
|
|
241
240
|
im = {}
|
|
242
241
|
im['file'] = image_file_relative
|
|
243
242
|
if convert_slashes:
|
|
244
243
|
im['file'] = im['file'].replace('\\','/')
|
|
245
|
-
|
|
244
|
+
|
|
246
245
|
image_id = image_file_relative_to_image_id[image_file_relative]
|
|
247
246
|
if int_formatted_image_ids:
|
|
248
247
|
image_id = int(image_id)
|
|
@@ -254,7 +253,7 @@ def yolo_json_output_to_md_output(yolo_json_file,
|
|
|
254
253
|
detections = []
|
|
255
254
|
else:
|
|
256
255
|
detections = image_id_to_detections[image_id]
|
|
257
|
-
|
|
256
|
+
|
|
258
257
|
image_full_path = os.path.join(image_folder,image_file_relative)
|
|
259
258
|
try:
|
|
260
259
|
pil_im = vis_utils.open_image(image_full_path)
|
|
@@ -264,17 +263,17 @@ def yolo_json_output_to_md_output(yolo_json_file,
|
|
|
264
263
|
im['failure'] = 'Conversion error: {}'.format(s)
|
|
265
264
|
output_images.append(im)
|
|
266
265
|
continue
|
|
267
|
-
|
|
266
|
+
|
|
268
267
|
im['detections'] = []
|
|
269
|
-
|
|
268
|
+
|
|
270
269
|
image_w = pil_im.size[0]
|
|
271
270
|
image_h = pil_im.size[1]
|
|
272
|
-
|
|
271
|
+
|
|
273
272
|
# det = detections[0]
|
|
274
273
|
for det in detections:
|
|
275
|
-
|
|
274
|
+
|
|
276
275
|
output_det = {}
|
|
277
|
-
|
|
276
|
+
|
|
278
277
|
yolo_cat_id = int(det['category_id'])
|
|
279
278
|
if offset_yolo_class_ids:
|
|
280
279
|
yolo_cat_id += 1
|
|
@@ -284,63 +283,62 @@ def yolo_json_output_to_md_output(yolo_json_file,
|
|
|
284
283
|
conf = ct_utils.round_float(conf,CONF_DIGITS)
|
|
285
284
|
output_det['conf'] = conf
|
|
286
285
|
input_bbox = det['bbox']
|
|
287
|
-
|
|
286
|
+
|
|
288
287
|
# YOLO's COCO .json is not *that* COCO-like, but it is COCO-like in
|
|
289
288
|
# that the boxes are already [xmin/ymin/w/h]
|
|
290
289
|
box_xmin_absolute = input_bbox[0]
|
|
291
290
|
box_ymin_absolute = input_bbox[1]
|
|
292
291
|
box_width_absolute = input_bbox[2]
|
|
293
292
|
box_height_absolute = input_bbox[3]
|
|
294
|
-
|
|
293
|
+
|
|
295
294
|
box_xmin_relative = box_xmin_absolute / image_w
|
|
296
295
|
box_ymin_relative = box_ymin_absolute / image_h
|
|
297
296
|
box_width_relative = box_width_absolute / image_w
|
|
298
297
|
box_height_relative = box_height_absolute / image_h
|
|
299
|
-
|
|
298
|
+
|
|
300
299
|
output_bbox = [box_xmin_relative,box_ymin_relative,
|
|
301
300
|
box_width_relative,box_height_relative]
|
|
302
|
-
|
|
301
|
+
|
|
303
302
|
if truncate_to_standard_md_precision:
|
|
304
303
|
output_bbox = ct_utils.round_float_array(output_bbox,COORD_DIGITS)
|
|
305
|
-
|
|
304
|
+
|
|
306
305
|
output_det['bbox'] = output_bbox
|
|
307
306
|
im['detections'].append(output_det)
|
|
308
|
-
|
|
309
|
-
# ...for each detection
|
|
310
|
-
|
|
307
|
+
|
|
308
|
+
# ...for each detection
|
|
309
|
+
|
|
311
310
|
output_images.append(im)
|
|
312
|
-
|
|
311
|
+
|
|
313
312
|
# ...for each image file
|
|
314
|
-
|
|
313
|
+
|
|
315
314
|
d = {}
|
|
316
315
|
d['images'] = output_images
|
|
317
316
|
d['info'] = {'format_version':'1.4','detector':detector_name}
|
|
318
317
|
d['detection_categories'] = {}
|
|
319
|
-
|
|
318
|
+
|
|
320
319
|
for cat_id in yolo_category_id_to_name:
|
|
321
320
|
yolo_cat_id = int(cat_id)
|
|
322
321
|
if offset_yolo_class_ids:
|
|
323
322
|
yolo_cat_id += 1
|
|
324
323
|
d['detection_categories'][str(yolo_cat_id)] = yolo_category_id_to_name[cat_id]
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
324
|
+
|
|
325
|
+
ct_utils.write_json(output_file, d)
|
|
326
|
+
|
|
329
327
|
# ...def yolo_json_output_to_md_output(...)
|
|
330
|
-
|
|
331
328
|
|
|
332
|
-
|
|
329
|
+
|
|
330
|
+
def yolo_txt_output_to_md_output(input_results_folder,
|
|
333
331
|
image_folder,
|
|
334
|
-
output_file,
|
|
332
|
+
output_file,
|
|
335
333
|
detector_tag=None,
|
|
336
334
|
truncate_to_standard_md_precision=True):
|
|
337
335
|
"""
|
|
338
336
|
Converts a folder of YOLO-output .txt files to MD .json format.
|
|
339
|
-
|
|
340
|
-
Less finished than the .json conversion function; this .txt conversion assumes
|
|
341
|
-
a hard-coded mapping representing the standard MD categories (in MD indexing,
|
|
337
|
+
|
|
338
|
+
Less finished than the .json conversion function; this .txt conversion assumes
|
|
339
|
+
a hard-coded mapping representing the standard MD categories (in MD indexing,
|
|
342
340
|
1/2/3=animal/person/vehicle; in YOLO indexing, 0/1/2=animal/person/vehicle).
|
|
343
|
-
|
|
341
|
+
|
|
344
342
|
Args:
|
|
345
343
|
input_results_folder (str): the folder containing YOLO-output .txt files
|
|
346
344
|
image_folder (str): the folder where images live, may be the same as
|
|
@@ -349,47 +347,47 @@ def yolo_txt_output_to_md_output(input_results_folder,
|
|
|
349
347
|
results
|
|
350
348
|
detector_tag (str, optional): string to put in the 'detector' field in the
|
|
351
349
|
output file
|
|
352
|
-
truncate_to_standard_md_precision (bool, optional): set this to truncate to
|
|
350
|
+
truncate_to_standard_md_precision (bool, optional): set this to truncate to
|
|
353
351
|
COORD_DIGITS and CONF_DIGITS, like the standard MD pipeline does.
|
|
354
352
|
"""
|
|
355
|
-
|
|
353
|
+
|
|
356
354
|
assert os.path.isdir(input_results_folder)
|
|
357
355
|
assert os.path.isdir(image_folder)
|
|
358
|
-
|
|
356
|
+
|
|
359
357
|
## Enumerate results files and image files
|
|
360
|
-
|
|
358
|
+
|
|
361
359
|
yolo_results_files = os.listdir(input_results_folder)
|
|
362
360
|
yolo_results_files = [f for f in yolo_results_files if f.lower().endswith('.txt')]
|
|
363
361
|
# print('Found {} results files'.format(len(yolo_results_files)))
|
|
364
|
-
|
|
362
|
+
|
|
365
363
|
image_files = path_utils.find_images(image_folder,recursive=False)
|
|
366
364
|
image_files_relative = [os.path.basename(f) for f in image_files]
|
|
367
365
|
# print('Found {} images'.format(len(image_files)))
|
|
368
|
-
|
|
366
|
+
|
|
369
367
|
image_files_relative_no_extension = [os.path.splitext(f)[0] for f in image_files_relative]
|
|
370
|
-
|
|
368
|
+
|
|
371
369
|
## Make sure that every results file corresponds to an image
|
|
372
|
-
|
|
370
|
+
|
|
373
371
|
for f in yolo_results_files:
|
|
374
372
|
result_no_extension = os.path.splitext(f)[0]
|
|
375
373
|
assert result_no_extension in image_files_relative_no_extension
|
|
376
|
-
|
|
374
|
+
|
|
377
375
|
## Build MD output data
|
|
378
|
-
|
|
376
|
+
|
|
379
377
|
# Map 0-indexed YOLO categories to 1-indexed MD categories
|
|
380
378
|
yolo_cat_map = { 0: 1, 1: 2, 2: 3 }
|
|
381
|
-
|
|
379
|
+
|
|
382
380
|
images_entries = []
|
|
383
381
|
|
|
384
382
|
# image_fn = image_files_relative[0]
|
|
385
383
|
for image_fn in image_files_relative:
|
|
386
|
-
|
|
387
|
-
image_name, ext = os.path.splitext(image_fn)
|
|
384
|
+
|
|
385
|
+
image_name, ext = os.path.splitext(image_fn)
|
|
388
386
|
label_fn = image_name + '.txt'
|
|
389
387
|
label_path = os.path.join(input_results_folder, label_fn)
|
|
390
|
-
|
|
388
|
+
|
|
391
389
|
detections = []
|
|
392
|
-
|
|
390
|
+
|
|
393
391
|
if not os.path.exists(label_path):
|
|
394
392
|
# This is assumed to be an image with no detections
|
|
395
393
|
pass
|
|
@@ -397,36 +395,36 @@ def yolo_txt_output_to_md_output(input_results_folder,
|
|
|
397
395
|
with open(label_path, newline='') as f:
|
|
398
396
|
reader = csv.reader(f, delimiter=' ')
|
|
399
397
|
for row in reader:
|
|
400
|
-
category = yolo_cat_map[int(row[0])]
|
|
401
|
-
api_box = ct_utils.convert_yolo_to_xywh([float(row[1]), float(row[2]),
|
|
398
|
+
category = yolo_cat_map[int(row[0])]
|
|
399
|
+
api_box = ct_utils.convert_yolo_to_xywh([float(row[1]), float(row[2]),
|
|
402
400
|
float(row[3]), float(row[4])])
|
|
403
|
-
|
|
401
|
+
|
|
404
402
|
conf = float(row[5])
|
|
405
|
-
|
|
403
|
+
|
|
406
404
|
if truncate_to_standard_md_precision:
|
|
407
405
|
conf = ct_utils.round_float(conf, precision=CONF_DIGITS)
|
|
408
406
|
api_box = ct_utils.round_float_array(api_box, precision=COORD_DIGITS)
|
|
409
|
-
|
|
407
|
+
|
|
410
408
|
detections.append({
|
|
411
409
|
'category': str(category),
|
|
412
410
|
'conf': conf,
|
|
413
411
|
'bbox': api_box
|
|
414
412
|
})
|
|
415
|
-
|
|
413
|
+
|
|
416
414
|
images_entries.append({
|
|
417
415
|
'file': image_fn,
|
|
418
416
|
'detections': detections
|
|
419
417
|
})
|
|
420
|
-
|
|
418
|
+
|
|
421
419
|
# ...for each image
|
|
422
|
-
|
|
420
|
+
|
|
423
421
|
## Save output file
|
|
424
|
-
|
|
422
|
+
|
|
425
423
|
detector_string = 'converted_from_yolo_format'
|
|
426
|
-
|
|
424
|
+
|
|
427
425
|
if detector_tag is not None:
|
|
428
426
|
detector_string = detector_tag
|
|
429
|
-
|
|
427
|
+
|
|
430
428
|
output_content = {
|
|
431
429
|
'info': {
|
|
432
430
|
'detector': detector_string,
|
|
@@ -440,24 +438,23 @@ def yolo_txt_output_to_md_output(input_results_folder,
|
|
|
440
438
|
},
|
|
441
439
|
'images': images_entries
|
|
442
440
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
441
|
+
|
|
442
|
+
ct_utils.write_json(output_file, output_content)
|
|
443
|
+
|
|
447
444
|
# ...def yolo_txt_output_to_md_output(...)
|
|
448
445
|
|
|
449
446
|
|
|
450
447
|
#%% Interactive driver
|
|
451
448
|
|
|
452
449
|
if False:
|
|
453
|
-
|
|
450
|
+
|
|
454
451
|
pass
|
|
455
452
|
|
|
456
|
-
#%%
|
|
457
|
-
|
|
453
|
+
#%%
|
|
454
|
+
|
|
458
455
|
input_results_folder = os.path.expanduser('~/tmp/model-version-experiments/pt-test-kru/exp/labels')
|
|
459
456
|
image_folder = os.path.expanduser('~/data/KRU-test')
|
|
460
|
-
output_file = os.path.expanduser('~/data/mdv5a-yolo-pt-kru.json')
|
|
457
|
+
output_file = os.path.expanduser('~/data/mdv5a-yolo-pt-kru.json')
|
|
461
458
|
yolo_txt_output_to_md_output(input_results_folder,image_folder,output_file)
|
|
462
459
|
|
|
463
460
|
|