megadetector 5.0.27__py3-none-any.whl → 5.0.29__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (176) hide show
  1. megadetector/api/batch_processing/api_core/batch_service/score.py +4 -5
  2. megadetector/api/batch_processing/api_core_support/aggregate_results_manually.py +1 -1
  3. megadetector/api/batch_processing/api_support/summarize_daily_activity.py +1 -1
  4. megadetector/api/batch_processing/integration/digiKam/xmp_integration.py +2 -2
  5. megadetector/api/batch_processing/integration/eMammal/test_scripts/push_annotations_to_emammal.py +1 -1
  6. megadetector/api/batch_processing/integration/eMammal/test_scripts/select_images_for_testing.py +1 -1
  7. megadetector/api/synchronous/api_core/tests/load_test.py +2 -3
  8. megadetector/classification/aggregate_classifier_probs.py +3 -3
  9. megadetector/classification/analyze_failed_images.py +5 -5
  10. megadetector/classification/cache_batchapi_outputs.py +5 -5
  11. megadetector/classification/create_classification_dataset.py +11 -12
  12. megadetector/classification/crop_detections.py +10 -10
  13. megadetector/classification/csv_to_json.py +8 -8
  14. megadetector/classification/detect_and_crop.py +13 -15
  15. megadetector/classification/evaluate_model.py +7 -7
  16. megadetector/classification/identify_mislabeled_candidates.py +6 -6
  17. megadetector/classification/json_to_azcopy_list.py +1 -1
  18. megadetector/classification/json_validator.py +29 -32
  19. megadetector/classification/map_classification_categories.py +9 -9
  20. megadetector/classification/merge_classification_detection_output.py +12 -9
  21. megadetector/classification/prepare_classification_script.py +19 -19
  22. megadetector/classification/prepare_classification_script_mc.py +23 -23
  23. megadetector/classification/run_classifier.py +4 -4
  24. megadetector/classification/save_mislabeled.py +6 -6
  25. megadetector/classification/train_classifier.py +1 -1
  26. megadetector/classification/train_classifier_tf.py +9 -9
  27. megadetector/classification/train_utils.py +10 -10
  28. megadetector/data_management/annotations/annotation_constants.py +1 -1
  29. megadetector/data_management/camtrap_dp_to_coco.py +45 -45
  30. megadetector/data_management/cct_json_utils.py +101 -101
  31. megadetector/data_management/cct_to_md.py +49 -49
  32. megadetector/data_management/cct_to_wi.py +33 -33
  33. megadetector/data_management/coco_to_labelme.py +75 -75
  34. megadetector/data_management/coco_to_yolo.py +189 -189
  35. megadetector/data_management/databases/add_width_and_height_to_db.py +3 -2
  36. megadetector/data_management/databases/combine_coco_camera_traps_files.py +38 -38
  37. megadetector/data_management/databases/integrity_check_json_db.py +202 -188
  38. megadetector/data_management/databases/subset_json_db.py +33 -33
  39. megadetector/data_management/generate_crops_from_cct.py +38 -38
  40. megadetector/data_management/get_image_sizes.py +54 -49
  41. megadetector/data_management/labelme_to_coco.py +130 -124
  42. megadetector/data_management/labelme_to_yolo.py +78 -72
  43. megadetector/data_management/lila/create_lila_blank_set.py +81 -83
  44. megadetector/data_management/lila/create_lila_test_set.py +32 -31
  45. megadetector/data_management/lila/create_links_to_md_results_files.py +18 -18
  46. megadetector/data_management/lila/download_lila_subset.py +21 -24
  47. megadetector/data_management/lila/generate_lila_per_image_labels.py +91 -91
  48. megadetector/data_management/lila/get_lila_annotation_counts.py +30 -30
  49. megadetector/data_management/lila/get_lila_image_counts.py +22 -22
  50. megadetector/data_management/lila/lila_common.py +70 -70
  51. megadetector/data_management/lila/test_lila_metadata_urls.py +13 -14
  52. megadetector/data_management/mewc_to_md.py +339 -340
  53. megadetector/data_management/ocr_tools.py +258 -252
  54. megadetector/data_management/read_exif.py +232 -223
  55. megadetector/data_management/remap_coco_categories.py +26 -26
  56. megadetector/data_management/remove_exif.py +31 -20
  57. megadetector/data_management/rename_images.py +187 -187
  58. megadetector/data_management/resize_coco_dataset.py +41 -41
  59. megadetector/data_management/speciesnet_to_md.py +41 -41
  60. megadetector/data_management/wi_download_csv_to_coco.py +55 -55
  61. megadetector/data_management/yolo_output_to_md_output.py +117 -120
  62. megadetector/data_management/yolo_to_coco.py +195 -188
  63. megadetector/detection/change_detection.py +831 -0
  64. megadetector/detection/process_video.py +341 -338
  65. megadetector/detection/pytorch_detector.py +308 -266
  66. megadetector/detection/run_detector.py +186 -166
  67. megadetector/detection/run_detector_batch.py +366 -364
  68. megadetector/detection/run_inference_with_yolov5_val.py +328 -325
  69. megadetector/detection/run_tiled_inference.py +312 -253
  70. megadetector/detection/tf_detector.py +24 -24
  71. megadetector/detection/video_utils.py +291 -283
  72. megadetector/postprocessing/add_max_conf.py +15 -11
  73. megadetector/postprocessing/categorize_detections_by_size.py +44 -44
  74. megadetector/postprocessing/classification_postprocessing.py +808 -311
  75. megadetector/postprocessing/combine_batch_outputs.py +20 -21
  76. megadetector/postprocessing/compare_batch_results.py +528 -517
  77. megadetector/postprocessing/convert_output_format.py +97 -97
  78. megadetector/postprocessing/create_crop_folder.py +220 -147
  79. megadetector/postprocessing/detector_calibration.py +173 -168
  80. megadetector/postprocessing/generate_csv_report.py +508 -0
  81. megadetector/postprocessing/load_api_results.py +25 -22
  82. megadetector/postprocessing/md_to_coco.py +129 -98
  83. megadetector/postprocessing/md_to_labelme.py +89 -83
  84. megadetector/postprocessing/md_to_wi.py +40 -40
  85. megadetector/postprocessing/merge_detections.py +87 -114
  86. megadetector/postprocessing/postprocess_batch_results.py +319 -302
  87. megadetector/postprocessing/remap_detection_categories.py +36 -36
  88. megadetector/postprocessing/render_detection_confusion_matrix.py +205 -199
  89. megadetector/postprocessing/repeat_detection_elimination/find_repeat_detections.py +57 -57
  90. megadetector/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +27 -28
  91. megadetector/postprocessing/repeat_detection_elimination/repeat_detections_core.py +702 -677
  92. megadetector/postprocessing/separate_detections_into_folders.py +226 -211
  93. megadetector/postprocessing/subset_json_detector_output.py +265 -262
  94. megadetector/postprocessing/top_folders_to_bottom.py +45 -45
  95. megadetector/postprocessing/validate_batch_results.py +70 -70
  96. megadetector/taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +52 -52
  97. megadetector/taxonomy_mapping/map_new_lila_datasets.py +15 -15
  98. megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +14 -14
  99. megadetector/taxonomy_mapping/preview_lila_taxonomy.py +66 -69
  100. megadetector/taxonomy_mapping/retrieve_sample_image.py +16 -16
  101. megadetector/taxonomy_mapping/simple_image_download.py +8 -8
  102. megadetector/taxonomy_mapping/species_lookup.py +33 -33
  103. megadetector/taxonomy_mapping/taxonomy_csv_checker.py +14 -14
  104. megadetector/taxonomy_mapping/taxonomy_graph.py +11 -11
  105. megadetector/taxonomy_mapping/validate_lila_category_mappings.py +13 -13
  106. megadetector/utils/azure_utils.py +22 -22
  107. megadetector/utils/ct_utils.py +1019 -200
  108. megadetector/utils/directory_listing.py +21 -77
  109. megadetector/utils/gpu_test.py +22 -22
  110. megadetector/utils/md_tests.py +541 -518
  111. megadetector/utils/path_utils.py +1511 -406
  112. megadetector/utils/process_utils.py +41 -41
  113. megadetector/utils/sas_blob_utils.py +53 -49
  114. megadetector/utils/split_locations_into_train_val.py +73 -60
  115. megadetector/utils/string_utils.py +147 -26
  116. megadetector/utils/url_utils.py +463 -173
  117. megadetector/utils/wi_utils.py +2629 -2868
  118. megadetector/utils/write_html_image_list.py +137 -137
  119. megadetector/visualization/plot_utils.py +21 -21
  120. megadetector/visualization/render_images_with_thumbnails.py +37 -73
  121. megadetector/visualization/visualization_utils.py +424 -404
  122. megadetector/visualization/visualize_db.py +197 -190
  123. megadetector/visualization/visualize_detector_output.py +126 -98
  124. {megadetector-5.0.27.dist-info → megadetector-5.0.29.dist-info}/METADATA +6 -3
  125. megadetector-5.0.29.dist-info/RECORD +163 -0
  126. {megadetector-5.0.27.dist-info → megadetector-5.0.29.dist-info}/WHEEL +1 -1
  127. megadetector/data_management/importers/add_nacti_sizes.py +0 -52
  128. megadetector/data_management/importers/add_timestamps_to_icct.py +0 -79
  129. megadetector/data_management/importers/animl_results_to_md_results.py +0 -158
  130. megadetector/data_management/importers/auckland_doc_test_to_json.py +0 -373
  131. megadetector/data_management/importers/auckland_doc_to_json.py +0 -201
  132. megadetector/data_management/importers/awc_to_json.py +0 -191
  133. megadetector/data_management/importers/bellevue_to_json.py +0 -272
  134. megadetector/data_management/importers/cacophony-thermal-importer.py +0 -793
  135. megadetector/data_management/importers/carrizo_shrubfree_2018.py +0 -269
  136. megadetector/data_management/importers/carrizo_trail_cam_2017.py +0 -289
  137. megadetector/data_management/importers/cct_field_adjustments.py +0 -58
  138. megadetector/data_management/importers/channel_islands_to_cct.py +0 -913
  139. megadetector/data_management/importers/eMammal/copy_and_unzip_emammal.py +0 -180
  140. megadetector/data_management/importers/eMammal/eMammal_helpers.py +0 -249
  141. megadetector/data_management/importers/eMammal/make_eMammal_json.py +0 -223
  142. megadetector/data_management/importers/ena24_to_json.py +0 -276
  143. megadetector/data_management/importers/filenames_to_json.py +0 -386
  144. megadetector/data_management/importers/helena_to_cct.py +0 -283
  145. megadetector/data_management/importers/idaho-camera-traps.py +0 -1407
  146. megadetector/data_management/importers/idfg_iwildcam_lila_prep.py +0 -294
  147. megadetector/data_management/importers/import_desert_lion_conservation_camera_traps.py +0 -387
  148. megadetector/data_management/importers/jb_csv_to_json.py +0 -150
  149. megadetector/data_management/importers/mcgill_to_json.py +0 -250
  150. megadetector/data_management/importers/missouri_to_json.py +0 -490
  151. megadetector/data_management/importers/nacti_fieldname_adjustments.py +0 -79
  152. megadetector/data_management/importers/noaa_seals_2019.py +0 -181
  153. megadetector/data_management/importers/osu-small-animals-to-json.py +0 -364
  154. megadetector/data_management/importers/pc_to_json.py +0 -365
  155. megadetector/data_management/importers/plot_wni_giraffes.py +0 -123
  156. megadetector/data_management/importers/prepare_zsl_imerit.py +0 -131
  157. megadetector/data_management/importers/raic_csv_to_md_results.py +0 -416
  158. megadetector/data_management/importers/rspb_to_json.py +0 -356
  159. megadetector/data_management/importers/save_the_elephants_survey_A.py +0 -320
  160. megadetector/data_management/importers/save_the_elephants_survey_B.py +0 -329
  161. megadetector/data_management/importers/snapshot_safari_importer.py +0 -758
  162. megadetector/data_management/importers/snapshot_serengeti_lila.py +0 -1067
  163. megadetector/data_management/importers/snapshotserengeti/make_full_SS_json.py +0 -150
  164. megadetector/data_management/importers/snapshotserengeti/make_per_season_SS_json.py +0 -153
  165. megadetector/data_management/importers/sulross_get_exif.py +0 -65
  166. megadetector/data_management/importers/timelapse_csv_set_to_json.py +0 -490
  167. megadetector/data_management/importers/ubc_to_json.py +0 -399
  168. megadetector/data_management/importers/umn_to_json.py +0 -507
  169. megadetector/data_management/importers/wellington_to_json.py +0 -263
  170. megadetector/data_management/importers/wi_to_json.py +0 -442
  171. megadetector/data_management/importers/zamba_results_to_md_results.py +0 -180
  172. megadetector/data_management/lila/add_locations_to_island_camera_traps.py +0 -101
  173. megadetector/data_management/lila/add_locations_to_nacti.py +0 -151
  174. megadetector-5.0.27.dist-info/RECORD +0 -208
  175. {megadetector-5.0.27.dist-info → megadetector-5.0.29.dist-info}/licenses/LICENSE +0 -0
  176. {megadetector-5.0.27.dist-info → megadetector-5.0.29.dist-info}/top_level.txt +0 -0
