megadetector 5.0.10__py3-none-any.whl → 5.0.11__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-5.0.10.dist-info → megadetector-5.0.11.dist-info}/LICENSE +0 -0
- {megadetector-5.0.10.dist-info → megadetector-5.0.11.dist-info}/METADATA +12 -11
- megadetector-5.0.11.dist-info/RECORD +5 -0
- megadetector-5.0.11.dist-info/top_level.txt +1 -0
- api/__init__.py +0 -0
- api/batch_processing/__init__.py +0 -0
- api/batch_processing/api_core/__init__.py +0 -0
- api/batch_processing/api_core/batch_service/__init__.py +0 -0
- api/batch_processing/api_core/batch_service/score.py +0 -439
- api/batch_processing/api_core/server.py +0 -294
- api/batch_processing/api_core/server_api_config.py +0 -98
- api/batch_processing/api_core/server_app_config.py +0 -55
- api/batch_processing/api_core/server_batch_job_manager.py +0 -220
- api/batch_processing/api_core/server_job_status_table.py +0 -152
- api/batch_processing/api_core/server_orchestration.py +0 -360
- api/batch_processing/api_core/server_utils.py +0 -92
- api/batch_processing/api_core_support/__init__.py +0 -0
- api/batch_processing/api_core_support/aggregate_results_manually.py +0 -46
- api/batch_processing/api_support/__init__.py +0 -0
- api/batch_processing/api_support/summarize_daily_activity.py +0 -152
- api/batch_processing/data_preparation/__init__.py +0 -0
- api/batch_processing/data_preparation/manage_local_batch.py +0 -2391
- api/batch_processing/data_preparation/manage_video_batch.py +0 -327
- api/batch_processing/integration/digiKam/setup.py +0 -6
- api/batch_processing/integration/digiKam/xmp_integration.py +0 -465
- api/batch_processing/integration/eMammal/test_scripts/config_template.py +0 -5
- api/batch_processing/integration/eMammal/test_scripts/push_annotations_to_emammal.py +0 -126
- api/batch_processing/integration/eMammal/test_scripts/select_images_for_testing.py +0 -55
- api/batch_processing/postprocessing/__init__.py +0 -0
- api/batch_processing/postprocessing/add_max_conf.py +0 -64
- api/batch_processing/postprocessing/categorize_detections_by_size.py +0 -163
- api/batch_processing/postprocessing/combine_api_outputs.py +0 -249
- api/batch_processing/postprocessing/compare_batch_results.py +0 -958
- api/batch_processing/postprocessing/convert_output_format.py +0 -397
- api/batch_processing/postprocessing/load_api_results.py +0 -195
- api/batch_processing/postprocessing/md_to_coco.py +0 -310
- api/batch_processing/postprocessing/md_to_labelme.py +0 -330
- api/batch_processing/postprocessing/merge_detections.py +0 -401
- api/batch_processing/postprocessing/postprocess_batch_results.py +0 -1904
- api/batch_processing/postprocessing/remap_detection_categories.py +0 -170
- api/batch_processing/postprocessing/render_detection_confusion_matrix.py +0 -661
- api/batch_processing/postprocessing/repeat_detection_elimination/find_repeat_detections.py +0 -211
- api/batch_processing/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +0 -82
- api/batch_processing/postprocessing/repeat_detection_elimination/repeat_detections_core.py +0 -1631
- api/batch_processing/postprocessing/separate_detections_into_folders.py +0 -731
- api/batch_processing/postprocessing/subset_json_detector_output.py +0 -696
- api/batch_processing/postprocessing/top_folders_to_bottom.py +0 -223
- api/synchronous/__init__.py +0 -0
- api/synchronous/api_core/animal_detection_api/__init__.py +0 -0
- api/synchronous/api_core/animal_detection_api/api_backend.py +0 -152
- api/synchronous/api_core/animal_detection_api/api_frontend.py +0 -266
- api/synchronous/api_core/animal_detection_api/config.py +0 -35
- api/synchronous/api_core/animal_detection_api/data_management/annotations/annotation_constants.py +0 -47
- api/synchronous/api_core/animal_detection_api/detection/detector_training/copy_checkpoints.py +0 -43
- api/synchronous/api_core/animal_detection_api/detection/detector_training/model_main_tf2.py +0 -114
- api/synchronous/api_core/animal_detection_api/detection/process_video.py +0 -543
- api/synchronous/api_core/animal_detection_api/detection/pytorch_detector.py +0 -304
- api/synchronous/api_core/animal_detection_api/detection/run_detector.py +0 -627
- api/synchronous/api_core/animal_detection_api/detection/run_detector_batch.py +0 -1029
- api/synchronous/api_core/animal_detection_api/detection/run_inference_with_yolov5_val.py +0 -581
- api/synchronous/api_core/animal_detection_api/detection/run_tiled_inference.py +0 -754
- api/synchronous/api_core/animal_detection_api/detection/tf_detector.py +0 -165
- api/synchronous/api_core/animal_detection_api/detection/video_utils.py +0 -495
- api/synchronous/api_core/animal_detection_api/md_utils/azure_utils.py +0 -174
- api/synchronous/api_core/animal_detection_api/md_utils/ct_utils.py +0 -262
- api/synchronous/api_core/animal_detection_api/md_utils/directory_listing.py +0 -251
- api/synchronous/api_core/animal_detection_api/md_utils/matlab_porting_tools.py +0 -97
- api/synchronous/api_core/animal_detection_api/md_utils/path_utils.py +0 -416
- api/synchronous/api_core/animal_detection_api/md_utils/process_utils.py +0 -110
- api/synchronous/api_core/animal_detection_api/md_utils/sas_blob_utils.py +0 -509
- api/synchronous/api_core/animal_detection_api/md_utils/string_utils.py +0 -59
- api/synchronous/api_core/animal_detection_api/md_utils/url_utils.py +0 -144
- api/synchronous/api_core/animal_detection_api/md_utils/write_html_image_list.py +0 -226
- api/synchronous/api_core/animal_detection_api/md_visualization/visualization_utils.py +0 -841
- api/synchronous/api_core/tests/__init__.py +0 -0
- api/synchronous/api_core/tests/load_test.py +0 -110
- classification/__init__.py +0 -0
- classification/aggregate_classifier_probs.py +0 -108
- classification/analyze_failed_images.py +0 -227
- classification/cache_batchapi_outputs.py +0 -198
- classification/create_classification_dataset.py +0 -627
- classification/crop_detections.py +0 -516
- classification/csv_to_json.py +0 -226
- classification/detect_and_crop.py +0 -855
- classification/efficientnet/__init__.py +0 -9
- classification/efficientnet/model.py +0 -415
- classification/efficientnet/utils.py +0 -610
- classification/evaluate_model.py +0 -520
- classification/identify_mislabeled_candidates.py +0 -152
- classification/json_to_azcopy_list.py +0 -63
- classification/json_validator.py +0 -695
- classification/map_classification_categories.py +0 -276
- classification/merge_classification_detection_output.py +0 -506
- classification/prepare_classification_script.py +0 -194
- classification/prepare_classification_script_mc.py +0 -228
- classification/run_classifier.py +0 -286
- classification/save_mislabeled.py +0 -110
- classification/train_classifier.py +0 -825
- classification/train_classifier_tf.py +0 -724
- classification/train_utils.py +0 -322
- data_management/__init__.py +0 -0
- data_management/annotations/__init__.py +0 -0
- data_management/annotations/annotation_constants.py +0 -34
- data_management/camtrap_dp_to_coco.py +0 -238
- data_management/cct_json_utils.py +0 -395
- data_management/cct_to_md.py +0 -176
- data_management/cct_to_wi.py +0 -289
- data_management/coco_to_labelme.py +0 -272
- data_management/coco_to_yolo.py +0 -662
- data_management/databases/__init__.py +0 -0
- data_management/databases/add_width_and_height_to_db.py +0 -33
- data_management/databases/combine_coco_camera_traps_files.py +0 -206
- data_management/databases/integrity_check_json_db.py +0 -477
- data_management/databases/subset_json_db.py +0 -115
- data_management/generate_crops_from_cct.py +0 -149
- data_management/get_image_sizes.py +0 -188
- data_management/importers/add_nacti_sizes.py +0 -52
- data_management/importers/add_timestamps_to_icct.py +0 -79
- data_management/importers/animl_results_to_md_results.py +0 -158
- data_management/importers/auckland_doc_test_to_json.py +0 -372
- data_management/importers/auckland_doc_to_json.py +0 -200
- data_management/importers/awc_to_json.py +0 -189
- data_management/importers/bellevue_to_json.py +0 -273
- data_management/importers/cacophony-thermal-importer.py +0 -796
- data_management/importers/carrizo_shrubfree_2018.py +0 -268
- data_management/importers/carrizo_trail_cam_2017.py +0 -287
- data_management/importers/cct_field_adjustments.py +0 -57
- data_management/importers/channel_islands_to_cct.py +0 -913
- data_management/importers/eMammal/copy_and_unzip_emammal.py +0 -180
- data_management/importers/eMammal/eMammal_helpers.py +0 -249
- data_management/importers/eMammal/make_eMammal_json.py +0 -223
- data_management/importers/ena24_to_json.py +0 -275
- data_management/importers/filenames_to_json.py +0 -385
- data_management/importers/helena_to_cct.py +0 -282
- data_management/importers/idaho-camera-traps.py +0 -1407
- data_management/importers/idfg_iwildcam_lila_prep.py +0 -294
- data_management/importers/jb_csv_to_json.py +0 -150
- data_management/importers/mcgill_to_json.py +0 -250
- data_management/importers/missouri_to_json.py +0 -489
- data_management/importers/nacti_fieldname_adjustments.py +0 -79
- data_management/importers/noaa_seals_2019.py +0 -181
- data_management/importers/pc_to_json.py +0 -365
- data_management/importers/plot_wni_giraffes.py +0 -123
- data_management/importers/prepare-noaa-fish-data-for-lila.py +0 -359
- data_management/importers/prepare_zsl_imerit.py +0 -131
- data_management/importers/rspb_to_json.py +0 -356
- data_management/importers/save_the_elephants_survey_A.py +0 -320
- data_management/importers/save_the_elephants_survey_B.py +0 -332
- data_management/importers/snapshot_safari_importer.py +0 -758
- data_management/importers/snapshot_safari_importer_reprise.py +0 -665
- data_management/importers/snapshot_serengeti_lila.py +0 -1067
- data_management/importers/snapshotserengeti/make_full_SS_json.py +0 -150
- data_management/importers/snapshotserengeti/make_per_season_SS_json.py +0 -153
- data_management/importers/sulross_get_exif.py +0 -65
- data_management/importers/timelapse_csv_set_to_json.py +0 -490
- data_management/importers/ubc_to_json.py +0 -399
- data_management/importers/umn_to_json.py +0 -507
- data_management/importers/wellington_to_json.py +0 -263
- data_management/importers/wi_to_json.py +0 -441
- data_management/importers/zamba_results_to_md_results.py +0 -181
- data_management/labelme_to_coco.py +0 -548
- data_management/labelme_to_yolo.py +0 -272
- data_management/lila/__init__.py +0 -0
- data_management/lila/add_locations_to_island_camera_traps.py +0 -97
- data_management/lila/add_locations_to_nacti.py +0 -147
- data_management/lila/create_lila_blank_set.py +0 -557
- data_management/lila/create_lila_test_set.py +0 -151
- data_management/lila/create_links_to_md_results_files.py +0 -106
- data_management/lila/download_lila_subset.py +0 -177
- data_management/lila/generate_lila_per_image_labels.py +0 -515
- data_management/lila/get_lila_annotation_counts.py +0 -170
- data_management/lila/get_lila_image_counts.py +0 -111
- data_management/lila/lila_common.py +0 -300
- data_management/lila/test_lila_metadata_urls.py +0 -132
- data_management/ocr_tools.py +0 -874
- data_management/read_exif.py +0 -681
- data_management/remap_coco_categories.py +0 -84
- data_management/remove_exif.py +0 -66
- data_management/resize_coco_dataset.py +0 -189
- data_management/wi_download_csv_to_coco.py +0 -246
- data_management/yolo_output_to_md_output.py +0 -441
- data_management/yolo_to_coco.py +0 -676
- detection/__init__.py +0 -0
- detection/detector_training/__init__.py +0 -0
- detection/detector_training/model_main_tf2.py +0 -114
- detection/process_video.py +0 -703
- detection/pytorch_detector.py +0 -337
- detection/run_detector.py +0 -779
- detection/run_detector_batch.py +0 -1219
- detection/run_inference_with_yolov5_val.py +0 -917
- detection/run_tiled_inference.py +0 -935
- detection/tf_detector.py +0 -188
- detection/video_utils.py +0 -606
- docs/source/conf.py +0 -43
- md_utils/__init__.py +0 -0
- md_utils/azure_utils.py +0 -174
- md_utils/ct_utils.py +0 -612
- md_utils/directory_listing.py +0 -246
- md_utils/md_tests.py +0 -968
- md_utils/path_utils.py +0 -1044
- md_utils/process_utils.py +0 -157
- md_utils/sas_blob_utils.py +0 -509
- md_utils/split_locations_into_train_val.py +0 -228
- md_utils/string_utils.py +0 -92
- md_utils/url_utils.py +0 -323
- md_utils/write_html_image_list.py +0 -225
- md_visualization/__init__.py +0 -0
- md_visualization/plot_utils.py +0 -293
- md_visualization/render_images_with_thumbnails.py +0 -275
- md_visualization/visualization_utils.py +0 -1537
- md_visualization/visualize_db.py +0 -551
- md_visualization/visualize_detector_output.py +0 -406
- megadetector-5.0.10.dist-info/RECORD +0 -224
- megadetector-5.0.10.dist-info/top_level.txt +0 -8
- taxonomy_mapping/__init__.py +0 -0
- taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +0 -491
- taxonomy_mapping/map_new_lila_datasets.py +0 -154
- taxonomy_mapping/prepare_lila_taxonomy_release.py +0 -142
- taxonomy_mapping/preview_lila_taxonomy.py +0 -591
- taxonomy_mapping/retrieve_sample_image.py +0 -71
- taxonomy_mapping/simple_image_download.py +0 -218
- taxonomy_mapping/species_lookup.py +0 -834
- taxonomy_mapping/taxonomy_csv_checker.py +0 -159
- taxonomy_mapping/taxonomy_graph.py +0 -346
- taxonomy_mapping/validate_lila_category_mappings.py +0 -83
- {megadetector-5.0.10.dist-info → megadetector-5.0.11.dist-info}/WHEEL +0 -0
|
@@ -1,397 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
convert_output_format.py
|
|
4
|
-
|
|
5
|
-
Converts between file formats output by our batch processing API. Currently
|
|
6
|
-
supports json <--> csv conversion, but this should be the landing place for any
|
|
7
|
-
conversion - including between hypothetical alternative .json versions - that we support
|
|
8
|
-
in the future.
|
|
9
|
-
|
|
10
|
-
The .csv format is largely obsolete, don't use it unless you're super-duper sure you need it.
|
|
11
|
-
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
#%% Constants and imports
|
|
15
|
-
|
|
16
|
-
import argparse
|
|
17
|
-
import json
|
|
18
|
-
import csv
|
|
19
|
-
import sys
|
|
20
|
-
import os
|
|
21
|
-
|
|
22
|
-
from tqdm import tqdm
|
|
23
|
-
|
|
24
|
-
from api.batch_processing.postprocessing.load_api_results import load_api_results_csv
|
|
25
|
-
from data_management.annotations import annotation_constants
|
|
26
|
-
|
|
27
|
-
from md_utils import ct_utils
|
|
28
|
-
|
|
29
|
-
CONF_DIGITS = 3
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
#%% Conversion functions
|
|
33
|
-
|
|
34
|
-
def convert_json_to_csv(input_path,output_path=None,min_confidence=None,
|
|
35
|
-
omit_bounding_boxes=False,output_encoding=None,
|
|
36
|
-
overwrite=True):
|
|
37
|
-
"""
|
|
38
|
-
Converts a MD results .json file to a totally non-standard .csv format.
|
|
39
|
-
|
|
40
|
-
If [output_path] is None, will convert x.json to x.csv.
|
|
41
|
-
|
|
42
|
-
TODO: this function should obviously be using Pandas or some other sensible structured
|
|
43
|
-
representation of tabular data. Even a list of dicts. This implementation is quite
|
|
44
|
-
brittle and depends on adding fields to every row in exactly the right order.
|
|
45
|
-
|
|
46
|
-
Args:
|
|
47
|
-
input_path (str): the input .json file to convert
|
|
48
|
-
output_path (str, optional): the output .csv file to generate; if this is None, uses
|
|
49
|
-
[input_path].csv
|
|
50
|
-
min_confidence (float, optional): the minimum-confidence detection we should include
|
|
51
|
-
in the "detections" column; has no impact on the other columns
|
|
52
|
-
omit_bounding_boxes (bool): whether to leave out the json-formatted bounding boxes
|
|
53
|
-
that make up the "detections" column, which are not generally useful for someone who
|
|
54
|
-
wants to consume this data as a .csv file
|
|
55
|
-
output_encoding (str, optional): encoding to use for the .csv file
|
|
56
|
-
overwrite (bool): whether to overwrite an existing .csv file; if this is False and the
|
|
57
|
-
output file exists, no-ops and returns
|
|
58
|
-
|
|
59
|
-
"""
|
|
60
|
-
|
|
61
|
-
if output_path is None:
|
|
62
|
-
output_path = os.path.splitext(input_path)[0]+'.csv'
|
|
63
|
-
|
|
64
|
-
if os.path.isfile(output_path) and (not overwrite):
|
|
65
|
-
print('File {} exists, skipping json --> csv conversion'.format(output_path))
|
|
66
|
-
return
|
|
67
|
-
|
|
68
|
-
print('Loading json results from {}...'.format(input_path))
|
|
69
|
-
json_output = json.load(open(input_path))
|
|
70
|
-
|
|
71
|
-
rows = []
|
|
72
|
-
|
|
73
|
-
fixed_columns = ['image_path', 'max_confidence', 'detections']
|
|
74
|
-
|
|
75
|
-
# We add an output column for each class other than 'empty',
|
|
76
|
-
# containing the maximum probability of that class for each image
|
|
77
|
-
# n_non_empty_detection_categories = len(annotation_constants.annotation_bbox_categories) - 1
|
|
78
|
-
n_non_empty_detection_categories = annotation_constants.NUM_DETECTOR_CATEGORIES
|
|
79
|
-
detection_category_column_names = []
|
|
80
|
-
assert annotation_constants.detector_bbox_categories[0] == 'empty'
|
|
81
|
-
for cat_id in range(1,n_non_empty_detection_categories+1):
|
|
82
|
-
cat_name = annotation_constants.detector_bbox_categories[cat_id]
|
|
83
|
-
detection_category_column_names.append('max_conf_' + cat_name)
|
|
84
|
-
|
|
85
|
-
n_classification_categories = 0
|
|
86
|
-
|
|
87
|
-
if 'classification_categories' in json_output.keys():
|
|
88
|
-
classification_category_id_to_name = json_output['classification_categories']
|
|
89
|
-
classification_category_ids = list(classification_category_id_to_name.keys())
|
|
90
|
-
classification_category_id_to_column_number = {}
|
|
91
|
-
classification_category_column_names = []
|
|
92
|
-
for i_category,category_id in enumerate(classification_category_ids):
|
|
93
|
-
category_name = classification_category_id_to_name[category_id].\
|
|
94
|
-
replace(' ','_').replace(',','')
|
|
95
|
-
classification_category_column_names.append('max_classification_conf_' + category_name)
|
|
96
|
-
classification_category_id_to_column_number[category_id] = i_category
|
|
97
|
-
|
|
98
|
-
n_classification_categories = len(classification_category_ids)
|
|
99
|
-
|
|
100
|
-
# There are several .json fields for which we add .csv columns; other random bespoke fields
|
|
101
|
-
# will be ignored.
|
|
102
|
-
optional_fields = ['width','height','datetime','exif_metadata']
|
|
103
|
-
optional_fields_present = set()
|
|
104
|
-
|
|
105
|
-
# Iterate once over the data to check for optional fields
|
|
106
|
-
print('Looking for optional fields...')
|
|
107
|
-
|
|
108
|
-
for im in tqdm(json_output['images']):
|
|
109
|
-
# Which optional fields are present for this image?
|
|
110
|
-
for k in im.keys():
|
|
111
|
-
if k in optional_fields:
|
|
112
|
-
optional_fields_present.add(k)
|
|
113
|
-
|
|
114
|
-
optional_fields_present = sorted(list(optional_fields_present))
|
|
115
|
-
if len(optional_fields_present) > 0:
|
|
116
|
-
print('Found {} optional fields'.format(len(optional_fields_present)))
|
|
117
|
-
|
|
118
|
-
expected_row_length = len(fixed_columns) + len(detection_category_column_names) + \
|
|
119
|
-
n_classification_categories + len(optional_fields_present)
|
|
120
|
-
|
|
121
|
-
print('Formatting results...')
|
|
122
|
-
|
|
123
|
-
# i_image = 0; im = json_output['images'][i_image]
|
|
124
|
-
for im in tqdm(json_output['images']):
|
|
125
|
-
|
|
126
|
-
image_id = im['file']
|
|
127
|
-
|
|
128
|
-
if 'failure' in im and im['failure'] is not None:
|
|
129
|
-
row = [image_id, 'failure', im['failure']]
|
|
130
|
-
rows.append(row)
|
|
131
|
-
# print('Skipping failed image {} ({})'.format(im['file'],im['failure']))
|
|
132
|
-
continue
|
|
133
|
-
|
|
134
|
-
max_conf = ct_utils.get_max_conf(im)
|
|
135
|
-
detections = []
|
|
136
|
-
max_detection_category_probabilities = [None] * n_non_empty_detection_categories
|
|
137
|
-
max_classification_category_probabilities = [0] * n_classification_categories
|
|
138
|
-
|
|
139
|
-
# d = im['detections'][0]
|
|
140
|
-
for d in im['detections']:
|
|
141
|
-
|
|
142
|
-
# Skip sub-threshold detections
|
|
143
|
-
if (min_confidence is not None) and (d['conf'] < min_confidence):
|
|
144
|
-
continue
|
|
145
|
-
|
|
146
|
-
input_bbox = d['bbox']
|
|
147
|
-
|
|
148
|
-
# Our .json format is xmin/ymin/w/h
|
|
149
|
-
#
|
|
150
|
-
# Our .csv format was ymin/xmin/ymax/xmax
|
|
151
|
-
xmin = input_bbox[0]
|
|
152
|
-
ymin = input_bbox[1]
|
|
153
|
-
xmax = input_bbox[0] + input_bbox[2]
|
|
154
|
-
ymax = input_bbox[1] + input_bbox[3]
|
|
155
|
-
output_detection = [ymin, xmin, ymax, xmax]
|
|
156
|
-
|
|
157
|
-
output_detection.append(d['conf'])
|
|
158
|
-
|
|
159
|
-
# Category 0 is empty, for which we don't have a column, so the max
|
|
160
|
-
# confidence for category N goes in column N-1
|
|
161
|
-
detection_category_id = int(d['category'])
|
|
162
|
-
assert detection_category_id > 0 and detection_category_id <= \
|
|
163
|
-
n_non_empty_detection_categories
|
|
164
|
-
detection_category_column = detection_category_id - 1
|
|
165
|
-
detection_category_max = max_detection_category_probabilities[detection_category_column]
|
|
166
|
-
if detection_category_max is None or d['conf'] > detection_category_max:
|
|
167
|
-
max_detection_category_probabilities[detection_category_column] = d['conf']
|
|
168
|
-
|
|
169
|
-
output_detection.append(detection_category_id)
|
|
170
|
-
detections.append(output_detection)
|
|
171
|
-
|
|
172
|
-
if 'classifications' in d:
|
|
173
|
-
assert n_classification_categories > 0,\
|
|
174
|
-
'Oops, I have classification results, but no classification metadata'
|
|
175
|
-
for c in d['classifications']:
|
|
176
|
-
category_id = c[0]
|
|
177
|
-
p = c[1]
|
|
178
|
-
category_index = classification_category_id_to_column_number[category_id]
|
|
179
|
-
if (max_classification_category_probabilities[category_index] < p):
|
|
180
|
-
max_classification_category_probabilities[category_index] = p
|
|
181
|
-
|
|
182
|
-
# ...for each classification
|
|
183
|
-
|
|
184
|
-
# ...if we have classification results for this detection
|
|
185
|
-
|
|
186
|
-
# ...for each detection
|
|
187
|
-
|
|
188
|
-
detection_string = ''
|
|
189
|
-
if not omit_bounding_boxes:
|
|
190
|
-
detection_string = json.dumps(detections)
|
|
191
|
-
|
|
192
|
-
row = [image_id, max_conf, detection_string]
|
|
193
|
-
row.extend(max_detection_category_probabilities)
|
|
194
|
-
row.extend(max_classification_category_probabilities)
|
|
195
|
-
|
|
196
|
-
for field_name in optional_fields_present:
|
|
197
|
-
if field_name not in im:
|
|
198
|
-
row.append('')
|
|
199
|
-
else:
|
|
200
|
-
row.append(str(im[field_name]))
|
|
201
|
-
|
|
202
|
-
assert len(row) == expected_row_length
|
|
203
|
-
rows.append(row)
|
|
204
|
-
|
|
205
|
-
# ...for each image
|
|
206
|
-
|
|
207
|
-
print('Writing to csv...')
|
|
208
|
-
|
|
209
|
-
with open(output_path, 'w', newline='', encoding=output_encoding) as f:
|
|
210
|
-
writer = csv.writer(f, delimiter=',')
|
|
211
|
-
header = fixed_columns
|
|
212
|
-
header.extend(detection_category_column_names)
|
|
213
|
-
if n_classification_categories > 0:
|
|
214
|
-
header.extend(classification_category_column_names)
|
|
215
|
-
for field_name in optional_fields_present:
|
|
216
|
-
header.append(field_name)
|
|
217
|
-
writer.writerow(header)
|
|
218
|
-
writer.writerows(rows)
|
|
219
|
-
|
|
220
|
-
# ...def convert_json_to_csv(...)
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
def convert_csv_to_json(input_path,output_path=None,overwrite=True):
|
|
224
|
-
"""
|
|
225
|
-
Convert .csv to .json. If output_path is None, will convert x.csv to x.json.
|
|
226
|
-
|
|
227
|
-
Args:
|
|
228
|
-
input_path (str): .csv filename to convert to .json
|
|
229
|
-
output_path (str, optional): the output .json file to generate; if this is None, uses
|
|
230
|
-
[input_path].json
|
|
231
|
-
overwrite (bool): whether to overwrite an existing .json file; if this is False and the
|
|
232
|
-
output file exists, no-ops and returns
|
|
233
|
-
|
|
234
|
-
"""
|
|
235
|
-
|
|
236
|
-
if output_path is None:
|
|
237
|
-
output_path = os.path.splitext(input_path)[0]+'.json'
|
|
238
|
-
|
|
239
|
-
if os.path.isfile(output_path) and (not overwrite):
|
|
240
|
-
print('File {} exists, skipping csv --> json conversion'.format(output_path))
|
|
241
|
-
return
|
|
242
|
-
|
|
243
|
-
# Format spec:
|
|
244
|
-
#
|
|
245
|
-
# https://github.com/agentmorris/MegaDetector/tree/master/api/batch_processing
|
|
246
|
-
|
|
247
|
-
print('Loading csv results...')
|
|
248
|
-
df = load_api_results_csv(input_path)
|
|
249
|
-
|
|
250
|
-
info = {
|
|
251
|
-
"format_version":"1.2",
|
|
252
|
-
"detector": "unknown",
|
|
253
|
-
"detection_completion_time" : "unknown",
|
|
254
|
-
"classifier": "unknown",
|
|
255
|
-
"classification_completion_time": "unknown"
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
classification_categories = {}
|
|
259
|
-
detection_categories = annotation_constants.detector_bbox_categories
|
|
260
|
-
|
|
261
|
-
images = []
|
|
262
|
-
|
|
263
|
-
# iFile = 0; row = df.iloc[iFile]
|
|
264
|
-
for iFile,row in df.iterrows():
|
|
265
|
-
|
|
266
|
-
image = {}
|
|
267
|
-
image['file'] = row['image_path']
|
|
268
|
-
image['max_detection_conf'] = round(row['max_confidence'], CONF_DIGITS)
|
|
269
|
-
src_detections = row['detections']
|
|
270
|
-
out_detections = []
|
|
271
|
-
|
|
272
|
-
for iDetection,detection in enumerate(src_detections):
|
|
273
|
-
|
|
274
|
-
# Our .csv format was ymin/xmin/ymax/xmax
|
|
275
|
-
#
|
|
276
|
-
# Our .json format is xmin/ymin/w/h
|
|
277
|
-
ymin = detection[0]
|
|
278
|
-
xmin = detection[1]
|
|
279
|
-
ymax = detection[2]
|
|
280
|
-
xmax = detection[3]
|
|
281
|
-
bbox = [xmin, ymin, xmax-xmin, ymax-ymin]
|
|
282
|
-
conf = detection[4]
|
|
283
|
-
iClass = detection[5]
|
|
284
|
-
out_detection = {}
|
|
285
|
-
out_detection['category'] = str(iClass)
|
|
286
|
-
out_detection['conf'] = conf
|
|
287
|
-
out_detection['bbox'] = bbox
|
|
288
|
-
out_detections.append(out_detection)
|
|
289
|
-
|
|
290
|
-
# ...for each detection
|
|
291
|
-
|
|
292
|
-
image['detections'] = out_detections
|
|
293
|
-
images.append(image)
|
|
294
|
-
|
|
295
|
-
# ...for each image
|
|
296
|
-
json_out = {}
|
|
297
|
-
json_out['info'] = info
|
|
298
|
-
json_out['detection_categories'] = detection_categories
|
|
299
|
-
json_out['classification_categories'] = classification_categories
|
|
300
|
-
json_out['images'] = images
|
|
301
|
-
|
|
302
|
-
json.dump(json_out,open(output_path,'w'),indent=1)
|
|
303
|
-
|
|
304
|
-
# ...def convert_csv_to_json(...)
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
#%% Interactive driver
|
|
308
|
-
|
|
309
|
-
if False:
|
|
310
|
-
|
|
311
|
-
#%%
|
|
312
|
-
|
|
313
|
-
input_path = r'c:\temp\test.json'
|
|
314
|
-
min_confidence = None
|
|
315
|
-
output_path = input_path + '.csv'
|
|
316
|
-
convert_json_to_csv(input_path,output_path,min_confidence=min_confidence,
|
|
317
|
-
omit_bounding_boxes=False)
|
|
318
|
-
|
|
319
|
-
#%%
|
|
320
|
-
|
|
321
|
-
base_path = r'c:\temp\json'
|
|
322
|
-
input_paths = os.listdir(base_path)
|
|
323
|
-
input_paths = [os.path.join(base_path,s) for s in input_paths]
|
|
324
|
-
|
|
325
|
-
min_confidence = None
|
|
326
|
-
for input_path in input_paths:
|
|
327
|
-
output_path = input_path + '.csv'
|
|
328
|
-
convert_json_to_csv(input_path,output_path,min_confidence=min_confidence,
|
|
329
|
-
omit_bounding_boxes=True)
|
|
330
|
-
|
|
331
|
-
#%% Concatenate .csv files from a folder
|
|
332
|
-
|
|
333
|
-
import glob
|
|
334
|
-
csv_files = glob.glob(os.path.join(base_path,'*.json.csv' ))
|
|
335
|
-
master_csv = os.path.join(base_path,'all.csv')
|
|
336
|
-
|
|
337
|
-
print('Concatenating {} files to {}'.format(len(csv_files),master_csv))
|
|
338
|
-
|
|
339
|
-
header = None
|
|
340
|
-
with open(master_csv, 'w') as fout:
|
|
341
|
-
|
|
342
|
-
for filename in tqdm(csv_files):
|
|
343
|
-
|
|
344
|
-
with open(filename) as fin:
|
|
345
|
-
|
|
346
|
-
lines = fin.readlines()
|
|
347
|
-
|
|
348
|
-
if header is not None:
|
|
349
|
-
assert lines[0] == header
|
|
350
|
-
else:
|
|
351
|
-
header = lines[0]
|
|
352
|
-
fout.write(header)
|
|
353
|
-
|
|
354
|
-
for line in lines[1:]:
|
|
355
|
-
if len(line.strip()) == 0:
|
|
356
|
-
continue
|
|
357
|
-
fout.write(line)
|
|
358
|
-
|
|
359
|
-
# ...for each .csv file
|
|
360
|
-
|
|
361
|
-
# with open(master_csv)
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
#%% Command-line driver
|
|
365
|
-
|
|
366
|
-
def main():
|
|
367
|
-
|
|
368
|
-
parser = argparse.ArgumentParser()
|
|
369
|
-
parser.add_argument('input_path',type=str,
|
|
370
|
-
help='Input filename ending in .json or .csv')
|
|
371
|
-
parser.add_argument('--output_path',type=str,default=None,
|
|
372
|
-
help='Output filename ending in .json or .csv (defaults to ' + \
|
|
373
|
-
'input file, with .json/.csv replaced by .csv/.json)')
|
|
374
|
-
|
|
375
|
-
if len(sys.argv[1:]) == 0:
|
|
376
|
-
parser.print_help()
|
|
377
|
-
parser.exit()
|
|
378
|
-
|
|
379
|
-
args = parser.parse_args()
|
|
380
|
-
|
|
381
|
-
if args.output_path is None:
|
|
382
|
-
if args.input_path.endswith('.csv'):
|
|
383
|
-
args.output_path = args.input_path[:-4] + '.json'
|
|
384
|
-
elif args.input_path.endswith('.json'):
|
|
385
|
-
args.output_path = args.input_path[:-5] + '.csv'
|
|
386
|
-
else:
|
|
387
|
-
raise ValueError('Illegal input file extension')
|
|
388
|
-
|
|
389
|
-
if args.input_path.endswith('.csv') and args.output_path.endswith('.json'):
|
|
390
|
-
convert_csv_to_json(args.input_path,args.output_path)
|
|
391
|
-
elif args.input_path.endswith('.json') and args.output_path.endswith('.csv'):
|
|
392
|
-
convert_json_to_csv(args.input_path,args.output_path)
|
|
393
|
-
else:
|
|
394
|
-
raise ValueError('Illegal format combination')
|
|
395
|
-
|
|
396
|
-
if __name__ == '__main__':
|
|
397
|
-
main()
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
load_api_results.py
|
|
4
|
-
|
|
5
|
-
DEPRECATED
|
|
6
|
-
|
|
7
|
-
As of 2023.12, this module is used in postprocessing and RDE. Not recommended
|
|
8
|
-
for new code.
|
|
9
|
-
|
|
10
|
-
Loads the output of the batch processing API (json) into a Pandas dataframe.
|
|
11
|
-
|
|
12
|
-
Includes functions to read/write the (very very old) .csv results format.
|
|
13
|
-
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
|
-
#%% Imports
|
|
17
|
-
|
|
18
|
-
import json
|
|
19
|
-
import os
|
|
20
|
-
|
|
21
|
-
from typing import Dict, Mapping, Optional, Tuple
|
|
22
|
-
|
|
23
|
-
import pandas as pd
|
|
24
|
-
|
|
25
|
-
from md_utils import ct_utils
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
#%% Functions for loading .json results into a Pandas DataFrame, and writing back to .json
|
|
29
|
-
|
|
30
|
-
def load_api_results(api_output_path: str, normalize_paths: bool = True,
|
|
31
|
-
filename_replacements: Optional[Mapping[str, str]] = None,
|
|
32
|
-
force_forward_slashes: bool = True
|
|
33
|
-
) -> Tuple[pd.DataFrame, Dict]:
|
|
34
|
-
r"""
|
|
35
|
-
Loads json-formatted MegaDetector results to a Pandas DataFrame.
|
|
36
|
-
|
|
37
|
-
Args:
|
|
38
|
-
api_output_path: path to the output json file
|
|
39
|
-
normalize_paths: whether to apply os.path.normpath to the 'file' field
|
|
40
|
-
in each image entry in the output file
|
|
41
|
-
filename_replacements: replace some path tokens to match local paths to
|
|
42
|
-
the original blob structure
|
|
43
|
-
force_forward_slashes: whether to convert backslashes to forward slashes
|
|
44
|
-
in filenames
|
|
45
|
-
|
|
46
|
-
Returns:
|
|
47
|
-
detection_results: pd.DataFrame, contains at least the columns ['file', 'detections','failure']
|
|
48
|
-
other_fields: a dict containing fields in the results other than 'images'
|
|
49
|
-
"""
|
|
50
|
-
|
|
51
|
-
print('Loading results from {}'.format(api_output_path))
|
|
52
|
-
|
|
53
|
-
with open(api_output_path) as f:
|
|
54
|
-
detection_results = json.load(f)
|
|
55
|
-
|
|
56
|
-
# Validate that this is really a detector output file
|
|
57
|
-
for s in ['info', 'detection_categories', 'images']:
|
|
58
|
-
assert s in detection_results, 'Missing field {} in detection results'.format(s)
|
|
59
|
-
|
|
60
|
-
# Fields in the output json other than 'images'
|
|
61
|
-
other_fields = {}
|
|
62
|
-
for k, v in detection_results.items():
|
|
63
|
-
if k != 'images':
|
|
64
|
-
other_fields[k] = v
|
|
65
|
-
|
|
66
|
-
if normalize_paths:
|
|
67
|
-
for image in detection_results['images']:
|
|
68
|
-
image['file'] = os.path.normpath(image['file'])
|
|
69
|
-
|
|
70
|
-
if force_forward_slashes:
|
|
71
|
-
for image in detection_results['images']:
|
|
72
|
-
image['file'] = image['file'].replace('\\','/')
|
|
73
|
-
|
|
74
|
-
# Replace some path tokens to match local paths to original blob structure
|
|
75
|
-
if filename_replacements is not None:
|
|
76
|
-
for string_to_replace in filename_replacements.keys():
|
|
77
|
-
replacement_string = filename_replacements[string_to_replace]
|
|
78
|
-
for im in detection_results['images']:
|
|
79
|
-
im['file'] = im['file'].replace(string_to_replace,replacement_string)
|
|
80
|
-
|
|
81
|
-
print('Converting results to dataframe')
|
|
82
|
-
|
|
83
|
-
# If this is a newer file that doesn't include maximum detection confidence values,
|
|
84
|
-
# add them, because our unofficial internal dataframe format includes this.
|
|
85
|
-
for im in detection_results['images']:
|
|
86
|
-
if 'max_detection_conf' not in im:
|
|
87
|
-
im['max_detection_conf'] = ct_utils.get_max_conf(im)
|
|
88
|
-
|
|
89
|
-
# Pack the json output into a Pandas DataFrame
|
|
90
|
-
detection_results = pd.DataFrame(detection_results['images'])
|
|
91
|
-
|
|
92
|
-
print('Finished loading MegaDetector results for {} images from {}'.format(
|
|
93
|
-
len(detection_results),api_output_path))
|
|
94
|
-
|
|
95
|
-
return detection_results, other_fields
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
def write_api_results(detection_results_table, other_fields, out_path):
|
|
99
|
-
"""
|
|
100
|
-
Writes a Pandas DataFrame to the MegaDetector .json format.
|
|
101
|
-
"""
|
|
102
|
-
|
|
103
|
-
print('Writing detection results to {}'.format(out_path))
|
|
104
|
-
|
|
105
|
-
fields = other_fields
|
|
106
|
-
|
|
107
|
-
images = detection_results_table.to_json(orient='records',
|
|
108
|
-
double_precision=3)
|
|
109
|
-
images = json.loads(images)
|
|
110
|
-
fields['images'] = images
|
|
111
|
-
|
|
112
|
-
# Convert the 'version' field back to a string as per format convention
|
|
113
|
-
try:
|
|
114
|
-
version = other_fields['info']['format_version']
|
|
115
|
-
if not isinstance(version,str):
|
|
116
|
-
other_fields['info']['format_version'] = str(version)
|
|
117
|
-
except Exception:
|
|
118
|
-
print('Warning: error determining format version')
|
|
119
|
-
pass
|
|
120
|
-
|
|
121
|
-
# Remove 'max_detection_conf' as per newer file convention (format >= v1.3)
|
|
122
|
-
try:
|
|
123
|
-
version = other_fields['info']['format_version']
|
|
124
|
-
version = float(version)
|
|
125
|
-
if version >= 1.3:
|
|
126
|
-
for im in images:
|
|
127
|
-
if 'max_detection_conf' in im:
|
|
128
|
-
del im['max_detection_conf']
|
|
129
|
-
except Exception:
|
|
130
|
-
print('Warning: error removing max_detection_conf from output')
|
|
131
|
-
pass
|
|
132
|
-
|
|
133
|
-
with open(out_path, 'w') as f:
|
|
134
|
-
json.dump(fields, f, indent=1)
|
|
135
|
-
|
|
136
|
-
print('Finished writing detection results to {}'.format(out_path))
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def load_api_results_csv(filename, normalize_paths=True, filename_replacements={}, nrows=None):
|
|
140
|
-
"""
|
|
141
|
-
[DEPRECATED]
|
|
142
|
-
|
|
143
|
-
Loads .csv-formatted MegaDetector results to a pandas table
|
|
144
|
-
"""
|
|
145
|
-
|
|
146
|
-
print('Loading MegaDetector results from {}'.format(filename))
|
|
147
|
-
|
|
148
|
-
detection_results = pd.read_csv(filename,nrows=nrows)
|
|
149
|
-
|
|
150
|
-
print('De-serializing MegaDetector results from {}'.format(filename))
|
|
151
|
-
|
|
152
|
-
# Confirm that this is really a detector output file
|
|
153
|
-
for s in ['image_path','max_confidence','detections']:
|
|
154
|
-
assert s in detection_results.columns
|
|
155
|
-
|
|
156
|
-
# Normalize paths to simplify comparisons later
|
|
157
|
-
if normalize_paths:
|
|
158
|
-
detection_results['image_path'] = detection_results['image_path'].apply(os.path.normpath)
|
|
159
|
-
|
|
160
|
-
# De-serialize detections
|
|
161
|
-
detection_results['detections'] = detection_results['detections'].apply(json.loads)
|
|
162
|
-
|
|
163
|
-
# Optionally replace some path tokens to match local paths to the original blob structure
|
|
164
|
-
# string_to_replace = list(options.detector_output_filename_replacements.keys())[0]
|
|
165
|
-
for string_to_replace in filename_replacements:
|
|
166
|
-
|
|
167
|
-
replacement_string = filename_replacements[string_to_replace]
|
|
168
|
-
|
|
169
|
-
# iRow = 0
|
|
170
|
-
for iRow in range(0,len(detection_results)):
|
|
171
|
-
row = detection_results.iloc[iRow]
|
|
172
|
-
fn = row['image_path']
|
|
173
|
-
fn = fn.replace(string_to_replace,replacement_string)
|
|
174
|
-
detection_results.at[iRow,'image_path'] = fn
|
|
175
|
-
|
|
176
|
-
print('Finished loading and de-serializing MD results for {} images from {}'.format(
|
|
177
|
-
len(detection_results),filename))
|
|
178
|
-
|
|
179
|
-
return detection_results
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
def write_api_results_csv(detection_results, filename):
|
|
183
|
-
"""
|
|
184
|
-
[DEPRECATED]
|
|
185
|
-
|
|
186
|
-
Writes a Pandas table to csv in a way that's compatible with the .csv output
|
|
187
|
-
format. Currently just a wrapper around to_csv that forces output writing
|
|
188
|
-
to go through a common code path.
|
|
189
|
-
"""
|
|
190
|
-
|
|
191
|
-
print('Writing detection results to {}'.format(filename))
|
|
192
|
-
|
|
193
|
-
detection_results.to_csv(filename, index=False)
|
|
194
|
-
|
|
195
|
-
print('Finished writing detection results to {}'.format(filename))
|