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
@@ -8,9 +8,11 @@ Converts a folder of labelme-formatted .json files to COCO.
8
8
 
9
9
  #%% Constants and imports
10
10
 
11
- import json
12
11
  import os
12
+ import sys
13
+ import json
13
14
  import uuid
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
@@ -230,6 +232,8 @@ def labelme_to_coco(input_folder,
230
232
  category_id_to_category_name (dict, optional): dict mapping category IDs to category names;
231
233
  really used to map Labelme category names to COCO category IDs. IDs will be auto-generated
232
234
  if this is None.
235
+ empty_category_name (str, optional): if images are present without boxes, the category name
236
+ we should use for whole-image (and not-very-COCO-like) empty categories.
233
237
  empty_category_id (int, optional): category ID to use for the not-very-COCO-like "empty" category;
234
238
  also see the no_json_handling parameter.
235
239
  info_struct (dict, optional): dict to stash in the "info" field of the resulting COCO dict
@@ -240,9 +244,9 @@ def labelme_to_coco(input_folder,
240
244
  use_folders_as_labels (bool, optional): if this is True, class names will be pulled from folder names,
241
245
  useful if you have images like a/b/cat/image001.jpg, a/b/dog/image002.jpg, etc.
242
246
  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,
247
+ no_json_handling (str, optional): how to deal with image files that have no corresponding .json files,
244
248
  can be:
245
-
249
+
246
250
  - 'skip': ignore image files with no corresponding .json files
247
251
  - 'empty': treat image files with no corresponding .json files as empty
248
252
  - 'error': throw an error when an image file has no corresponding .json file
@@ -252,15 +256,15 @@ def labelme_to_coco(input_folder,
252
256
  parallelization
253
257
  use_threads (bool, optional): whether to use threads (True) or processes (False) for parallelization,
254
258
  not relevant if max_workers <= 1
255
-
259
+
256
260
  Returns:
257
261
  dict: a COCO-formatted dictionary, identical to what's written to [output_file] if [output_file] is not None.
258
262
  """
259
-
263
+
260
264
  if max_workers > 1:
261
265
  assert category_id_to_category_name is not None, \
262
266
  'When parallelizing labelme --> COCO conversion, you must supply a category mapping'
263
-
267
+
264
268
  if category_id_to_category_name is None:
265
269
  category_name_to_id = {}
266
270
  else:
@@ -270,7 +274,7 @@ def labelme_to_coco(input_folder,
270
274
  category_name_to_id[category_name] = int(category_name_to_id[category_name])
271
275
  except ValueError:
272
276
  raise ValueError('Category IDs must be ints or string-formatted ints')
273
-
277
+
274
278
  # If the user supplied an explicit empty category ID, and the empty category
275
279
  # name is already in category_name_to_id, make sure they match.
276
280
  if empty_category_id is not None:
@@ -285,13 +289,13 @@ def labelme_to_coco(input_folder,
285
289
  empty_category_id = category_name_to_id[empty_category_name]
286
290
 
287
291
  del category_id_to_category_name
288
-
292
+
289
293
  # Enumerate images
290
- print('Enumerating images in {}'.format(input_folder))
294
+ print('Enumerating images in {}'.format(input_folder))
291
295
  image_filenames_relative = path_utils.find_images(input_folder,recursive=recursive,
292
296
  return_relative_paths=True,
293
- convert_slashes=True)
294
-
297
+ convert_slashes=True)
298
+
295
299
  # Remove any images we're supposed to skip
296
300
  if (relative_paths_to_include is not None) or (relative_paths_to_exclude is not None):
297
301
  image_filenames_relative_to_process = []
@@ -305,65 +309,71 @@ def labelme_to_coco(input_folder,
305
309
  len(image_filenames_relative_to_process),
306
310
  len(image_filenames_relative)))
307
311
  image_filenames_relative = image_filenames_relative_to_process
308
-
312
+
309
313
  # If the user supplied a category ID to use for empty images...
310
314
  if empty_category_id is not None:
311
315
  try:
312
316
  empty_category_id = int(empty_category_id)
313
317
  except ValueError:
314
318
  raise ValueError('Category IDs must be ints or string-formatted ints')
315
-
319
+
316
320
  if empty_category_id is None:
317
321
  empty_category_id = _add_category(empty_category_name,category_name_to_id)
318
-
322
+
319
323
  if max_workers <= 1:
320
-
324
+
321
325
  image_results = []
322
326
  for image_fn_relative in tqdm(image_filenames_relative):
323
-
327
+
324
328
  result = _process_labelme_file(image_fn_relative,input_folder,use_folders_as_labels,
325
329
  no_json_handling,validate_image_sizes,
326
- category_name_to_id,allow_new_categories=True)
330
+ category_name_to_id,allow_new_categories=True)
327
331
  image_results.append(result)
328
-
329
- else:
330
-
332
+
333
+ else:
334
+
331
335
  n_workers = min(max_workers,len(image_filenames_relative))
332
336
  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
-
337
+
338
+ pool = None
339
+ try:
340
+ if use_threads:
341
+ pool = ThreadPool(n_workers)
342
+ else:
343
+ pool = Pool(n_workers)
344
+
345
+ image_results = list(tqdm(pool.imap(
346
+ partial(_process_labelme_file,
347
+ input_folder=input_folder,
348
+ use_folders_as_labels=use_folders_as_labels,
349
+ no_json_handling=no_json_handling,
350
+ validate_image_sizes=validate_image_sizes,
351
+ category_name_to_id=category_name_to_id,
352
+ allow_new_categories=False
353
+ ),image_filenames_relative), total=len(image_filenames_relative)))
354
+ finally:
355
+ pool.close()
356
+ pool.join()
357
+ print("Pool closed and joined for labelme file processing")
358
+
349
359
  images = []
350
360
  annotations = []
351
-
361
+
352
362
  # Flatten the lists of images and annotations
353
363
  for result in image_results:
354
364
  im = result['im']
355
365
  annotations_this_image = result['annotations_this_image']
356
-
366
+
357
367
  if im is None:
358
368
  assert annotations_this_image is None
359
369
  else:
360
370
  images.append(im)
361
371
  annotations.extend(annotations_this_image)
362
-
372
+
363
373
  output_dict = {}
364
374
  output_dict['images'] = images
365
375
  output_dict['annotations'] = annotations
366
-
376
+
367
377
  if info_struct is None:
368
378
  info_struct = {}
369
379
  if 'description' not in info_struct:
@@ -371,17 +381,17 @@ def labelme_to_coco(input_folder,
371
381
  'Converted to COCO from labelme annotations in folder {}'.format(input_folder)
372
382
  if 'version' not in info_struct:
373
383
  info_struct['version'] = 1.0
374
-
384
+
375
385
  output_dict['info'] = info_struct
376
386
  categories = []
377
387
  for category_name in category_name_to_id:
378
388
  categories.append({'name':category_name,'id':category_name_to_id[category_name]})
379
389
  output_dict['categories'] = categories
380
-
390
+
381
391
  if output_file is not None:
382
392
  with open(output_file,'w') as f:
383
393
  json.dump(output_dict,f,indent=1)
384
-
394
+
385
395
  return output_dict
386
396
 
387
397
  # ...def labelme_to_coco()
@@ -389,59 +399,59 @@ def labelme_to_coco(input_folder,
389
399
 
390
400
  def find_empty_labelme_files(input_folder,recursive=True):
391
401
  """
392
- Returns a list of all image files in in [input_folder] associated with .json files that have
402
+ Returns a list of all image files in in [input_folder] associated with .json files that have
393
403
  no boxes in them. Also returns a list of images with no associated .json files. Specifically,
394
404
  returns a dict:
395
-
405
+
396
406
  .. code-block: none
397
-
407
+
398
408
  {
399
409
  'images_with_empty_json_files':[list],
400
410
  'images_with_no_json_files':[list],
401
411
  'images_with_non_empty_json_files':[list]
402
412
  }
403
-
413
+
404
414
  Args:
405
415
  input_folder (str): the folder to search for empty (i.e., box-less) Labelme .json files
406
416
  recursive (bool, optional): whether to recurse into [input_folder]
407
-
417
+
408
418
  Returns:
409
419
  dict: a dict with fields:
410
- - images_with_empty_json_files: a list of all image files in [input_folder] associated with
420
+ - images_with_empty_json_files: a list of all image files in [input_folder] associated with
411
421
  .json files that have no boxes in them
412
422
  - images_with_no_json_files: a list of images in [input_folder] with no associated .json files
413
423
  - images_with_non_empty_json_files: a list of images in [input_folder] associated with .json
414
- files that have at least one box
424
+ files that have at least one box
415
425
  """
416
426
  image_filenames_relative = path_utils.find_images(input_folder,recursive=True,
417
427
  return_relative_paths=True)
418
-
428
+
419
429
  images_with_empty_json_files = []
420
430
  images_with_no_json_files = []
421
431
  images_with_non_empty_json_files = []
422
-
432
+
423
433
  # fn_relative = image_filenames_relative[0]
424
434
  for fn_relative in image_filenames_relative:
425
-
435
+
426
436
  image_fn_abs = os.path.join(input_folder,fn_relative)
427
437
  json_fn_abs = os.path.splitext(image_fn_abs)[0] + '.json'
428
-
438
+
429
439
  if not os.path.isfile(json_fn_abs):
430
440
  images_with_no_json_files.append(fn_relative)
431
441
  continue
432
-
442
+
433
443
  else:
434
444
  # Read the .json file
435
445
  with open(json_fn_abs,'r') as f:
436
- labelme_data = json.load(f)
446
+ labelme_data = json.load(f)
437
447
  shapes = labelme_data['shapes']
438
448
  if len(shapes) == 0:
439
449
  images_with_empty_json_files.append(fn_relative)
440
450
  else:
441
451
  images_with_non_empty_json_files.append(fn_relative)
442
-
452
+
443
453
  # ...for every image
444
-
454
+
445
455
  return {'images_with_empty_json_files':images_with_empty_json_files,
446
456
  'images_with_no_json_files':images_with_no_json_files,
447
457
  'images_with_non_empty_json_files':images_with_non_empty_json_files}
@@ -452,22 +462,22 @@ def find_empty_labelme_files(input_folder,recursive=True):
452
462
  #%% Interactive driver
453
463
 
454
464
  if False:
455
-
465
+
456
466
  pass
457
467
 
458
468
  #%% Options
459
-
469
+
460
470
  empty_category_name = 'empty'
461
471
  empty_category_id = None
462
472
  category_id_to_category_name = None
463
473
  info_struct = None
464
-
474
+
465
475
  input_folder = os.path.expanduser('~/data/md-test')
466
476
  output_file = os.path.expanduser('~/data/md-test-labelme-to-coco.json')
467
-
468
-
477
+
478
+
469
479
  #%% Programmatic execution
470
-
480
+
471
481
  output_dict = labelme_to_coco(input_folder,output_file,
472
482
  category_id_to_category_name=category_id_to_category_name,
473
483
  empty_category_name=empty_category_name,
@@ -476,25 +486,25 @@ if False:
476
486
  use_folders_as_labels=False,
477
487
  validate_image_sizes=False,
478
488
  no_json_handling='empty')
479
-
480
-
489
+
490
+
481
491
  #%% Validate
482
-
492
+
483
493
  from megadetector.data_management.databases import integrity_check_json_db
484
-
494
+
485
495
  options = integrity_check_json_db.IntegrityCheckOptions()
486
-
496
+
487
497
  options.baseDir = input_folder
488
498
  options.bCheckImageSizes = True
489
499
  options.bCheckImageExistence = True
490
500
  options.bFindUnusedImages = True
491
501
  options.bRequireLocation = False
492
-
493
- sortedCategories, _, errorInfo = integrity_check_json_db.integrity_check_json_db(output_file,options)
494
-
502
+
503
+ sortec_categories, _, error_info = integrity_check_json_db.integrity_check_json_db(output_file,options)
504
+
495
505
 
496
506
  #%% Preview
497
-
507
+
498
508
  from megadetector.visualization import visualize_db
499
509
  options = visualize_db.DbVizOptions()
500
510
  options.parallelize_rendering = True
@@ -503,38 +513,36 @@ if False:
503
513
 
504
514
  html_file,_ = visualize_db.visualize_db(output_file,os.path.expanduser('~/tmp/labelme_to_coco_preview'),
505
515
  input_folder,options)
506
-
516
+
507
517
 
508
518
  from megadetector.utils import path_utils # noqa
509
519
  path_utils.open_file(html_file)
510
-
511
-
520
+
521
+
512
522
  #%% Prepare command line
513
523
 
514
524
  s = 'python labelme_to_coco.py {} {}'.format(input_folder,output_file)
515
525
  print(s)
516
526
  import clipboard; clipboard.copy(s)
517
527
 
518
-
519
- #%% Command-line driver
520
528
 
521
- import sys,argparse
529
+ #%% Command-line driver
522
530
 
523
- def main():
531
+ def main(): # noqa
524
532
 
525
533
  parser = argparse.ArgumentParser(
526
534
  description='Convert labelme-formatted data to COCO')
527
-
535
+
528
536
  parser.add_argument(
529
537
  'input_folder',
530
538
  type=str,
531
539
  help='Path to images and .json annotation files')
532
-
540
+
533
541
  parser.add_argument(
534
542
  'output_file',
535
543
  type=str,
536
544
  help='Output filename (.json)')
537
-
545
+
538
546
  if len(sys.argv[1:]) == 0:
539
547
  parser.print_help()
540
548
  parser.exit()
@@ -542,6 +550,6 @@ def main():
542
550
  args = parser.parse_args()
543
551
 
544
552
  labelme_to_coco(args.input_folder,args.output_file)
545
-
553
+
546
554
  if __name__ == '__main__':
547
555
  main()