@@ -3,7 +3,7 @@
3
3
  md_to_coco.py
4
4
 
5
5
  "Converts" MegaDetector output files to COCO format. "Converts" is in quotes because
6
- this is an opinionated transformation that requires a confidence threshold for most
6
+ this is an opinionated transformation that requires a confidence threshold for most
7
7
  applications.
8
8
 
9
9
  Does not currently handle classification information.
@@ -15,6 +15,8 @@ Does not currently handle classification information.
15
15
  import os
16
16
  import json
17
17
  import uuid
18
+ import sys
19
+ import argparse
18
20
 
19
21
  from tqdm import tqdm
20
22
 
@@ -36,40 +38,41 @@ def md_to_coco(md_results_file,
36
38
  include_failed_images=True,
37
39
  include_annotations_without_bounding_boxes=True,
38
40
  empty_category_id='0',
39
- overwrite_behavior='skip',
41
+ overwrite_behavior='skip',
40
42
  verbose=True,
41
- image_filename_to_size=None):
43
+ image_filename_to_size=None,
44
+ unrecognized_category_handling='error'):
42
45
  """
43
46
  "Converts" MegaDetector output files to COCO format. "Converts" is in quotes because
44
47
  this is an opinionated transformation that typically requires a confidence threshold.
45
-
46
- The default confidence threshold is not 0; the assumption is that by default, you are
48
+
49
+ The default confidence threshold is not 0; the assumption is that by default, you are
47
50
  going to treat the resulting COCO file as a set of labels. If you are using the resulting COCO
48
- file to *evaluate* a detector, rather than as a set of labels, you likely want a
49
- confidence threshold of 0. Confidence values will be written to the semi-standard "score"
51
+ file to *evaluate* a detector, rather than as a set of labels, you likely want a
52
+ confidence threshold of 0. Confidence values will be written to the semi-standard "score"
50
53
  field for each image (regardless of the threshold) if preserve_nonstandard_metadata is True.
51
-
52
- A folder of images is required if width and height information are not available
54
+
55
+ A folder of images is required if width and height information are not available
53
56
  in the MD results file.
54
57
 
55
58
  Args:
56
- md_results_file (str): MD results .json file to convert to COCO
59
+ md_results_file (str): MD results .json file to convert to COCO
57
60
  format
58
- coco_output_file (str, optional): COCO .json file to write; if this is None, we'll return
61
+ coco_output_file (str, optional): COCO .json file to write; if this is None, we'll return
59
62
  a COCO-formatted dict, but won't write it to disk. If this is 'auto', we'll write to
60
63
  [md_results_file_without_extension].coco.json.
61
64
  image_folder (str, optional): folder of images, required if 'width' and 'height' are not
62
65
  present in the MD results file (they are not required by the format)
63
66
  confidence_threshold (float, optional): boxes below this confidence threshold will not be
64
67
  included in the output data
65
- validate_image_sizes (bool, optional): if this is True, we'll check the image sizes
68
+ validate_image_sizes (bool, optional): if this is True, we'll check the image sizes
66
69
  regardless of whether "width" and "height" are present in the MD results file.
67
70
  info (dict, optional): arbitrary metadata to include in an "info" field in the COCO-formatted
68
71
  output
69
- preserve_nonstandard_metadata (bool, optional): if this is True, confidence will be preserved in a
70
- non-standard "score" field in each annotation, and any random fields present in each image's
71
- data (e.g. EXIF metadata) will be propagated to COCO output
72
- include_failed_images (bool, optional): if this is True, failed images will be propagated to COCO output
72
+ preserve_nonstandard_metadata (bool, optional): if this is True, confidence will be preserved in a
73
+ non-standard "score" field in each annotation, and any random fields present in each image's
74
+ data (e.g. EXIF metadata) will be propagated to COCO output
75
+ include_failed_images (bool, optional): if this is True, failed images will be propagated to COCO output
73
76
  with a non-empty "failure" field and no other fields, otherwise failed images will be skipped.
74
77
  include_annotations_without_bounding_boxes (bool, optional): if this is True, annotations with
75
78
  only class labels (no bounding boxes) will be included in the output. If this is False, empty
@@ -84,19 +87,26 @@ def md_to_coco(md_results_file,
84
87
  image sizes is the slowest step, so if you need to convert many results files at once for the same
85
88
  set of images, things will be gobs faster if you read the image sizes in advance and pass them in
86
89
  via this argument. The format used here is the same format output by parallel_get_image_sizes().
87
-
90
+ unrecognized_category_handling (str or float, optional): specifies what to do when encountering category
91
+ IDs not in the category mapping. Can be "error", "ignore", or "warning". Can also be a float,
92
+ in which case an error is thrown if an unrecognized category has a confidence value higher than
93
+ this value.
94
+
88
95
  Returns:
89
96
  dict: the COCO data dict, identical to what's written to [coco_output_file] if [coco_output_file]
90
97
  is not None.
91
98
  """
