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.
- megadetector/api/batch_processing/integration/digiKam/xmp_integration.py +2 -2
- megadetector/api/batch_processing/integration/eMammal/test_scripts/push_annotations_to_emammal.py +1 -1
- megadetector/api/batch_processing/integration/eMammal/test_scripts/select_images_for_testing.py +1 -1
- megadetector/classification/aggregate_classifier_probs.py +3 -3
- megadetector/classification/analyze_failed_images.py +5 -5
- megadetector/classification/cache_batchapi_outputs.py +5 -5
- megadetector/classification/create_classification_dataset.py +11 -12
- megadetector/classification/crop_detections.py +10 -10
- megadetector/classification/csv_to_json.py +8 -8
- megadetector/classification/detect_and_crop.py +13 -15
- megadetector/classification/efficientnet/model.py +8 -8
- megadetector/classification/efficientnet/utils.py +6 -5
- megadetector/classification/evaluate_model.py +7 -7
- megadetector/classification/identify_mislabeled_candidates.py +6 -6
- megadetector/classification/json_to_azcopy_list.py +1 -1
- megadetector/classification/json_validator.py +29 -32
- megadetector/classification/map_classification_categories.py +9 -9
- megadetector/classification/merge_classification_detection_output.py +12 -9
- megadetector/classification/prepare_classification_script.py +19 -19
- megadetector/classification/prepare_classification_script_mc.py +26 -26
- megadetector/classification/run_classifier.py +4 -4
- megadetector/classification/save_mislabeled.py +6 -6
- megadetector/classification/train_classifier.py +1 -1
- megadetector/classification/train_classifier_tf.py +9 -9
- megadetector/classification/train_utils.py +10 -10
- megadetector/data_management/annotations/annotation_constants.py +1 -2
- megadetector/data_management/camtrap_dp_to_coco.py +79 -46
- megadetector/data_management/cct_json_utils.py +103 -103
- megadetector/data_management/cct_to_md.py +49 -49
- megadetector/data_management/cct_to_wi.py +33 -33
- megadetector/data_management/coco_to_labelme.py +75 -75
- megadetector/data_management/coco_to_yolo.py +210 -193
- megadetector/data_management/databases/add_width_and_height_to_db.py +86 -12
- megadetector/data_management/databases/combine_coco_camera_traps_files.py +40 -40
- megadetector/data_management/databases/integrity_check_json_db.py +228 -200
- megadetector/data_management/databases/subset_json_db.py +33 -33
- megadetector/data_management/generate_crops_from_cct.py +88 -39
- megadetector/data_management/get_image_sizes.py +54 -49
- megadetector/data_management/labelme_to_coco.py +133 -125
- megadetector/data_management/labelme_to_yolo.py +159 -73
- megadetector/data_management/lila/create_lila_blank_set.py +81 -83
- megadetector/data_management/lila/create_lila_test_set.py +32 -31
- megadetector/data_management/lila/create_links_to_md_results_files.py +18 -18
- megadetector/data_management/lila/download_lila_subset.py +21 -24
- megadetector/data_management/lila/generate_lila_per_image_labels.py +365 -107
- megadetector/data_management/lila/get_lila_annotation_counts.py +35 -33
- megadetector/data_management/lila/get_lila_image_counts.py +22 -22
- megadetector/data_management/lila/lila_common.py +73 -70
- megadetector/data_management/lila/test_lila_metadata_urls.py +28 -19
- megadetector/data_management/mewc_to_md.py +344 -340
- megadetector/data_management/ocr_tools.py +262 -255
- megadetector/data_management/read_exif.py +249 -227
- megadetector/data_management/remap_coco_categories.py +90 -28
- megadetector/data_management/remove_exif.py +81 -21
- megadetector/data_management/rename_images.py +187 -187
- megadetector/data_management/resize_coco_dataset.py +588 -120
- megadetector/data_management/speciesnet_to_md.py +41 -41
- megadetector/data_management/wi_download_csv_to_coco.py +55 -55
- megadetector/data_management/yolo_output_to_md_output.py +248 -122
- megadetector/data_management/yolo_to_coco.py +333 -191
- megadetector/detection/change_detection.py +832 -0
- megadetector/detection/process_video.py +340 -337
- megadetector/detection/pytorch_detector.py +358 -278
- megadetector/detection/run_detector.py +399 -186
- megadetector/detection/run_detector_batch.py +404 -377
- megadetector/detection/run_inference_with_yolov5_val.py +340 -327
- megadetector/detection/run_tiled_inference.py +257 -249
- megadetector/detection/tf_detector.py +24 -24
- megadetector/detection/video_utils.py +332 -295
- megadetector/postprocessing/add_max_conf.py +19 -11
- megadetector/postprocessing/categorize_detections_by_size.py +45 -45
- megadetector/postprocessing/classification_postprocessing.py +468 -433
- megadetector/postprocessing/combine_batch_outputs.py +23 -23
- megadetector/postprocessing/compare_batch_results.py +590 -525
- megadetector/postprocessing/convert_output_format.py +106 -102
- megadetector/postprocessing/create_crop_folder.py +347 -147
- megadetector/postprocessing/detector_calibration.py +173 -168
- megadetector/postprocessing/generate_csv_report.py +508 -499
- megadetector/postprocessing/load_api_results.py +48 -27
- megadetector/postprocessing/md_to_coco.py +133 -102
- megadetector/postprocessing/md_to_labelme.py +107 -90
- megadetector/postprocessing/md_to_wi.py +40 -40
- megadetector/postprocessing/merge_detections.py +92 -114
- megadetector/postprocessing/postprocess_batch_results.py +319 -301
- megadetector/postprocessing/remap_detection_categories.py +91 -38
- megadetector/postprocessing/render_detection_confusion_matrix.py +214 -205
- megadetector/postprocessing/repeat_detection_elimination/find_repeat_detections.py +57 -57
- megadetector/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +27 -28
- megadetector/postprocessing/repeat_detection_elimination/repeat_detections_core.py +704 -679
- megadetector/postprocessing/separate_detections_into_folders.py +226 -211
- megadetector/postprocessing/subset_json_detector_output.py +265 -262
- megadetector/postprocessing/top_folders_to_bottom.py +45 -45
- megadetector/postprocessing/validate_batch_results.py +70 -70
- megadetector/taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +52 -52
- megadetector/taxonomy_mapping/map_new_lila_datasets.py +18 -19
- megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +54 -33
- megadetector/taxonomy_mapping/preview_lila_taxonomy.py +67 -67
- megadetector/taxonomy_mapping/retrieve_sample_image.py +16 -16
- megadetector/taxonomy_mapping/simple_image_download.py +8 -8
- megadetector/taxonomy_mapping/species_lookup.py +156 -74
- megadetector/taxonomy_mapping/taxonomy_csv_checker.py +14 -14
- megadetector/taxonomy_mapping/taxonomy_graph.py +10 -10
- megadetector/taxonomy_mapping/validate_lila_category_mappings.py +13 -13
- megadetector/utils/ct_utils.py +1049 -211
- megadetector/utils/directory_listing.py +21 -77
- megadetector/utils/gpu_test.py +22 -22
- megadetector/utils/md_tests.py +632 -529
- megadetector/utils/path_utils.py +1520 -431
- megadetector/utils/process_utils.py +41 -41
- megadetector/utils/split_locations_into_train_val.py +62 -62
- megadetector/utils/string_utils.py +148 -27
- megadetector/utils/url_utils.py +489 -176
- megadetector/utils/wi_utils.py +2658 -2526
- megadetector/utils/write_html_image_list.py +137 -137
- megadetector/visualization/plot_utils.py +34 -30
- megadetector/visualization/render_images_with_thumbnails.py +39 -74
- megadetector/visualization/visualization_utils.py +487 -435
- megadetector/visualization/visualize_db.py +232 -198
- megadetector/visualization/visualize_detector_output.py +82 -76
- {megadetector-5.0.28.dist-info → megadetector-10.0.0.dist-info}/METADATA +5 -2
- megadetector-10.0.0.dist-info/RECORD +139 -0
- {megadetector-5.0.28.dist-info → megadetector-10.0.0.dist-info}/WHEEL +1 -1
- megadetector/api/batch_processing/api_core/__init__.py +0 -0
- megadetector/api/batch_processing/api_core/batch_service/__init__.py +0 -0
- megadetector/api/batch_processing/api_core/batch_service/score.py +0 -439
- megadetector/api/batch_processing/api_core/server.py +0 -294
- megadetector/api/batch_processing/api_core/server_api_config.py +0 -97
- megadetector/api/batch_processing/api_core/server_app_config.py +0 -55
- megadetector/api/batch_processing/api_core/server_batch_job_manager.py +0 -220
- megadetector/api/batch_processing/api_core/server_job_status_table.py +0 -149
- megadetector/api/batch_processing/api_core/server_orchestration.py +0 -360
- megadetector/api/batch_processing/api_core/server_utils.py +0 -88
- megadetector/api/batch_processing/api_core_support/__init__.py +0 -0
- megadetector/api/batch_processing/api_core_support/aggregate_results_manually.py +0 -46
- megadetector/api/batch_processing/api_support/__init__.py +0 -0
- megadetector/api/batch_processing/api_support/summarize_daily_activity.py +0 -152
- megadetector/api/batch_processing/data_preparation/__init__.py +0 -0
- megadetector/api/synchronous/__init__.py +0 -0
- megadetector/api/synchronous/api_core/animal_detection_api/__init__.py +0 -0
- megadetector/api/synchronous/api_core/animal_detection_api/api_backend.py +0 -151
- megadetector/api/synchronous/api_core/animal_detection_api/api_frontend.py +0 -263
- megadetector/api/synchronous/api_core/animal_detection_api/config.py +0 -35
- megadetector/api/synchronous/api_core/tests/__init__.py +0 -0
- megadetector/api/synchronous/api_core/tests/load_test.py +0 -110
- megadetector/data_management/importers/add_nacti_sizes.py +0 -52
- megadetector/data_management/importers/add_timestamps_to_icct.py +0 -79
- megadetector/data_management/importers/animl_results_to_md_results.py +0 -158
- megadetector/data_management/importers/auckland_doc_test_to_json.py +0 -373
- megadetector/data_management/importers/auckland_doc_to_json.py +0 -201
- megadetector/data_management/importers/awc_to_json.py +0 -191
- megadetector/data_management/importers/bellevue_to_json.py +0 -272
- megadetector/data_management/importers/cacophony-thermal-importer.py +0 -793
- megadetector/data_management/importers/carrizo_shrubfree_2018.py +0 -269
- megadetector/data_management/importers/carrizo_trail_cam_2017.py +0 -289
- megadetector/data_management/importers/cct_field_adjustments.py +0 -58
- megadetector/data_management/importers/channel_islands_to_cct.py +0 -913
- megadetector/data_management/importers/eMammal/copy_and_unzip_emammal.py +0 -180
- megadetector/data_management/importers/eMammal/eMammal_helpers.py +0 -249
- megadetector/data_management/importers/eMammal/make_eMammal_json.py +0 -223
- megadetector/data_management/importers/ena24_to_json.py +0 -276
- megadetector/data_management/importers/filenames_to_json.py +0 -386
- megadetector/data_management/importers/helena_to_cct.py +0 -283
- megadetector/data_management/importers/idaho-camera-traps.py +0 -1407
- megadetector/data_management/importers/idfg_iwildcam_lila_prep.py +0 -294
- megadetector/data_management/importers/import_desert_lion_conservation_camera_traps.py +0 -387
- megadetector/data_management/importers/jb_csv_to_json.py +0 -150
- megadetector/data_management/importers/mcgill_to_json.py +0 -250
- megadetector/data_management/importers/missouri_to_json.py +0 -490
- megadetector/data_management/importers/nacti_fieldname_adjustments.py +0 -79
- megadetector/data_management/importers/noaa_seals_2019.py +0 -181
- megadetector/data_management/importers/osu-small-animals-to-json.py +0 -364
- megadetector/data_management/importers/pc_to_json.py +0 -365
- megadetector/data_management/importers/plot_wni_giraffes.py +0 -123
- megadetector/data_management/importers/prepare_zsl_imerit.py +0 -131
- megadetector/data_management/importers/raic_csv_to_md_results.py +0 -416
- megadetector/data_management/importers/rspb_to_json.py +0 -356
- megadetector/data_management/importers/save_the_elephants_survey_A.py +0 -320
- megadetector/data_management/importers/save_the_elephants_survey_B.py +0 -329
- megadetector/data_management/importers/snapshot_safari_importer.py +0 -758
- megadetector/data_management/importers/snapshot_serengeti_lila.py +0 -1067
- megadetector/data_management/importers/snapshotserengeti/make_full_SS_json.py +0 -150
- megadetector/data_management/importers/snapshotserengeti/make_per_season_SS_json.py +0 -153
- megadetector/data_management/importers/sulross_get_exif.py +0 -65
- megadetector/data_management/importers/timelapse_csv_set_to_json.py +0 -490
- megadetector/data_management/importers/ubc_to_json.py +0 -399
- megadetector/data_management/importers/umn_to_json.py +0 -507
- megadetector/data_management/importers/wellington_to_json.py +0 -263
- megadetector/data_management/importers/wi_to_json.py +0 -442
- megadetector/data_management/importers/zamba_results_to_md_results.py +0 -180
- megadetector/data_management/lila/add_locations_to_island_camera_traps.py +0 -101
- megadetector/data_management/lila/add_locations_to_nacti.py +0 -151
- megadetector/utils/azure_utils.py +0 -178
- megadetector/utils/sas_blob_utils.py +0 -509
- megadetector-5.0.28.dist-info/RECORD +0 -209
- /megadetector/{api/batch_processing/__init__.py → __init__.py} +0 -0
- {megadetector-5.0.28.dist-info → megadetector-10.0.0.dist-info}/licenses/LICENSE +0 -0
- {megadetector-5.0.28.dist-info → megadetector-10.0.0.dist-info}/top_level.txt +0 -0
|
@@ -10,12 +10,14 @@ Create YOLO .txt files in a folder containing labelme .json files.
|
|
|
10
10
|
|
|
11
11
|
import os
|
|
12
12
|
import json
|
|
13
|
+
import argparse
|
|
13
14
|
|
|
14
15
|
from multiprocessing.pool import Pool, ThreadPool
|
|
15
16
|
from functools import partial
|
|
16
17
|
from tqdm import tqdm
|
|
17
18
|
|
|
18
19
|
from megadetector.utils.path_utils import recursive_file_list
|
|
20
|
+
from megadetector.utils.ct_utils import write_json
|
|
19
21
|
|
|
20
22
|
|
|
21
23
|
#%% Main function
|
|
@@ -28,23 +30,30 @@ def labelme_file_to_yolo_file(labelme_file,
|
|
|
28
30
|
"""
|
|
29
31
|
Convert the single .json file labelme_file to yolo format, writing the results to the text
|
|
30
32
|
file yolo_file (defaults to s/json/txt).
|
|
31
|
-
|
|
33
|
+
|
|
32
34
|
If required_token is not None and the dict in labelme_file does not contain the key [required_token],
|
|
33
|
-
this function no-ops (i.e., does not generate a YOLO file).
|
|
34
|
-
|
|
35
|
+
this function no-ops (i.e., does not generate a YOLO file).
|
|
36
|
+
|
|
35
37
|
overwrite_behavior should be 'skip' or 'overwrite' (default).
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
labelme_file (str): .json file to convert
|
|
41
|
+
category_name_to_category_id (dict): category name --> ID mapping
|
|
42
|
+
yolo_file (str, optional): output .txt file defaults to s/json/txt
|
|
43
|
+
required_token (str, optional): only process filenames containing this token
|
|
44
|
+
overwrite_behavior (str, optional): "skip" or "overwrite"
|
|
36
45
|
"""
|
|
37
|
-
|
|
46
|
+
|
|
38
47
|
result = {}
|
|
39
48
|
result['labelme_file'] = labelme_file
|
|
40
49
|
result['status'] = 'unknown'
|
|
41
|
-
|
|
50
|
+
|
|
42
51
|
assert os.path.isfile(labelme_file), 'Could not find labelme .json file {}'.format(labelme_file)
|
|
43
52
|
assert labelme_file.endswith('.json'), 'Illegal labelme .json file {}'.format(labelme_file)
|
|
44
|
-
|
|
53
|
+
|
|
45
54
|
if yolo_file is None:
|
|
46
55
|
yolo_file = os.path.splitext(labelme_file)[0] + '.txt'
|
|
47
|
-
|
|
56
|
+
|
|
48
57
|
if os.path.isfile(yolo_file):
|
|
49
58
|
if overwrite_behavior == 'skip':
|
|
50
59
|
result['status'] = 'skip-exists'
|
|
@@ -52,31 +61,31 @@ def labelme_file_to_yolo_file(labelme_file,
|
|
|
52
61
|
else:
|
|
53
62
|
assert overwrite_behavior == 'overwrite', \
|
|
54
63
|
'Unrecognized overwrite behavior {}'.format(overwrite_behavior)
|
|
55
|
-
|
|
64
|
+
|
|
56
65
|
with open(labelme_file,'r') as f:
|
|
57
66
|
labelme_data = json.load(f)
|
|
58
|
-
|
|
67
|
+
|
|
59
68
|
if required_token is not None and required_token not in labelme_data:
|
|
60
69
|
result['status'] = 'skip-no-required-token'
|
|
61
70
|
return result
|
|
62
|
-
|
|
71
|
+
|
|
63
72
|
im_height = labelme_data['imageHeight']
|
|
64
73
|
im_width = labelme_data['imageWidth']
|
|
65
|
-
|
|
74
|
+
|
|
66
75
|
yolo_lines = []
|
|
67
|
-
|
|
76
|
+
|
|
68
77
|
for shape in labelme_data['shapes']:
|
|
69
|
-
|
|
78
|
+
|
|
70
79
|
assert shape['shape_type'] == 'rectangle', \
|
|
71
80
|
'I only know how to convert rectangles to YOLO format'
|
|
72
81
|
assert shape['label'] in category_name_to_category_id, \
|
|
73
82
|
'Category {} not in category mapping'.format(shape['label'])
|
|
74
83
|
assert len(shape['points']) == 2, 'Illegal rectangle'
|
|
75
84
|
category_id = category_name_to_category_id[shape['label']]
|
|
76
|
-
|
|
85
|
+
|
|
77
86
|
p0 = shape['points'][0]
|
|
78
87
|
p1 = shape['points'][1]
|
|
79
|
-
|
|
88
|
+
|
|
80
89
|
# Labelme: [[x0,y0],[x1,y1]] (arbitrarily sorted) (absolute coordinates)
|
|
81
90
|
#
|
|
82
91
|
# YOLO: [class, x_center, y_center, width, height] (normalized coordinates)
|
|
@@ -84,12 +93,12 @@ def labelme_file_to_yolo_file(labelme_file,
|
|
|
84
93
|
maxx_abs = max(p0[0],p1[0])
|
|
85
94
|
miny_abs = min(p0[1],p1[1])
|
|
86
95
|
maxy_abs = max(p0[1],p1[1])
|
|
87
|
-
|
|
96
|
+
|
|
88
97
|
if (minx_abs >= (im_width-1)) or (maxx_abs <= 0) or \
|
|
89
98
|
(miny_abs >= (im_height-1)) or (maxy_abs <= 0):
|
|
90
|
-
print('Skipping invalid shape in {}'.format(labelme_file))
|
|
99
|
+
print('Skipping invalid shape in {}'.format(labelme_file))
|
|
91
100
|
continue
|
|
92
|
-
|
|
101
|
+
|
|
93
102
|
# Clip to [0,1]... it's not obvious that the YOLO format doesn't allow bounding
|
|
94
103
|
# boxes to extend outside the image, but YOLOv5 and YOLOv8 get sad about boxes
|
|
95
104
|
# that extend outside the image.
|
|
@@ -97,30 +106,30 @@ def labelme_file_to_yolo_file(labelme_file,
|
|
|
97
106
|
maxy_abs = min(maxy_abs,im_height-1)
|
|
98
107
|
minx_abs = max(minx_abs,0.0)
|
|
99
108
|
miny_abs = max(miny_abs,0.0)
|
|
100
|
-
|
|
109
|
+
|
|
101
110
|
minx_rel = minx_abs / (im_width-1)
|
|
102
111
|
maxx_rel = maxx_abs / (im_width-1)
|
|
103
112
|
miny_rel = miny_abs / (im_height-1)
|
|
104
113
|
maxy_rel = maxy_abs / (im_height-1)
|
|
105
|
-
|
|
114
|
+
|
|
106
115
|
assert maxx_rel >= minx_rel
|
|
107
116
|
assert maxy_rel >= miny_rel
|
|
108
|
-
|
|
117
|
+
|
|
109
118
|
xcenter_rel = (maxx_rel + minx_rel) / 2.0
|
|
110
119
|
ycenter_rel = (maxy_rel + miny_rel) / 2.0
|
|
111
120
|
w_rel = maxx_rel - minx_rel
|
|
112
121
|
h_rel = maxy_rel - miny_rel
|
|
113
|
-
|
|
122
|
+
|
|
114
123
|
yolo_line = '{} {:.3f} {:.3f} {:.3f} {:.3f}'.format(category_id,
|
|
115
124
|
xcenter_rel, ycenter_rel, w_rel, h_rel)
|
|
116
125
|
yolo_lines.append(yolo_line)
|
|
117
|
-
|
|
126
|
+
|
|
118
127
|
# ...for each shape
|
|
119
|
-
|
|
128
|
+
|
|
120
129
|
with open(yolo_file,'w') as f:
|
|
121
130
|
for s in yolo_lines:
|
|
122
131
|
f.write(s + '\n')
|
|
123
|
-
|
|
132
|
+
|
|
124
133
|
result['status'] = 'converted'
|
|
125
134
|
return result
|
|
126
135
|
|
|
@@ -136,22 +145,31 @@ def labelme_folder_to_yolo(labelme_folder,
|
|
|
136
145
|
Given a folder with images and labelme .json files, convert the .json files
|
|
137
146
|
to YOLO .txt format. If category_name_to_category_id is None, first reads
|
|
138
147
|
all the labels in the folder to build a zero-indexed name --> ID mapping.
|
|
139
|
-
|
|
148
|
+
|
|
140
149
|
If required_token is not None and a labelme_file does not contain the key [required_token],
|
|
141
150
|
it won't be converted. Typically used to specify a field that indicates which files have
|
|
142
151
|
been reviewed.
|
|
143
|
-
|
|
152
|
+
|
|
144
153
|
If relative_filenames_to_convert is not None, this should be a list of .json (not image)
|
|
145
154
|
files that should get converted, relative to the base folder.
|
|
146
|
-
|
|
155
|
+
|
|
147
156
|
overwrite_behavior should be 'skip' or 'overwrite' (default).
|
|
148
|
-
|
|
157
|
+
|
|
149
158
|
returns a dict with:
|
|
150
159
|
'category_name_to_category_id', whether it was passed in or constructed
|
|
151
160
|
'image_results': a list of results for each image (converted, skipped, error)
|
|
152
|
-
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
labelme_folder (str): folder of .json files to convert
|
|
164
|
+
category_name_to_category_id (dict): category name --> ID mapping
|
|
165
|
+
required_token (str, optional): only process filenames containing this token
|
|
166
|
+
overwrite_behavior (str, optional): "skip" or "overwrite"
|
|
167
|
+
relative_filenames_to_convert (list of str, optional): only process filenames on this list
|
|
168
|
+
n_workers (int, optional): parallelism level
|
|
169
|
+
use_threads (bool, optional): whether to use threads (True) or processes (False) for
|
|
170
|
+
parallelism
|
|
153
171
|
"""
|
|
154
|
-
|
|
172
|
+
|
|
155
173
|
if relative_filenames_to_convert is not None:
|
|
156
174
|
labelme_files_relative = relative_filenames_to_convert
|
|
157
175
|
assert all([fn.endswith('.json') for fn in labelme_files_relative]), \
|
|
@@ -159,35 +177,35 @@ def labelme_folder_to_yolo(labelme_folder,
|
|
|
159
177
|
else:
|
|
160
178
|
labelme_files_relative = recursive_file_list(labelme_folder,return_relative_paths=True)
|
|
161
179
|
labelme_files_relative = [fn for fn in labelme_files_relative if fn.endswith('.json')]
|
|
162
|
-
|
|
180
|
+
|
|
163
181
|
if required_token is None:
|
|
164
182
|
valid_labelme_files_relative = labelme_files_relative
|
|
165
|
-
else:
|
|
183
|
+
else:
|
|
166
184
|
valid_labelme_files_relative = []
|
|
167
|
-
|
|
185
|
+
|
|
168
186
|
# fn_relative = labelme_files_relative[-1]
|
|
169
187
|
for fn_relative in labelme_files_relative:
|
|
170
|
-
|
|
171
|
-
fn_abs = os.path.join(labelme_folder,fn_relative)
|
|
172
|
-
|
|
188
|
+
|
|
189
|
+
fn_abs = os.path.join(labelme_folder,fn_relative)
|
|
190
|
+
|
|
173
191
|
with open(fn_abs,'r') as f:
|
|
174
192
|
labelme_data = json.load(f)
|
|
175
193
|
if required_token not in labelme_data:
|
|
176
194
|
continue
|
|
177
|
-
|
|
195
|
+
|
|
178
196
|
valid_labelme_files_relative.append(fn_relative)
|
|
179
|
-
|
|
197
|
+
|
|
180
198
|
print('{} of {} files are valid'.format(len(valid_labelme_files_relative),
|
|
181
199
|
len(labelme_files_relative)))
|
|
182
|
-
|
|
200
|
+
|
|
183
201
|
del labelme_files_relative
|
|
184
|
-
|
|
202
|
+
|
|
185
203
|
if category_name_to_category_id is None:
|
|
186
|
-
|
|
204
|
+
|
|
187
205
|
category_name_to_category_id = {}
|
|
188
|
-
|
|
206
|
+
|
|
189
207
|
for fn_relative in valid_labelme_files_relative:
|
|
190
|
-
|
|
208
|
+
|
|
191
209
|
fn_abs = os.path.join(labelme_folder,fn_relative)
|
|
192
210
|
with open(fn_abs,'r') as f:
|
|
193
211
|
labelme_data = json.load(f)
|
|
@@ -196,16 +214,16 @@ def labelme_folder_to_yolo(labelme_folder,
|
|
|
196
214
|
if label not in category_name_to_category_id:
|
|
197
215
|
category_name_to_category_id[label] = len(category_name_to_category_id)
|
|
198
216
|
# ...for each file
|
|
199
|
-
|
|
217
|
+
|
|
200
218
|
# ...if we need to build a category mapping
|
|
201
|
-
|
|
219
|
+
|
|
202
220
|
image_results = []
|
|
203
|
-
|
|
221
|
+
|
|
204
222
|
n_workers = min(n_workers,len(valid_labelme_files_relative))
|
|
205
|
-
|
|
223
|
+
|
|
206
224
|
if n_workers <= 1:
|
|
207
225
|
for fn_relative in tqdm(valid_labelme_files_relative):
|
|
208
|
-
|
|
226
|
+
|
|
209
227
|
fn_abs = os.path.join(labelme_folder,fn_relative)
|
|
210
228
|
image_result = labelme_file_to_yolo_file(fn_abs,
|
|
211
229
|
category_name_to_category_id,
|
|
@@ -215,35 +233,41 @@ def labelme_folder_to_yolo(labelme_folder,
|
|
|
215
233
|
image_results.append(image_result)
|
|
216
234
|
# ...for each file
|
|
217
235
|
else:
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
236
|
+
pool = None
|
|
237
|
+
try:
|
|
238
|
+
if use_threads:
|
|
239
|
+
pool = ThreadPool(n_workers)
|
|
240
|
+
else:
|
|
241
|
+
pool = Pool(n_workers)
|
|
242
|
+
|
|
243
|
+
valid_labelme_files_abs = [os.path.join(labelme_folder,fn_relative) for \
|
|
244
|
+
fn_relative in valid_labelme_files_relative]
|
|
245
|
+
|
|
246
|
+
image_results = list(tqdm(pool.imap(
|
|
247
|
+
partial(labelme_file_to_yolo_file,
|
|
248
|
+
category_name_to_category_id=category_name_to_category_id,
|
|
249
|
+
yolo_file=None,
|
|
250
|
+
required_token=required_token,
|
|
251
|
+
overwrite_behavior=overwrite_behavior),
|
|
252
|
+
valid_labelme_files_abs),
|
|
253
|
+
total=len(valid_labelme_files_abs)))
|
|
254
|
+
finally:
|
|
255
|
+
pool.close()
|
|
256
|
+
pool.join()
|
|
257
|
+
print('Pool closed and joined for labelme conversion to YOLO')
|
|
234
258
|
|
|
235
259
|
assert len(valid_labelme_files_relative) == len(image_results)
|
|
236
|
-
|
|
260
|
+
|
|
237
261
|
print('Converted {} labelme .json files to YOLO'.format(
|
|
238
262
|
len(valid_labelme_files_relative)))
|
|
239
|
-
|
|
263
|
+
|
|
240
264
|
labelme_to_yolo_results = {}
|
|
241
265
|
labelme_to_yolo_results['category_name_to_category_id'] = category_name_to_category_id
|
|
242
266
|
labelme_to_yolo_results['image_results'] = image_results
|
|
243
|
-
|
|
267
|
+
|
|
244
268
|
return labelme_to_yolo_results
|
|
245
|
-
|
|
246
|
-
# ...def labelme_folder_to_yolo(...)
|
|
269
|
+
|
|
270
|
+
# ...def labelme_folder_to_yolo(...)
|
|
247
271
|
|
|
248
272
|
|
|
249
273
|
#%% Interactive driver
|
|
@@ -260,13 +284,75 @@ if False:
|
|
|
260
284
|
labelme_folder = os.path.expanduser('~/tmp/labels')
|
|
261
285
|
|
|
262
286
|
#%%
|
|
263
|
-
|
|
287
|
+
|
|
264
288
|
category_name_to_category_id = \
|
|
265
289
|
labelme_folder_to_yolo(labelme_folder,
|
|
266
290
|
category_name_to_category_id=category_name_to_category_id,
|
|
267
291
|
required_token=required_token,
|
|
268
292
|
overwrite_behavior='overwrite')
|
|
269
|
-
|
|
293
|
+
|
|
270
294
|
#%% Command-line driver
|
|
271
295
|
|
|
272
|
-
|
|
296
|
+
def main():
|
|
297
|
+
"""
|
|
298
|
+
Command-line interface to convert Labelme JSON files to YOLO format
|
|
299
|
+
"""
|
|
300
|
+
|
|
301
|
+
parser = argparse.ArgumentParser(
|
|
302
|
+
description='Convert a folder of Labelme .json files to YOLO .txt format'
|
|
303
|
+
)
|
|
304
|
+
parser.add_argument(
|
|
305
|
+
'labelme_folder',
|
|
306
|
+
type=str,
|
|
307
|
+
help='Folder of Labelme .json files to convert'
|
|
308
|
+
)
|
|
309
|
+
parser.add_argument(
|
|
310
|
+
'--output_category_file',
|
|
311
|
+
type=str,
|
|
312
|
+
default=None,
|
|
313
|
+
help='Path to save the generated category mapping (.json)'
|
|
314
|
+
)
|
|
315
|
+
parser.add_argument(
|
|
316
|
+
'--required_token',
|
|
317
|
+
type=str,
|
|
318
|
+
default=None,
|
|
319
|
+
help='Only process files containing this token as a key in the Labelme JSON dict'
|
|
320
|
+
)
|
|
321
|
+
parser.add_argument(
|
|
322
|
+
'--overwrite_behavior',
|
|
323
|
+
type=str,
|
|
324
|
+
default='overwrite',
|
|
325
|
+
choices=['skip', 'overwrite'],
|
|
326
|
+
help="Behavior if YOLO .txt files exist (default: 'overwrite')"
|
|
327
|
+
)
|
|
328
|
+
parser.add_argument(
|
|
329
|
+
'--n_workers',
|
|
330
|
+
type=int,
|
|
331
|
+
default=1,
|
|
332
|
+
help='Number of workers for parallel processing (default: 1)'
|
|
333
|
+
)
|
|
334
|
+
parser.add_argument(
|
|
335
|
+
'--use_processes',
|
|
336
|
+
action='store_true',
|
|
337
|
+
help='Use processes instead of threads for parallelization (defaults to threads)'
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
args = parser.parse_args()
|
|
341
|
+
|
|
342
|
+
results = labelme_folder_to_yolo(
|
|
343
|
+
labelme_folder=args.labelme_folder,
|
|
344
|
+
category_name_to_category_id=None,
|
|
345
|
+
required_token=args.required_token,
|
|
346
|
+
overwrite_behavior=args.overwrite_behavior,
|
|
347
|
+
relative_filenames_to_convert=None,
|
|
348
|
+
n_workers=args.n_workers,
|
|
349
|
+
use_threads=(not args.use_processes)
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
if args.output_category_file:
|
|
353
|
+
category_map = results['category_name_to_category_id']
|
|
354
|
+
write_json(args.output_category_file,category_map)
|
|
355
|
+
print(f'Saved category mapping to {args.output_category_file}')
|
|
356
|
+
|
|
357
|
+
if __name__ == '__main__':
|
|
358
|
+
main()
|