megadetector 5.0.28__py3-none-any.whl → 10.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of megadetector might be problematic. Click here for more details.

Files changed (197) hide show
  1. megadetector/api/batch_processing/integration/digiKam/xmp_integration.py +2 -2
  2. megadetector/api/batch_processing/integration/eMammal/test_scripts/push_annotations_to_emammal.py +1 -1
  3. megadetector/api/batch_processing/integration/eMammal/test_scripts/select_images_for_testing.py +1 -1
  4. megadetector/classification/aggregate_classifier_probs.py +3 -3
  5. megadetector/classification/analyze_failed_images.py +5 -5
  6. megadetector/classification/cache_batchapi_outputs.py +5 -5
  7. megadetector/classification/create_classification_dataset.py +11 -12
  8. megadetector/classification/crop_detections.py +10 -10
  9. megadetector/classification/csv_to_json.py +8 -8
  10. megadetector/classification/detect_and_crop.py +13 -15
  11. megadetector/classification/efficientnet/model.py +8 -8
  12. megadetector/classification/efficientnet/utils.py +6 -5
  13. megadetector/classification/evaluate_model.py +7 -7
  14. megadetector/classification/identify_mislabeled_candidates.py +6 -6
  15. megadetector/classification/json_to_azcopy_list.py +1 -1
  16. megadetector/classification/json_validator.py +29 -32
  17. megadetector/classification/map_classification_categories.py +9 -9
  18. megadetector/classification/merge_classification_detection_output.py +12 -9
  19. megadetector/classification/prepare_classification_script.py +19 -19
  20. megadetector/classification/prepare_classification_script_mc.py +26 -26
  21. megadetector/classification/run_classifier.py +4 -4
  22. megadetector/classification/save_mislabeled.py +6 -6
  23. megadetector/classification/train_classifier.py +1 -1
  24. megadetector/classification/train_classifier_tf.py +9 -9
  25. megadetector/classification/train_utils.py +10 -10
  26. megadetector/data_management/annotations/annotation_constants.py +1 -2
  27. megadetector/data_management/camtrap_dp_to_coco.py +79 -46
  28. megadetector/data_management/cct_json_utils.py +103 -103
  29. megadetector/data_management/cct_to_md.py +49 -49
  30. megadetector/data_management/cct_to_wi.py +33 -33
  31. megadetector/data_management/coco_to_labelme.py +75 -75
  32. megadetector/data_management/coco_to_yolo.py +210 -193
  33. megadetector/data_management/databases/add_width_and_height_to_db.py +86 -12
  34. megadetector/data_management/databases/combine_coco_camera_traps_files.py +40 -40
  35. megadetector/data_management/databases/integrity_check_json_db.py +228 -200
  36. megadetector/data_management/databases/subset_json_db.py +33 -33
  37. megadetector/data_management/generate_crops_from_cct.py +88 -39
  38. megadetector/data_management/get_image_sizes.py +54 -49
  39. megadetector/data_management/labelme_to_coco.py +133 -125
  40. megadetector/data_management/labelme_to_yolo.py +159 -73
  41. megadetector/data_management/lila/create_lila_blank_set.py +81 -83
  42. megadetector/data_management/lila/create_lila_test_set.py +32 -31
  43. megadetector/data_management/lila/create_links_to_md_results_files.py +18 -18
  44. megadetector/data_management/lila/download_lila_subset.py +21 -24
  45. megadetector/data_management/lila/generate_lila_per_image_labels.py +365 -107
  46. megadetector/data_management/lila/get_lila_annotation_counts.py +35 -33
  47. megadetector/data_management/lila/get_lila_image_counts.py +22 -22
  48. megadetector/data_management/lila/lila_common.py +73 -70
  49. megadetector/data_management/lila/test_lila_metadata_urls.py +28 -19
  50. megadetector/data_management/mewc_to_md.py +344 -340
  51. megadetector/data_management/ocr_tools.py +262 -255
  52. megadetector/data_management/read_exif.py +249 -227
  53. megadetector/data_management/remap_coco_categories.py +90 -28
  54. megadetector/data_management/remove_exif.py +81 -21
  55. megadetector/data_management/rename_images.py +187 -187
  56. megadetector/data_management/resize_coco_dataset.py +588 -120
  57. megadetector/data_management/speciesnet_to_md.py +41 -41
  58. megadetector/data_management/wi_download_csv_to_coco.py +55 -55
  59. megadetector/data_management/yolo_output_to_md_output.py +248 -122
  60. megadetector/data_management/yolo_to_coco.py +333 -191
  61. megadetector/detection/change_detection.py +832 -0
  62. megadetector/detection/process_video.py +340 -337
  63. megadetector/detection/pytorch_detector.py +358 -278
  64. megadetector/detection/run_detector.py +399 -186
  65. megadetector/detection/run_detector_batch.py +404 -377
  66. megadetector/detection/run_inference_with_yolov5_val.py +340 -327
  67. megadetector/detection/run_tiled_inference.py +257 -249
  68. megadetector/detection/tf_detector.py +24 -24
  69. megadetector/detection/video_utils.py +332 -295
  70. megadetector/postprocessing/add_max_conf.py +19 -11
  71. megadetector/postprocessing/categorize_detections_by_size.py +45 -45
  72. megadetector/postprocessing/classification_postprocessing.py +468 -433
  73. megadetector/postprocessing/combine_batch_outputs.py +23 -23
  74. megadetector/postprocessing/compare_batch_results.py +590 -525
  75. megadetector/postprocessing/convert_output_format.py +106 -102
  76. megadetector/postprocessing/create_crop_folder.py +347 -147
  77. megadetector/postprocessing/detector_calibration.py +173 -168
  78. megadetector/postprocessing/generate_csv_report.py +508 -499
  79. megadetector/postprocessing/load_api_results.py +48 -27
  80. megadetector/postprocessing/md_to_coco.py +133 -102
  81. megadetector/postprocessing/md_to_labelme.py +107 -90
  82. megadetector/postprocessing/md_to_wi.py +40 -40
  83. megadetector/postprocessing/merge_detections.py +92 -114
  84. megadetector/postprocessing/postprocess_batch_results.py +319 -301
  85. megadetector/postprocessing/remap_detection_categories.py +91 -38
  86. megadetector/postprocessing/render_detection_confusion_matrix.py +214 -205
  87. megadetector/postprocessing/repeat_detection_elimination/find_repeat_detections.py +57 -57
  88. megadetector/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +27 -28
  89. megadetector/postprocessing/repeat_detection_elimination/repeat_detections_core.py +704 -679
  90. megadetector/postprocessing/separate_detections_into_folders.py +226 -211
  91. megadetector/postprocessing/subset_json_detector_output.py +265 -262
  92. megadetector/postprocessing/top_folders_to_bottom.py +45 -45
  93. megadetector/postprocessing/validate_batch_results.py +70 -70
  94. megadetector/taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +52 -52
  95. megadetector/taxonomy_mapping/map_new_lila_datasets.py +18 -19
  96. megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +54 -33
  97. megadetector/taxonomy_mapping/preview_lila_taxonomy.py +67 -67
  98. megadetector/taxonomy_mapping/retrieve_sample_image.py +16 -16
  99. megadetector/taxonomy_mapping/simple_image_download.py +8 -8
  100. megadetector/taxonomy_mapping/species_lookup.py +156 -74
  101. megadetector/taxonomy_mapping/taxonomy_csv_checker.py +14 -14
  102. megadetector/taxonomy_mapping/taxonomy_graph.py +10 -10
  103. megadetector/taxonomy_mapping/validate_lila_category_mappings.py +13 -13
  104. megadetector/utils/ct_utils.py +1049 -211
  105. megadetector/utils/directory_listing.py +21 -77
  106. megadetector/utils/gpu_test.py +22 -22
  107. megadetector/utils/md_tests.py +632 -529
  108. megadetector/utils/path_utils.py +1520 -431
  109. megadetector/utils/process_utils.py +41 -41
  110. megadetector/utils/split_locations_into_train_val.py +62 -62
  111. megadetector/utils/string_utils.py +148 -27
  112. megadetector/utils/url_utils.py +489 -176
  113. megadetector/utils/wi_utils.py +2658 -2526
  114. megadetector/utils/write_html_image_list.py +137 -137
  115. megadetector/visualization/plot_utils.py +34 -30
  116. megadetector/visualization/render_images_with_thumbnails.py +39 -74
  117. megadetector/visualization/visualization_utils.py +487 -435
  118. megadetector/visualization/visualize_db.py +232 -198
  119. megadetector/visualization/visualize_detector_output.py +82 -76
  120. {megadetector-5.0.28.dist-info → megadetector-10.0.0.dist-info}/METADATA +5 -2
  121. megadetector-10.0.0.dist-info/RECORD +139 -0
  122. {megadetector-5.0.28.dist-info → megadetector-10.0.0.dist-info}/WHEEL +1 -1
  123. megadetector/api/batch_processing/api_core/__init__.py +0 -0
  124. megadetector/api/batch_processing/api_core/batch_service/__init__.py +0 -0
  125. megadetector/api/batch_processing/api_core/batch_service/score.py +0 -439
  126. megadetector/api/batch_processing/api_core/server.py +0 -294
  127. megadetector/api/batch_processing/api_core/server_api_config.py +0 -97
  128. megadetector/api/batch_processing/api_core/server_app_config.py +0 -55
  129. megadetector/api/batch_processing/api_core/server_batch_job_manager.py +0 -220
  130. megadetector/api/batch_processing/api_core/server_job_status_table.py +0 -149
  131. megadetector/api/batch_processing/api_core/server_orchestration.py +0 -360
  132. megadetector/api/batch_processing/api_core/server_utils.py +0 -88
  133. megadetector/api/batch_processing/api_core_support/__init__.py +0 -0
  134. megadetector/api/batch_processing/api_core_support/aggregate_results_manually.py +0 -46
  135. megadetector/api/batch_processing/api_support/__init__.py +0 -0
  136. megadetector/api/batch_processing/api_support/summarize_daily_activity.py +0 -152
  137. megadetector/api/batch_processing/data_preparation/__init__.py +0 -0
  138. megadetector/api/synchronous/__init__.py +0 -0
  139. megadetector/api/synchronous/api_core/animal_detection_api/__init__.py +0 -0
  140. megadetector/api/synchronous/api_core/animal_detection_api/api_backend.py +0 -151
  141. megadetector/api/synchronous/api_core/animal_detection_api/api_frontend.py +0 -263
  142. megadetector/api/synchronous/api_core/animal_detection_api/config.py +0 -35
  143. megadetector/api/synchronous/api_core/tests/__init__.py +0 -0
  144. megadetector/api/synchronous/api_core/tests/load_test.py +0 -110
  145. megadetector/data_management/importers/add_nacti_sizes.py +0 -52
  146. megadetector/data_management/importers/add_timestamps_to_icct.py +0 -79
  147. megadetector/data_management/importers/animl_results_to_md_results.py +0 -158
  148. megadetector/data_management/importers/auckland_doc_test_to_json.py +0 -373
  149. megadetector/data_management/importers/auckland_doc_to_json.py +0 -201
  150. megadetector/data_management/importers/awc_to_json.py +0 -191
  151. megadetector/data_management/importers/bellevue_to_json.py +0 -272
  152. megadetector/data_management/importers/cacophony-thermal-importer.py +0 -793
  153. megadetector/data_management/importers/carrizo_shrubfree_2018.py +0 -269
  154. megadetector/data_management/importers/carrizo_trail_cam_2017.py +0 -289
  155. megadetector/data_management/importers/cct_field_adjustments.py +0 -58
  156. megadetector/data_management/importers/channel_islands_to_cct.py +0 -913
  157. megadetector/data_management/importers/eMammal/copy_and_unzip_emammal.py +0 -180
  158. megadetector/data_management/importers/eMammal/eMammal_helpers.py +0 -249
  159. megadetector/data_management/importers/eMammal/make_eMammal_json.py +0 -223
  160. megadetector/data_management/importers/ena24_to_json.py +0 -276
  161. megadetector/data_management/importers/filenames_to_json.py +0 -386
  162. megadetector/data_management/importers/helena_to_cct.py +0 -283
  163. megadetector/data_management/importers/idaho-camera-traps.py +0 -1407
  164. megadetector/data_management/importers/idfg_iwildcam_lila_prep.py +0 -294
  165. megadetector/data_management/importers/import_desert_lion_conservation_camera_traps.py +0 -387
  166. megadetector/data_management/importers/jb_csv_to_json.py +0 -150
  167. megadetector/data_management/importers/mcgill_to_json.py +0 -250
  168. megadetector/data_management/importers/missouri_to_json.py +0 -490
  169. megadetector/data_management/importers/nacti_fieldname_adjustments.py +0 -79
  170. megadetector/data_management/importers/noaa_seals_2019.py +0 -181
  171. megadetector/data_management/importers/osu-small-animals-to-json.py +0 -364
  172. megadetector/data_management/importers/pc_to_json.py +0 -365
  173. megadetector/data_management/importers/plot_wni_giraffes.py +0 -123
  174. megadetector/data_management/importers/prepare_zsl_imerit.py +0 -131
  175. megadetector/data_management/importers/raic_csv_to_md_results.py +0 -416
  176. megadetector/data_management/importers/rspb_to_json.py +0 -356
  177. megadetector/data_management/importers/save_the_elephants_survey_A.py +0 -320
  178. megadetector/data_management/importers/save_the_elephants_survey_B.py +0 -329
  179. megadetector/data_management/importers/snapshot_safari_importer.py +0 -758
  180. megadetector/data_management/importers/snapshot_serengeti_lila.py +0 -1067
  181. megadetector/data_management/importers/snapshotserengeti/make_full_SS_json.py +0 -150
  182. megadetector/data_management/importers/snapshotserengeti/make_per_season_SS_json.py +0 -153
  183. megadetector/data_management/importers/sulross_get_exif.py +0 -65
  184. megadetector/data_management/importers/timelapse_csv_set_to_json.py +0 -490
  185. megadetector/data_management/importers/ubc_to_json.py +0 -399
  186. megadetector/data_management/importers/umn_to_json.py +0 -507
  187. megadetector/data_management/importers/wellington_to_json.py +0 -263
  188. megadetector/data_management/importers/wi_to_json.py +0 -442
  189. megadetector/data_management/importers/zamba_results_to_md_results.py +0 -180
  190. megadetector/data_management/lila/add_locations_to_island_camera_traps.py +0 -101
  191. megadetector/data_management/lila/add_locations_to_nacti.py +0 -151
  192. megadetector/utils/azure_utils.py +0 -178
  193. megadetector/utils/sas_blob_utils.py +0 -509
  194. megadetector-5.0.28.dist-info/RECORD +0 -209
  195. /megadetector/{api/batch_processing/__init__.py → __init__.py} +0 -0
  196. {megadetector-5.0.28.dist-info → megadetector-10.0.0.dist-info}/licenses/LICENSE +0 -0
  197. {megadetector-5.0.28.dist-info → megadetector-10.0.0.dist-info}/top_level.txt +0 -0