92
-
99
+
93
100
  assert isinstance(md_results_file,str)
94
101
  assert os.path.isfile(md_results_file), \
95
102
  'MD results file {} does not exist'.format(md_results_file)
103
+ assert (isinstance(unrecognized_category_handling,float)) or \
104
+ (unrecognized_category_handling in ('error','warning','ignore')), \
105
+ 'Invalid category handling behavior {}'.format(unrecognized_category_handling)
96
106
 
97
107
  if coco_output_file == 'auto':
98
108
  coco_output_file = insert_before_extension(md_results_file,'coco')
99
-
109
+
100
110
  if coco_output_file is not None:
101
111
  if os.path.isfile(coco_output_file):
102
112
  if overwrite_behavior == 'skip':
@@ -120,155 +130,177 @@ def md_to_coco(md_results_file,
120
130
  pass
121
131
  elif overwrite_behavior == 'error':
122
132
  raise ValueError('Output file {} exists'.format(coco_output_file))
123
-
124
- with open(md_results_file,'r') as f:
125
- md_results = json.load(f)
126
-
133
+
134
+ with open(md_results_file,'r') as f:
135
+ md_results = json.load(f)
136
+
127
137
  coco_images = []
128
138
  coco_annotations = []
129
-
130
- print('Converting MD results file {} to COCO file {}...'.format(
131
- md_results_file, coco_output_file))
132
-
139
+
140
+ if verbose:
141
+ print('Converting MD results file {} to COCO file {}...'.format(
142
+ md_results_file, coco_output_file))
143
+
133
144
  # im = md_results['images'][0]
134
145
  for im in tqdm(md_results['images'],disable=(not verbose)):
135
-
146
+
136
147
  coco_im = {}
137
148
  coco_im['id'] = im['file']
138
149
  coco_im['file_name'] = im['file']
139
-
140
- # There is no concept of this in the COCO standard
150
+
151
+ # There is no concept of this in the COCO standard
141
152
  if 'failure' in im and im['failure'] is not None:
142
153
  if include_failed_images:
143
154
  coco_im['failure'] = im['failure']
144
155
  coco_images.append(coco_im)
145
156
  continue
146
-
157
+
147
158
  # Read/validate image size
148
159
  w = None
149
160
  h = None
150
-
161
+
151
162
  if ('width' not in im) or ('height' not in im) or validate_image_sizes:
152
163
  if (image_folder is None) and (image_filename_to_size is None):
153
- raise ValueError('Must provide an image folder or a size mapping when height/width need to be read from images')
154
-
164
+ raise ValueError('Must provide an image folder or a size mapping when ' + \
165
+ 'height/width need to be read from images')
166
+
155
167
  w = None; h = None
156
-
168
+
157
169
  if image_filename_to_size is not None:
158
-
170
+
159
171
  if im['file'] not in image_filename_to_size:
160
- print('Warning: file {} not in image size mapping dict, reading from file'.format(im['file']))
172
+ print('Warning: file {} not in image size mapping dict, reading from file'.format(
173
+ im['file']))
161
174
  else:
162
175
  image_size = image_filename_to_size[im['file']]
163
176
  if image_size is not None:
164
177
  assert len(image_size) == 2
165
178
  w = image_size[0]
166
179
  h = image_size[1]
167
-
180
+
168
181
  if w is None:
169
-
182
+
170
183
  image_file_abs = os.path.join(image_folder,im['file'])
171
184
  pil_im = vis_utils.open_image(image_file_abs)
172
185
  w = pil_im.width
173
186
  h = pil_im.height
174
-
187
+
175
188
  if validate_image_sizes:
176
189
  if 'width' in im:
177
190
  assert im['width'] == w, 'Width mismatch for image {}'.format(im['file'])
178
191
  if 'height' in im:
179
192
  assert im['height'] == h, 'Height mismatch for image {}'.format(im['file'])
180
193
  else:
181
-
194
+
182
195
  w = im['width']
183
196
  h = im['height']
184
-
197
+
185
198
  coco_im['width'] = w
186
199
  coco_im['height'] = h
187
-
200
+
188
201
  # Add other, non-standard fields to the output dict
189
202
  if preserve_nonstandard_metadata:
190
203
  for k in im.keys():
191
204
  if k not in ('file','detections','width','height'):
192
205
  coco_im[k] = im[k]
193
-
206
+
194
207
  coco_images.append(coco_im)
195
-
208
+
196
209
  # detection = im['detections'][0]
197
210
  for detection in im['detections']:
198
-
211
+
199
212
  # Skip below-threshold detections
200
213
  if confidence_threshold is not None and detection['conf'] < confidence_threshold:
201
214
  continue
202
-
215
+
203
216
  # Create an annotation
204
- ann = {}
217
+ ann = {}
205
218
  ann['id'] = str(uuid.uuid1())
206
- ann['image_id'] = coco_im['id']
207
-
219
+ ann['image_id'] = coco_im['id']
220
+
208
221
  md_category_id = detection['category']
222
+
223
+ if md_category_id not in md_results['detection_categories']:
224
+
225
+ s = 'unrecognized category ID {} occurred with confidence {} in file {}'.format(
226
+ md_category_id,detection['conf'],im['file'])
227
+ if isinstance(unrecognized_category_handling,float):
228
+ if detection['conf'] > unrecognized_category_handling:
229
+ raise ValueError(s)
230
+ else:
231
+ continue
232
+ elif unrecognized_category_handling == 'warning':
233
+ print('Warning: {}'.format(s))
234
+ continue
235
+ elif unrecognized_category_handling == 'ignore':
236
+ continue
237
+ else:
238
+ raise ValueError(s)
239
+
209
240
  coco_category_id = int(md_category_id)
210
241
  ann['category_id'] = coco_category_id
211
-
242
+
212
243
  if md_category_id != empty_category_id:
213
-
244
+
214
245
  assert 'bbox' in detection,\
215
246
  'Oops: non-empty category with no bbox in {}'.format(im['file'])
216
-
247
+
217
248
  ann['bbox'] = detection['bbox']
218
-
249
+
219
250
  # MegaDetector: [x,y,width,height] (normalized, origin upper-left)
220
251
  # COCO: [x,y,width,height] (absolute, origin upper-left)
221
252
  ann['bbox'][0] = ann['bbox'][0] * coco_im['width']
222
253
  ann['bbox'][1] = ann['bbox'][1] * coco_im['height']
223
254
  ann['bbox'][2] = ann['bbox'][2] * coco_im['width']
224
- ann['bbox'][3] = ann['bbox'][3] * coco_im['height']
225
-
255
+ ann['bbox'][3] = ann['bbox'][3] * coco_im['height']
256
+
226
257
  else:
227
-
258
+
228
259
  # In very esoteric cases, we use the empty category (0) in MD-formatted output files
229
260
  print('Warning: empty category ({}) used for annotation in file {}'.format(
230
261
  empty_category_id,im['file']))
231
262
  pass
232
-
263
+
233
264
  if preserve_nonstandard_metadata:
234
265
  # "Score" is a semi-standard string here, recognized by at least pycocotools
235
266
  # ann['conf'] = detection['conf']
236
267
  ann['score'] = detection['conf']
237
-
268
+
238
269
  if 'bbox' in ann or include_annotations_without_bounding_boxes:
239
- coco_annotations.append(ann)
240
-
270
+ coco_annotations.append(ann)
271
+
241
272
  # ...for each detection
242
-
273
+
243
274
  # ...for each image
244
275
 
245
276
  output_dict = {}
246
-
277
+
247
278
  if info is not None:
248
279
  output_dict['info'] = info
249
280
  else:
250
281
  output_dict['info'] = {'description':'Converted from MD results file {}'.format(md_results_file)}
251
282
  output_dict['info']['confidence_threshold'] = confidence_threshold
252
-
283
+
253
284
  output_dict['images'] = coco_images
254
285
  output_dict['annotations'] = coco_annotations
255
-
286
+
256
287
  output_dict['categories'] = []
257
-
288
+
258
289
  for md_category_id in md_results['detection_categories'].keys():
259
-
290
+
260
291
  coco_category_id = int(md_category_id)
261
292
  coco_category = {'id':coco_category_id,
262
293
  'name':md_results['detection_categories'][md_category_id]}
263
294
  output_dict['categories'].append(coco_category)
264
-
265
- print('Writing COCO output file...')
266
-
295
+
296
+ if verbose:
297
+ print('Writing COCO output file...')
298
+
267
299
  if coco_output_file is not None:
268
300
  with open(coco_output_file,'w') as f:
269
301
  json.dump(output_dict,f,indent=1)
270
-
271
- return output_dict
302
+
303
+ return output_dict
272
304
 
273
305
  # ...def md_to_coco(...)
274
306
 
@@ -276,11 +308,11 @@ def md_to_coco(md_results_file,
276
308
  #%% Interactive driver
277
309
 
278
310
  if False:
279
-
311
+
280
312
  pass
281
313
 
282
314
  #%% Configure options
283
-
315
+
284
316
  md_results_file = os.path.expanduser('~/data/md-test.json')
285
317
  coco_output_file = os.path.expanduser('~/data/md-test-coco.json')
286
318
  image_folder = os.path.expanduser('~/data/md-test')
@@ -290,10 +322,10 @@ if False:
290
322
  info=None
291
323
  preserve_nonstandard_metadata=True
292
324
  include_failed_images=False
293
-
294
-
325
+
326
+
295
327
  #%% Programmatic execution
296
-
328
+
297
329
  output_dict = md_to_coco(md_results_file,
298
330
  coco_output_file=coco_output_file,
299
331
  image_folder=image_folder,
@@ -302,10 +334,10 @@ if False:
302
334
  info=info,
303
335
  preserve_nonstandard_metadata=preserve_nonstandard_metadata,
304
336
  include_failed_images=include_failed_images)
305
-
306
-
337
+
338
+
307
339
  #%% Command-line example
308
-
340
+
309
341
  s = f'python md_to_coco.py {md_results_file} {coco_output_file} {confidence_threshold} '
310
342
  if image_folder is not None:
311
343
  s += f' --image_folder {image_folder}'
@@ -316,9 +348,9 @@ if False:
316
348
 
317
349
  print(s); import clipboard; clipboard.copy(s)
318
350
 
319
-
351
+
320
352
  #%% Preview the resulting file
321
-
353
+
322
354
  from megadetector.visualization import visualize_db
323
355
  options = visualize_db.DbVizOptions()
324
356
  options.parallelize_rendering = True
@@ -327,49 +359,48 @@ if False:
327
359
 
328
360
  html_file,_ = visualize_db.visualize_db(coco_output_file,
329
361
  os.path.expanduser('~/tmp/md_to_coco_preview'),
330
- image_folder,options)
362
+ image_folder,options)
331
363
 
332
364
  from megadetector.utils import path_utils # noqa
333
365
  path_utils.open_file(html_file)
334
-
335
-
336
- #%% Command-line driver
337
366
 
338
- import sys,argparse
339
367
 
340
- def main():
368
+ #%% Command-line driver
369
+
370
+ def main(): # noqa
341
371
 
342
372
  parser = argparse.ArgumentParser(
343
- description='"Convert" MD output to COCO format, in quotes because this is an opinionated transformation that requires a confidence threshold')
344
-
373
+ description='"Convert" MD output to COCO format, in quotes because this is an opinionated ' + \
374
+ 'transformation that requires a confidence threshold')
375
+
345
376
  parser.add_argument(
346
377
  'md_results_file',
347
378
  type=str,
348
379
  help='Path to MD results file (.json)')
