megadetector 10.0.10__py3-none-any.whl → 10.0.11__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/data_management/animl_to_md.py +5 -2
- megadetector/data_management/cct_json_utils.py +4 -2
- megadetector/data_management/cct_to_md.py +5 -4
- megadetector/data_management/cct_to_wi.py +5 -1
- megadetector/data_management/coco_to_yolo.py +3 -2
- megadetector/data_management/databases/combine_coco_camera_traps_files.py +4 -4
- megadetector/data_management/databases/integrity_check_json_db.py +2 -2
- megadetector/data_management/databases/subset_json_db.py +0 -3
- megadetector/data_management/generate_crops_from_cct.py +6 -4
- megadetector/data_management/get_image_sizes.py +5 -35
- megadetector/data_management/labelme_to_coco.py +10 -6
- megadetector/data_management/labelme_to_yolo.py +19 -28
- megadetector/data_management/lila/create_lila_test_set.py +22 -2
- megadetector/data_management/lila/generate_lila_per_image_labels.py +7 -5
- megadetector/data_management/lila/lila_common.py +2 -2
- megadetector/data_management/lila/test_lila_metadata_urls.py +0 -1
- megadetector/data_management/ocr_tools.py +6 -10
- megadetector/data_management/read_exif.py +59 -16
- megadetector/data_management/remap_coco_categories.py +1 -1
- megadetector/data_management/remove_exif.py +10 -5
- megadetector/data_management/rename_images.py +20 -13
- megadetector/data_management/resize_coco_dataset.py +10 -4
- megadetector/data_management/speciesnet_to_md.py +3 -3
- megadetector/data_management/yolo_output_to_md_output.py +3 -1
- megadetector/data_management/yolo_to_coco.py +28 -19
- megadetector/detection/change_detection.py +26 -18
- megadetector/detection/process_video.py +1 -1
- megadetector/detection/pytorch_detector.py +5 -5
- megadetector/detection/run_detector.py +34 -10
- megadetector/detection/run_detector_batch.py +2 -1
- megadetector/detection/run_inference_with_yolov5_val.py +3 -1
- megadetector/detection/run_md_and_speciesnet.py +215 -101
- megadetector/detection/run_tiled_inference.py +7 -7
- megadetector/detection/tf_detector.py +1 -1
- megadetector/detection/video_utils.py +9 -6
- megadetector/postprocessing/add_max_conf.py +4 -4
- megadetector/postprocessing/categorize_detections_by_size.py +3 -2
- megadetector/postprocessing/classification_postprocessing.py +7 -8
- megadetector/postprocessing/combine_batch_outputs.py +3 -2
- megadetector/postprocessing/compare_batch_results.py +49 -27
- megadetector/postprocessing/convert_output_format.py +8 -6
- megadetector/postprocessing/create_crop_folder.py +13 -4
- megadetector/postprocessing/generate_csv_report.py +22 -8
- megadetector/postprocessing/load_api_results.py +8 -4
- megadetector/postprocessing/md_to_coco.py +2 -3
- megadetector/postprocessing/md_to_labelme.py +12 -8
- megadetector/postprocessing/md_to_wi.py +2 -1
- megadetector/postprocessing/merge_detections.py +4 -6
- megadetector/postprocessing/postprocess_batch_results.py +4 -3
- megadetector/postprocessing/remap_detection_categories.py +6 -3
- megadetector/postprocessing/render_detection_confusion_matrix.py +18 -10
- megadetector/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +1 -1
- megadetector/postprocessing/repeat_detection_elimination/repeat_detections_core.py +5 -3
- megadetector/postprocessing/separate_detections_into_folders.py +10 -4
- megadetector/postprocessing/subset_json_detector_output.py +1 -1
- megadetector/postprocessing/top_folders_to_bottom.py +22 -7
- megadetector/postprocessing/validate_batch_results.py +1 -1
- megadetector/taxonomy_mapping/map_new_lila_datasets.py +59 -3
- megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +1 -1
- megadetector/taxonomy_mapping/preview_lila_taxonomy.py +26 -17
- megadetector/taxonomy_mapping/species_lookup.py +51 -2
- megadetector/utils/ct_utils.py +9 -4
- megadetector/utils/extract_frames_from_video.py +4 -0
- megadetector/utils/gpu_test.py +6 -6
- megadetector/utils/md_tests.py +21 -21
- megadetector/utils/path_utils.py +112 -44
- megadetector/utils/split_locations_into_train_val.py +0 -4
- megadetector/utils/url_utils.py +5 -3
- megadetector/utils/wi_taxonomy_utils.py +37 -8
- megadetector/utils/write_html_image_list.py +1 -2
- megadetector/visualization/plot_utils.py +31 -19
- megadetector/visualization/render_images_with_thumbnails.py +3 -0
- megadetector/visualization/visualization_utils.py +18 -7
- megadetector/visualization/visualize_db.py +9 -26
- megadetector/visualization/visualize_video_output.py +14 -2
- {megadetector-10.0.10.dist-info → megadetector-10.0.11.dist-info}/METADATA +1 -1
- {megadetector-10.0.10.dist-info → megadetector-10.0.11.dist-info}/RECORD +80 -80
- {megadetector-10.0.10.dist-info → megadetector-10.0.11.dist-info}/WHEEL +0 -0
- {megadetector-10.0.10.dist-info → megadetector-10.0.11.dist-info}/licenses/LICENSE +0 -0
- {megadetector-10.0.10.dist-info → megadetector-10.0.11.dist-info}/top_level.txt +0 -0
|
@@ -25,6 +25,7 @@ from megadetector.utils.ct_utils import is_empty
|
|
|
25
25
|
from megadetector.utils.ct_utils import sort_dictionary_by_value
|
|
26
26
|
from megadetector.utils.ct_utils import sort_dictionary_by_key
|
|
27
27
|
from megadetector.utils.ct_utils import invert_dictionary
|
|
28
|
+
from megadetector.utils.ct_utils import write_json
|
|
28
29
|
|
|
29
30
|
from megadetector.utils.wi_taxonomy_utils import clean_taxonomy_string
|
|
30
31
|
from megadetector.utils.wi_taxonomy_utils import taxonomy_level_index
|
|
@@ -420,7 +421,7 @@ def _smooth_classifications_for_list_of_detections(detections,
|
|
|
420
421
|
|
|
421
422
|
if verbose_debug_enabled:
|
|
422
423
|
_print_counts_with_names(category_to_count,classification_descriptions)
|
|
423
|
-
from IPython import embed; embed()
|
|
424
|
+
# from IPython import embed; embed()
|
|
424
425
|
|
|
425
426
|
|
|
426
427
|
## Possibly change "other" classifications to the most common category
|
|
@@ -448,7 +449,7 @@ def _smooth_classifications_for_list_of_detections(detections,
|
|
|
448
449
|
if verbose_debug_enabled:
|
|
449
450
|
print('Replacing {} with {}'.format(
|
|
450
451
|
classification_descriptions[c[0]],
|
|
451
|
-
|
|
452
|
+
most_common_category))
|
|
452
453
|
|
|
453
454
|
n_other_classifications_changed_this_image += 1
|
|
454
455
|
c[0] = most_common_category
|
|
@@ -918,8 +919,7 @@ def smooth_classification_results_image_level(input_file,output_file=None,option
|
|
|
918
919
|
|
|
919
920
|
if output_file is not None:
|
|
920
921
|
print('Writing results after image-level smoothing to:\n{}'.format(output_file))
|
|
921
|
-
|
|
922
|
-
json.dump(d,f,indent=1)
|
|
922
|
+
write_json(output_file,d)
|
|
923
923
|
|
|
924
924
|
return d
|
|
925
925
|
|
|
@@ -1092,8 +1092,7 @@ def smooth_classification_results_sequence_level(input_file,
|
|
|
1092
1092
|
if output_file is not None:
|
|
1093
1093
|
print('Writing sequence-smoothed classification results to {}'.format(
|
|
1094
1094
|
output_file))
|
|
1095
|
-
|
|
1096
|
-
json.dump(d,f,indent=1)
|
|
1095
|
+
write_json(output_file,d)
|
|
1097
1096
|
|
|
1098
1097
|
return d
|
|
1099
1098
|
|
|
@@ -1681,7 +1680,7 @@ def restrict_to_taxa_list(taxa_list,
|
|
|
1681
1680
|
|
|
1682
1681
|
##%% Write output
|
|
1683
1682
|
|
|
1684
|
-
|
|
1685
|
-
|
|
1683
|
+
write_json(output_file,output_data)
|
|
1684
|
+
|
|
1686
1685
|
|
|
1687
1686
|
# ...def restrict_to_taxa_list(...)
|
|
@@ -203,7 +203,8 @@ def combine_api_shard_files(input_files, output_file=None):
|
|
|
203
203
|
input_lists = []
|
|
204
204
|
print('Loading input files')
|
|
205
205
|
for fn in input_files:
|
|
206
|
-
|
|
206
|
+
with open(fn,'r') as f:
|
|
207
|
+
input_lists.append(json.load(f))
|
|
207
208
|
|
|
208
209
|
detections = []
|
|
209
210
|
# detection_list = input_lists[0]
|
|
@@ -214,7 +215,7 @@ def combine_api_shard_files(input_files, output_file=None):
|
|
|
214
215
|
assert 'file' in d
|
|
215
216
|
assert 'max_detection_conf' in d
|
|
216
217
|
assert 'detections' in d
|
|
217
|
-
detections.
|
|
218
|
+
detections.append(d)
|
|
218
219
|
|
|
219
220
|
print('Writing output')
|
|
220
221
|
if output_file is not None:
|
|
@@ -353,10 +353,11 @@ def _render_image_pair(fn,image_pairs,category_folder,options,pairwise_options):
|
|
|
353
353
|
im_gt = image_pair['im_gt']
|
|
354
354
|
annotations_gt = image_pair['annotations_gt']
|
|
355
355
|
gt_boxes = []
|
|
356
|
+
gt_categories = []
|
|
356
357
|
for ann in annotations_gt:
|
|
357
358
|
if 'bbox' in ann:
|
|
358
359
|
gt_boxes.append(ann['bbox'])
|
|
359
|
-
|
|
360
|
+
gt_categories.append(ann['category_id'])
|
|
360
361
|
|
|
361
362
|
if len(gt_boxes) > 0:
|
|
362
363
|
|
|
@@ -474,7 +475,7 @@ def _result_types_to_comparison_category(result_types_present_a,
|
|
|
474
475
|
('tp' not in result_types_present_b):
|
|
475
476
|
return 'clean_tp_a_only'
|
|
476
477
|
# Otherwise, TPs are cases where one model has only TPs, and the other model
|
|
477
|
-
# has any
|
|
478
|
+
# has any mistakes
|
|
478
479
|
if ('fn' in result_types_present_b) or ('fp' in result_types_present_b):
|
|
479
480
|
return 'tp_a_only'
|
|
480
481
|
|
|
@@ -486,7 +487,7 @@ def _result_types_to_comparison_category(result_types_present_a,
|
|
|
486
487
|
('tp' not in result_types_present_a):
|
|
487
488
|
return 'clean_tp_b_only'
|
|
488
489
|
# Otherwise, TPs are cases where one model has only TPs, and the other model
|
|
489
|
-
# has any
|
|
490
|
+
# has any mistakes
|
|
490
491
|
if ('fn' in result_types_present_a) or ('fp' in result_types_present_a):
|
|
491
492
|
return 'tp_b_only'
|
|
492
493
|
|
|
@@ -674,11 +675,17 @@ def _pairwise_compare_batch_results(options,output_index,pairwise_options):
|
|
|
674
675
|
category_ids_to_include_a = []
|
|
675
676
|
category_ids_to_include_b = []
|
|
676
677
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
678
|
+
# If we're supposed to be including all categories, we don't actually need to
|
|
679
|
+
# populate category_ids_to_include_a/b, but we're doing this for future-proofing.
|
|
680
|
+
if options.category_names_to_include is None:
|
|
681
|
+
category_ids_to_include_a = sorted(list(category_name_to_id_a.values()))
|
|
682
|
+
category_ids_to_include_b = sorted(list(category_name_to_id_b.values()))
|
|
683
|
+
else:
|
|
684
|
+
for category_name in options.category_names_to_include:
|
|
685
|
+
if category_name in category_name_to_id_a:
|
|
686
|
+
category_ids_to_include_a.append(category_name_to_id_a[category_name])
|
|
687
|
+
if category_name in category_name_to_id_b:
|
|
688
|
+
category_ids_to_include_b.append(category_name_to_id_b[category_name])
|
|
682
689
|
|
|
683
690
|
if pairwise_options.results_description_a is None:
|
|
684
691
|
if 'detector' not in results_a['info']:
|
|
@@ -814,7 +821,7 @@ def _pairwise_compare_batch_results(options,output_index,pairwise_options):
|
|
|
814
821
|
print('Warning: {} files are only available in the ground truth (not in MD results)'.format(
|
|
815
822
|
len(filenames_only_in_gt)))
|
|
816
823
|
|
|
817
|
-
filenames_only_in_results =
|
|
824
|
+
filenames_only_in_results = filenames_to_compare_set.difference(gt_filenames_set)
|
|
818
825
|
if len(filenames_only_in_results) > 0:
|
|
819
826
|
print('Warning: {} files are only available in the MD results (not in ground truth)'.format(
|
|
820
827
|
len(filenames_only_in_results)))
|
|
@@ -1185,13 +1192,6 @@ def _pairwise_compare_batch_results(options,output_index,pairwise_options):
|
|
|
1185
1192
|
if ('tp' in result_types_present_b) or ('fn' in result_types_present_b):
|
|
1186
1193
|
assert 'tp' in result_types_present_a or 'fn' in result_types_present_a
|
|
1187
1194
|
|
|
1188
|
-
# If either model has a TP or FN, the other has to have a TP or FN, since
|
|
1189
|
-
# there was something in the GT
|
|
1190
|
-
if ('tp' in result_types_present_a) or ('fn' in result_types_present_a):
|
|
1191
|
-
assert 'tp' in result_types_present_b or 'fn' in result_types_present_b
|
|
1192
|
-
if ('tp' in result_types_present_b) or ('fn' in result_types_present_b):
|
|
1193
|
-
assert 'tp' in result_types_present_a or 'fn' in result_types_present_a
|
|
1194
|
-
|
|
1195
1195
|
|
|
1196
1196
|
## Choose a comparison category based on result types
|
|
1197
1197
|
|
|
@@ -1677,8 +1677,8 @@ def n_way_comparison(filenames,
|
|
|
1677
1677
|
'[detection_thresholds] should be the same length as [filenames]'
|
|
1678
1678
|
|
|
1679
1679
|
if rendering_thresholds is not None:
|
|
1680
|
-
assert len(rendering_thresholds) == len(filenames)
|
|
1681
|
-
|
|
1680
|
+
assert len(rendering_thresholds) == len(filenames), \
|
|
1681
|
+
'[rendering_thresholds] should be the same length as [filenames]'
|
|
1682
1682
|
else:
|
|
1683
1683
|
rendering_thresholds = [(x*0.6666) for x in detection_thresholds]
|
|
1684
1684
|
|
|
@@ -1932,32 +1932,54 @@ def find_equivalent_threshold(results_a,
|
|
|
1932
1932
|
|
|
1933
1933
|
if False:
|
|
1934
1934
|
|
|
1935
|
+
#%% Prepare test files
|
|
1936
|
+
|
|
1937
|
+
from megadetector.utils.path_utils import insert_before_extension
|
|
1938
|
+
|
|
1939
|
+
model_names = ['mdv5a','mdv5b']
|
|
1940
|
+
image_folder = 'g:/temp/md-test-images'
|
|
1941
|
+
output_filename_base = os.path.join(image_folder,'comparison_test.json')
|
|
1942
|
+
|
|
1943
|
+
output_filenames = []
|
|
1944
|
+
|
|
1945
|
+
commands = []
|
|
1946
|
+
|
|
1947
|
+
for model_name in model_names:
|
|
1948
|
+
output_filename = insert_before_extension(output_filename_base,model_name)
|
|
1949
|
+
output_filenames.append(output_filename)
|
|
1950
|
+
cmd = 'python -m megadetector.detection.run_detector_batch'
|
|
1951
|
+
cmd += ' {} {} {} --recursive --output_relative_filenames'.format(
|
|
1952
|
+
model_name, image_folder,output_filename)
|
|
1953
|
+
commands.append(cmd)
|
|
1954
|
+
|
|
1955
|
+
cmd = '\n\n'.join(commands)
|
|
1956
|
+
print(cmd)
|
|
1957
|
+
import clipboard
|
|
1958
|
+
clipboard.copy(cmd)
|
|
1959
|
+
|
|
1960
|
+
|
|
1935
1961
|
#%% Test two-way comparison
|
|
1936
1962
|
|
|
1937
1963
|
options = BatchComparisonOptions()
|
|
1938
1964
|
|
|
1939
1965
|
options.parallelize_rendering_with_threads = True
|
|
1940
1966
|
|
|
1941
|
-
options.job_name = '
|
|
1967
|
+
options.job_name = 'md-test-images'
|
|
1942
1968
|
options.output_folder = r'g:\temp\comparisons'
|
|
1943
|
-
options.image_folder =
|
|
1969
|
+
options.image_folder = image_folder
|
|
1944
1970
|
options.max_images_per_category = 100
|
|
1945
1971
|
options.sort_by_confidence = True
|
|
1946
1972
|
|
|
1947
1973
|
options.pairwise_options = []
|
|
1948
1974
|
|
|
1949
1975
|
results_base = os.path.expanduser('~/postprocessing/bellevue-camera-traps')
|
|
1950
|
-
filenames = [
|
|
1951
|
-
os.path.join(results_base,r'bellevue-camera-traps-2023-12-05-v5a.0.0\combined_api_outputs\bellevue-camera-traps-2023-12-05-v5a.0.0_detections.json'),
|
|
1952
|
-
os.path.join(results_base,r'bellevue-camera-traps-2023-12-05-aug-v5a.0.0\combined_api_outputs\bellevue-camera-traps-2023-12-05-aug-v5a.0.0_detections.json')
|
|
1953
|
-
]
|
|
1954
1976
|
|
|
1955
1977
|
detection_thresholds = [0.15,0.15]
|
|
1956
1978
|
rendering_thresholds = None
|
|
1957
1979
|
|
|
1958
|
-
results = n_way_comparison(filenames,
|
|
1959
|
-
options,
|
|
1960
|
-
detection_thresholds,
|
|
1980
|
+
results = n_way_comparison(filenames=output_filenames,
|
|
1981
|
+
options=options,
|
|
1982
|
+
detection_thresholds=detection_thresholds,
|
|
1961
1983
|
rendering_thresholds=rendering_thresholds)
|
|
1962
1984
|
|
|
1963
1985
|
from megadetector.utils.path_utils import open_file
|
|
@@ -22,7 +22,8 @@ import pandas as pd
|
|
|
22
22
|
from megadetector.postprocessing.load_api_results import load_api_results_csv
|
|
23
23
|
from megadetector.utils.wi_taxonomy_utils import load_md_or_speciesnet_file
|
|
24
24
|
from megadetector.data_management.annotations import annotation_constants
|
|
25
|
-
from megadetector.utils import
|
|
25
|
+
from megadetector.utils.ct_utils import get_max_conf
|
|
26
|
+
from megadetector.utils.ct_utils import write_json
|
|
26
27
|
|
|
27
28
|
CONF_DIGITS = 3
|
|
28
29
|
|
|
@@ -138,7 +139,7 @@ def convert_json_to_csv(input_path,
|
|
|
138
139
|
# print('Skipping failed image {} ({})'.format(im['file'],im['failure']))
|
|
139
140
|
continue
|
|
140
141
|
|
|
141
|
-
max_conf =
|
|
142
|
+
max_conf = get_max_conf(im)
|
|
142
143
|
detection_category_id_to_max_conf = defaultdict(float)
|
|
143
144
|
classification_category_id_to_max_conf = defaultdict(float)
|
|
144
145
|
detections = []
|
|
@@ -177,7 +178,8 @@ def convert_json_to_csv(input_path,
|
|
|
177
178
|
classification_category_max = \
|
|
178
179
|
classification_category_id_to_max_conf[classification_category_id]
|
|
179
180
|
if classification_conf > classification_category_max:
|
|
180
|
-
classification_category_id_to_max_conf[classification_category_id] =
|
|
181
|
+
classification_category_id_to_max_conf[classification_category_id] = \
|
|
182
|
+
classification_conf
|
|
181
183
|
|
|
182
184
|
# ...for each classification
|
|
183
185
|
|
|
@@ -210,7 +212,7 @@ def convert_json_to_csv(input_path,
|
|
|
210
212
|
|
|
211
213
|
if omit_bounding_boxes:
|
|
212
214
|
df = df.drop('detections',axis=1)
|
|
213
|
-
df.to_csv(output_path,index=False,header=True)
|
|
215
|
+
df.to_csv(output_path,index=False,header=True,encoding=output_encoding)
|
|
214
216
|
|
|
215
217
|
# ...def convert_json_to_csv(...)
|
|
216
218
|
|
|
@@ -295,7 +297,7 @@ def convert_csv_to_json(input_path,output_path=None,overwrite=True):
|
|
|
295
297
|
json_out['classification_categories'] = classification_categories
|
|
296
298
|
json_out['images'] = images
|
|
297
299
|
|
|
298
|
-
|
|
300
|
+
write_json(output_path,json_out)
|
|
299
301
|
|
|
300
302
|
# ...def convert_csv_to_json(...)
|
|
301
303
|
|
|
@@ -372,7 +374,7 @@ def main():
|
|
|
372
374
|
help='Output filename ending in .json or .csv (defaults to ' + \
|
|
373
375
|
'input file, with .json/.csv replaced by .csv/.json)')
|
|
374
376
|
parser.add_argument('--omit_bounding_boxes',action='store_true',
|
|
375
|
-
help='
|
|
377
|
+
help='Omit bounding box text from .csv output (large and usually not useful)')
|
|
376
378
|
|
|
377
379
|
if len(sys.argv[1:]) == 0:
|
|
378
380
|
parser.print_help()
|
|
@@ -169,7 +169,9 @@ def crop_results_to_image_results(image_results_file_with_crop_ids,
|
|
|
169
169
|
'Could not find image-level input file {}'.format(image_results_file_with_crop_ids)
|
|
170
170
|
assert os.path.isfile(crop_results_file), \
|
|
171
171
|
'Could not find crop results file {}'.format(crop_results_file)
|
|
172
|
-
os.
|
|
172
|
+
output_dir = os.path.dirname(output_file)
|
|
173
|
+
if len(output_dir) > 0:
|
|
174
|
+
os.makedirs(output_dir,exist_ok=True)
|
|
173
175
|
|
|
174
176
|
|
|
175
177
|
##%% Read input files
|
|
@@ -259,7 +261,11 @@ def crop_results_to_image_results(image_results_file_with_crop_ids,
|
|
|
259
261
|
detections_without_classification_handling
|
|
260
262
|
))
|
|
261
263
|
|
|
262
|
-
if
|
|
264
|
+
if skip_detection:
|
|
265
|
+
|
|
266
|
+
n_skipped_detections += 1
|
|
267
|
+
|
|
268
|
+
else:
|
|
263
269
|
|
|
264
270
|
crop_results_this_detection = crop_filename_to_results[crop_filename_relative]
|
|
265
271
|
|
|
@@ -340,8 +346,11 @@ def create_crop_folder(input_file,
|
|
|
340
346
|
assert os.path.isfile(input_file), 'Input file {} not found'.format(input_file)
|
|
341
347
|
assert os.path.isdir(input_folder), 'Input folder {} not found'.format(input_folder)
|
|
342
348
|
os.makedirs(output_folder,exist_ok=True)
|
|
349
|
+
|
|
343
350
|
if output_file is not None:
|
|
344
|
-
os.
|
|
351
|
+
output_dir = os.path.dirname(output_file)
|
|
352
|
+
if len(output_dir) > 0:
|
|
353
|
+
os.makedirs(output_dir,exist_ok=True)
|
|
345
354
|
|
|
346
355
|
|
|
347
356
|
##%% Read input
|
|
@@ -599,7 +608,7 @@ def main():
|
|
|
599
608
|
|
|
600
609
|
print('Starting crop folder creation...')
|
|
601
610
|
print('Input MD results: {}'.format(args.input_file))
|
|
602
|
-
print('Input image folder {}'.format(args.input_folder))
|
|
611
|
+
print('Input image folder: {}'.format(args.input_folder))
|
|
603
612
|
print('Output crop folder: {}'.format(args.output_folder))
|
|
604
613
|
|
|
605
614
|
if args.output_file:
|
|
@@ -126,6 +126,7 @@ def generate_csv_report(md_results_file,
|
|
|
126
126
|
recursive=True)
|
|
127
127
|
|
|
128
128
|
else:
|
|
129
|
+
|
|
129
130
|
assert os.path.isfile(datetime_source), \
|
|
130
131
|
'datetime source {} is neither a folder nor a file'.format(datetime_source)
|
|
131
132
|
|
|
@@ -153,11 +154,14 @@ def generate_csv_report(md_results_file,
|
|
|
153
154
|
print('Warning: a MD results file was supplied as the datetime source, but it does not appear '
|
|
154
155
|
'to contain datetime information.')
|
|
155
156
|
|
|
157
|
+
# ...if datetime_source is a folder/file
|
|
158
|
+
|
|
156
159
|
assert all_exif_results is not None
|
|
157
160
|
|
|
158
161
|
filename_to_datetime_string = {}
|
|
159
162
|
|
|
160
163
|
for exif_result in all_exif_results:
|
|
164
|
+
|
|
161
165
|
datetime_string = unknown_datetime_tag
|
|
162
166
|
if ('exif_tags' in exif_result) and \
|
|
163
167
|
(exif_result['exif_tags'] is not None) and \
|
|
@@ -169,6 +173,8 @@ def generate_csv_report(md_results_file,
|
|
|
169
173
|
assert isinstance(datetime_string,str), 'Unrecognized datetime format'
|
|
170
174
|
filename_to_datetime_string[exif_result['file_name']] = datetime_string
|
|
171
175
|
|
|
176
|
+
# ...for each exif result
|
|
177
|
+
|
|
172
178
|
image_files = [im['file'] for im in results['images']]
|
|
173
179
|
image_files_set = set(image_files)
|
|
174
180
|
|
|
@@ -250,11 +256,10 @@ def generate_csv_report(md_results_file,
|
|
|
250
256
|
base_record['filename'] = im['file'].replace('\\','/')
|
|
251
257
|
|
|
252
258
|
# Datetime (if necessary)
|
|
259
|
+
datetime_string = ''
|
|
253
260
|
if filename_to_datetime_string is not None:
|
|
254
261
|
if im['file'] in filename_to_datetime_string:
|
|
255
262
|
datetime_string = filename_to_datetime_string[im['file']]
|
|
256
|
-
else:
|
|
257
|
-
datetime_string = ''
|
|
258
263
|
base_record['datetime'] = datetime_string
|
|
259
264
|
|
|
260
265
|
for s in ['detection_category','max_detection_confidence',
|
|
@@ -383,13 +388,22 @@ def generate_csv_report(md_results_file,
|
|
|
383
388
|
# ...for each image
|
|
384
389
|
|
|
385
390
|
# Make sure every record has the same columns
|
|
386
|
-
column_names = output_records[0].keys()
|
|
387
|
-
for record in output_records:
|
|
388
|
-
assert record.keys() == column_names
|
|
389
391
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
392
|
+
if len(output_records) == 0:
|
|
393
|
+
print('Warning: no output records generated')
|
|
394
|
+
else:
|
|
395
|
+
column_names = output_records[0].keys()
|
|
396
|
+
for record in output_records:
|
|
397
|
+
assert record.keys() == column_names
|
|
398
|
+
|
|
399
|
+
# Create folder for output file if necessary
|
|
400
|
+
output_dir = os.path.dirname(output_file)
|
|
401
|
+
if len(output_dir) > 0:
|
|
402
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
403
|
+
|
|
404
|
+
# Write to .csv
|
|
405
|
+
df = pd.DataFrame(output_records)
|
|
406
|
+
df.to_csv(output_file,header=True,index=False)
|
|
393
407
|
|
|
394
408
|
# from megadetector.utils.path_utils import open_file; open_file(output_file)
|
|
395
409
|
|
|
@@ -23,7 +23,8 @@ from collections.abc import Mapping
|
|
|
23
23
|
|
|
24
24
|
import pandas as pd
|
|
25
25
|
|
|
26
|
-
from megadetector.utils import
|
|
26
|
+
from megadetector.utils.ct_utils import get_max_conf
|
|
27
|
+
from megadetector.utils.ct_utils import write_json
|
|
27
28
|
from megadetector.utils.wi_taxonomy_utils import load_md_or_speciesnet_file
|
|
28
29
|
|
|
29
30
|
|
|
@@ -85,7 +86,7 @@ def load_api_results(api_output_path: str, normalize_paths: bool = True,
|
|
|
85
86
|
# add them, because our unofficial internal dataframe format includes this.
|
|
86
87
|
for im in detection_results['images']:
|
|
87
88
|
if 'max_detection_conf' not in im:
|
|
88
|
-
im['max_detection_conf'] =
|
|
89
|
+
im['max_detection_conf'] = get_max_conf(im)
|
|
89
90
|
|
|
90
91
|
# Pack the json output into a Pandas DataFrame
|
|
91
92
|
detection_results = pd.DataFrame(detection_results['images'])
|
|
@@ -139,8 +140,7 @@ def write_api_results(detection_results_table, other_fields, out_path):
|
|
|
139
140
|
print('Warning: error removing max_detection_conf from output')
|
|
140
141
|
pass
|
|
141
142
|
|
|
142
|
-
|
|
143
|
-
json.dump(fields, f, indent=1)
|
|
143
|
+
write_json(out_path,fields)
|
|
144
144
|
|
|
145
145
|
print('Finished writing detection results to {}'.format(out_path))
|
|
146
146
|
|
|
@@ -214,6 +214,10 @@ def write_api_results_csv(detection_results, filename):
|
|
|
214
214
|
|
|
215
215
|
print('Writing detection results to {}'.format(filename))
|
|
216
216
|
|
|
217
|
+
output_dir = os.path.dirname(filename)
|
|
218
|
+
if len(output_dir) > 0:
|
|
219
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
220
|
+
|
|
217
221
|
detection_results.to_csv(filename, index=False)
|
|
218
222
|
|
|
219
223
|
print('Finished writing detection results to {}'.format(filename))
|
|
@@ -22,6 +22,7 @@ from tqdm import tqdm
|
|
|
22
22
|
|
|
23
23
|
from megadetector.visualization import visualization_utils as vis_utils
|
|
24
24
|
from megadetector.utils.path_utils import insert_before_extension
|
|
25
|
+
from megadetector.utils.ct_utils import write_json
|
|
25
26
|
|
|
26
27
|
default_confidence_threshold = 0.15
|
|
27
28
|
|
|
@@ -296,9 +297,7 @@ def md_to_coco(md_results_file,
|
|
|
296
297
|
if verbose:
|
|
297
298
|
print('Writing COCO output file...')
|
|
298
299
|
|
|
299
|
-
|
|
300
|
-
with open(coco_output_file,'w') as f:
|
|
301
|
-
json.dump(output_dict,f,indent=1)
|
|
300
|
+
write_json(coco_output_file,output_dict)
|
|
302
301
|
|
|
303
302
|
return output_dict
|
|
304
303
|
|
|
@@ -28,6 +28,7 @@ from functools import partial
|
|
|
28
28
|
|
|
29
29
|
from megadetector.visualization.visualization_utils import open_image
|
|
30
30
|
from megadetector.utils.ct_utils import round_float
|
|
31
|
+
from megadetector.utils.ct_utils import write_json
|
|
31
32
|
from megadetector.detection.run_detector import DEFAULT_DETECTOR_LABEL_MAP, FAILURE_IMAGE_OPEN
|
|
32
33
|
|
|
33
34
|
output_precision = 3
|
|
@@ -36,8 +37,11 @@ default_confidence_threshold = 0.15
|
|
|
36
37
|
|
|
37
38
|
#%% Functions
|
|
38
39
|
|
|
39
|
-
def get_labelme_dict_for_image(im,
|
|
40
|
-
|
|
40
|
+
def get_labelme_dict_for_image(im,
|
|
41
|
+
image_base_name=None,
|
|
42
|
+
category_id_to_name=None,
|
|
43
|
+
info=None,
|
|
44
|
+
confidence_threshold=None):
|
|
41
45
|
"""
|
|
42
46
|
For the given image struct in MD results format, reformat the detections into
|
|
43
47
|
labelme format.
|
|
@@ -60,7 +64,7 @@ def get_labelme_dict_for_image(im,image_base_name=None,category_id_to_name=None,
|
|
|
60
64
|
if image_base_name is None:
|
|
61
65
|
image_base_name = os.path.basename(im['file'])
|
|
62
66
|
|
|
63
|
-
if category_id_to_name:
|
|
67
|
+
if category_id_to_name is None:
|
|
64
68
|
category_id_to_name = DEFAULT_DETECTOR_LABEL_MAP
|
|
65
69
|
|
|
66
70
|
if confidence_threshold is None:
|
|
@@ -138,8 +142,7 @@ def _write_output_for_image(im,
|
|
|
138
142
|
info=info,
|
|
139
143
|
confidence_threshold=confidence_threshold)
|
|
140
144
|
|
|
141
|
-
|
|
142
|
-
json.dump(output_dict,f,indent=1)
|
|
145
|
+
write_json(json_path,output_dict)
|
|
143
146
|
|
|
144
147
|
# ...def write_output_for_image(...)
|
|
145
148
|
|
|
@@ -256,9 +259,10 @@ def md_to_labelme(results_file,
|
|
|
256
259
|
md_results['images']),
|
|
257
260
|
total=len(md_results['images'])))
|
|
258
261
|
finally:
|
|
259
|
-
pool
|
|
260
|
-
|
|
261
|
-
|
|
262
|
+
if pool is not None:
|
|
263
|
+
pool.close()
|
|
264
|
+
pool.join()
|
|
265
|
+
print("Pool closed and joined for labelme file writes")
|
|
262
266
|
|
|
263
267
|
# ...for each image
|
|
264
268
|
|
|
@@ -10,6 +10,7 @@ Converts the MD .json format to the WI predictions.json format.
|
|
|
10
10
|
|
|
11
11
|
import sys
|
|
12
12
|
import argparse
|
|
13
|
+
|
|
13
14
|
from megadetector.utils.wi_taxonomy_utils import generate_predictions_json_from_md_results
|
|
14
15
|
|
|
15
16
|
|
|
@@ -34,7 +35,7 @@ def main(): # noqa
|
|
|
34
35
|
|
|
35
36
|
generate_predictions_json_from_md_results(args.md_results_file,
|
|
36
37
|
args.predictions_json_file,
|
|
37
|
-
base_folder=
|
|
38
|
+
base_folder=args.base_folder)
|
|
38
39
|
|
|
39
40
|
if __name__ == '__main__':
|
|
40
41
|
main()
|
|
@@ -23,6 +23,7 @@ import os
|
|
|
23
23
|
from tqdm import tqdm
|
|
24
24
|
|
|
25
25
|
from megadetector.utils.ct_utils import get_iou
|
|
26
|
+
from megadetector.utils.ct_utils import write_json
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
#%% Structs
|
|
@@ -121,8 +122,6 @@ def merge_detections(source_files,target_file,output_file,options=None):
|
|
|
121
122
|
|
|
122
123
|
assert os.path.isfile(target_file)
|
|
123
124
|
|
|
124
|
-
os.makedirs(os.path.dirname(output_file),exist_ok=True)
|
|
125
|
-
|
|
126
125
|
with open(target_file,'r') as f:
|
|
127
126
|
output_data = json.load(f)
|
|
128
127
|
|
|
@@ -290,8 +289,7 @@ def merge_detections(source_files,target_file,output_file,options=None):
|
|
|
290
289
|
|
|
291
290
|
# ...for each source file
|
|
292
291
|
|
|
293
|
-
|
|
294
|
-
json.dump(output_data,f,indent=1)
|
|
292
|
+
write_json(output_file,output_data)
|
|
295
293
|
|
|
296
294
|
print('Saved merged results to {}'.format(output_file))
|
|
297
295
|
|
|
@@ -308,7 +306,7 @@ def main():
|
|
|
308
306
|
default_options = MergeDetectionsOptions()
|
|
309
307
|
|
|
310
308
|
parser = argparse.ArgumentParser(
|
|
311
|
-
description='Merge detections from one or more MegaDetector results files into an existing
|
|
309
|
+
description='Merge detections from one or more MegaDetector results files into an existing results file')
|
|
312
310
|
parser.add_argument(
|
|
313
311
|
'source_files',
|
|
314
312
|
nargs='+',
|
|
@@ -359,7 +357,7 @@ def main():
|
|
|
359
357
|
type=int,
|
|
360
358
|
nargs='+',
|
|
361
359
|
default=None,
|
|
362
|
-
help='List of numeric detection categories to
|
|
360
|
+
help='List of numeric detection categories to exclude')
|
|
363
361
|
parser.add_argument(
|
|
364
362
|
'--merge_empty_only',
|
|
365
363
|
action='store_true',
|
|
@@ -1889,8 +1889,9 @@ def process_batch_results(options):
|
|
|
1889
1889
|
if options.include_classification_category_report:
|
|
1890
1890
|
|
|
1891
1891
|
# TODO: it's only for silly historical reasons that we re-read
|
|
1892
|
-
# the input file in this case;
|
|
1893
|
-
#
|
|
1892
|
+
# the input file in this case; because this module has used Pandas
|
|
1893
|
+
# forever, we're not currently carrying the json representation around,
|
|
1894
|
+
# only the Pandas representation.
|
|
1894
1895
|
|
|
1895
1896
|
print('Generating classification category report')
|
|
1896
1897
|
|
|
@@ -1905,7 +1906,7 @@ def process_batch_results(options):
|
|
|
1905
1906
|
if ('classifications' in det) and (len(det['classifications']) > 0):
|
|
1906
1907
|
class_id = det['classifications'][0][0]
|
|
1907
1908
|
if class_id not in classification_category_to_count:
|
|
1908
|
-
classification_category_to_count[class_id] =
|
|
1909
|
+
classification_category_to_count[class_id] = 1
|
|
1909
1910
|
else:
|
|
1910
1911
|
classification_category_to_count[class_id] = \
|
|
1911
1912
|
classification_category_to_count[class_id] + 1
|
|
@@ -18,6 +18,7 @@ import argparse
|
|
|
18
18
|
from tqdm import tqdm
|
|
19
19
|
|
|
20
20
|
from megadetector.utils.ct_utils import invert_dictionary
|
|
21
|
+
from megadetector.utils.ct_utils import write_json
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
#%% Main function
|
|
@@ -132,14 +133,16 @@ def remap_detection_categories(input_file,
|
|
|
132
133
|
for det in im['detections']:
|
|
133
134
|
det['category'] = input_category_id_to_output_category_id[det['category']]
|
|
134
135
|
|
|
135
|
-
|
|
136
|
+
# ...for each image
|
|
136
137
|
|
|
137
|
-
|
|
138
|
-
json.dump(input_data,f,indent=1)
|
|
138
|
+
input_data['detection_categories'] = target_category_map
|
|
139
139
|
|
|
140
|
+
write_json(output_file,input_data)
|
|
140
141
|
|
|
141
142
|
print('Saved remapped results to {}'.format(output_file))
|
|
142
143
|
|
|
144
|
+
# ...def remap_detection_categories(...)
|
|
145
|
+
|
|
143
146
|
|
|
144
147
|
#%% Interactive driver
|
|
145
148
|
|