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
@@ -11,6 +11,8 @@ Converts a folder of labelme-formatted .json files to COCO.
11
11
  import json
12
12
  import os
13
13
  import uuid
14
+ import sys
15
+ import argparse
14
16
 
15
17
  from multiprocessing.pool import Pool, ThreadPool
16
18
  from functools import partial
@@ -25,9 +27,9 @@ from megadetector.visualization.visualization_utils import open_image
25
27
  def _add_category(category_name,category_name_to_id,candidate_category_id=0):
26
28
  """
27
29
  Adds the category [category_name] to the dict [category_name_to_id], by default
28
- using the next available integer index.
30
+ using the next available integer index.
29
31
  """
30
-
32
+
31
33
  if category_name in category_name_to_id:
32
34
  return category_name_to_id[category_name]
33
35
  while candidate_category_id in category_name_to_id.values():
@@ -42,33 +44,33 @@ def _process_labelme_file(image_fn_relative,input_folder,use_folders_as_labels,
42
44
  """
43
45
  Internal function for processing each image; this support function facilitates parallelization.
44
46
  """
45
-
47
+
46
48
  result = {}
47
49
  result['im'] = None
48
50
  result['annotations_this_image'] = None
49
51
  result['status'] = None
50
-
52
+
51
53
  image_fn_abs = os.path.join(input_folder,image_fn_relative)
52
54
  json_fn_abs = os.path.splitext(image_fn_abs)[0] + '.json'
53
-
55
+
54
56
  im = {}
55
57
  im['id'] = image_fn_relative
56
58
  im['file_name'] = image_fn_relative
57
-
59
+
58
60
  # If there's no .json file for this image...
59
61
  if not os.path.isfile(json_fn_abs):
60
-
62
+
61
63
  # Either skip it...
62
64
  if no_json_handling == 'skip':
63
65
  print('Skipping image {} (no .json file)'.format(image_fn_relative))
64
66
  result['status'] = 'skipped (no .json file)'
65
67
  return result
66
-
68
+
67
69
  # ...or error
68
70
  elif no_json_handling == 'error':
69
71
  raise ValueError('Image file {} has no corresponding .json file'.format(
70
72
  image_fn_relative))
71
-
73
+
72
74
  # ...or treat it as empty.
73
75
  elif no_json_handling == 'empty':
74
76
  try:
@@ -79,23 +81,23 @@ def _process_labelme_file(image_fn_relative,input_folder,use_folders_as_labels,
79
81
  return result
80
82
  im['width'] = pil_im.width
81
83
  im['height'] = pil_im.height
82
-
84
+
83
85
  # Just in case we need to differentiate between "no .json file" and "a .json file with no annotations"
84
86
  im['no_labelme_json'] = True
85
87
  shapes = []
86
88
  else:
87
89
  raise ValueError('Unrecognized specifier {} for handling images with no .json files'.format(
88
- no_json_handling))
89
-
90
+ no_json_handling))
91
+
90
92
  # If we found a .json file for this image...
91
93
  else:
92
-
94
+
93
95
  # Read the .json file
94
96
  with open(json_fn_abs,'r') as f:
95
- labelme_data = json.load(f)
97
+ labelme_data = json.load(f)
96
98
  im['width'] = labelme_data['imageWidth']
97
99
  im['height'] = labelme_data['imageHeight']
98
-
100
+
99
101
  if validate_image_sizes:
100
102
  try:
101
103
  pil_im = open_image(image_fn_abs)
