megadetector 5.0.6__py3-none-any.whl → 5.0.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of megadetector might be problematic. Click here for more details.

Files changed (75) hide show
  1. api/batch_processing/data_preparation/manage_local_batch.py +297 -202
  2. api/batch_processing/data_preparation/manage_video_batch.py +7 -2
  3. api/batch_processing/postprocessing/add_max_conf.py +1 -0
  4. api/batch_processing/postprocessing/combine_api_outputs.py +2 -2
  5. api/batch_processing/postprocessing/compare_batch_results.py +111 -61
  6. api/batch_processing/postprocessing/convert_output_format.py +24 -6
  7. api/batch_processing/postprocessing/load_api_results.py +56 -72
  8. api/batch_processing/postprocessing/md_to_labelme.py +119 -51
  9. api/batch_processing/postprocessing/merge_detections.py +30 -5
  10. api/batch_processing/postprocessing/postprocess_batch_results.py +175 -55
  11. api/batch_processing/postprocessing/remap_detection_categories.py +163 -0
  12. api/batch_processing/postprocessing/render_detection_confusion_matrix.py +628 -0
  13. api/batch_processing/postprocessing/repeat_detection_elimination/find_repeat_detections.py +71 -23
  14. api/batch_processing/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +1 -1
  15. api/batch_processing/postprocessing/repeat_detection_elimination/repeat_detections_core.py +224 -76
  16. api/batch_processing/postprocessing/subset_json_detector_output.py +132 -5
  17. api/batch_processing/postprocessing/top_folders_to_bottom.py +1 -1
  18. classification/prepare_classification_script.py +191 -191
  19. data_management/cct_json_utils.py +7 -2
  20. data_management/coco_to_labelme.py +263 -0
  21. data_management/coco_to_yolo.py +72 -48
  22. data_management/databases/integrity_check_json_db.py +75 -64
  23. data_management/databases/subset_json_db.py +1 -1
  24. data_management/generate_crops_from_cct.py +1 -1
  25. data_management/get_image_sizes.py +44 -26
  26. data_management/importers/animl_results_to_md_results.py +3 -5
  27. data_management/importers/noaa_seals_2019.py +2 -2
  28. data_management/importers/zamba_results_to_md_results.py +2 -2
  29. data_management/labelme_to_coco.py +264 -127
  30. data_management/labelme_to_yolo.py +96 -53
  31. data_management/lila/create_lila_blank_set.py +557 -0
  32. data_management/lila/create_lila_test_set.py +2 -1
  33. data_management/lila/create_links_to_md_results_files.py +1 -1
  34. data_management/lila/download_lila_subset.py +138 -45
  35. data_management/lila/generate_lila_per_image_labels.py +23 -14
  36. data_management/lila/get_lila_annotation_counts.py +16 -10
  37. data_management/lila/lila_common.py +15 -42
  38. data_management/lila/test_lila_metadata_urls.py +116 -0
  39. data_management/read_exif.py +65 -16
  40. data_management/remap_coco_categories.py +84 -0
  41. data_management/resize_coco_dataset.py +14 -31
  42. data_management/wi_download_csv_to_coco.py +239 -0
  43. data_management/yolo_output_to_md_output.py +40 -13
  44. data_management/yolo_to_coco.py +313 -100
  45. detection/process_video.py +36 -14
  46. detection/pytorch_detector.py +1 -1
  47. detection/run_detector.py +73 -18
  48. detection/run_detector_batch.py +116 -27
  49. detection/run_inference_with_yolov5_val.py +135 -27
  50. detection/run_tiled_inference.py +153 -43
  51. detection/tf_detector.py +2 -1
  52. detection/video_utils.py +4 -2
  53. md_utils/ct_utils.py +101 -6
  54. md_utils/md_tests.py +264 -17
  55. md_utils/path_utils.py +326 -47
  56. md_utils/process_utils.py +26 -7
  57. md_utils/split_locations_into_train_val.py +215 -0
  58. md_utils/string_utils.py +10 -0
  59. md_utils/url_utils.py +66 -3
  60. md_utils/write_html_image_list.py +12 -2
  61. md_visualization/visualization_utils.py +380 -74
  62. md_visualization/visualize_db.py +41 -10
  63. md_visualization/visualize_detector_output.py +185 -104
  64. {megadetector-5.0.6.dist-info → megadetector-5.0.8.dist-info}/METADATA +11 -13
  65. {megadetector-5.0.6.dist-info → megadetector-5.0.8.dist-info}/RECORD +74 -67
  66. {megadetector-5.0.6.dist-info → megadetector-5.0.8.dist-info}/WHEEL +1 -1
  67. taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +1 -1
  68. taxonomy_mapping/map_new_lila_datasets.py +43 -39
  69. taxonomy_mapping/prepare_lila_taxonomy_release.py +5 -2
  70. taxonomy_mapping/preview_lila_taxonomy.py +27 -27
  71. taxonomy_mapping/species_lookup.py +33 -13
  72. taxonomy_mapping/taxonomy_csv_checker.py +7 -5
  73. md_visualization/visualize_megadb.py +0 -183
  74. {megadetector-5.0.6.dist-info → megadetector-5.0.8.dist-info}/LICENSE +0 -0
  75. {megadetector-5.0.6.dist-info → megadetector-5.0.8.dist-info}/top_level.txt +0 -0