@@ -4,7 +4,7 @@ convert_output_format.py
4
4
 
5
5
  Converts between file formats output by our batch processing API. Currently
6
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
7
+ conversion - including between hypothetical alternative .json versions - that we support
8
8
  in the future.
9
9
 
10
10
  The .csv format is largely obsolete, don't use it unless you're super-duper sure you need it.
@@ -38,43 +38,43 @@ def convert_json_to_csv(input_path,
38
38
  overwrite=True):
39
39
  """
40
40
  Converts a MD results .json file to a totally non-standard .csv format.
41
-
41
+
42
42
  If [output_path] is None, will convert x.json to x.csv.
43
-
43
+
44
44
  TODO: this function should obviously be using Pandas or some other sensible structured
45
45
  representation of tabular data. Even a list of dicts. This implementation is quite
46
46
  brittle and depends on adding fields to every row in exactly the right order.
47
-
47
+
48
48
  Args:
49
49
  input_path (str): the input .json file to convert
50
- output_path (str, optional): the output .csv file to generate; if this is None, uses
50
+ output_path (str, optional): the output .csv file to generate; if this is None, uses
51
51
  [input_path].csv
52
- min_confidence (float, optional): the minimum-confidence detection we should include
52
+ min_confidence (float, optional): the minimum-confidence detection we should include
53
53
  in the "detections" column; has no impact on the other columns
54
- omit_bounding_boxes (bool): whether to leave out the json-formatted bounding boxes
55
- that make up the "detections" column, which are not generally useful for someone who
56
- wants to consume this data as a .csv file
54
+ omit_bounding_boxes (bool, optional): whether to leave out the json-formatted bounding
55
+ boxes that make up the "detections" column, which are not generally useful for someone
56
+ who wants to consume this data as a .csv file
57
57
  output_encoding (str, optional): encoding to use for the .csv file
58
- overwrite (bool): whether to overwrite an existing .csv file; if this is False and the
59
- output file exists, no-ops and returns
60
-
58
+ overwrite (bool, optional): whether to overwrite an existing .csv file; if this is False and
59
+ the output file exists, no-ops and returns
60
+
61
61
  """