@@ -116,55 +118,55 @@ def _process_labelme_file(image_fn_relative,input_folder,use_folders_as_labels,
116
118
  im['flags'] = labelme_data['flags']
117
119
 
118
120
  annotations_this_image = []
119
-
121
+
120
122
  if len(shapes) == 0:
121
-
123
+
122
124
  if allow_new_categories:
123
125
  category_id = _add_category('empty',category_name_to_id)
124
126
  else:
125
127
  assert 'empty' in category_name_to_id
126
128
  category_id = category_name_to_id['empty']
127
-
129
+
128
130
  ann = {}
129
131
  ann['id'] = str(uuid.uuid1())
130
132
  ann['image_id'] = im['id']
131
133
  ann['category_id'] = category_id
132
134
  ann['sequence_level_annotation'] = False
133
135
  annotations_this_image.append(ann)
134
-
136
+
135
137
  else:
136
-
138
+
137
139
  for shape in shapes:
138
-
140
+
139
141
  if shape['shape_type'] != 'rectangle':
140
142
  print('Only rectangles are supported, skipping an annotation of type {} in {}'.format(
141
143
  shape['shape_type'],image_fn_relative))
142
144
  continue
143
-
145
+
144
146
  if use_folders_as_labels:
145
147
  category_name = os.path.basename(os.path.dirname(image_fn_abs))
146
148
  else:
147
- category_name = shape['label']
148
-
149
+ category_name = shape['label']
150
+
149
151
  if allow_new_categories:
150
152
  category_id = _add_category(category_name,category_name_to_id)
151
153
  else:
152
154
  assert category_name in category_name_to_id
153
155
  category_id = category_name_to_id[category_name]
154
-
156
+
155
157
  points = shape['points']
156
158
  if len(points) != 2:
157
159
  print('Warning: illegal rectangle with {} points for {}'.format(
158
160
  len(points),image_fn_relative))
159
161
  continue
160
-
162
+
161
163
  p0 = points[0]
162
164
  p1 = points[1]
163
165
  x0 = min(p0[0],p1[0])
164
166
  x1 = max(p0[0],p1[0])
165
167
  y0 = min(p0[1],p1[1])
166
168
  y1 = max(p0[1],p1[1])
167
-
169
+
168
170
  bbox = [x0,y0,abs(x1-x0),abs(y1-y0)]
169
171
  ann = {}
170
172
  ann['id'] = str(uuid.uuid1())
@@ -173,14 +175,14 @@ def _process_labelme_file(image_fn_relative,input_folder,use_folders_as_labels,
173
175
  ann['sequence_level_annotation'] = False
174
176
  ann['bbox'] = bbox
175
177
  annotations_this_image.append(ann)
176
-
178
+
177
179
  # ...for each shape
178
-
180
+
179
181
  result['im'] = im
180
182
  result['annotations_this_image'] = annotations_this_image
181
183
 
182
184
  return result
183
-
185
+
184
186
  # ...def _process_labelme_file(...)
185
187
 
186
188
 
@@ -198,31 +200,31 @@ def labelme_to_coco(input_folder,
198
200
  recursive=True,
199
201
  no_json_handling='skip',
200
202
  validate_image_sizes=True,
201
- max_workers=1,
203
+ max_workers=1,
202
204
  use_threads=True):
203
205
  """
204
206
  Finds all images in [input_folder] that have corresponding .json files, and converts
205
207
  to a COCO .json file.
206
-
208
+
207
209
  Currently only supports bounding box annotations and image-level flags (i.e., does not
208
210
  support point or general polygon annotations).
209
-
211
+
210
212
  Labelme's image-level flags don't quite fit the COCO annotations format, so they are attached
211
213
  to image objects, rather than annotation objects.
212
-
213
- If output_file is None, just returns the resulting dict, does not write to file.
214
-
214
+
215
+ If output_file is None, just returns the resulting dict, does not write to file.
216
+
215
217
  if use_folders_as_labels is False (default), the output labels come from the labelme
216
218
  .json files. If use_folders_as_labels is True, the lowest-level folder name containing
217
219
  each .json file will determine the output label. E.g., if use_folders_as_labels is True,
218
220
  and the folder contains:
219
-
221
+
220
222
  images/train/lion/image0001.json
221
-
222
- ...all boxes in image0001.json will be given the label "lion", regardless of the labels in the
223
- file. Empty images in the "lion" folder will still be given the label "empty" (or
223
+
224
+ ...all boxes in image0001.json will be given the label "lion", regardless of the labels in the
225
+ file. Empty images in the "lion" folder will still be given the label "empty" (or
224
226
  [empty_category_name]).
225
-
227
+
226
228
  Args:
227
229
  input_folder (str): input folder to search for images and Labelme .json files
228
230
  output_file (str, optional): output file to which we should write COCO-formatted data; if None
@@ -240,9 +242,9 @@ def labelme_to_coco(input_folder,
240
242
  use_folders_as_labels (bool, optional): if this is True, class names will be pulled from folder names,
241
243
  useful if you have images like a/b/cat/image001.jpg, a/b/dog/image002.jpg, etc.
242
244
  recursive (bool, optional): whether to recurse into [input_folder]
243
- no_json_handling (str, optional): how to deal with image files that have no corresponding .json files,
245
+ no_json_handling (str, optional): how to deal with image files that have no corresponding .json files,
244
246
  can be:
245
-
247
+
246
248
  - 'skip': ignore image files with no corresponding .json files
247
249
  - 'empty': treat image files with no corresponding .json files as empty
248
250
  - 'error': throw an error when an image file has no corresponding .json file
@@ -252,15 +254,15 @@ def labelme_to_coco(input_folder,
252
254
  parallelization
253
255
  use_threads (bool, optional): whether to use threads (True) or processes (False) for parallelization,
254
256
  not relevant if max_workers <= 1
255
-
257
+
256
258
  Returns:
257
259
  dict: a COCO-formatted dictionary, identical to what's written to [output_file] if [output_file] is not None.
258
260
  """
259
-
261
+
260
262
  if max_workers > 1:
261
263
  assert category_id_to_category_name is not None, \
262
264
  'When parallelizing labelme --> COCO conversion, you must supply a category mapping'
263
-
265
+
264
266
  if category_id_to_category_name is None:
265
267
  category_name_to_id = {}
266
268
  else:
@@ -270,7 +272,7 @@ def labelme_to_coco(input_folder,
270
272
  category_name_to_id[category_name] = int(category_name_to_id[category_name])
271
273
  except ValueError:
272
274
  raise ValueError('Category IDs must be ints or string-formatted ints')
273
-
275
+
274
276
  # If the user supplied an explicit empty category ID, and the empty category
275
277
  # name is already in category_name_to_id, make sure they match.
276
278
  if empty_category_id is not None:
@@ -285,13 +287,13 @@ def labelme_to_coco(input_folder,
285
287
  empty_category_id = category_name_to_id[empty_category_name]
286
288
 
287
289
  del category_id_to_category_name
288
-
290
+
289
291
  # Enumerate images
290
- print('Enumerating images in {}'.format(input_folder))
292
+ print('Enumerating images in {}'.format(input_folder))
291
293
  image_filenames_relative = path_utils.find_images(input_folder,recursive=recursive,
292
294
  return_relative_paths=True,
293
- convert_slashes=True)
294
-
295
+ convert_slashes=True)
296
+
295
297
  # Remove any images we're supposed to skip
296
298
  if (relative_paths_to_include is not None) or (relative_paths_to_exclude is not None):
297
299
  image_filenames_relative_to_process = []
@@ -305,65 +307,71 @@ def labelme_to_coco(input_folder,
305
307
  len(image_filenames_relative_to_process),
306
308
  len(image_filenames_relative)))
307
309
  image_filenames_relative = image_filenames_relative_to_process
308
-
310
+
309
311
  # If the user supplied a category ID to use for empty images...
310
312
  if empty_category_id is not None:
311
313
  try:
312
314
  empty_category_id = int(empty_category_id)
313
315
  except ValueError:
314
316
  raise ValueError('Category IDs must be ints or string-formatted ints')
315
-
317
+
316
318
  if empty_category_id is None:
317
319
  empty_category_id = _add_category(empty_category_name,category_name_to_id)
318
-
320
+
319
321
  if max_workers <= 1:
320
-
322
+
321
323
  image_results = []
322
324
  for image_fn_relative in tqdm(image_filenames_relative):
323
-
325
+
324
326
  result = _process_labelme_file(image_fn_relative,input_folder,use_folders_as_labels,
325
327
  no_json_handling,validate_image_sizes,
326
- category_name_to_id,allow_new_categories=True)
328
+ category_name_to_id,allow_new_categories=True)
327
329
  image_results.append(result)
328
-
329
- else:
330
-
330
+
331
+ else:
332
+
331
333
  n_workers = min(max_workers,len(image_filenames_relative))
332
334
  assert category_name_to_id is not None
333
-
334
- if use_threads:
335
- pool = ThreadPool(n_workers)
336
- else:
337
- pool = Pool(n_workers)
338
-
339
- image_results = list(tqdm(pool.imap(
340
- partial(_process_labelme_file,
341
- input_folder=input_folder,
342
- use_folders_as_labels=use_folders_as_labels,
343
- no_json_handling=no_json_handling,
344
- validate_image_sizes=validate_image_sizes,
345
- category_name_to_id=category_name_to_id,
346
- allow_new_categories=False
347
- ),image_filenames_relative), total=len(image_filenames_relative)))
348
-
335
+
336
+ pool = None
337
+ try:
338
+ if use_threads:
339
+ pool = ThreadPool(n_workers)
340
+ else:
341
+ pool = Pool(n_workers)
342
+
343
+ image_results = list(tqdm(pool.imap(
344
+ partial(_process_labelme_file,
345
+ input_folder=input_folder,
346
+ use_folders_as_labels=use_folders_as_labels,
347
+ no_json_handling=no_json_handling,
348
+ validate_image_sizes=validate_image_sizes,
349
+ category_name_to_id=category_name_to_id,
350
+ allow_new_categories=False
351
+ ),image_filenames_relative), total=len(image_filenames_relative)))
352
+ finally:
353
+ pool.close()
354
+ pool.join()
355
+ print("Pool closed and joined for labelme file processing")
356
+
349
357
  images = []
350
358
  annotations = []
351
-
359
+
352
360
  # Flatten the lists of images and annotations
353
361
  for result in image_results:
354
362
  im = result['im']
355
363
  annotations_this_image = result['annotations_this_image']
356
-
364
+
357
365
  if im is None:
358
366
  assert annotations_this_image is None
359
367
  else:
360
368
  images.append(im)
361
369
  annotations.extend(annotations_this_image)
362
-
370
+
363
371
  output_dict = {}
364
372
  output_dict['images'] = images
365
373
  output_dict['annotations'] = annotations
366
-
374
+
367
375
  if info_struct is None:
368
376
  info_struct = {}
369
377
  if 'description' not in info_struct:
@@ -371,17 +379,17 @@ def labelme_to_coco(input_folder,
371
379
  'Converted to COCO from labelme annotations in folder {}'.format(input_folder)
372
380
  if 'version' not in info_struct:
373
381
  info_struct['version'] = 1.0
374
-
382
+
375
383
  output_dict['info'] = info_struct
376
384
  categories = []
377
385
  for category_name in category_name_to_id:
378
386
  categories.append({'name':category_name,'id':category_name_to_id[category_name]})
379
387
  output_dict['categories'] = categories
380
-
388
+
381
389
  if output_file is not None:
382
390
  with open(output_file,'w') as f:
383
391
  json.dump(output_dict,f,indent=1)
384
-
392
+
385
393
  return output_dict
386
394
 
387
395
  # ...def labelme_to_coco()
@@ -389,59 +397,59 @@ def labelme_to_coco(input_folder,
389
397
 
390
398
  def find_empty_labelme_files(input_folder,recursive=True):
391
399
  """
392
- Returns a list of all image files in in [input_folder] associated with .json files that have
400
+ Returns a list of all image files in in [input_folder] associated with .json files that have
393
401
  no boxes in them. Also returns a list of images with no associated .json files. Specifically,
394
402
  returns a dict:
395
-
403
+
396
404
  .. code-block: none
397
-
405
+
398
406
  {
399
407
  'images_with_empty_json_files':[list],
400
408
  'images_with_no_json_files':[list],
401
409
  'images_with_non_empty_json_files':[list]
402
410
  }
403
-
411
+
404
412
  Args:
405
413
  input_folder (str): the folder to search for empty (i.e., box-less) Labelme .json files
406
414
  recursive (bool, optional): whether to recurse into [input_folder]
407
-
415
+
408
416
  Returns:
409
417
  dict: a dict with fields:
410
- - images_with_empty_json_files: a list of all image files in [input_folder] associated with
418
+ - images_with_empty_json_files: a list of all image files in [input_folder] associated with
411
419
  .json files that have no boxes in them
412
420
  - images_with_no_json_files: a list of images in [input_folder] with no associated .json files
413
421
  - images_with_non_empty_json_files: a list of images in [input_folder] associated with .json
414
- files that have at least one box
422
+ files that have at least one box
415
423
  """
416
424
  image_filenames_relative = path_utils.find_images(input_folder,recursive=True,
417
425
  return_relative_paths=True)
418
-
426
+
419
427
  images_with_empty_json_files = []
420
428
  images_with_no_json_files = []
421
429
  images_with_non_empty_json_files = []
422
-
430
+
423
431
  # fn_relative = image_filenames_relative[0]
424
432
  for fn_relative in image_filenames_relative:
425
-
433
+
426
434
  image_fn_abs = os.path.join(input_folder,fn_relative)
427
435
  json_fn_abs = os.path.splitext(image_fn_abs)[0] + '.json'
428
-
436
+
429
437
  if not os.path.isfile(json_fn_abs):
430
438
  images_with_no_json_files.append(fn_relative)
431
439
  continue
432
-
440
+
433
441
  else:
434
442
  # Read the .json file
435
443
  with open(json_fn_abs,'r') as f:
436
- labelme_data = json.load(f)
444
+ labelme_data = json.load(f)
437
445
  shapes = labelme_data['shapes']
438
446
  if len(shapes) == 0:
439
447
  images_with_empty_json_files.append(fn_relative)
440
448
  else:
441
449
  images_with_non_empty_json_files.append(fn_relative)
442
-
450
+
443
451
  # ...for every image
444
-
452
+
445
453
  return {'images_with_empty_json_files':images_with_empty_json_files,
446
454
  'images_with_no_json_files':images_with_no_json_files,
447
455
  'images_with_non_empty_json_files':images_with_non_empty_json_files}
@@ -452,22 +460,22 @@ def find_empty_labelme_files(input_folder,recursive=True):
452
460
  #%% Interactive driver
453
461
 
454
462
  if False:
455
-
463
+
456
464
  pass
457
465
 
458
466
  #%% Options
459
-
467
+
460
468
  empty_category_name = 'empty'
461
469
  empty_category_id = None
462
470
  category_id_to_category_name = None
463
471
  info_struct = None
464
-
472
+
465
473
  input_folder = os.path.expanduser('~/data/md-test')
466
474
  output_file = os.path.expanduser('~/data/md-test-labelme-to-coco.json')
467
-
468
-
475
+
476
+
469
477
  #%% Programmatic execution
470
-
478
+
471
479
  output_dict = labelme_to_coco(input_folder,output_file,
472
480
  category_id_to_category_name=category_id_to_category_name,
473
481
  empty_category_name=empty_category_name,
@@ -476,25 +484,25 @@ if False:
476
484
  use_folders_as_labels=False,
477
485
  validate_image_sizes=False,
478
486
  no_json_handling='empty')
479
-
480
-
487
+
488
+
481
489
  #%% Validate
482
-
490
+
483
491
  from megadetector.data_management.databases import integrity_check_json_db
484
-
492
+
485
493
  options = integrity_check_json_db.IntegrityCheckOptions()
486
-
494
+
487
495
  options.baseDir = input_folder
488
496
  options.bCheckImageSizes = True
489
497
  options.bCheckImageExistence = True
490
498
  options.bFindUnusedImages = True
491
499
  options.bRequireLocation = False
492
-
493
- sortedCategories, _, errorInfo = integrity_check_json_db.integrity_check_json_db(output_file,options)
494
-
500
+
501
+ sortec_categories, _, error_info = integrity_check_json_db.integrity_check_json_db(output_file,options)
502
+
495
503
 
496
504
  #%% Preview
497
-
505
+
498
506
  from megadetector.visualization import visualize_db
499
507
  options = visualize_db.DbVizOptions()
500
508
  options.parallelize_rendering = True
@@ -503,38 +511,36 @@ if False:
503
511
 
504
512
  html_file,_ = visualize_db.visualize_db(output_file,os.path.expanduser('~/tmp/labelme_to_coco_preview'),
505
513
  input_folder,options)
506
-
514
+
507
515
 
508
516
  from megadetector.utils import path_utils # noqa
509
517
  path_utils.open_file(html_file)
510
-
511
-
518
+
519
+
512
520
  #%% Prepare command line
513
521
 
514
522
  s = 'python labelme_to_coco.py {} {}'.format(input_folder,output_file)
515
523
  print(s)
516
524
  import clipboard; clipboard.copy(s)
517
525
 
518
-
519
- #%% Command-line driver
520
526
 
521
- import sys,argparse
527
+ #%% Command-line driver
522
528
 
523
- def main():
529
+ def main(): # noqa
524
530
 
525
531
  parser = argparse.ArgumentParser(
526
532
  description='Convert labelme-formatted data to COCO')
527
-
533
+
528
534
  parser.add_argument(
529
535
  'input_folder',
530
536
  type=str,
531
537
  help='Path to images and .json annotation files')
532
-
538
+
533
539
  parser.add_argument(
534
540
  'output_file',
535
541
  type=str,
536
542
  help='Output filename (.json)')
537
-
543
+
538
544
  if len(sys.argv[1:]) == 0:
539
545
  parser.print_help()
540
546
  parser.exit()
@@ -542,6 +548,6 @@ def main():
542
548
  args = parser.parse_args()
543
549
 
544
550
  labelme_to_coco(args.input_folder,args.output_file)
545
-
551
+
546
552
  if __name__ == '__main__':
547
553
  main()