megadetector 10.0.15__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.
- megadetector/__init__.py +0 -0
- megadetector/api/__init__.py +0 -0
- megadetector/api/batch_processing/integration/digiKam/setup.py +6 -0
- megadetector/api/batch_processing/integration/digiKam/xmp_integration.py +465 -0
- megadetector/api/batch_processing/integration/eMammal/test_scripts/config_template.py +5 -0
- megadetector/api/batch_processing/integration/eMammal/test_scripts/push_annotations_to_emammal.py +125 -0
- megadetector/api/batch_processing/integration/eMammal/test_scripts/select_images_for_testing.py +55 -0
- megadetector/classification/__init__.py +0 -0
- megadetector/classification/aggregate_classifier_probs.py +108 -0
- megadetector/classification/analyze_failed_images.py +227 -0
- megadetector/classification/cache_batchapi_outputs.py +198 -0
- megadetector/classification/create_classification_dataset.py +626 -0
- megadetector/classification/crop_detections.py +516 -0
- megadetector/classification/csv_to_json.py +226 -0
- megadetector/classification/detect_and_crop.py +853 -0
- megadetector/classification/efficientnet/__init__.py +9 -0
- megadetector/classification/efficientnet/model.py +415 -0
- megadetector/classification/efficientnet/utils.py +608 -0
- megadetector/classification/evaluate_model.py +520 -0
- megadetector/classification/identify_mislabeled_candidates.py +152 -0
- megadetector/classification/json_to_azcopy_list.py +63 -0
- megadetector/classification/json_validator.py +696 -0
- megadetector/classification/map_classification_categories.py +276 -0
- megadetector/classification/merge_classification_detection_output.py +509 -0
- megadetector/classification/prepare_classification_script.py +194 -0
- megadetector/classification/prepare_classification_script_mc.py +228 -0
- megadetector/classification/run_classifier.py +287 -0
- megadetector/classification/save_mislabeled.py +110 -0
- megadetector/classification/train_classifier.py +827 -0
- megadetector/classification/train_classifier_tf.py +725 -0
- megadetector/classification/train_utils.py +323 -0
- megadetector/data_management/__init__.py +0 -0
- megadetector/data_management/animl_to_md.py +161 -0
- megadetector/data_management/annotations/__init__.py +0 -0
- megadetector/data_management/annotations/annotation_constants.py +33 -0
- megadetector/data_management/camtrap_dp_to_coco.py +270 -0
- megadetector/data_management/cct_json_utils.py +566 -0
- megadetector/data_management/cct_to_md.py +184 -0
- megadetector/data_management/cct_to_wi.py +293 -0
- megadetector/data_management/coco_to_labelme.py +284 -0
- megadetector/data_management/coco_to_yolo.py +701 -0
- megadetector/data_management/databases/__init__.py +0 -0
- megadetector/data_management/databases/add_width_and_height_to_db.py +107 -0
- megadetector/data_management/databases/combine_coco_camera_traps_files.py +210 -0
- megadetector/data_management/databases/integrity_check_json_db.py +563 -0
- megadetector/data_management/databases/subset_json_db.py +195 -0
- megadetector/data_management/generate_crops_from_cct.py +200 -0
- megadetector/data_management/get_image_sizes.py +164 -0
- megadetector/data_management/labelme_to_coco.py +559 -0
- megadetector/data_management/labelme_to_yolo.py +349 -0
- megadetector/data_management/lila/__init__.py +0 -0
- megadetector/data_management/lila/create_lila_blank_set.py +556 -0
- megadetector/data_management/lila/create_lila_test_set.py +192 -0
- megadetector/data_management/lila/create_links_to_md_results_files.py +106 -0
- megadetector/data_management/lila/download_lila_subset.py +182 -0
- megadetector/data_management/lila/generate_lila_per_image_labels.py +777 -0
- megadetector/data_management/lila/get_lila_annotation_counts.py +174 -0
- megadetector/data_management/lila/get_lila_image_counts.py +112 -0
- megadetector/data_management/lila/lila_common.py +319 -0
- megadetector/data_management/lila/test_lila_metadata_urls.py +164 -0
- megadetector/data_management/mewc_to_md.py +344 -0
- megadetector/data_management/ocr_tools.py +873 -0
- megadetector/data_management/read_exif.py +964 -0
- megadetector/data_management/remap_coco_categories.py +195 -0
- megadetector/data_management/remove_exif.py +156 -0
- megadetector/data_management/rename_images.py +194 -0
- megadetector/data_management/resize_coco_dataset.py +665 -0
- megadetector/data_management/speciesnet_to_md.py +41 -0
- megadetector/data_management/wi_download_csv_to_coco.py +247 -0
- megadetector/data_management/yolo_output_to_md_output.py +594 -0
- megadetector/data_management/yolo_to_coco.py +984 -0
- megadetector/data_management/zamba_to_md.py +188 -0
- megadetector/detection/__init__.py +0 -0
- megadetector/detection/change_detection.py +840 -0
- megadetector/detection/process_video.py +479 -0
- megadetector/detection/pytorch_detector.py +1451 -0
- megadetector/detection/run_detector.py +1267 -0
- megadetector/detection/run_detector_batch.py +2172 -0
- megadetector/detection/run_inference_with_yolov5_val.py +1314 -0
- megadetector/detection/run_md_and_speciesnet.py +1604 -0
- megadetector/detection/run_tiled_inference.py +1044 -0
- megadetector/detection/tf_detector.py +209 -0
- megadetector/detection/video_utils.py +1379 -0
- megadetector/postprocessing/__init__.py +0 -0
- megadetector/postprocessing/add_max_conf.py +72 -0
- megadetector/postprocessing/categorize_detections_by_size.py +166 -0
- megadetector/postprocessing/classification_postprocessing.py +1943 -0
- megadetector/postprocessing/combine_batch_outputs.py +249 -0
- megadetector/postprocessing/compare_batch_results.py +2110 -0
- megadetector/postprocessing/convert_output_format.py +403 -0
- megadetector/postprocessing/create_crop_folder.py +629 -0
- megadetector/postprocessing/detector_calibration.py +570 -0
- megadetector/postprocessing/generate_csv_report.py +522 -0
- megadetector/postprocessing/load_api_results.py +223 -0
- megadetector/postprocessing/md_to_coco.py +428 -0
- megadetector/postprocessing/md_to_labelme.py +351 -0
- megadetector/postprocessing/md_to_wi.py +41 -0
- megadetector/postprocessing/merge_detections.py +392 -0
- megadetector/postprocessing/postprocess_batch_results.py +2140 -0
- megadetector/postprocessing/remap_detection_categories.py +226 -0
- megadetector/postprocessing/render_detection_confusion_matrix.py +677 -0
- megadetector/postprocessing/repeat_detection_elimination/find_repeat_detections.py +206 -0
- megadetector/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +82 -0
- megadetector/postprocessing/repeat_detection_elimination/repeat_detections_core.py +1665 -0
- megadetector/postprocessing/separate_detections_into_folders.py +795 -0
- megadetector/postprocessing/subset_json_detector_output.py +964 -0
- megadetector/postprocessing/top_folders_to_bottom.py +238 -0
- megadetector/postprocessing/validate_batch_results.py +332 -0
- megadetector/taxonomy_mapping/__init__.py +0 -0
- megadetector/taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +491 -0
- megadetector/taxonomy_mapping/map_new_lila_datasets.py +211 -0
- megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +165 -0
- megadetector/taxonomy_mapping/preview_lila_taxonomy.py +543 -0
- megadetector/taxonomy_mapping/retrieve_sample_image.py +71 -0
- megadetector/taxonomy_mapping/simple_image_download.py +231 -0
- megadetector/taxonomy_mapping/species_lookup.py +1008 -0
- megadetector/taxonomy_mapping/taxonomy_csv_checker.py +159 -0
- megadetector/taxonomy_mapping/taxonomy_graph.py +346 -0
- megadetector/taxonomy_mapping/validate_lila_category_mappings.py +83 -0
- megadetector/tests/__init__.py +0 -0
- megadetector/tests/test_nms_synthetic.py +335 -0
- megadetector/utils/__init__.py +0 -0
- megadetector/utils/ct_utils.py +1857 -0
- megadetector/utils/directory_listing.py +199 -0
- megadetector/utils/extract_frames_from_video.py +307 -0
- megadetector/utils/gpu_test.py +125 -0
- megadetector/utils/md_tests.py +2072 -0
- megadetector/utils/path_utils.py +2872 -0
- megadetector/utils/process_utils.py +172 -0
- megadetector/utils/split_locations_into_train_val.py +237 -0
- megadetector/utils/string_utils.py +234 -0
- megadetector/utils/url_utils.py +825 -0
- megadetector/utils/wi_platform_utils.py +968 -0
- megadetector/utils/wi_taxonomy_utils.py +1766 -0
- megadetector/utils/write_html_image_list.py +239 -0
- megadetector/visualization/__init__.py +0 -0
- megadetector/visualization/plot_utils.py +309 -0
- megadetector/visualization/render_images_with_thumbnails.py +243 -0
- megadetector/visualization/visualization_utils.py +1973 -0
- megadetector/visualization/visualize_db.py +630 -0
- megadetector/visualization/visualize_detector_output.py +498 -0
- megadetector/visualization/visualize_video_output.py +705 -0
- megadetector-10.0.15.dist-info/METADATA +115 -0
- megadetector-10.0.15.dist-info/RECORD +147 -0
- megadetector-10.0.15.dist-info/WHEEL +5 -0
- megadetector-10.0.15.dist-info/licenses/LICENSE +19 -0
- megadetector-10.0.15.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
"""
|
|
2
|
+
|
|
3
|
+
convert_output_format.py
|
|
4
|
+
|
|
5
|
+
Converts between file .json and .csv representations of MD output. The .csv format is
|
|
6
|
+
largely obsolete, don't use it unless you're super-duper sure you need it.
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
#%% Constants and imports
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
import json
|
|
14
|
+
import sys
|
|
15
|
+
import os
|
|
16
|
+
|
|
17
|
+
from tqdm import tqdm
|
|
18
|
+
from collections import defaultdict
|
|
19
|
+
|
|
20
|
+
import pandas as pd
|
|
21
|
+
|
|
22
|
+
from megadetector.postprocessing.load_api_results import load_api_results_csv
|
|
23
|
+
from megadetector.utils.wi_taxonomy_utils import load_md_or_speciesnet_file
|
|
24
|
+
from megadetector.data_management.annotations import annotation_constants
|
|
25
|
+
from megadetector.utils.ct_utils import get_max_conf
|
|
26
|
+
from megadetector.utils.ct_utils import write_json
|
|
27
|
+
|
|
28
|
+
CONF_DIGITS = 3
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
#%% Conversion functions
|
|
32
|
+
|
|
33
|
+
def convert_json_to_csv(input_path,
|
|
34
|
+
output_path=None,
|
|
35
|
+
min_confidence=None,
|
|
36
|
+
omit_bounding_boxes=False,
|
|
37
|
+
output_encoding=None,
|
|
38
|
+
overwrite=True,
|
|
39
|
+
verbose=False):
|
|
40
|
+
"""
|
|
41
|
+
Converts a MD results .json file to a totally non-standard .csv format.
|
|
42
|
+
|
|
43
|
+
If [output_path] is None, will convert x.json to x.csv.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
input_path (str): the input .json file to convert
|
|
47
|
+
output_path (str, optional): the output .csv file to generate; if this is None, uses
|
|
48
|
+
[input_path].csv
|
|
49
|
+
min_confidence (float, optional): the minimum-confidence detection we should include
|
|
50
|
+
in the "detections" column; has no impact on the other columns
|
|
51
|
+
omit_bounding_boxes (bool, optional): whether to leave out the json-formatted bounding
|
|
52
|
+
boxes that make up the "detections" column, which are not generally useful for someone
|
|
53
|
+
who wants to consume this data as a .csv file
|
|
54
|
+
output_encoding (str, optional): encoding to use for the .csv file
|
|
55
|
+
overwrite (bool, optional): whether to overwrite an existing .csv file; if this is False and
|
|
56
|
+
the output file exists, no-ops and returns
|
|
57
|
+
verbose (bool, optional): enable additional debug output
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
if output_path is None:
|
|
61
|
+
output_path = os.path.splitext(input_path)[0]+'.csv'
|
|
62
|
+
|
|
63
|
+
if os.path.isfile(output_path) and (not overwrite):
|
|
64
|
+
print('File {} exists, skipping json --> csv conversion'.format(output_path))
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
print('Loading json results from {}...'.format(input_path))
|
|
68
|
+
json_output = load_md_or_speciesnet_file(input_path,
|
|
69
|
+
verbose=verbose)
|
|
70
|
+
|
|
71
|
+
def clean_category_name(s):
|
|
72
|
+
return s.replace(',','_').replace(' ','_').lower()
|
|
73
|
+
|
|
74
|
+
# Create column names for max detection confidences
|
|
75
|
+
detection_category_id_to_max_conf_column_name = {}
|
|
76
|
+
for category_id in json_output['detection_categories'].keys():
|
|
77
|
+
category_name = clean_category_name(json_output['detection_categories'][category_id])
|
|
78
|
+
detection_category_id_to_max_conf_column_name[category_id] = \
|
|
79
|
+
'max_conf_' + category_name
|
|
80
|
+
|
|
81
|
+
classification_category_id_to_max_conf_column_name = {}
|
|
82
|
+
|
|
83
|
+
# Create column names for max classification confidences (if necessary)
|
|
84
|
+
if 'classification_categories' in json_output.keys():
|
|
85
|
+
|
|
86
|
+
for category_id in json_output['classification_categories'].keys():
|
|
87
|
+
category_name = clean_category_name(json_output['classification_categories'][category_id])
|
|
88
|
+
classification_category_id_to_max_conf_column_name[category_id] = \
|
|
89
|
+
'max_classification_conf_' + category_name
|
|
90
|
+
|
|
91
|
+
# There are several .json fields for which we add .csv columns; other random bespoke fields
|
|
92
|
+
# will be ignored.
|
|
93
|
+
optional_fields = ['width','height','datetime','exif_metadata']
|
|
94
|
+
optional_fields_present = set()
|
|
95
|
+
|
|
96
|
+
# Iterate once over the data to check for optional fields
|
|
97
|
+
print('Looking for optional fields...')
|
|
98
|
+
|
|
99
|
+
for im in tqdm(json_output['images']):
|
|
100
|
+
# Which optional fields are present for this image?
|
|
101
|
+
for k in im.keys():
|
|
102
|
+
if k in optional_fields:
|
|
103
|
+
optional_fields_present.add(k)
|
|
104
|
+
|
|
105
|
+
optional_fields_present = sorted(list(optional_fields_present))
|
|
106
|
+
if len(optional_fields_present) > 0:
|
|
107
|
+
print('Found {} optional fields'.format(len(optional_fields_present)))
|
|
108
|
+
|
|
109
|
+
print('Formatting results...')
|
|
110
|
+
|
|
111
|
+
output_records = []
|
|
112
|
+
|
|
113
|
+
# i_image = 0; im = json_output['images'][i_image]
|
|
114
|
+
for im in tqdm(json_output['images']):
|
|
115
|
+
|
|
116
|
+
output_record = {}
|
|
117
|
+
output_records.append(output_record)
|
|
118
|
+
|
|
119
|
+
output_record['image_path'] = im['file']
|
|
120
|
+
output_record['max_confidence'] = ''
|
|
121
|
+
output_record['detections'] = ''
|
|
122
|
+
|
|
123
|
+
for field_name in optional_fields_present:
|
|
124
|
+
output_record[field_name] = ''
|
|
125
|
+
if field_name in im:
|
|
126
|
+
output_record[field_name] = im[field_name]
|
|
127
|
+
|
|
128
|
+
for detection_category_id in detection_category_id_to_max_conf_column_name:
|
|
129
|
+
column_name = detection_category_id_to_max_conf_column_name[detection_category_id]
|
|
130
|
+
output_record[column_name] = 0
|
|
131
|
+
|
|
132
|
+
for classification_category_id in classification_category_id_to_max_conf_column_name:
|
|
133
|
+
column_name = classification_category_id_to_max_conf_column_name[classification_category_id]
|
|
134
|
+
output_record[column_name] = 0
|
|
135
|
+
|
|
136
|
+
if 'failure' in im and im['failure'] is not None:
|
|
137
|
+
output_record['max_confidence'] = 'failure'
|
|
138
|
+
output_record['detections'] = im['failure']
|
|
139
|
+
# print('Skipping failed image {} ({})'.format(im['file'],im['failure']))
|
|
140
|
+
continue
|
|
141
|
+
|
|
142
|
+
max_conf = get_max_conf(im)
|
|
143
|
+
detection_category_id_to_max_conf = defaultdict(float)
|
|
144
|
+
classification_category_id_to_max_conf = defaultdict(float)
|
|
145
|
+
detections = []
|
|
146
|
+
|
|
147
|
+
# d = im['detections'][0]
|
|
148
|
+
for d in im['detections']:
|
|
149
|
+
|
|
150
|
+
# Skip sub-threshold detections
|
|
151
|
+
if (min_confidence is not None) and (d['conf'] < min_confidence):
|
|
152
|
+
continue
|
|
153
|
+
|
|
154
|
+
input_bbox = d['bbox']
|
|
155
|
+
|
|
156
|
+
# Our .json format is xmin/ymin/w/h
|
|
157
|
+
#
|
|
158
|
+
# Our .csv format was ymin/xmin/ymax/xmax
|
|
159
|
+
xmin = input_bbox[0]
|
|
160
|
+
ymin = input_bbox[1]
|
|
161
|
+
xmax = input_bbox[0] + input_bbox[2]
|
|
162
|
+
ymax = input_bbox[1] + input_bbox[3]
|
|
163
|
+
output_detection = [ymin, xmin, ymax, xmax]
|
|
164
|
+
output_detection.append(d['conf'])
|
|
165
|
+
output_detection.append(int(d['category']))
|
|
166
|
+
detections.append(output_detection)
|
|
167
|
+
|
|
168
|
+
detection_category_id = d['category']
|
|
169
|
+
detection_category_max = detection_category_id_to_max_conf[detection_category_id]
|
|
170
|
+
if d['conf'] > detection_category_max:
|
|
171
|
+
detection_category_id_to_max_conf[detection_category_id] = d['conf']
|
|
172
|
+
|
|
173
|
+
if 'classifications' in d:
|
|
174
|
+
|
|
175
|
+
for c in d['classifications']:
|
|
176
|
+
classification_category_id = c[0]
|
|
177
|
+
classification_conf = c[1]
|
|
178
|
+
classification_category_max = \
|
|
179
|
+
classification_category_id_to_max_conf[classification_category_id]
|
|
180
|
+
if classification_conf > classification_category_max:
|
|
181
|
+
classification_category_id_to_max_conf[classification_category_id] = \
|
|
182
|
+
classification_conf
|
|
183
|
+
|
|
184
|
+
# ...for each classification
|
|
185
|
+
|
|
186
|
+
# ...if we have classification results for this detection
|
|
187
|
+
|
|
188
|
+
# ...for each detection
|
|
189
|
+
|
|
190
|
+
detection_string = ''
|
|
191
|
+
if not omit_bounding_boxes:
|
|
192
|
+
detection_string = json.dumps(detections)
|
|
193
|
+
|
|
194
|
+
output_record['detections'] = detection_string
|
|
195
|
+
output_record['max_confidence'] = max_conf
|
|
196
|
+
|
|
197
|
+
for detection_category_id in detection_category_id_to_max_conf_column_name:
|
|
198
|
+
column_name = detection_category_id_to_max_conf_column_name[detection_category_id]
|
|
199
|
+
output_record[column_name] = \
|
|
200
|
+
detection_category_id_to_max_conf[detection_category_id]
|
|
201
|
+
|
|
202
|
+
for classification_category_id in classification_category_id_to_max_conf_column_name:
|
|
203
|
+
column_name = classification_category_id_to_max_conf_column_name[classification_category_id]
|
|
204
|
+
output_record[column_name] = \
|
|
205
|
+
classification_category_id_to_max_conf[classification_category_id]
|
|
206
|
+
|
|
207
|
+
# ...for each image
|
|
208
|
+
|
|
209
|
+
print('Writing to csv...')
|
|
210
|
+
|
|
211
|
+
df = pd.DataFrame(output_records)
|
|
212
|
+
|
|
213
|
+
if omit_bounding_boxes:
|
|
214
|
+
df = df.drop('detections',axis=1)
|
|
215
|
+
df.to_csv(output_path,index=False,header=True,encoding=output_encoding)
|
|
216
|
+
|
|
217
|
+
# ...def convert_json_to_csv(...)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def convert_csv_to_json(input_path,output_path=None,overwrite=True):
|
|
221
|
+
"""
|
|
222
|
+
Convert .csv to .json. If output_path is None, will convert x.csv to x.json. This
|
|
223
|
+
supports a largely obsolete .csv format, there's almost no reason you want to do this.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
input_path (str): .csv filename to convert to .json
|
|
227
|
+
output_path (str, optional): the output .json file to generate; if this is None, uses
|
|
228
|
+
[input_path].json
|
|
229
|
+
overwrite (bool, optional): whether to overwrite an existing .json file; if this is
|
|
230
|
+
False and the output file exists, no-ops and returns
|
|
231
|
+
|
|
232
|
+
"""
|
|
233
|
+
|
|
234
|
+
if output_path is None:
|
|
235
|
+
output_path = os.path.splitext(input_path)[0]+'.json'
|
|
236
|
+
|
|
237
|
+
if os.path.isfile(output_path) and (not overwrite):
|
|
238
|
+
print('File {} exists, skipping csv --> json conversion'.format(output_path))
|
|
239
|
+
return
|
|
240
|
+
|
|
241
|
+
# Format spec:
|
|
242
|
+
#
|
|
243
|
+
# https://github.com/agentmorris/MegaDetector/tree/main/megadetector/api/batch_processing
|
|
244
|
+
|
|
245
|
+
print('Loading csv results...')
|
|
246
|
+
df = load_api_results_csv(input_path)
|
|
247
|
+
|
|
248
|
+
info = {
|
|
249
|
+
"format_version":"1.2",
|
|
250
|
+
"detector": "unknown",
|
|
251
|
+
"detection_completion_time" : "unknown",
|
|
252
|
+
"classifier": "unknown",
|
|
253
|
+
"classification_completion_time": "unknown"
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
classification_categories = {}
|
|
257
|
+
detection_categories = annotation_constants.detector_bbox_categories
|
|
258
|
+
|
|
259
|
+
images = []
|
|
260
|
+
|
|
261
|
+
# i_file = 0; row = df.iloc[i_file]
|
|
262
|
+
for i_file,row in df.iterrows():
|
|
263
|
+
|
|
264
|
+
image = {}
|
|
265
|
+
image['file'] = row['image_path']
|
|
266
|
+
image['max_detection_conf'] = round(row['max_confidence'], CONF_DIGITS)
|
|
267
|
+
src_detections = row['detections']
|
|
268
|
+
out_detections = []
|
|
269
|
+
|
|
270
|
+
for i_detection,detection in enumerate(src_detections):
|
|
271
|
+
|
|
272
|
+
# Our .csv format was ymin/xmin/ymax/xmax
|
|
273
|
+
#
|
|
274
|
+
# Our .json format is xmin/ymin/w/h
|
|
275
|
+
ymin = detection[0]
|
|
276
|
+
xmin = detection[1]
|
|
277
|
+
ymax = detection[2]
|
|
278
|
+
xmax = detection[3]
|
|
279
|
+
bbox = [xmin, ymin, xmax-xmin, ymax-ymin]
|
|
280
|
+
conf = detection[4]
|
|
281
|
+
i_class = detection[5]
|
|
282
|
+
out_detection = {}
|
|
283
|
+
out_detection['category'] = str(i_class)
|
|
284
|
+
out_detection['conf'] = conf
|
|
285
|
+
out_detection['bbox'] = bbox
|
|
286
|
+
out_detections.append(out_detection)
|
|
287
|
+
|
|
288
|
+
# ...for each detection
|
|
289
|
+
|
|
290
|
+
image['detections'] = out_detections
|
|
291
|
+
images.append(image)
|
|
292
|
+
|
|
293
|
+
# ...for each image
|
|
294
|
+
json_out = {}
|
|
295
|
+
json_out['info'] = info
|
|
296
|
+
json_out['detection_categories'] = detection_categories
|
|
297
|
+
json_out['classification_categories'] = classification_categories
|
|
298
|
+
json_out['images'] = images
|
|
299
|
+
|
|
300
|
+
write_json(output_path,json_out)
|
|
301
|
+
|
|
302
|
+
# ...def convert_csv_to_json(...)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
#%% Interactive driver
|
|
306
|
+
|
|
307
|
+
if False:
|
|
308
|
+
|
|
309
|
+
#%%
|
|
310
|
+
|
|
311
|
+
input_path = r'c:\temp\test.json'
|
|
312
|
+
min_confidence = None
|
|
313
|
+
output_path = input_path + '.csv'
|
|
314
|
+
convert_json_to_csv(input_path,output_path,min_confidence=min_confidence,
|
|
315
|
+
omit_bounding_boxes=False)
|
|
316
|
+
|
|
317
|
+
#%%
|
|
318
|
+
|
|
319
|
+
base_path = r'c:\temp\json'
|
|
320
|
+
input_paths = os.listdir(base_path)
|
|
321
|
+
input_paths = [os.path.join(base_path,s) for s in input_paths]
|
|
322
|
+
|
|
323
|
+
min_confidence = None
|
|
324
|
+
for input_path in input_paths:
|
|
325
|
+
output_path = input_path + '.csv'
|
|
326
|
+
convert_json_to_csv(input_path,output_path,min_confidence=min_confidence,
|
|
327
|
+
omit_bounding_boxes=True)
|
|
328
|
+
|
|
329
|
+
#%% Concatenate .csv files from a folder
|
|
330
|
+
|
|
331
|
+
import glob
|
|
332
|
+
csv_files = glob.glob(os.path.join(base_path,'*.json.csv' ))
|
|
333
|
+
master_csv = os.path.join(base_path,'all.csv')
|
|
334
|
+
|
|
335
|
+
print('Concatenating {} files to {}'.format(len(csv_files),master_csv))
|
|
336
|
+
|
|
337
|
+
header = None
|
|
338
|
+
with open(master_csv, 'w') as fout:
|
|
339
|
+
|
|
340
|
+
for filename in tqdm(csv_files):
|
|
341
|
+
|
|
342
|
+
with open(filename) as fin:
|
|
343
|
+
|
|
344
|
+
lines = fin.readlines()
|
|
345
|
+
|
|
346
|
+
if header is not None:
|
|
347
|
+
assert lines[0] == header
|
|
348
|
+
else:
|
|
349
|
+
header = lines[0]
|
|
350
|
+
fout.write(header)
|
|
351
|
+
|
|
352
|
+
for line in lines[1:]:
|
|
353
|
+
if len(line.strip()) == 0:
|
|
354
|
+
continue
|
|
355
|
+
fout.write(line)
|
|
356
|
+
|
|
357
|
+
# ...for each .csv file
|
|
358
|
+
|
|
359
|
+
# with open(master_csv)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
#%% Command-line driver
|
|
363
|
+
|
|
364
|
+
def main():
|
|
365
|
+
"""
|
|
366
|
+
Command-line driver for convert_output_format(), which converts
|
|
367
|
+
json <--> csv.
|
|
368
|
+
"""
|
|
369
|
+
|
|
370
|
+
parser = argparse.ArgumentParser()
|
|
371
|
+
parser.add_argument('input_path',type=str,
|
|
372
|
+
help='Input filename ending in .json or .csv')
|
|
373
|
+
parser.add_argument('--output_path',type=str,default=None,
|
|
374
|
+
help='Output filename ending in .json or .csv (defaults to ' + \
|
|
375
|
+
'input file, with .json/.csv replaced by .csv/.json)')
|
|
376
|
+
parser.add_argument('--omit_bounding_boxes',action='store_true',
|
|
377
|
+
help='Omit bounding box text from .csv output (large and usually not useful)')
|
|
378
|
+
|
|
379
|
+
if len(sys.argv[1:]) == 0:
|
|
380
|
+
parser.print_help()
|
|
381
|
+
parser.exit()
|
|
382
|
+
|
|
383
|
+
args = parser.parse_args()
|
|
384
|
+
|
|
385
|
+
if args.output_path is None:
|
|
386
|
+
if args.input_path.endswith('.csv'):
|
|
387
|
+
args.output_path = args.input_path[:-4] + '.json'
|
|
388
|
+
elif args.input_path.endswith('.json'):
|
|
389
|
+
args.output_path = args.input_path[:-5] + '.csv'
|
|
390
|
+
else:
|
|
391
|
+
raise ValueError('Illegal input file extension')
|
|
392
|
+
|
|
393
|
+
if args.input_path.endswith('.csv') and args.output_path.endswith('.json'):
|
|
394
|
+
assert not args.omit_bounding_boxes, \
|
|
395
|
+
'--omit_bounding_boxes does not apply to csv --> json conversion'
|
|
396
|
+
convert_csv_to_json(args.input_path,args.output_path)
|
|
397
|
+
elif args.input_path.endswith('.json') and args.output_path.endswith('.csv'):
|
|
398
|
+
convert_json_to_csv(args.input_path,args.output_path,omit_bounding_boxes=args.omit_bounding_boxes)
|
|
399
|
+
else:
|
|
400
|
+
raise ValueError('Illegal format combination')
|
|
401
|
+
|
|
402
|
+
if __name__ == '__main__':
|
|
403
|
+
main()
|