349
-
380
+
350
381
  parser.add_argument(
351
382
  'coco_output_file',
352
383
  type=str,
353
384
  help='Output filename (.json)')
354
-
385
+
355
386
  parser.add_argument(
356
387
  'confidence_threshold',
357
388
  type=float,
358
389
  default=default_confidence_threshold,
359
390
  help='Confidence threshold (default {})'.format(default_confidence_threshold)
360
391
  )
361
-
392
+
362
393
  parser.add_argument(
363
394
  '--image_folder',
364
395
  type=str,
365
396
  default=None,
366
397
  help='Image folder, only required if we will need to access image sizes'
367
398
  )
368
-
399
+
369
400
  parser.add_argument(
370
401
  '--preserve_nonstandard_metadata',
371
402
  action='store_true',
372
- help='Preserve metadata that isn\'t normally included in ' +
403
+ help='Preserve metadata that isn\'t normally included in ' +
373
404
  'COCO-formatted data (e.g. EXIF metadata, confidence values)'
374
405
  )
375
406
 
@@ -378,7 +409,7 @@ def main():
378
409
  action='store_true',
379
410
  help='Keep a record of corrupted images in the output; may not be completely COCO-compliant'
380
411
  )
381
-
412
+
382
413
  if len(sys.argv[1:]) == 0:
383
414
  parser.print_help()
384
415
  parser.exit()
@@ -392,7 +423,7 @@ def main():
392
423
  validate_image_sizes=False,
393
424
  info=None,
394
425
  preserve_nonstandard_metadata=args.preserve_nonstandard_metadata,
395
- include_failed_images=args.include_failed_images)
396
-
426
+ include_failed_images=args.include_failed_images)
427
+
397
428
  if __name__ == '__main__':
398
429
  main()