megadetector 5.0.29__py3-none-any.whl → 10.0.1__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/classification/efficientnet/model.py +8 -8
- megadetector/classification/efficientnet/utils.py +6 -5
- megadetector/classification/prepare_classification_script_mc.py +3 -3
- megadetector/data_management/annotations/annotation_constants.py +0 -1
- megadetector/data_management/camtrap_dp_to_coco.py +34 -1
- megadetector/data_management/cct_json_utils.py +2 -2
- megadetector/data_management/coco_to_yolo.py +22 -5
- megadetector/data_management/databases/add_width_and_height_to_db.py +85 -12
- megadetector/data_management/databases/combine_coco_camera_traps_files.py +2 -2
- megadetector/data_management/databases/integrity_check_json_db.py +29 -15
- megadetector/data_management/generate_crops_from_cct.py +50 -1
- megadetector/data_management/labelme_to_coco.py +4 -2
- megadetector/data_management/labelme_to_yolo.py +82 -2
- megadetector/data_management/lila/generate_lila_per_image_labels.py +276 -18
- megadetector/data_management/lila/get_lila_annotation_counts.py +5 -3
- megadetector/data_management/lila/lila_common.py +3 -0
- megadetector/data_management/lila/test_lila_metadata_urls.py +15 -5
- megadetector/data_management/mewc_to_md.py +5 -0
- megadetector/data_management/ocr_tools.py +4 -3
- megadetector/data_management/read_exif.py +20 -5
- megadetector/data_management/remap_coco_categories.py +66 -4
- megadetector/data_management/remove_exif.py +50 -1
- megadetector/data_management/rename_images.py +3 -3
- megadetector/data_management/resize_coco_dataset.py +563 -95
- megadetector/data_management/yolo_output_to_md_output.py +131 -2
- megadetector/data_management/yolo_to_coco.py +140 -5
- megadetector/detection/change_detection.py +4 -3
- megadetector/detection/pytorch_detector.py +60 -22
- megadetector/detection/run_detector.py +225 -25
- megadetector/detection/run_detector_batch.py +42 -16
- megadetector/detection/run_inference_with_yolov5_val.py +12 -2
- megadetector/detection/run_tiled_inference.py +1 -0
- megadetector/detection/video_utils.py +53 -24
- megadetector/postprocessing/add_max_conf.py +4 -0
- megadetector/postprocessing/categorize_detections_by_size.py +1 -1
- megadetector/postprocessing/classification_postprocessing.py +55 -20
- megadetector/postprocessing/combine_batch_outputs.py +3 -2
- megadetector/postprocessing/compare_batch_results.py +64 -10
- megadetector/postprocessing/convert_output_format.py +12 -8
- megadetector/postprocessing/create_crop_folder.py +137 -10
- megadetector/postprocessing/load_api_results.py +26 -8
- megadetector/postprocessing/md_to_coco.py +4 -4
- megadetector/postprocessing/md_to_labelme.py +18 -7
- megadetector/postprocessing/merge_detections.py +5 -0
- megadetector/postprocessing/postprocess_batch_results.py +6 -3
- megadetector/postprocessing/remap_detection_categories.py +55 -2
- megadetector/postprocessing/render_detection_confusion_matrix.py +9 -6
- megadetector/postprocessing/repeat_detection_elimination/repeat_detections_core.py +2 -2
- megadetector/taxonomy_mapping/map_new_lila_datasets.py +3 -4
- megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +40 -19
- megadetector/taxonomy_mapping/preview_lila_taxonomy.py +1 -1
- megadetector/taxonomy_mapping/species_lookup.py +123 -41
- megadetector/utils/ct_utils.py +133 -113
- megadetector/utils/md_tests.py +93 -13
- megadetector/utils/path_utils.py +137 -107
- megadetector/utils/split_locations_into_train_val.py +2 -2
- megadetector/utils/string_utils.py +7 -7
- megadetector/utils/url_utils.py +81 -58
- megadetector/utils/wi_utils.py +46 -17
- megadetector/visualization/plot_utils.py +13 -9
- megadetector/visualization/render_images_with_thumbnails.py +2 -1
- megadetector/visualization/visualization_utils.py +94 -46
- megadetector/visualization/visualize_db.py +36 -9
- megadetector/visualization/visualize_detector_output.py +4 -4
- {megadetector-5.0.29.dist-info → megadetector-10.0.1.dist-info}/METADATA +135 -135
- megadetector-10.0.1.dist-info/RECORD +139 -0
- {megadetector-5.0.29.dist-info → megadetector-10.0.1.dist-info}/licenses/LICENSE +0 -0
- {megadetector-5.0.29.dist-info → megadetector-10.0.1.dist-info}/top_level.txt +0 -0
- 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 -438
- 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 -109
- megadetector/utils/azure_utils.py +0 -178
- megadetector/utils/sas_blob_utils.py +0 -513
- megadetector-5.0.29.dist-info/RECORD +0 -163
- /megadetector/{api/batch_processing/__init__.py → __init__.py} +0 -0
- {megadetector-5.0.29.dist-info → megadetector-10.0.1.dist-info}/WHEEL +0 -0
|
@@ -40,6 +40,8 @@ import json
|
|
|
40
40
|
import csv
|
|
41
41
|
import os
|
|
42
42
|
import re
|
|
43
|
+
import sys
|
|
44
|
+
import argparse
|
|
43
45
|
|
|
44
46
|
from collections import defaultdict
|
|
45
47
|
from tqdm import tqdm
|
|
@@ -123,8 +125,9 @@ def yolo_json_output_to_md_output(yolo_json_file,
|
|
|
123
125
|
Converts a YOLOv5/YOLOv8 .json file to MD .json format.
|
|
124
126
|
|
|
125
127
|
Args:
|
|
126
|
-
yolo_json_file (str): the .json file to convert
|
|
128
|
+
yolo_json_file (str): the YOLO-formatted .json file to convert to MD output format
|
|
127
129
|
image_folder (str): the .json file contains relative path names, this is the path base
|
|
130
|
+
output_file (str): the MD-formatted .json file to write
|
|
128
131
|
yolo_category_id_to_name (str or dict): the .json results file contains only numeric
|
|
129
132
|
identifiers for categories, but we want names and numbers for the output format;
|
|
130
133
|
yolo_category_id_to_name provides that mapping either as a dict or as a YOLOv5
|
|
@@ -460,4 +463,130 @@ if False:
|
|
|
460
463
|
|
|
461
464
|
#%% Command-line driver
|
|
462
465
|
|
|
463
|
-
|
|
466
|
+
def main():
|
|
467
|
+
"""
|
|
468
|
+
Command-line interface to convert YOLOv5/YOLOv8 output (.json or .txt)
|
|
469
|
+
to MegaDetector output format.
|
|
470
|
+
"""
|
|
471
|
+
|
|
472
|
+
parser = argparse.ArgumentParser(
|
|
473
|
+
description='Converts YOLOv5 output (.json or .txt) to MD output format.'
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
# The first argument determines which series of additional arguments are supported, for
|
|
477
|
+
# json/txt input
|
|
478
|
+
subparsers = parser.add_subparsers(dest='mode', required=True,
|
|
479
|
+
help="Mode of operation: 'json' for YOLO JSON output, 'txt' for YOLO TXT output.")
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
## 'json' mode subparser
|
|
483
|
+
|
|
484
|
+
parser_json = subparsers.add_parser('json', help='Convert YOLO-formatted .json results.')
|
|
485
|
+
|
|
486
|
+
parser_json.add_argument(
|
|
487
|
+
'yolo_json_file', type=str,
|
|
488
|
+
help='Path to the input YOLO-formatted .json results file'
|
|
489
|
+
)
|
|
490
|
+
parser_json.add_argument(
|
|
491
|
+
'image_folder', type=str,
|
|
492
|
+
help='Path to the image folder'
|
|
493
|
+
)
|
|
494
|
+
parser_json.add_argument(
|
|
495
|
+
'output_file', type=str,
|
|
496
|
+
help='Path to the MD-formatted .json output file'
|
|
497
|
+
)
|
|
498
|
+
parser_json.add_argument(
|
|
499
|
+
'yolo_category_id_to_name_file', type=str,
|
|
500
|
+
help='Path to the .yml, .yaml, .json, or .txt file mapping YOLO category IDs to names'
|
|
501
|
+
)
|
|
502
|
+
parser_json.add_argument(
|
|
503
|
+
'--detector_name', type=str, default='unknown',
|
|
504
|
+
help="Detector name to store in the output file (default: 'unknown')"
|
|
505
|
+
)
|
|
506
|
+
parser_json.add_argument(
|
|
507
|
+
'--image_id_to_relative_path_file', type=str, default=None,
|
|
508
|
+
help='Path to a .json file mapping image IDs to relative paths'
|
|
509
|
+
)
|
|
510
|
+
parser_json.add_argument(
|
|
511
|
+
'--offset_yolo_class_ids', type=str, default='true', choices=['true', 'false'],
|
|
512
|
+
help="Offset YOLO class IDs in the output (default: 'true')"
|
|
513
|
+
)
|
|
514
|
+
parser_json.add_argument(
|
|
515
|
+
'--truncate_to_standard_md_precision', type=str, default='true', choices=['true', 'false'],
|
|
516
|
+
help="Truncate coordinates and confidences to standard MD precision (default: 'true')"
|
|
517
|
+
)
|
|
518
|
+
parser_json.add_argument(
|
|
519
|
+
'--convert_slashes', type=str, default='true', choices=['true', 'false'],
|
|
520
|
+
help="Convert backslashes to forward slashes in output file paths (default: 'true')"
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
## 'txt' mode subparser
|
|
525
|
+
|
|
526
|
+
parser_txt = subparsers.add_parser('txt', help='Convert YOLO-formatted .txt results from a folder')
|
|
527
|
+
parser_txt.add_argument(
|
|
528
|
+
'input_results_folder', type=str,
|
|
529
|
+
help='Path to the folder containing YOLO .txt output files'
|
|
530
|
+
)
|
|
531
|
+
parser_txt.add_argument(
|
|
532
|
+
'image_folder', type=str,
|
|
533
|
+
help='Path to the image folder'
|
|
534
|
+
)
|
|
535
|
+
parser_txt.add_argument(
|
|
536
|
+
'output_file', type=str,
|
|
537
|
+
help='Path to the MD-formatted .json file output'
|
|
538
|
+
)
|
|
539
|
+
parser_txt.add_argument(
|
|
540
|
+
'--detector_tag', type=str, default=None,
|
|
541
|
+
help='Detector tag to store in the output file'
|
|
542
|
+
)
|
|
543
|
+
parser_txt.add_argument(
|
|
544
|
+
'--truncate_to_standard_md_precision', type=str, default='true', choices=['true', 'false'],
|
|
545
|
+
help="Truncate coordinates and confidences to standard MD precision (default: 'true')."
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
args = parser.parse_args()
|
|
549
|
+
|
|
550
|
+
if args.mode == 'json':
|
|
551
|
+
|
|
552
|
+
image_id_to_relative_path = None
|
|
553
|
+
if args.image_id_to_relative_path_file:
|
|
554
|
+
try:
|
|
555
|
+
with open(args.image_id_to_relative_path_file, 'r') as f:
|
|
556
|
+
image_id_to_relative_path = json.load(f)
|
|
557
|
+
except Exception as e:
|
|
558
|
+
print(f"Error loading image_id_to_relative_path_file: {e}")
|
|
559
|
+
sys.exit(1)
|
|
560
|
+
|
|
561
|
+
offset_yolo_class_ids = args.offset_yolo_class_ids.lower() == 'true'
|
|
562
|
+
truncate_json = args.truncate_to_standard_md_precision.lower() == 'true'
|
|
563
|
+
convert_slashes = args.convert_slashes.lower() == 'true'
|
|
564
|
+
|
|
565
|
+
yolo_json_output_to_md_output(
|
|
566
|
+
yolo_json_file=args.yolo_json_file,
|
|
567
|
+
image_folder=args.image_folder,
|
|
568
|
+
output_file=args.output_file,
|
|
569
|
+
yolo_category_id_to_name=args.yolo_category_id_to_name_file, # Function handles reading this file
|
|
570
|
+
detector_name=args.detector_name,
|
|
571
|
+
image_id_to_relative_path=image_id_to_relative_path,
|
|
572
|
+
offset_yolo_class_ids=offset_yolo_class_ids,
|
|
573
|
+
truncate_to_standard_md_precision=truncate_json,
|
|
574
|
+
convert_slashes=convert_slashes
|
|
575
|
+
)
|
|
576
|
+
print('Converted {} to {}'.format(args.yolo_json_file,args.output_file))
|
|
577
|
+
|
|
578
|
+
elif args.mode == 'txt':
|
|
579
|
+
|
|
580
|
+
truncate_txt = args.truncate_to_standard_md_precision.lower() == 'true'
|
|
581
|
+
|
|
582
|
+
yolo_txt_output_to_md_output(
|
|
583
|
+
input_results_folder=args.input_results_folder,
|
|
584
|
+
image_folder=args.image_folder,
|
|
585
|
+
output_file=args.output_file,
|
|
586
|
+
detector_tag=args.detector_tag,
|
|
587
|
+
truncate_to_standard_md_precision=truncate_txt
|
|
588
|
+
)
|
|
589
|
+
print('Converted results from {} to {}'.format(args.input_results_folder,args.output_file))
|
|
590
|
+
|
|
591
|
+
if __name__ == '__main__':
|
|
592
|
+
main()
|
|
@@ -10,6 +10,8 @@ Converts a folder of YOLO-formatted annotation files to a COCO-formatted dataset
|
|
|
10
10
|
|
|
11
11
|
import json
|
|
12
12
|
import os
|
|
13
|
+
import argparse
|
|
14
|
+
import sys
|
|
13
15
|
|
|
14
16
|
from multiprocessing.pool import ThreadPool
|
|
15
17
|
from multiprocessing.pool import Pool
|
|
@@ -22,7 +24,8 @@ from megadetector.utils.path_utils import recursive_file_list
|
|
|
22
24
|
from megadetector.utils.path_utils import find_image_strings
|
|
23
25
|
from megadetector.utils.ct_utils import invert_dictionary
|
|
24
26
|
from megadetector.visualization.visualization_utils import open_image
|
|
25
|
-
from megadetector.data_management.yolo_output_to_md_output import
|
|
27
|
+
from megadetector.data_management.yolo_output_to_md_output import \
|
|
28
|
+
read_classes_from_yolo_dataset_file
|
|
26
29
|
|
|
27
30
|
|
|
28
31
|
#%% Support functions
|
|
@@ -430,7 +433,7 @@ def yolo_to_coco(input_folder,
|
|
|
430
433
|
input_folder as the base folder, though this is not explicitly checked.
|
|
431
434
|
output_file (str, optional): .json file to which we should write COCO .json data
|
|
432
435
|
empty_image_handling (str, optional): how to handle images with no boxes; whether
|
|
433
|
-
this includes images with no .txt files
|
|
436
|
+
this includes images with no .txt files depends on the value of
|
|
434
437
|
[allow_images_without_label_files]. Can be:
|
|
435
438
|
|
|
436
439
|
- 'no_annotations': include the image in the image list, with no annotations
|
|
@@ -438,12 +441,15 @@ def yolo_to_coco(input_folder,
|
|
|
438
441
|
any bounding boxes, using a category called [empty_image_category_name].
|
|
439
442
|
- 'skip': don't include the image in the image list
|
|
440
443
|
- 'error': there shouldn't be any empty images
|
|
444
|
+
empty_image_category_name (str, optional): if we're going to be inserting annotations for
|
|
445
|
+
images with no boxes, what category name should we use?
|
|
441
446
|
error_image_handling (str, optional): how to handle images that don't load properly; can
|
|
442
447
|
be:
|
|
443
448
|
|
|
444
449
|
- 'skip': don't include the image at all
|
|
445
450
|
- 'no_annotations': include with no annotations
|
|
446
|
-
|
|
451
|
+
allow_images_without_label_files (bool, optional): whether to silently allow images with
|
|
452
|
+
no label files (True) or raise errors for images with no label files (False)
|
|
447
453
|
n_workers (int, optional): number of concurrent workers, set to <= 1 to disable
|
|
448
454
|
parallelization
|
|
449
455
|
pool_type (str, optional): 'thread' or 'process', worker type to use for parallelization;
|
|
@@ -493,7 +499,7 @@ def yolo_to_coco(input_folder,
|
|
|
493
499
|
# Find or create the empty image category, if necessary
|
|
494
500
|
empty_category_id = None
|
|
495
501
|
|
|
496
|
-
if
|
|
502
|
+
if empty_image_handling == 'empty_annotations':
|
|
497
503
|
category_name_to_id = invert_dictionary(category_id_to_name)
|
|
498
504
|
if empty_image_category_name in category_name_to_id:
|
|
499
505
|
empty_category_id = category_name_to_id[empty_image_category_name]
|
|
@@ -652,6 +658,11 @@ def yolo_to_coco(input_folder,
|
|
|
652
658
|
|
|
653
659
|
# ...for each image result
|
|
654
660
|
|
|
661
|
+
# Clean up unnecessary error fields
|
|
662
|
+
for im in images:
|
|
663
|
+
if 'error' in im and im['error'] is None:
|
|
664
|
+
del im['error']
|
|
665
|
+
|
|
655
666
|
print('Read {} annotations for {} images'.format(len(annotations),
|
|
656
667
|
len(images)))
|
|
657
668
|
|
|
@@ -729,4 +740,128 @@ if False:
|
|
|
729
740
|
|
|
730
741
|
#%% Command-line driver
|
|
731
742
|
|
|
732
|
-
|
|
743
|
+
def main():
|
|
744
|
+
"""
|
|
745
|
+
Command-line driver for YOLO to COCO conversion.
|
|
746
|
+
"""
|
|
747
|
+
|
|
748
|
+
parser = argparse.ArgumentParser(
|
|
749
|
+
description='Convert a YOLO-formatted dataset to COCO format'
|
|
750
|
+
)
|
|
751
|
+
parser.add_argument(
|
|
752
|
+
'input_folder',
|
|
753
|
+
type=str,
|
|
754
|
+
help='Path to the YOLO dataset folder (image folder)'
|
|
755
|
+
)
|
|
756
|
+
parser.add_argument(
|
|
757
|
+
'class_name_file',
|
|
758
|
+
type=str,
|
|
759
|
+
help='Path to the file containing class names (e.g., classes.txt or dataset.yaml)'
|
|
760
|
+
)
|
|
761
|
+
parser.add_argument(
|
|
762
|
+
'output_file',
|
|
763
|
+
type=str,
|
|
764
|
+
help='Path to the output COCO .json file.'
|
|
765
|
+
)
|
|
766
|
+
parser.add_argument(
|
|
767
|
+
'--label_folder',
|
|
768
|
+
type=str,
|
|
769
|
+
default=None,
|
|
770
|
+
help='Label folder, if different from the image folder. Default: None (labels are in the image folder)'
|
|
771
|
+
)
|
|
772
|
+
parser.add_argument(
|
|
773
|
+
'--empty_image_handling',
|
|
774
|
+
type=str,
|
|
775
|
+
default='no_annotations',
|
|
776
|
+
choices=['no_annotations', 'empty_annotations', 'skip', 'error'],
|
|
777
|
+
help='How to handle images with no bounding boxes.'
|
|
778
|
+
)
|
|
779
|
+
parser.add_argument(
|
|
780
|
+
'--empty_image_category_name',
|
|
781
|
+
type=str,
|
|
782
|
+
default='empty',
|
|
783
|
+
help='Category name for empty images if empty_image_handling is "empty_annotations"'
|
|
784
|
+
)
|
|
785
|
+
parser.add_argument(
|
|
786
|
+
'--error_image_handling',
|
|
787
|
+
type=str,
|
|
788
|
+
default='no_annotations',
|
|
789
|
+
choices=['skip', 'no_annotations'],
|
|
790
|
+
help='How to handle images that fail to load'
|
|
791
|
+
)
|
|
792
|
+
parser.add_argument(
|
|
793
|
+
'--allow_images_without_label_files',
|
|
794
|
+
type=str,
|
|
795
|
+
default='true',
|
|
796
|
+
choices=['true', 'false'],
|
|
797
|
+
help='Whether to allow images that do not have corresponding label files (true/false)'
|
|
798
|
+
)
|
|
799
|
+
parser.add_argument(
|
|
800
|
+
'--n_workers',
|
|
801
|
+
type=int,
|
|
802
|
+
default=1,
|
|
803
|
+
help='Number of workers for parallel processing. <=1 for sequential'
|
|
804
|
+
)
|
|
805
|
+
parser.add_argument(
|
|
806
|
+
'--pool_type',
|
|
807
|
+
type=str,
|
|
808
|
+
default='thread',
|
|
809
|
+
choices=['thread', 'process'],
|
|
810
|
+
help='Type of multiprocessing pool if n_workers > 1'
|
|
811
|
+
)
|
|
812
|
+
parser.add_argument(
|
|
813
|
+
'--recursive',
|
|
814
|
+
type=str,
|
|
815
|
+
default='true',
|
|
816
|
+
choices=['true', 'false'],
|
|
817
|
+
help='Whether to search for images recursively in the input folder (true/false)'
|
|
818
|
+
)
|
|
819
|
+
parser.add_argument(
|
|
820
|
+
'--exclude_string',
|
|
821
|
+
type=str,
|
|
822
|
+
default=None,
|
|
823
|
+
help='Exclude images whose filename contains this string'
|
|
824
|
+
)
|
|
825
|
+
parser.add_argument(
|
|
826
|
+
'--include_string',
|
|
827
|
+
type=str,
|
|
828
|
+
default=None,
|
|
829
|
+
help='Include images only if filename contains this string'
|
|
830
|
+
)
|
|
831
|
+
parser.add_argument(
|
|
832
|
+
'--overwrite_handling',
|
|
833
|
+
type=str,
|
|
834
|
+
default='overwrite',
|
|
835
|
+
choices=['load', 'overwrite', 'error'],
|
|
836
|
+
help='Behavior if output_file exists.'
|
|
837
|
+
)
|
|
838
|
+
|
|
839
|
+
if len(sys.argv[1:]) == 0:
|
|
840
|
+
parser.print_help()
|
|
841
|
+
parser.exit()
|
|
842
|
+
|
|
843
|
+
args = parser.parse_args()
|
|
844
|
+
|
|
845
|
+
parsed_allow_images = args.allow_images_without_label_files.lower() == 'true'
|
|
846
|
+
parsed_recursive = args.recursive.lower() == 'true'
|
|
847
|
+
|
|
848
|
+
yolo_to_coco(
|
|
849
|
+
args.input_folder,
|
|
850
|
+
args.class_name_file,
|
|
851
|
+
output_file=args.output_file,
|
|
852
|
+
label_folder=args.label_folder,
|
|
853
|
+
empty_image_handling=args.empty_image_handling,
|
|
854
|
+
empty_image_category_name=args.empty_image_category_name,
|
|
855
|
+
error_image_handling=args.error_image_handling,
|
|
856
|
+
allow_images_without_label_files=parsed_allow_images,
|
|
857
|
+
n_workers=args.n_workers,
|
|
858
|
+
pool_type=args.pool_type,
|
|
859
|
+
recursive=parsed_recursive,
|
|
860
|
+
exclude_string=args.exclude_string,
|
|
861
|
+
include_string=args.include_string,
|
|
862
|
+
overwrite_handling=args.overwrite_handling
|
|
863
|
+
)
|
|
864
|
+
print(f"Dataset conversion complete, output written to {args.output_file}")
|
|
865
|
+
|
|
866
|
+
if __name__ == '__main__':
|
|
867
|
+
main()
|
|
@@ -228,7 +228,8 @@ def detect_motion(prev_image_path,
|
|
|
228
228
|
curr_image_path (str): path to the current image
|
|
229
229
|
options (ChangeDetectionOptions, optional): detection settings
|
|
230
230
|
motion_state (MotionHistoryState, optional): state for motion history
|
|
231
|
-
bg_subtractor: background subtractor model for
|
|
231
|
+
bg_subtractor (cv2 background subtractor object): background subtractor model for
|
|
232
|
+
MOG2/KNN methods
|
|
232
233
|
|
|
233
234
|
Returns:
|
|
234
235
|
tuple: (motion_result, updated_motion_state)
|
|
@@ -611,8 +612,8 @@ def create_change_previews(motion_results, output_folder, num_samples=10, random
|
|
|
611
612
|
Create side-by-side previews of images with detected motion
|
|
612
613
|
|
|
613
614
|
Args:
|
|
614
|
-
motion_results: DataFrame with motion detection results
|
|
615
|
-
output_folder: folder where preview images will be saved
|
|
615
|
+
motion_results (DataFrame): DataFrame with motion detection results
|
|
616
|
+
output_folder (str): folder where preview images will be saved
|
|
616
617
|
num_samples (int, optional): number of random samples to create
|
|
617
618
|
random_seed (int, optional): seed for random sampling (for reproducibility)
|
|
618
619
|
|
|
@@ -28,7 +28,6 @@ from megadetector.detection.run_detector import \
|
|
|
28
28
|
get_detector_version_from_model_file, \
|
|
29
29
|
known_models
|
|
30
30
|
from megadetector.utils.ct_utils import parse_bool_string
|
|
31
|
-
from megadetector.utils.ct_utils import to_bool
|
|
32
31
|
from megadetector.utils import ct_utils
|
|
33
32
|
|
|
34
33
|
# We support a few ways of accessing the YOLOv5 dependencies:
|
|
@@ -176,7 +175,7 @@ def _initialize_yolo_imports_for_model(model_file,
|
|
|
176
175
|
return model_type
|
|
177
176
|
|
|
178
177
|
|
|
179
|
-
def _clean_yolo_imports(verbose=False):
|
|
178
|
+
def _clean_yolo_imports(verbose=False,aggressive_cleanup=False):
|
|
180
179
|
"""
|
|
181
180
|
Remove all YOLO-related imports from sys.modules and sys.path, to allow a clean re-import
|
|
182
181
|
of another YOLO library version. The reason we jump through all these hoops, rather than
|
|
@@ -187,30 +186,68 @@ def _clean_yolo_imports(verbose=False):
|
|
|
187
186
|
|
|
188
187
|
Args:
|
|
189
188
|
verbose (bool, optional): enable additional debug output
|
|
189
|
+
aggressive_cleanup (bool, optional): err on the side of removing modules,
|
|
190
|
+
at least by ignoring whether they are/aren't in a site-packages folder.
|
|
191
|
+
By default, only modules in a folder that includes "site-packages" will
|
|
192
|
+
be considered for unloading.
|
|
190
193
|
"""
|
|
191
194
|
|
|
192
195
|
modules_to_delete = []
|
|
196
|
+
|
|
193
197
|
for module_name in sys.modules.keys():
|
|
198
|
+
|
|
194
199
|
module = sys.modules[module_name]
|
|
200
|
+
if not hasattr(module,'__file__') or (module.__file__ is None):
|
|
201
|
+
continue
|
|
195
202
|
try:
|
|
196
203
|
module_file = module.__file__.replace('\\','/')
|
|
197
|
-
if
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
204
|
+
if not aggressive_cleanup:
|
|
205
|
+
if 'site-packages' not in module_file:
|
|
206
|
+
continue
|
|
207
|
+
tokens = module_file.split('/')
|
|
208
|
+
|
|
209
|
+
# For local path imports, a module filename that should be unloaded might
|
|
210
|
+
# look like:
|
|
211
|
+
#
|
|
212
|
+
# c:/git/yolov9/models/common.py
|
|
213
|
+
#
|
|
214
|
+
# For pip imports, a module filename that should be unloaded might look like:
|
|
215
|
+
#
|
|
216
|
+
# c:/users/user/miniforge3/envs/megadetector/lib/site-packages/yolov9/utils/__init__.py
|
|
217
|
+
first_token_to_check = len(tokens) - 4
|
|
218
|
+
for i_token,token in enumerate(tokens):
|
|
219
|
+
if i_token < first_token_to_check:
|
|
220
|
+
continue
|
|
221
|
+
# Don't remove anything based on the environment name, which
|
|
222
|
+
# always follows "envs" in the path
|
|
223
|
+
if (i_token > 1) and (tokens[i_token-1] == 'envs'):
|
|
224
|
+
continue
|
|
225
|
+
if ('yolov5' in token) or ('yolov9' in token) or ('ultralytics' in token):
|
|
226
|
+
if verbose:
|
|
227
|
+
print('Module {} ({}) looks deletable'.format(module_name,module_file))
|
|
202
228
|
modules_to_delete.append(module_name)
|
|
203
229
|
break
|
|
204
|
-
except Exception:
|
|
230
|
+
except Exception as e:
|
|
231
|
+
if verbose:
|
|
232
|
+
print('Exception during module review: {}'.format(str(e)))
|
|
205
233
|
pass
|
|
206
234
|
|
|
235
|
+
# ...for each module in the global namespace
|
|
236
|
+
|
|
207
237
|
for module_name in modules_to_delete:
|
|
238
|
+
|
|
208
239
|
if module_name in sys.modules.keys():
|
|
209
|
-
module_file = module.__file__.replace('\\','/')
|
|
210
240
|
if verbose:
|
|
211
|
-
|
|
241
|
+
try:
|
|
242
|
+
module = sys.modules[module_name]
|
|
243
|
+
module_file = module.__file__.replace('\\','/')
|
|
244
|
+
print('clean_yolo_imports: deleting module {}: {}'.format(module_name,module_file))
|
|
245
|
+
except Exception:
|
|
246
|
+
pass
|
|
212
247
|
del sys.modules[module_name]
|
|
213
248
|
|
|
249
|
+
# ...for each module we want to remove from the global namespace
|
|
250
|
+
|
|
214
251
|
paths_to_delete = []
|
|
215
252
|
|
|
216
253
|
for p in sys.path:
|
|
@@ -227,7 +264,7 @@ def _clean_yolo_imports(verbose=False):
|
|
|
227
264
|
def _initialize_yolo_imports(model_type='yolov5',
|
|
228
265
|
allow_fallback_import=True,
|
|
229
266
|
force_reimport=False,
|
|
230
|
-
verbose=
|
|
267
|
+
verbose=False):
|
|
231
268
|
"""
|
|
232
269
|
Imports required functions from one or more yolo libraries (yolov5, yolov9,
|
|
233
270
|
ultralytics, targeting support for [model_type]).
|
|
@@ -246,7 +283,7 @@ def _initialize_yolo_imports(model_type='yolov5',
|
|
|
246
283
|
"""
|
|
247
284
|
|
|
248
285
|
# When running in pytest, the megadetector 'utils' module is put in the global
|
|
249
|
-
# namespace, which creates conflicts with yolov5; remove it from the global
|
|
286
|
+
# namespace, which creates conflicts with yolov5; remove it from the global
|
|
250
287
|
# namespsace.
|
|
251
288
|
if ('PYTEST_CURRENT_TEST' in os.environ):
|
|
252
289
|
print('*** pytest detected ***')
|
|
@@ -328,7 +365,7 @@ def _initialize_yolo_imports(model_type='yolov5',
|
|
|
328
365
|
|
|
329
366
|
try:
|
|
330
367
|
|
|
331
|
-
import ultralytics # noqa
|
|
368
|
+
import ultralytics # type: ignore # noqa
|
|
332
369
|
|
|
333
370
|
except Exception:
|
|
334
371
|
|
|
@@ -340,15 +377,15 @@ def _initialize_yolo_imports(model_type='yolov5',
|
|
|
340
377
|
|
|
341
378
|
try:
|
|
342
379
|
|
|
343
|
-
from ultralytics.utils.ops import non_max_suppression # noqa
|
|
344
|
-
from ultralytics.utils.ops import xyxy2xywh # noqa
|
|
380
|
+
from ultralytics.utils.ops import non_max_suppression # type: ignore # noqa
|
|
381
|
+
from ultralytics.utils.ops import xyxy2xywh # type: ignore # noqa
|
|
345
382
|
|
|
346
383
|
# In the ultralytics package, scale_boxes and scale_coords both exist;
|
|
347
384
|
# we want scale_boxes.
|
|
348
385
|
#
|
|
349
386
|
# from ultralytics.utils.ops import scale_coords # noqa
|
|
350
|
-
from ultralytics.utils.ops import scale_boxes as scale_coords # noqa
|
|
351
|
-
from ultralytics.data.augment import LetterBox
|
|
387
|
+
from ultralytics.utils.ops import scale_boxes as scale_coords # type: ignore # noqa
|
|
388
|
+
from ultralytics.data.augment import LetterBox # type: ignore # noqa
|
|
352
389
|
|
|
353
390
|
# letterbox() became a LetterBox class in the ultralytics package. Create a
|
|
354
391
|
# backwards-compatible letterbox function wrapper that wraps the class up.
|
|
@@ -373,7 +410,7 @@ def _initialize_yolo_imports(model_type='yolov5',
|
|
|
373
410
|
else:
|
|
374
411
|
letterbox_transformer = LetterBox(new_shape,auto=auto,scale_fill=scaleFill,
|
|
375
412
|
scaleup=scaleup,center=center,stride=stride)
|
|
376
|
-
|
|
413
|
+
|
|
377
414
|
letterbox_result = letterbox_transformer(image=img)
|
|
378
415
|
|
|
379
416
|
if isinstance(new_shape,int):
|
|
@@ -516,6 +553,7 @@ def read_metadata_from_megadetector_model_file(model_file,
|
|
|
516
553
|
archive from which we should read the metadata. This is not relative to the root
|
|
517
554
|
of the archive, it's relative to the one and only folder at the root of the archive
|
|
518
555
|
(this is a PyTorch convention).
|
|
556
|
+
verbose (str, optional): enable additional debug output
|
|
519
557
|
|
|
520
558
|
Returns:
|
|
521
559
|
object: whatever we read from the metadata file, always a dict in practice. Returns
|
|
@@ -575,7 +613,7 @@ class PTDetector:
|
|
|
575
613
|
|
|
576
614
|
if verbose:
|
|
577
615
|
print('Initializing PTDetector (verbose)')
|
|
578
|
-
|
|
616
|
+
|
|
579
617
|
# Set up the import environment for this model, unloading previous
|
|
580
618
|
# YOLO library versions if necessary.
|
|
581
619
|
_initialize_yolo_imports_for_model(model_path,
|
|
@@ -620,9 +658,9 @@ class PTDetector:
|
|
|
620
658
|
#: aspect ratio".
|
|
621
659
|
if model_metadata is not None and 'image_size' in model_metadata:
|
|
622
660
|
self.default_image_size = model_metadata['image_size']
|
|
623
|
-
|
|
624
|
-
print('Loaded image size {} from model metadata'.format(self.default_image_size))
|
|
661
|
+
print('Loaded image size {} from model metadata'.format(self.default_image_size))
|
|
625
662
|
else:
|
|
663
|
+
print('No image size available in model metadata, defaulting to 1280')
|
|
626
664
|
self.default_image_size = 1280
|
|
627
665
|
|
|
628
666
|
#: Either a string ('cpu','cuda:0') or a torch.device()
|
|
@@ -764,7 +802,7 @@ class PTDetector:
|
|
|
764
802
|
of the output object
|
|
765
803
|
detection_threshold (float, optional): only detections above this confidence threshold
|
|
766
804
|
will be included in the return value
|
|
767
|
-
image_size (
|
|
805
|
+
image_size (int, optional): image size to use for inference, only mess with this if
|
|
768
806
|
(a) you're using a model other than MegaDetector or (b) you know what you're getting into
|
|
769
807
|
skip_image_resizing (bool, optional): whether to skip internal image resizing (and rely on
|
|
770
808
|
external resizing), only mess with this if (a) you're using a model other than MegaDetector
|