md_utils/md_tests.py CHANGED
@@ -1,24 +1,17 @@
1
1
  ########
2
2
  #
3
- # md-tests.py
3
+ # md_tests.py
4
4
  #
5
5
  # A series of tests to validate basic repo functionality and verify either "correct"
6
6
  # inference behavior, or - when operating in environments other than the training
7
7
  # environment - acceptable deviation from the correct results.
8
8
  #
9
- # This module should not depend on anything else in this repo outside of the,
10
- # tests themselves, even if it means some duplicated code (e.g. for downloading files), since
11
- # much of what it tries to test is, e.g., imports.
9
+ # This module should not depend on anything else in this repo outside of the
10
+ # tests themselves, even if it means some duplicated code (e.g. for downloading files),
11
+ # since much of what it tries to test is, e.g., imports.
12
12
  #
13
13
  ########
14
14
 
15
- #%% TODO
16
-
17
- # Video tests
18
- # Augmented inference tests
19
- # Checkpoint tests
20
-
21
-
22
15
  #%% Imports and constants
23
16
 
24
17
  ### Only standard imports belong here, not MD-specific imports ###
@@ -54,6 +47,7 @@ class MDTestOptions:
54
47
  max_coord_error = 0.001
55
48
  max_conf_error = 0.005
56
49
  cli_working_dir = None
50
+ yolo_working_folder = None
57
51
 
58
52
 
59
53
  #%% Support functions
@@ -92,11 +86,14 @@ def get_expected_results_filename(gpu_is_available):
92
86
  return 'md-test-results-{}-{}.json'.format(hw_string,pt_string)
93
87
 
94
88
 
95
- def download_test_data(options):
89
+ def download_test_data(options=None):
96
90
  """
97
91
  Download the test zipfile if necessary, unzip if necessary.
98
92
  """
99
-
93
+
94
+ if options is None:
95
+ options = MDTestOptions()
96
+
100
97
  if options.scratch_dir is None:
101
98
  tempdir_base = tempfile.gettempdir()
102
99
  scratch_dir = os.path.join(tempdir_base,'md-tests')
@@ -119,9 +116,9 @@ def download_test_data(options):
119
116
  if download_zipfile:
120
117
  print('Downloading test data zipfile')
121
118
  urllib.request.urlretrieve(options.test_data_url, local_zipfile)
122
- print('Finished download')
119
+ print('Finished download to {}'.format(local_zipfile))
123
120
  else:
124
- print('Bypassing test data zipfile download')
121
+ print('Bypassing test data zipfile download for {}'.format(local_zipfile))
125
122
 
126
123
 
127
124
  ## Unzip data
@@ -164,7 +161,10 @@ def download_test_data(options):
164
161
  options.all_test_files = test_files
165
162
  options.test_images = [fn for fn in test_files if os.path.splitext(fn.lower())[1] in ('.jpg','.jpeg','.png')]
166
163
  options.test_videos = [fn for fn in test_files if os.path.splitext(fn.lower())[1] in ('.mp4','.avi')]
164
+ options.test_videos = [fn for fn in options.test_videos if 'rendered' not in fn]
167
165
 
166
+ print('Finished unzipping and enumerating test data')
167
+
168
168
  # ...def download_test_data(...)
169
169
 
170
170
 
@@ -247,6 +247,8 @@ def execute_and_print(cmd,print_output=True):
247
247
 
248
248
  def run_python_tests(options):
249
249
 
250
+ print('\n*** Starting module tests ***\n')
251
+
250
252
  ## Prepare data
251
253
 
252
254
  download_test_data(options)
@@ -393,7 +395,84 @@ def run_python_tests(options):
393
395
  assert os.path.isfile(rde_results.filterFile),\
394
396
  'Could not find RDE output file {}'.format(rde_results.filterFile)
395
397
 
396
- print('Finished running Python tests')
398
+
399
+ # TODO: add remove_repeat_detections test here
400
+ #
401
+ # It's already tested in the CLI tests, so this is not urgent.
402
+
403
+
404
+ ## Video test (single video)
405
+
406
+ from detection.process_video import ProcessVideoOptions, process_video
407
+
408
+ video_options = ProcessVideoOptions()
409
+ video_options.model_file = 'MDV5A'
410
+ video_options.input_video_file = os.path.join(options.scratch_dir,options.test_videos[0])
411
+ video_options.output_json_file = os.path.join(options.scratch_dir,'single_video_output.json')
412
+ video_options.output_video_file = os.path.join(options.scratch_dir,'video_scratch/rendered_video.mp4')
413
+ video_options.frame_folder = os.path.join(options.scratch_dir,'video_scratch/frame_folder')
414
+ video_options.frame_rendering_folder = os.path.join(options.scratch_dir,'video_scratch/rendered_frame_folder')
415
+ video_options.render_output_video = True
416
+ # video_options.keep_rendered_frames = False
417
+ # video_options.keep_rendered_frames = False
418
+ video_options.force_extracted_frame_folder_deletion = True
419
+ video_options.force_rendered_frame_folder_deletion = True
420
+ # video_options.reuse_results_if_available = False
421
+ # video_options.reuse_frames_if_available = False
422
+ video_options.recursive = True
423
+ video_options.verbose = False
424
+ video_options.fourcc = 'mp4v'
425
+ # video_options.rendering_confidence_threshold = None
426
+ # video_options.json_confidence_threshold = 0.005
427
+ video_options.frame_sample = 5
428
+ video_options.n_cores = 5
429
+ # video_options.debug_max_frames = -1
430
+ # video_options.class_mapping_filename = None
431
+
432
+ _ = process_video(video_options)
433
+
434
+ assert os.path.isfile(video_options.output_video_file), \
435
+ 'Python video test failed to render output video file'
436
+ assert os.path.isfile(video_options.output_json_file), \
437
+ 'Python video test failed to render output .json file'
438
+
439
+
440
+ ## Video test (folder)
441
+
442
+ from detection.process_video import ProcessVideoOptions, process_video_folder
443
+
444
+ video_options = ProcessVideoOptions()
445
+ video_options.model_file = 'MDV5A'
446
+ video_options.input_video_file = os.path.join(options.scratch_dir,
447
+ os.path.dirname(options.test_videos[0]))
448
+ video_options.output_json_file = os.path.join(options.scratch_dir,'video_folder_output.json')
449
+ # video_options.output_video_file = None
450
+ video_options.frame_folder = os.path.join(options.scratch_dir,'video_scratch/frame_folder')
451
+ video_options.frame_rendering_folder = os.path.join(options.scratch_dir,'video_scratch/rendered_frame_folder')
452
+ video_options.render_output_video = False
453
+ # video_options.keep_rendered_frames = False
454
+ # video_options.keep_rendered_frames = False
455
+ video_options.force_extracted_frame_folder_deletion = True
456
+ video_options.force_rendered_frame_folder_deletion = True
457
+ # video_options.reuse_results_if_available = False
458
+ # video_options.reuse_frames_if_available = False
459
+ video_options.recursive = True
460
+ video_options.verbose = False
461
+ # video_options.fourcc = None
462
+ # video_options.rendering_confidence_threshold = None
463
+ # video_options.json_confidence_threshold = 0.005
464
+ video_options.frame_sample = 5
465
+ video_options.n_cores = 5
466
+ # video_options.debug_max_frames = -1
467
+ # video_options.class_mapping_filename = None
468
+
469
+ _ = process_video_folder(video_options)
470
+
471
+ assert os.path.isfile(video_options.output_json_file), \
472
+ 'Python video test failed to render output .json file'
473
+
474
+
475
+ print('\n*** Finished module tests ***\n')
397
476
 