62
-
62
+
63
63
  if output_path is None:
64
64
  output_path = os.path.splitext(input_path)[0]+'.csv'
65
-
65
+
66
66
  if os.path.isfile(output_path) and (not overwrite):
67
67
  print('File {} exists, skipping json --> csv conversion'.format(output_path))
68
68
  return
69
-
69
+
70
70
  print('Loading json results from {}...'.format(input_path))
71
71
  json_output = json.load(open(input_path))
72
72
 
73
73
  rows = []
74
-
74
+
75
75
  fixed_columns = ['image_path', 'max_confidence', 'detections']
76
-
77
- # We add an output column for each class other than 'empty',
76
+
77
+ # We add an output column for each class other than 'empty',
78
78
  # containing the maximum probability of that class for each image
79
79
  # n_non_empty_detection_categories = len(annotation_constants.annotation_bbox_categories) - 1
80
80
  n_non_empty_detection_categories = annotation_constants.NUM_DETECTOR_CATEGORIES
@@ -83,9 +83,9 @@ def convert_json_to_csv(input_path,
83
83
  for cat_id in range(1,n_non_empty_detection_categories+1):
84
84
  cat_name = annotation_constants.detector_bbox_category_id_to_name[cat_id]
85
85
  detection_category_column_names.append('max_conf_' + cat_name)
86
-
86
+
87
87
  n_classification_categories = 0
88
-
88
+
89
89
  if 'classification_categories' in json_output.keys():
90
90
  classification_category_id_to_name = json_output['classification_categories']
91
91
  classification_category_ids = list(classification_category_id_to_name.keys())
@@ -98,35 +98,35 @@ def convert_json_to_csv(input_path,
98
98
  classification_category_id_to_column_number[category_id] = i_category
99
99
 
100
100
  n_classification_categories = len(classification_category_ids)
101
-
101
+
102
102
  # There are several .json fields for which we add .csv columns; other random bespoke fields
103
103
  # will be ignored.
104
104
  optional_fields = ['width','height','datetime','exif_metadata']
105
105
  optional_fields_present = set()
106
-
106
+
107
107
  # Iterate once over the data to check for optional fields
108
108
  print('Looking for optional fields...')
109
-
109
+
110
110
  for im in tqdm(json_output['images']):
111
111
  # Which optional fields are present for this image?
112
112
  for k in im.keys():
113
113
  if k in optional_fields:
114
114
  optional_fields_present.add(k)
115
-
115
+
116
116
  optional_fields_present = sorted(list(optional_fields_present))
117
117
  if len(optional_fields_present) > 0:
118
118
  print('Found {} optional fields'.format(len(optional_fields_present)))
119
-
119
+
120
120
  expected_row_length = len(fixed_columns) + len(detection_category_column_names) + \
121
121
  n_classification_categories + len(optional_fields_present)
122
-
122
+
123
123
  print('Formatting results...')
124
-
124
+
125
125
  # i_image = 0; im = json_output['images'][i_image]
126
126
  for im in tqdm(json_output['images']):
127
-
127
+
128
128
  image_id = im['file']
129
-
129
+
130
130
  if 'failure' in im and im['failure'] is not None:
131
131
  row = [image_id, 'failure', im['failure']]
132
132
  rows.append(row)
@@ -137,16 +137,16 @@ def convert_json_to_csv(input_path,
137
137
  detections = []
138
138
  max_detection_category_probabilities = [None] * n_non_empty_detection_categories
139
139
  max_classification_category_probabilities = [0] * n_classification_categories
140
-
140
+
141
141
  # d = im['detections'][0]
142
142
  for d in im['detections']:
143
-
143
+
144
144
  # Skip sub-threshold detections
145
145
  if (min_confidence is not None) and (d['conf'] < min_confidence):
146
146
  continue
147
-
147
+
148
148
  input_bbox = d['bbox']
149
-
149
+
150
150
  # Our .json format is xmin/ymin/w/h
151
151
  #
152
152
  # Our .csv format was ymin/xmin/ymax/xmax
@@ -155,9 +155,9 @@ def convert_json_to_csv(input_path,
155
155
  xmax = input_bbox[0] + input_bbox[2]
156
156
  ymax = input_bbox[1] + input_bbox[3]
157
157
  output_detection = [ymin, xmin, ymax, xmax]
158
-
158
+
159
159
  output_detection.append(d['conf'])
160
-
160
+
161
161
  # Category 0 is empty, for which we don't have a column, so the max
162
162
  # confidence for category N goes in column N-1
163
163
  detection_category_id = int(d['category'])
@@ -167,10 +167,10 @@ def convert_json_to_csv(input_path,
167
167
  detection_category_max = max_detection_category_probabilities[detection_category_column]
168
168
  if detection_category_max is None or d['conf'] > detection_category_max:
169
169
  max_detection_category_probabilities[detection_category_column] = d['conf']
170
-
170
+
171
171
  output_detection.append(detection_category_id)
172
172
  detections.append(output_detection)
173
-
173
+
174
174
  if 'classifications' in d:
175
175
  assert n_classification_categories > 0,\
176
176
  'Oops, I have classification results, but no classification metadata'
@@ -180,34 +180,34 @@ def convert_json_to_csv(input_path,
180
180
  category_index = classification_category_id_to_column_number[category_id]
181
181
  if (max_classification_category_probabilities[category_index] < p):
182
182
  max_classification_category_probabilities[category_index] = p
183
-
183
+
184
184
  # ...for each classification
185
-
185
+
186
186
  # ...if we have classification results for this detection
187
-
187
+
188
188
  # ...for each detection
189
-
189
+
190
190
  detection_string = ''
191
191
  if not omit_bounding_boxes:
192
192
  detection_string = json.dumps(detections)
193
-
193
+
194
194
  row = [image_id, max_conf, detection_string]
195
195
  row.extend(max_detection_category_probabilities)
196
196
  row.extend(max_classification_category_probabilities)
197
-
197
+
198
198
  for field_name in optional_fields_present:
199
199
  if field_name not in im:
200
200
  row.append('')
201
201
  else:
202
202
  row.append(str(im[field_name]))
203
-
203
+
204
204
  assert len(row) == expected_row_length
205
205
  rows.append(row)
206
-
206
+
207
207
  # ...for each image
208
208
 
209
209
  print('Writing to csv...')
210
-
210
+
211
211
  with open(output_path, 'w', newline='', encoding=output_encoding) as f:
212
212
  writer = csv.writer(f, delimiter=',')
213
213
  header = fixed_columns
@@ -221,31 +221,31 @@ def convert_json_to_csv(input_path,
221
221
 
222
222
  # ...def convert_json_to_csv(...)
223
223
 
224
-
224
+
225
225
  def convert_csv_to_json(input_path,output_path=None,overwrite=True):
226
226
  """
227
227
  Convert .csv to .json. If output_path is None, will convert x.csv to x.json.
228
-
228
+
229
229
  Args:
230
230
  input_path (str): .csv filename to convert to .json
231
- output_path (str, optional): the output .json file to generate; if this is None, uses
231
+ output_path (str, optional): the output .json file to generate; if this is None, uses
232
232
  [input_path].json
233
- overwrite (bool): whether to overwrite an existing .json file; if this is False and the
234
- output file exists, no-ops and returns
235
-
233
+ overwrite (bool, optional): whether to overwrite an existing .json file; if this is
234
+ False and the output file exists, no-ops and returns
235
+
236
236
  """
237
-
237
+
238
238
  if output_path is None:
239
239
  output_path = os.path.splitext(input_path)[0]+'.json'
240
-
240
+
241
241
  if os.path.isfile(output_path) and (not overwrite):
242
242
  print('File {} exists, skipping csv --> json conversion'.format(output_path))
243
243
  return
244
-
244
+
245
245
  # Format spec:
246
246
  #
247
247
  # https://github.com/agentmorris/MegaDetector/tree/main/megadetector/api/batch_processing
248
-
248
+
249
249
  print('Loading csv results...')
250
250
  df = load_api_results_csv(input_path)
251
251
 
@@ -256,23 +256,23 @@ def convert_csv_to_json(input_path,output_path=None,overwrite=True):
256
256
  "classifier": "unknown",
257
257
  "classification_completion_time": "unknown"
258
258
  }
259
-
259
+
260
260
  classification_categories = {}
261
261
  detection_categories = annotation_constants.detector_bbox_categories
262
262
 
263
263
  images = []
264
-
265
- # iFile = 0; row = df.iloc[iFile]
266
- for iFile,row in df.iterrows():
267
-
264
+
265
+ # i_file = 0; row = df.iloc[i_file]
266
+ for i_file,row in df.iterrows():
267
+
268
268
  image = {}
269
269
  image['file'] = row['image_path']
270
270
  image['max_detection_conf'] = round(row['max_confidence'], CONF_DIGITS)
271
- src_detections = row['detections']
271
+ src_detections = row['detections']
272
272
  out_detections = []
273
-
274
- for iDetection,detection in enumerate(src_detections):
275
-
273
+
274
+ for i_detection,detection in enumerate(src_detections):
275
+
276
276
  # Our .csv format was ymin/xmin/ymax/xmax
277
277
  #
278
278
  # Our .json format is xmin/ymin/w/h
@@ -282,91 +282,95 @@ def convert_csv_to_json(input_path,output_path=None,overwrite=True):
282
282
  xmax = detection[3]
283
283
  bbox = [xmin, ymin, xmax-xmin, ymax-ymin]
284
284
  conf = detection[4]
285
- iClass = detection[5]
285
+ i_class = detection[5]
286
286
  out_detection = {}
287
- out_detection['category'] = str(iClass)
287
+ out_detection['category'] = str(i_class)
288
288
  out_detection['conf'] = conf
289
- out_detection['bbox'] = bbox
289
+ out_detection['bbox'] = bbox
290
290
  out_detections.append(out_detection)
291
-
291
+
292
292
  # ...for each detection
293
-
293
+
294
294
  image['detections'] = out_detections
295
295
  images.append(image)
296
-
297
- # ...for each image
296
+
297
+ # ...for each image
298
298
  json_out = {}
299
299
  json_out['info'] = info
300
300
  json_out['detection_categories'] = detection_categories
301
301
  json_out['classification_categories'] = classification_categories
302
302
  json_out['images'] = images
303
-
303
+
304
304
  json.dump(json_out,open(output_path,'w'),indent=1)
305
-
305
+
306
306
  # ...def convert_csv_to_json(...)
307
307
 
308
308
 
309
309
  #%% Interactive driver
310
310
 
311
- if False:
311
+ if False:
312
312
 
313
313
  #%%
314
-
314
+
315
315
  input_path = r'c:\temp\test.json'
316
316
  min_confidence = None
317
317
  output_path = input_path + '.csv'
318
318
  convert_json_to_csv(input_path,output_path,min_confidence=min_confidence,
319
319
  omit_bounding_boxes=False)
320
-
320
+
321
321
  #%%
322
-
322
+
323
323
  base_path = r'c:\temp\json'
324
324
  input_paths = os.listdir(base_path)
325
325
  input_paths = [os.path.join(base_path,s) for s in input_paths]
326
-
327
- min_confidence = None
326
+
327
+ min_confidence = None
328
328
  for input_path in input_paths:
329
329
  output_path = input_path + '.csv'
330
330
  convert_json_to_csv(input_path,output_path,min_confidence=min_confidence,
331
- omit_bounding_boxes=True)
332
-
331
+ omit_bounding_boxes=True)
332
+
333
333
  #%% Concatenate .csv files from a folder
334
334
 
335
335
  import glob
336
336
  csv_files = glob.glob(os.path.join(base_path,'*.json.csv' ))
337
337
  master_csv = os.path.join(base_path,'all.csv')
338
-
338
+
339
339
  print('Concatenating {} files to {}'.format(len(csv_files),master_csv))
340
-
340
+
341
341
  header = None
342
342
  with open(master_csv, 'w') as fout:
343
-
343
+
344
344
  for filename in tqdm(csv_files):
345
-
345
+
346
346
  with open(filename) as fin:
347
-
347
+
348
348
  lines = fin.readlines()
349
-
349
+
350
350
  if header is not None:
351
351
  assert lines[0] == header
352
352
  else:
353
353
  header = lines[0]
354
354
  fout.write(header)
355
-
355
+
356
356
  for line in lines[1:]:
357
357
  if len(line.strip()) == 0:
358
- continue
358
+ continue
359
359
  fout.write(line)
360
-
360
+
361
361
  # ...for each .csv file
362
-
362
+
363
363
  # with open(master_csv)
364
-
365
-
364
+
365
+
366
366
  #%% Command-line driver
367
-
367
+
368
368
  def main():
369
-
369
+ """
370
+ Command-line driver for convert_output_format(), which converts
371
+ json <--> csv.
372
+ """
373
+
370
374
  parser = argparse.ArgumentParser()
371
375
  parser.add_argument('input_path',type=str,
372
376
  help='Input filename ending in .json or .csv')
@@ -374,22 +378,22 @@ def main():
374
378
  help='Output filename ending in .json or .csv (defaults to ' + \
375
379
  'input file, with .json/.csv replaced by .csv/.json)')
376
380
  parser.add_argument('--omit_bounding_boxes',action='store_true',
377
- help='Output bounding box text from .csv output (large and usually not useful)')
378
-
381
+ help='Output bounding box text from .csv output (large and usually not useful)')
382
+
379
383
  if len(sys.argv[1:]) == 0:
380
384
  parser.print_help()
381
385
  parser.exit()
382
386
 
383
387
  args = parser.parse_args()
384
-
388
+
385
389
  if args.output_path is None:
386
390
  if args.input_path.endswith('.csv'):
387
391
  args.output_path = args.input_path[:-4] + '.json'
388
392
  elif args.input_path.endswith('.json'):
389
393
  args.output_path = args.input_path[:-5] + '.csv'
390
394
  else:
391
- raise ValueError('Illegal input file extension')
392
-
395
+ raise ValueError('Illegal input file extension')
396
+
393
397
  if args.input_path.endswith('.csv') and args.output_path.endswith('.json'):
394
398
  assert not args.omit_bounding_boxes, \
395
399
  '--omit_bounding_boxes does not apply to csv --> json conversion'
@@ -397,7 +401,7 @@ def main():
397
401
  elif args.input_path.endswith('.json') and args.output_path.endswith('.csv'):
398
402
  convert_json_to_csv(args.input_path,args.output_path,omit_bounding_boxes=args.omit_bounding_boxes)
399
403
  else:
400
- raise ValueError('Illegal format combination')
404
+ raise ValueError('Illegal format combination')
401
405
 
402
406
  if __name__ == '__main__':
403
407
  main()