398
477
  # ...def run_python_tests(...)
399
478
 
@@ -402,6 +481,8 @@ def run_python_tests(options):
402
481
 
403
482
  def run_cli_tests(options):
404
483
 
484
+ print('\n*** Starting CLI tests ***\n')
485
+
405
486
  ## chdir if necessary
406
487
 
407
488
  if options.cli_working_dir is not None:
@@ -473,6 +554,158 @@ def run_cli_tests(options):
473
554
  print('Running: {}'.format(cmd))
474
555
  cmd_results = execute_and_print(cmd)
475
556
 
557
+
558
+ ## RDE
559
+
560
+ rde_output_dir = os.path.join(options.scratch_dir,'rde_output_cli')
561
+
562
+ if options.cli_working_dir is None:
563
+ cmd = 'python -m api.batch_processing.postprocessing.repeat_detection_elimination.find_repeat_detections'
564
+ else:
565
+ cmd = 'python api/batch_processing/postprocessing/repeat_detection_elimination/find_repeat_detections.py'
566
+ cmd += ' {}'.format(inference_output_file)
567
+ cmd += ' --imageBase {}'.format(image_folder)
568
+ cmd += ' --outputBase {}'.format(rde_output_dir)
569
+ cmd += ' --occurrenceThreshold 1' # Use an absurd number here to make sure we get some suspicious detections
570
+ print('Running: {}'.format(cmd))
571
+ cmd_results = execute_and_print(cmd)
572
+
573
+ # Find the latest filtering folder
574
+ filtering_output_dir = os.listdir(rde_output_dir)
575
+ filtering_output_dir = [fn for fn in filtering_output_dir if fn.startswith('filtering_')]
576
+ filtering_output_dir = [os.path.join(rde_output_dir,fn) for fn in filtering_output_dir]
577
+ filtering_output_dir = [fn for fn in filtering_output_dir if os.path.isdir(fn)]
578
+ filtering_output_dir = sorted(filtering_output_dir)[-1]
579
+
580
+ print('Using RDE filtering folder {}'.format(filtering_output_dir))
581
+
582
+ filtered_output_file = inference_output_file.replace('.json','_filtered.json')
583
+
584
+ if options.cli_working_dir is None:
585
+ cmd = 'python -m api.batch_processing.postprocessing.repeat_detection_elimination.remove_repeat_detections'
586
+ else:
587
+ cmd = 'python api/batch_processing/postprocessing/repeat_detection_elimination/remove_repeat_detections.py'
588
+ cmd += ' {} {} {}'.format(inference_output_file,filtered_output_file,filtering_output_dir)
589
+ print('Running: {}'.format(cmd))
590
+ cmd_results = execute_and_print(cmd)
591
+
592
+ assert os.path.isfile(filtered_output_file), \
593
+ 'Could not find RDE output file {}'.format(filtered_output_file)
594
+
595
+
596
+ ## Run inference on a folder (tiled)
597
+
598
+ image_folder = os.path.join(options.scratch_dir,'md-test-images')
599
+ tiling_folder = os.path.join(options.scratch_dir,'tiling-folder')
600
+ inference_output_file_tiled = os.path.join(options.scratch_dir,'folder_inference_output_tiled.json')
601
+ if options.cli_working_dir is None:
602
+ cmd = 'python -m detection.run_tiled_inference'
603
+ else:
604
+ cmd = 'python detection/run_tiled_inference.py'
605
+ cmd += ' {} {} {} {}'.format(
606
+ model_file,image_folder,tiling_folder,inference_output_file_tiled)
607
+ cmd += ' --overwrite_handling overwrite'
608
+ print('Running: {}'.format(cmd))
609
+ cmd_results = execute_and_print(cmd)
610
+
611
+ with open(inference_output_file_tiled,'r') as f:
612
+ results_from_file = json.load(f) # noqa
613
+
614
+
615
+ ## Run inference on a folder (augmented)
616
+
617
+ if options.yolo_working_folder is None:
618
+
619
+ print('Bypassing YOLOv5 val tests, no yolo folder supplied')
620
+
621
+ else:
622
+
623
+ image_folder = os.path.join(options.scratch_dir,'md-test-images')
624
+ yolo_results_folder = os.path.join(options.scratch_dir,'yolo-output-folder')
625
+ yolo_symlink_folder = os.path.join(options.scratch_dir,'yolo-symlink_folder')
626
+ inference_output_file_yolo_val = os.path.join(options.scratch_dir,'folder_inference_output_yolo_val.json')
627
+ if options.cli_working_dir is None:
628
+ cmd = 'python -m detection.run_inference_with_yolov5_val'
629
+ else:
630
+ cmd = 'python detection/run_inference_with_yolov5_val.py'
631
+ cmd += ' {} {} {}'.format(
632
+ model_file,image_folder,inference_output_file_yolo_val)
633
+ cmd += ' --yolo_working_folder {}'.format(options.yolo_working_folder)
634
+ cmd += ' --yolo_results_folder {}'.format(yolo_results_folder)
635
+ cmd += ' --symlink_folder {}'.format(yolo_symlink_folder)
636
+ cmd += ' --augment_enabled 1'
637
+ # cmd += ' --no_use_symlinks'
638
+ cmd += ' --overwrite_handling overwrite'
639
+ print('Running: {}'.format(cmd))
640
+ cmd_results = execute_and_print(cmd)
641
+
642
+ with open(inference_output_file_yolo_val,'r') as f:
643
+ results_from_file = json.load(f) # noqa
644
+
645
+
646
+ ## Video test
647
+
648
+ model_file = 'MDV5A'
649
+ video_inference_output_file = os.path.join(options.scratch_dir,'video_inference_output.json')
650
+ output_video_file = os.path.join(options.scratch_dir,'video_scratch/cli_rendered_video.mp4')
651
+ frame_folder = os.path.join(options.scratch_dir,'video_scratch/frame_folder_cli')
652
+ frame_rendering_folder = os.path.join(options.scratch_dir,'video_scratch/rendered_frame_folder_cli')
653
+
654
+ video_fn = os.path.join(options.scratch_dir,options.test_videos[-1])
655
+ output_dir = os.path.join(options.scratch_dir,'single_video_test_cli')
656
+ if options.cli_working_dir is None:
657
+ cmd = 'python -m detection.process_video'
658
+ else:
659
+ cmd = 'python detection/process_video.py'
660
+ cmd += ' {} {}'.format(model_file,video_fn)
661
+ cmd += ' --frame_folder {} --frame_rendering_folder {} --output_json_file {} --output_video_file {}'.format(
662
+ frame_folder,frame_rendering_folder,video_inference_output_file,output_video_file)
663
+ cmd += ' --render_output_video --fourcc mp4v'
664
+ cmd += ' --force_extracted_frame_folder_deletion --force_rendered_frame_folder_deletion --n_cores 5 --frame_sample 3'
665
+ print('Running: {}'.format(cmd))
666
+ cmd_results = execute_and_print(cmd)
667
+
668
+
669
+ ## Run inference on a folder (again, so we can do a comparison)
670
+
671
+ image_folder = os.path.join(options.scratch_dir,'md-test-images')
672
+ model_file = 'MDV5B'
673
+ inference_output_file_alt = os.path.join(options.scratch_dir,'folder_inference_output_alt.json')
674
+ if options.cli_working_dir is None:
675
+ cmd = 'python -m detection.run_detector_batch'
676
+ else:
677
+ cmd = 'python detection/run_detector_batch.py'
678
+ cmd += ' {} {} {} --recursive'.format(
679
+ model_file,image_folder,inference_output_file_alt)
680
+ cmd += ' --output_relative_filenames --quiet --include_image_size'
681
+ cmd += ' --include_image_timestamp --include_exif_data'
682
+ print('Running: {}'.format(cmd))
683
+ cmd_results = execute_and_print(cmd)
684
+
685
+ with open(inference_output_file_alt,'r') as f:
686
+ results_from_file = json.load(f) # noqa
687
+
688
+
689
+ ## Compare the two files
690
+
691
+ comparison_output_folder = os.path.join(options.scratch_dir,'results_comparison')
692
+ image_folder = os.path.join(options.scratch_dir,'md-test-images')
693
+ results_files_string = '"{}" "{}"'.format(
694
+ inference_output_file,inference_output_file_alt)
695
+ if options.cli_working_dir is None:
696
+ cmd = 'python -m api.batch_processing.postprocessing.compare_batch_results'
697
+ else:
698
+ cmd = 'python api/batch_processing/postprocessing/compare_batch_results.py'
699
+ cmd += ' {} {} {}'.format(comparison_output_folder,image_folder,results_files_string)
700
+ print('Running: {}'.format(cmd))
701
+ cmd_results = execute_and_print(cmd)
702
+
703
+ assert cmd_results['status'] == 0, 'Error generating comparison HTML'
704
+ assert os.path.isfile(os.path.join(comparison_output_folder,'index.html')), \
705
+ 'Failed to generate comparison HTML'
706
+
707
+ print('\n*** Finished CLI tests ***\n')
708
+
476
709
  # ...def run_cli_tests(...)
477
710
 
478
711
 
@@ -518,9 +751,19 @@ if False:
518
751
 
519
752
  options.disable_gpu = False
520
753
  options.cpu_execution_is_error = False
521
- options.disable_video_tests = False
754
+ options.skip_video_tests = False
755
+ options.skip_python_tests = False
756
+ options.skip_cli_tests = False
522
757
  options.scratch_dir = None
758
+ options.test_data_url = 'https://lila.science/public/md-test-package.zip'
759
+ options.force_data_download = False
760
+ options.force_data_unzip = False
761
+ options.warning_mode = True
762
+ options.test_image_subdir = 'md-test-images'
763
+ options.max_coord_error = 0.001
764
+ options.max_conf_error = 0.005
523
765
  options.cli_working_dir = r'c:\git\MegaDetector'
766
+ options.yolo_working_folder = r'c:\git\yolov5'
524
767
 
525
768
 
526
769
  #%%
@@ -602,6 +845,10 @@ def main():
602
845
  type=str,
603
846
  default=None,
604
847
  help='Working directory for CLI tests')
848
+
849
+ # token used for linting
850
+ #
851
+ # no_arguments_required
605
852
 
606
853
  args = parser.parse_args()
607
854