megadetector 10.0.1__py3-none-any.whl → 10.0.3__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.

@@ -53,12 +53,15 @@ class MDTestOptions:
53
53
  #: Skip tests related to video processing
54
54
  self.skip_video_tests = False
55
55
 
56
- #: Skip tests related to video rendering
57
- self.skip_video_rendering_tests = False
56
+ #: Skip tests related to still image processing
57
+ self.skip_image_tests = False
58
58
 
59
59
  #: Skip tests launched via Python functions (as opposed to CLIs)
60
60
  self.skip_python_tests = False
61
61
 
62
+ #: Skip module import tests
63
+ self.skip_import_tests = False
64
+
62
65
  #: Skip CLI tests
63
66
  self.skip_cli_tests = False
64
67
 
@@ -105,9 +108,6 @@ class MDTestOptions:
105
108
  #: If this is None, we'll skip that test.
106
109
  self.yolo_working_dir = None
107
110
 
108
- #: fourcc code to use for video tests that involve rendering video
109
- self.video_fourcc = 'mp4v'
110
-
111
111
  #: Default model to use for testing (filename, URL, or well-known model string)
112
112
  self.default_model = 'MDV5A'
113
113
 
@@ -144,6 +144,9 @@ class MDTestOptions:
144
144
  #: Number of cores to use for multi-CPU video tests
145
145
  self.n_cores_for_video_tests = 2
146
146
 
147
+ #: Batch size to use when testing batches of size > 1
148
+ self.alternative_batch_size = 3
149
+
147
150
  # ...def __init__()
148
151
 
149
152
  # ...class MDTestOptions()
@@ -169,7 +172,7 @@ def get_expected_results_filename(gpu_is_available,
169
172
  model_string (str, optional): the model for which we're retrieving expected results
170
173
  test_type (str, optional): the test type we're running ("image" or "video")
171
174
  augment (bool, optional): whether we're running this test with image augmentation
172
- options (MDTestOptiosn, optional): additional control flow options
175
+ options (MDTestOptions, optional): additional control flow options
173
176
 
174
177
  Returns:
175
178
  str: relative filename of the results file we should use (within the test
@@ -205,12 +208,11 @@ def get_expected_results_filename(gpu_is_available,
205
208
  if augment:
206
209
  aug_string = 'augment-'
207
210
 
208
- fn = '{}-{}{}-{}-{}.json'.format(model_string,aug_string,test_type,hw_string,pt_string)
209
-
210
- from megadetector.utils.path_utils import insert_before_extension
211
-
212
- if test_type == 'video':
213
- fn = insert_before_extension(fn,'frames')
211
+ # We only have a single set of video results
212
+ if test_type == 'image':
213
+ fn = '{}-{}{}-{}-{}.json'.format(model_string,aug_string,test_type,hw_string,pt_string)
214
+ else:
215
+ fn = '{}-{}.json'.format(model_string,test_type)
214
216
 
215
217
  if options is not None and options.scratch_dir is not None:
216
218
  fn = os.path.join(options.scratch_dir,fn)
@@ -306,7 +308,6 @@ def download_test_data(options=None):
306
308
  options.all_test_files = test_files
307
309
  options.test_images = [fn for fn in test_files if os.path.splitext(fn.lower())[1] in ('.jpg','.jpeg','.png')]
308
310
  options.test_videos = [fn for fn in test_files if os.path.splitext(fn.lower())[1] in ('.mp4','.avi')]
309
- options.test_videos = [fn for fn in options.test_videos if 'rendered' not in fn]
310
311
  options.test_videos = [fn for fn in options.test_videos if \
311
312
  os.path.isfile(os.path.join(scratch_dir,fn))]
312
313
 
@@ -401,7 +402,9 @@ def output_files_are_identical(fn1,fn2,verbose=False):
401
402
 
402
403
  if fn1_image['file'] != fn2_image['file']:
403
404
  if verbose:
404
- print('Filename difference at {}: {} vs {} '.format(i_image,fn1_image['file'],fn1_image['file']))
405
+ print('Filename difference at {}: {} vs {} '.format(i_image,
406
+ fn1_image['file'],
407
+ fn2_image['file']))
405
408
  return False
406
409
 
407
410
  if fn1_image != fn2_image:
@@ -430,6 +433,7 @@ def compare_detection_lists(detections_a,detections_b,options,bidirectional_comp
430
433
  Returns:
431
434
  dict: a dictionary with keys 'max_conf_error' and 'max_coord_error'.
432
435
  """
436
+
433
437
  from megadetector.utils.ct_utils import get_iou
434
438
 
435
439
  max_conf_error = 0
@@ -564,7 +568,9 @@ def compare_results(inference_output_file,
564
568
  filename_to_results_expected = {im['file'].replace('\\','/'):im for im in expected_results['images']}
565
569
 
566
570
  assert len(filename_to_results) == len(filename_to_results_expected), \
567
- 'Error: expected {} files in results, found {}'.format(
571
+ 'Error: comparing expected file {} to actual file {}, expected {} files in results, found {}'.format(
572
+ expected_results_file,
573
+ inference_output_file,
568
574
  len(filename_to_results_expected),
569
575
  len(filename_to_results))
570
576
 
@@ -583,9 +589,16 @@ def compare_results(inference_output_file,
583
589
  expected_image_results = filename_to_results_expected[fn]
584
590
 
585
591
  if 'failure' in actual_image_results:
592
+ # We allow some variation in how failures are represented
586
593
  assert 'failure' in expected_image_results and \
587
- 'detections' not in actual_image_results and \
588
- 'detections' not in expected_image_results
594
+ (
595
+ ('detections' not in actual_image_results) or \
596
+ (actual_image_results['detections'] is None)
597
+ ) and \
598
+ (
599
+ ('detections' not in expected_image_results) or \
600
+ (expected_image_results['detections'] is None)
601
+ )
589
602
  continue
590
603
  assert 'failure' not in expected_image_results
591
604
 
@@ -792,12 +805,14 @@ def run_python_tests(options):
792
805
 
793
806
  ## Import tests
794
807
 
795
- print('\n** Running package import tests **\n')
796
- test_package_imports('megadetector.visualization')
797
- test_package_imports('megadetector.postprocessing')
798
- test_package_imports('megadetector.postprocessing.repeat_detection_elimination')
799
- test_package_imports('megadetector.utils',exceptions=['md_tests'])
800
- test_package_imports('megadetector.data_management',exceptions=['lila','ocr_tools'])
808
+ if not options.skip_import_tests:
809
+
810
+ print('\n** Running package import tests **\n')
811
+ test_package_imports('megadetector.visualization')
812
+ test_package_imports('megadetector.postprocessing')
813
+ test_package_imports('megadetector.postprocessing.repeat_detection_elimination')
814
+ test_package_imports('megadetector.utils',exceptions=['md_tests'])
815
+ test_package_imports('megadetector.data_management',exceptions=['lila','ocr_tools'])
801
816
 
802
817
 
803
818
  ## Return early if we're not running torch-related tests
@@ -812,236 +827,246 @@ def run_python_tests(options):
812
827
  pytorch_detector.require_non_default_compatibility_mode = True
813
828
 
814
829
 
815
- ## Run inference on an image
830
+ if not options.skip_image_tests:
816
831
 
817
- print('\n** Running MD on a single image (module) **\n')
832
+ from megadetector.utils import path_utils # noqa
833
+ image_folder = os.path.join(options.scratch_dir,'md-test-images')
834
+ assert os.path.isdir(image_folder), 'Test image folder {} is not available'.format(image_folder)
835
+ inference_output_file = os.path.join(options.scratch_dir,'folder_inference_output.json')
836
+ image_file_names = path_utils.find_images(image_folder,recursive=True)
818
837
 
819
- from megadetector.detection import run_detector
820
- from megadetector.visualization import visualization_utils as vis_utils # noqa
821
- image_fn = os.path.join(options.scratch_dir,options.test_images[0])
822
- model = run_detector.load_detector(options.default_model,
823
- detector_options=copy(options.detector_options))
824
- pil_im = vis_utils.load_image(image_fn)
825
- result = model.generate_detections_one_image(pil_im) # noqa
826
838
 
827
- if options.python_test_depth <= 1:
828
- return
839
+ ## Run inference on an image
829
840
 
841
+ print('\n** Running MD on a single image (module) **\n')
830
842
 
831
- ## Run inference on a folder
843
+ from megadetector.detection import run_detector
844
+ from megadetector.visualization import visualization_utils as vis_utils # noqa
845
+ image_fn = os.path.join(options.scratch_dir,options.test_images[0])
846
+ model = run_detector.load_detector(options.default_model,
847
+ detector_options=copy(options.detector_options))
848
+ pil_im = vis_utils.load_image(image_fn)
849
+ result = model.generate_detections_one_image(pil_im) # noqa
832
850
 
833
- print('\n** Running MD on a folder of images (module) **\n')
851
+ if options.python_test_depth <= 1:
852
+ return
834
853
 
835
- from megadetector.detection.run_detector_batch import load_and_run_detector_batch,write_results_to_file
836
- from megadetector.utils import path_utils # noqa
837
854
 
838
- image_folder = os.path.join(options.scratch_dir,'md-test-images')
839
- assert os.path.isdir(image_folder), 'Test image folder {} is not available'.format(image_folder)
840
- inference_output_file = os.path.join(options.scratch_dir,'folder_inference_output.json')
841
- image_file_names = path_utils.find_images(image_folder,recursive=True)
842
- results = load_and_run_detector_batch(options.default_model,
843
- image_file_names,
844
- quiet=True,
845
- detector_options=copy(options.detector_options))
846
- _ = write_results_to_file(results,
847
- inference_output_file,
848
- relative_path_base=image_folder,
849
- detector_file=options.default_model)
855
+ ## Run inference on a folder
850
856
 
851
- ## Verify results
857
+ print('\n** Running MD on a folder of images (module) **\n')
852
858
 
853
- # Verify format correctness
854
- from megadetector.postprocessing.validate_batch_results import validate_batch_results #noqa
855
- validate_batch_results(inference_output_file)
859
+ from megadetector.detection.run_detector_batch import load_and_run_detector_batch,write_results_to_file
856
860
 
857
- # Verify value correctness
858
- expected_results_file = get_expected_results_filename(is_gpu_available(verbose=False),
859
- options=options)
860
- compare_results(inference_output_file,expected_results_file,options)
861
+ results = load_and_run_detector_batch(options.default_model,
862
+ image_file_names,
863
+ quiet=True,
864
+ detector_options=copy(options.detector_options))
865
+ _ = write_results_to_file(results,
866
+ inference_output_file,
867
+ relative_path_base=image_folder,
868
+ detector_file=options.default_model)
861
869
 
870
+ ## Verify results
862
871
 
863
- # Make note of this filename, we will use it again later
864
- inference_output_file_standard_inference = inference_output_file
872
+ # Verify format correctness
873
+ from megadetector.postprocessing.validate_batch_results import validate_batch_results #noqa
874
+ validate_batch_results(inference_output_file)
865
875
 
866
- if options.python_test_depth <= 2:
867
- return
876
+ # Verify value correctness
877
+ expected_results_file = get_expected_results_filename(is_gpu_available(verbose=False),
878
+ options=options)
879
+ compare_results(inference_output_file,expected_results_file,options)
868
880
 
869
881
 
870
- ## Run and verify again with augmentation enabled
882
+ # Make note of this filename, we will use it again later
883
+ inference_output_file_standard_inference = inference_output_file
871
884
 
872
- print('\n** Running MD on images with augmentation (module) **\n')
885
+ if options.python_test_depth <= 2:
886
+ return
873
887
 
874
- from megadetector.utils.path_utils import insert_before_extension
875
888
 
876
- inference_output_file_augmented = insert_before_extension(inference_output_file,'augmented')
877
- results = load_and_run_detector_batch(options.default_model,
878
- image_file_names,
879
- quiet=True,
880
- augment=True,
881
- detector_options=copy(options.detector_options))
882
- _ = write_results_to_file(results,
883
- inference_output_file_augmented,
884
- relative_path_base=image_folder,
885
- detector_file=options.default_model)
889
+ ## Run again with a batch size > 1
890
+
891
+ print('\n** Running MD on a folder of images with batch size > 1 (module) **\n')
886
892
 
887
- expected_results_file_augmented = \
888
- get_expected_results_filename(is_gpu_available(verbose=False),
889
- augment=True,options=options)
890
- compare_results(inference_output_file_augmented,expected_results_file_augmented,options)
893
+ from megadetector.detection.run_detector_batch import load_and_run_detector_batch,write_results_to_file
894
+ from megadetector.utils.path_utils import insert_before_extension
891
895
 
896
+ inference_output_file_batch = insert_before_extension(inference_output_file,'batch')
897
+ from megadetector.detection import run_detector_batch
898
+ run_detector_batch.verbose = True
899
+ results = load_and_run_detector_batch(options.default_model,
900
+ image_file_names,
901
+ quiet=True,
902
+ batch_size=options.alternative_batch_size,
903
+ detector_options=copy(options.detector_options))
904
+ run_detector_batch.verbose = False
905
+ _ = write_results_to_file(results,
906
+ inference_output_file_batch,
907
+ relative_path_base=image_folder,
908
+ detector_file=options.default_model)
892
909
 
893
- ## Postprocess results
910
+ expected_results_file = get_expected_results_filename(is_gpu_available(verbose=False),
911
+ options=options)
912
+ compare_results(inference_output_file_batch,expected_results_file,options)
894
913
 
895
- print('\n** Post-processing results (module) **\n')
914
+ ## Run and verify again with augmentation enabled
896
915
 
897
- from megadetector.postprocessing.postprocess_batch_results import \
898
- PostProcessingOptions,process_batch_results
899
- postprocessing_options = PostProcessingOptions()
916
+ print('\n** Running MD on images with augmentation (module) **\n')
900
917
 
901
- postprocessing_options.md_results_file = inference_output_file
902
- postprocessing_options.output_dir = os.path.join(options.scratch_dir,'postprocessing_output')
903
- postprocessing_options.image_base_dir = image_folder
918
+ inference_output_file_augmented = insert_before_extension(inference_output_file,'augmented')
919
+ results = load_and_run_detector_batch(options.default_model,
920
+ image_file_names,
921
+ quiet=True,
922
+ augment=True,
923
+ detector_options=copy(options.detector_options))
924
+ _ = write_results_to_file(results,
925
+ inference_output_file_augmented,
926
+ relative_path_base=image_folder,
927
+ detector_file=options.default_model)
904
928
 
905
- postprocessing_results = process_batch_results(postprocessing_options)
906
- assert os.path.isfile(postprocessing_results.output_html_file), \
907
- 'Postprocessing output file {} not found'.format(postprocessing_results.output_html_file)
929
+ expected_results_file_augmented = \
930
+ get_expected_results_filename(is_gpu_available(verbose=False),
931
+ augment=True,options=options)
932
+ compare_results(inference_output_file_augmented,expected_results_file_augmented,options)
908
933
 
909
934
 
910
- ## Partial RDE test
935
+ ## Postprocess results
911
936
 
912
- print('\n** Testing RDE (module) **\n')
937
+ print('\n** Post-processing results (module) **\n')
913
938
 
914
- from megadetector.postprocessing.repeat_detection_elimination.repeat_detections_core import \
915
- RepeatDetectionOptions, find_repeat_detections
939
+ from megadetector.postprocessing.postprocess_batch_results import \
940
+ PostProcessingOptions,process_batch_results
941
+ postprocessing_options = PostProcessingOptions()
916
942
 
917
- rde_options = RepeatDetectionOptions()
918
- rde_options.occurrenceThreshold = 2
919
- rde_options.confidenceMin = 0.001
920
- rde_options.outputBase = os.path.join(options.scratch_dir,'rde_working_dir')
921
- rde_options.imageBase = image_folder
922
- rde_output_file = inference_output_file.replace('.json','_filtered.json')
923
- assert rde_output_file != inference_output_file
924
- rde_results = find_repeat_detections(inference_output_file, rde_output_file, rde_options)
925
- assert os.path.isfile(rde_results.filterFile),\
926
- 'Could not find RDE output file {}'.format(rde_results.filterFile)
943
+ postprocessing_options.md_results_file = inference_output_file
944
+ postprocessing_options.output_dir = os.path.join(options.scratch_dir,'postprocessing_output')
945
+ postprocessing_options.image_base_dir = image_folder
927
946
 
947
+ postprocessing_results = process_batch_results(postprocessing_options)
948
+ assert os.path.isfile(postprocessing_results.output_html_file), \
949
+ 'Postprocessing output file {} not found'.format(postprocessing_results.output_html_file)
928
950
 
929
- ## Run inference on a folder (with YOLOv5 val script)
930
951
 
931
- if options.yolo_working_dir is None:
952
+ ## Partial RDE test
932
953
 
933
- print('Skipping YOLO val inference tests, no YOLO folder supplied')
954
+ print('\n** Testing RDE (module) **\n')
934
955
 
935
- else:
956
+ from megadetector.postprocessing.repeat_detection_elimination.repeat_detections_core import \
957
+ RepeatDetectionOptions, find_repeat_detections
936
958
 
937
- print('\n** Running YOLO val inference test (module) **\n')
959
+ rde_options = RepeatDetectionOptions()
960
+ rde_options.occurrenceThreshold = 2
961
+ rde_options.confidenceMin = 0.001
962
+ rde_options.outputBase = os.path.join(options.scratch_dir,'rde_working_dir')
963
+ rde_options.imageBase = image_folder
964
+ rde_output_file = inference_output_file.replace('.json','_filtered.json')
965
+ assert rde_output_file != inference_output_file
966
+ rde_results = find_repeat_detections(inference_output_file, rde_output_file, rde_options)
967
+ assert os.path.isfile(rde_results.filterFile),\
968
+ 'Could not find RDE output file {}'.format(rde_results.filterFile)
938
969
 
939
- from megadetector.detection.run_inference_with_yolov5_val import \
940
- YoloInferenceOptions, run_inference_with_yolo_val
941
- from megadetector.utils.path_utils import insert_before_extension
942
970
 
943
- inference_output_file_yolo_val = os.path.join(options.scratch_dir,'folder_inference_output_yolo_val.json')
971
+ ## Run inference on a folder (with YOLOv5 val script)
972
+
973
+ if options.yolo_working_dir is None:
974
+
975
+ print('Skipping YOLO val inference tests, no YOLO folder supplied')
976
+
977
+ else:
978
+
979
+ print('\n** Running YOLO val inference test (module) **\n')
944
980
 
945
- yolo_inference_options = YoloInferenceOptions()
946
- yolo_inference_options.input_folder = os.path.join(options.scratch_dir,'md-test-images')
947
- yolo_inference_options.output_file = inference_output_file_yolo_val
948
- yolo_inference_options.yolo_working_folder = options.yolo_working_dir
949
- yolo_inference_options.model_filename = options.default_model
950
- yolo_inference_options.augment = False
951
- yolo_inference_options.overwrite_handling = 'overwrite'
952
- from megadetector.detection.run_detector import DEFAULT_OUTPUT_CONFIDENCE_THRESHOLD
953
- yolo_inference_options.conf_thres = DEFAULT_OUTPUT_CONFIDENCE_THRESHOLD
981
+ from megadetector.detection.run_inference_with_yolov5_val import \
982
+ YoloInferenceOptions, run_inference_with_yolo_val
983
+ from megadetector.utils.path_utils import insert_before_extension
954
984
 
955
- run_inference_with_yolo_val(yolo_inference_options)
985
+ inference_output_file_yolo_val = os.path.join(options.scratch_dir,'folder_inference_output_yolo_val.json')
956
986
 
957
- ## Confirm this matches the standard inference path
987
+ yolo_inference_options = YoloInferenceOptions()
988
+ yolo_inference_options.input_folder = os.path.join(options.scratch_dir,'md-test-images')
989
+ yolo_inference_options.output_file = inference_output_file_yolo_val
990
+ yolo_inference_options.yolo_working_folder = options.yolo_working_dir
991
+ yolo_inference_options.model_filename = options.default_model
992
+ yolo_inference_options.augment = False
993
+ yolo_inference_options.overwrite_handling = 'overwrite'
994
+ from megadetector.detection.run_detector import DEFAULT_OUTPUT_CONFIDENCE_THRESHOLD
995
+ yolo_inference_options.conf_thres = DEFAULT_OUTPUT_CONFIDENCE_THRESHOLD
958
996
 
959
- if False:
960
- # TODO: compare_results() isn't quite ready for this yet
961
- compare_results(inference_output_file=inference_output_file_yolo_val,
962
- expected_results_file=inference_output_file_standard_inference,
963
- options=options)
997
+ run_inference_with_yolo_val(yolo_inference_options)
964
998
 
965
- # Run again, without symlinks this time
999
+ ## Confirm this matches the standard inference path
966
1000
 
967
- inference_output_file_yolo_val_no_links = insert_before_extension(inference_output_file_yolo_val,
968
- 'no-links')
969
- yolo_inference_options.output_file = inference_output_file_yolo_val_no_links
970
- yolo_inference_options.use_symlinks = False
971
- run_inference_with_yolo_val(yolo_inference_options)
1001
+ if False:
1002
+ # TODO: compare_results() isn't quite ready for this yet
1003
+ compare_results(inference_output_file=inference_output_file_yolo_val,
1004
+ expected_results_file=inference_output_file_standard_inference,
1005
+ options=options)
972
1006
 
973
- # Run again, with chunked inference and symlinks
1007
+ # Run again, without symlinks this time
974
1008
 
975
- inference_output_file_yolo_val_checkpoints = insert_before_extension(inference_output_file_yolo_val,
976
- 'checkpoints')
977
- yolo_inference_options.output_file = inference_output_file_yolo_val_checkpoints
978
- yolo_inference_options.use_symlinks = True
979
- yolo_inference_options.checkpoint_frequency = 5
980
- run_inference_with_yolo_val(yolo_inference_options)
1009
+ inference_output_file_yolo_val_no_links = insert_before_extension(inference_output_file_yolo_val,
1010
+ 'no-links')
1011
+ yolo_inference_options.output_file = inference_output_file_yolo_val_no_links
1012
+ yolo_inference_options.use_symlinks = False
1013
+ run_inference_with_yolo_val(yolo_inference_options)
981
1014
 
982
- # Run again, with chunked inference and no symlinks
1015
+ # Run again, with chunked inference and symlinks
983
1016
 
984
- inference_output_file_yolo_val_checkpoints_no_links = \
985
- insert_before_extension(inference_output_file_yolo_val,'checkpoints-no-links')
986
- yolo_inference_options.output_file = inference_output_file_yolo_val_checkpoints_no_links
987
- yolo_inference_options.use_symlinks = False
988
- yolo_inference_options.checkpoint_frequency = 5
989
- run_inference_with_yolo_val(yolo_inference_options)
1017
+ inference_output_file_yolo_val_checkpoints = insert_before_extension(inference_output_file_yolo_val,
1018
+ 'checkpoints')
1019
+ yolo_inference_options.output_file = inference_output_file_yolo_val_checkpoints
1020
+ yolo_inference_options.use_symlinks = True
1021
+ yolo_inference_options.checkpoint_frequency = 5
1022
+ run_inference_with_yolo_val(yolo_inference_options)
990
1023
 
991
- fn1 = inference_output_file_yolo_val
1024
+ # Run again, with chunked inference and no symlinks
992
1025
 
993
- output_files_to_compare = [
994
- inference_output_file_yolo_val_no_links,
995
- inference_output_file_yolo_val_checkpoints,
996
- inference_output_file_yolo_val_checkpoints_no_links
997
- ]
1026
+ inference_output_file_yolo_val_checkpoints_no_links = \
1027
+ insert_before_extension(inference_output_file_yolo_val,'checkpoints-no-links')
1028
+ yolo_inference_options.output_file = inference_output_file_yolo_val_checkpoints_no_links
1029
+ yolo_inference_options.use_symlinks = False
1030
+ yolo_inference_options.checkpoint_frequency = 5
1031
+ run_inference_with_yolo_val(yolo_inference_options)
998
1032
 
999
- for fn2 in output_files_to_compare:
1000
- assert output_files_are_identical(fn1, fn2, verbose=True)
1033
+ fn1 = inference_output_file_yolo_val
1001
1034
 
1002
- # ...if we need to run the YOLO val inference tests
1035
+ output_files_to_compare = [
1036
+ inference_output_file_yolo_val_no_links,
1037
+ inference_output_file_yolo_val_checkpoints,
1038
+ inference_output_file_yolo_val_checkpoints_no_links
1039
+ ]
1003
1040
 
1041
+ for fn2 in output_files_to_compare:
1042
+ assert output_files_are_identical(fn1, fn2, verbose=True)
1043
+
1044
+ # ...if we need to run the YOLO val inference tests
1045
+
1046
+ # ...if we're not skipping image tests
1004
1047
 
1005
1048
  if not options.skip_video_tests:
1006
1049
 
1007
1050
  ## Video test (single video)
1008
1051
 
1052
+ # This test just checks non-crashing-ness; we will test correctness in the next
1053
+ # test (which runs a folder of videos)
1054
+
1009
1055
  print('\n** Running MD on a single video (module) **\n')
1010
1056
 
1011
- from megadetector.detection.process_video import ProcessVideoOptions, process_video
1057
+ from megadetector.detection.process_video import ProcessVideoOptions, process_videos
1012
1058
  from megadetector.utils.path_utils import insert_before_extension
1013
1059
 
1014
1060
  video_options = ProcessVideoOptions()
1015
1061
  video_options.model_file = options.default_model
1016
1062
  video_options.input_video_file = os.path.join(options.scratch_dir,options.test_videos[0])
1017
1063
  video_options.output_json_file = os.path.join(options.scratch_dir,'single_video_output.json')
1018
- video_options.output_video_file = os.path.join(options.scratch_dir,'video_scratch/rendered_video.mp4')
1019
- video_options.frame_folder = os.path.join(options.scratch_dir,'video_scratch/frame_folder')
1020
- video_options.frame_rendering_folder = os.path.join(options.scratch_dir,'video_scratch/rendered_frame_folder')
1021
-
1022
- video_options.render_output_video = (not options.skip_video_rendering_tests)
1023
-
1024
- # video_options.keep_rendered_frames = False
1025
- # video_options.keep_extracted_frames = False
1026
- video_options.force_extracted_frame_folder_deletion = True
1027
- video_options.force_rendered_frame_folder_deletion = True
1028
- # video_options.reuse_results_if_available = False
1029
- # video_options.reuse_frames_if_available = False
1030
- video_options.recursive = True
1031
- video_options.verbose = False
1032
- video_options.fourcc = options.video_fourcc
1033
- # video_options.rendering_confidence_threshold = None
1034
- # video_options.json_confidence_threshold = 0.005
1035
1064
  video_options.frame_sample = 10
1036
1065
  video_options.n_cores = options.n_cores_for_video_tests
1037
- # video_options.debug_max_frames = -1
1038
- # video_options.class_mapping_filename = None
1039
1066
  video_options.detector_options = copy(options.detector_options)
1040
1067
 
1041
- _ = process_video(video_options)
1068
+ _ = process_videos(video_options)
1042
1069
 
1043
- assert os.path.isfile(video_options.output_video_file), \
1044
- 'Python video test failed to render output video file'
1045
1070
  assert os.path.isfile(video_options.output_json_file), \
1046
1071
  'Python video test failed to render output .json file'
1047
1072
 
@@ -1050,7 +1075,7 @@ def run_python_tests(options):
1050
1075
 
1051
1076
  print('\n** Running MD on a folder of videos (module) **\n')
1052
1077
 
1053
- from megadetector.detection.process_video import ProcessVideoOptions, process_video_folder
1078
+ from megadetector.detection.process_video import ProcessVideoOptions, process_videos
1054
1079
  from megadetector.utils.path_utils import insert_before_extension
1055
1080
 
1056
1081
  video_options = ProcessVideoOptions()
@@ -1059,74 +1084,29 @@ def run_python_tests(options):
1059
1084
  os.path.dirname(options.test_videos[0]))
1060
1085
  video_options.output_json_file = os.path.join(options.scratch_dir,'video_folder_output.json')
1061
1086
  video_options.output_video_file = None
1062
- video_options.frame_folder = os.path.join(options.scratch_dir,'video_scratch/frame_folder')
1063
- video_options.frame_rendering_folder = os.path.join(options.scratch_dir,'video_scratch/rendered_frame_folder')
1064
- video_options.render_output_video = False
1065
- video_options.keep_rendered_frames = False
1066
- video_options.keep_extracted_frames = False
1067
- video_options.force_extracted_frame_folder_deletion = True
1068
- video_options.force_rendered_frame_folder_deletion = True
1069
- video_options.reuse_results_if_available = False
1070
- video_options.reuse_frames_if_available = False
1071
1087
  video_options.recursive = True
1072
1088
  video_options.verbose = True
1073
- video_options.fourcc = options.video_fourcc
1074
- # video_options.rendering_confidence_threshold = None
1075
- # video_options.json_confidence_threshold = 0.005
1076
- video_options.frame_sample = 10
1077
1089
  video_options.n_cores = options.n_cores_for_video_tests
1078
-
1079
- # Force frame extraction to disk, since that's how we generated our expected results file
1080
- video_options.force_on_disk_frame_extraction = True
1081
- # video_options.debug_max_frames = -1
1082
- # video_options.class_mapping_filename = None
1083
-
1084
- # Use quality == None, because we can't control whether YOLOv5 has patched cm2.imread,
1085
- # and therefore can't rely on using the quality parameter
1086
- video_options.quality = None
1087
- video_options.max_width = None
1090
+ video_options.json_confidence_threshold = 0.05
1091
+ video_options.time_sample = 2
1088
1092
  video_options.detector_options = copy(options.detector_options)
1089
-
1090
- video_options.keep_extracted_frames = True
1091
- _ = process_video_folder(video_options)
1093
+ _ = process_videos(video_options)
1092
1094
 
1093
1095
  assert os.path.isfile(video_options.output_json_file), \
1094
1096
  'Python video test failed to render output .json file'
1095
1097
 
1096
- frame_output_file = insert_before_extension(video_options.output_json_file,'frames')
1097
- assert os.path.isfile(frame_output_file)
1098
-
1099
-
1100
1098
  ## Verify results
1101
1099
 
1102
1100
  expected_results_file = \
1103
1101
  get_expected_results_filename(is_gpu_available(verbose=False),test_type='video',options=options)
1104
1102
  assert os.path.isfile(expected_results_file)
1105
1103
 
1106
- compare_results(frame_output_file,expected_results_file,options)
1107
-
1108
-
1109
- ## Run again, this time in memory, and make sure the results are *almost* the same
1110
-
1111
- # They won't be quite the same, because the on-disk path goes through a jpeg intermediate
1112
-
1113
- print('\n** Running MD on a folder of videos (in memory) (module) **\n')
1114
-
1115
- video_options.output_json_file = insert_before_extension(video_options.output_json_file,'in-memory')
1116
- video_options.force_on_disk_frame_extraction = False
1117
- _ = process_video_folder(video_options)
1118
-
1119
- frame_output_file_in_memory = insert_before_extension(video_options.output_json_file,'frames')
1120
- assert os.path.isfile(frame_output_file_in_memory)
1121
-
1122
1104
  from copy import deepcopy
1123
1105
  options_loose = deepcopy(options)
1124
1106
  options_loose.max_conf_error = 0.05
1125
1107
  options_loose.max_coord_error = 0.01
1126
1108
 
1127
- compare_results(inference_output_file=frame_output_file,
1128
- expected_results_file=frame_output_file_in_memory,
1129
- options=options_loose)
1109
+ compare_results(video_options.output_json_file,expected_results_file,options_loose)
1130
1110
 
1131
1111
  # ...if we're not skipping video tests
1132
1112
 
@@ -1182,350 +1162,443 @@ def run_cli_tests(options):
1182
1162
  return
1183
1163
 
1184
1164
 
1185
- ## Run inference on an image
1165
+ if not options.skip_image_tests:
1186
1166
 
1187
- print('\n** Running MD on a single image (CLI) **\n')
1167
+ ## Run inference on an image
1188
1168
 
1189
- image_fn = os.path.join(options.scratch_dir,options.test_images[0])
1190
- output_dir = os.path.join(options.scratch_dir,'single_image_test')
1191
- if options.cli_working_dir is None:
1192
- cmd = 'python -m megadetector.detection.run_detector'
1193
- else:
1194
- cmd = 'python megadetector/detection/run_detector.py'
1195
- cmd += ' "{}" --image_file "{}" --output_dir "{}"'.format(
1196
- options.default_model,image_fn,output_dir)
1197
- cmd += ' --detector_options {}'.format(dict_to_kvp_list(options.detector_options))
1198
- cmd_results = execute_and_print(cmd)
1199
-
1200
- if options.cpu_execution_is_error:
1201
- gpu_available_via_cli = False
1202
- for s in cmd_results['output']:
1203
- if 'GPU available: True' in s:
1204
- gpu_available_via_cli = True
1205
- break
1206
- if not gpu_available_via_cli:
1207
- raise Exception('GPU execution is required, but not available')
1169
+ print('\n** Running MD on a single image (CLI) **\n')
1208
1170
 
1209
- # Make sure we can also pass an absolute path to a model file, instead of, e.g. "MDV5A"
1171
+ image_fn = os.path.join(options.scratch_dir,options.test_images[0])
1172
+ output_dir = os.path.join(options.scratch_dir,'single_image_test')
1173
+ if options.cli_working_dir is None:
1174
+ cmd = 'python -m megadetector.detection.run_detector'
1175
+ else:
1176
+ cmd = 'python megadetector/detection/run_detector.py'
1177
+ cmd += ' "{}" --image_file "{}" --output_dir "{}"'.format(
1178
+ options.default_model,image_fn,output_dir)
1179
+ cmd += ' --detector_options {}'.format(dict_to_kvp_list(options.detector_options))
1180
+ cmd_results = execute_and_print(cmd)
1210
1181
 
1211
- from megadetector.detection.run_detector import try_download_known_detector
1212
- model_file = try_download_known_detector(options.default_model,force_download=False,verbose=False)
1213
- cmd = cmd.replace(options.default_model,model_file)
1214
- cmd_results = execute_and_print(cmd)
1182
+ if options.cpu_execution_is_error:
1183
+ gpu_available_via_cli = False
1184
+ for s in cmd_results['output']:
1185
+ if 'GPU available: True' in s:
1186
+ gpu_available_via_cli = True
1187
+ break
1188
+ if not gpu_available_via_cli:
1189
+ raise Exception('GPU execution is required, but not available')
1215
1190
 
1216
1191
 
1217
- ## Run inference on a folder
1192
+ ## Make sure we can also pass an absolute path to a model file, instead of, e.g. "MDV5A"
1218
1193
 
1219
- print('\n** Running MD on a folder (CLI) **\n')
1194
+ print('\n** Running MD on a single image (CLI) (with symbolic model name) **\n')
1220
1195
 
1221
- image_folder = os.path.join(options.scratch_dir,'md-test-images')
1222
- assert os.path.isdir(image_folder), 'Test image folder {} is not available'.format(image_folder)
1223
- inference_output_file = os.path.join(options.scratch_dir,'folder_inference_output.json')
1224
- if options.cli_working_dir is None:
1225
- cmd = 'python -m megadetector.detection.run_detector_batch'
1226
- else:
1227
- cmd = 'python megadetector/detection/run_detector_batch.py'
1228
- cmd += ' "{}" "{}" "{}" --recursive'.format(
1229
- options.default_model,image_folder,inference_output_file)
1230
- cmd += ' --output_relative_filenames --quiet --include_image_size'
1231
- cmd += ' --include_image_timestamp --include_exif_data'
1232
- cmd += ' --detector_options {}'.format(dict_to_kvp_list(options.detector_options))
1233
- cmd_results = execute_and_print(cmd)
1196
+ from megadetector.detection.run_detector import try_download_known_detector
1197
+ model_file = try_download_known_detector(options.default_model,force_download=False,verbose=False)
1198
+ cmd = cmd.replace(options.default_model,model_file)
1199
+ cmd_results = execute_and_print(cmd)
1234
1200
 
1235
- base_cmd = cmd
1236
1201
 
1202
+ ## Run inference on a folder
1237
1203
 
1238
- ## Run again with checkpointing enabled, make sure the results are the same
1204
+ print('\n** Running MD on a folder (CLI) **\n')
1239
1205
 
1240
- print('\n** Running MD on a folder (with checkpoints) (CLI) **\n')
1206
+ image_folder = os.path.join(options.scratch_dir,'md-test-images')
1207
+ assert os.path.isdir(image_folder), 'Test image folder {} is not available'.format(image_folder)
1208
+ inference_output_file = os.path.join(options.scratch_dir,'folder_inference_output.json')
1209
+ if options.cli_working_dir is None:
1210
+ cmd = 'python -m megadetector.detection.run_detector_batch'
1211
+ else:
1212
+ cmd = 'python megadetector/detection/run_detector_batch.py'
1213
+ cmd += ' "{}" "{}" "{}" --recursive'.format(
1214
+ options.default_model,image_folder,inference_output_file)
1215
+ cmd += ' --output_relative_filenames --quiet --include_image_size'
1216
+ cmd += ' --include_image_timestamp --include_exif_data'
1241
1217
 
1242
- checkpoint_string = ' --checkpoint_frequency 5'
1243
- cmd = base_cmd + checkpoint_string
1244
- inference_output_file_checkpoint = insert_before_extension(inference_output_file,'_checkpoint')
1245
- cmd = cmd.replace(inference_output_file,inference_output_file_checkpoint)
1246
- cmd += ' --detector_options {}'.format(dict_to_kvp_list(options.detector_options))
1247
- cmd_results = execute_and_print(cmd)
1218
+ base_cmd = cmd
1219
+
1220
+ cmd += ' --detector_options {}'.format(dict_to_kvp_list(options.detector_options))
1221
+ cmd_results = execute_and_print(cmd)
1248
1222
 
1249
- assert output_files_are_identical(fn1=inference_output_file,
1250
- fn2=inference_output_file_checkpoint,
1251
- verbose=True)
1252
1223
 
1224
+ ## Run again with a batch size > 1
1253
1225
 
1254
- ## Run again with the image queue enabled, make sure the results are the same
1226
+ print('\n** Running MD on a folder (with a batch size > 1) (CLI) **\n')
1227
+
1228
+ batch_string = ' --batch_size {}'.format(options.alternative_batch_size)
1229
+ cmd = base_cmd + batch_string
1230
+ inference_output_file_batch = insert_before_extension(inference_output_file,'batch')
1231
+ cmd = cmd.replace(inference_output_file,inference_output_file_batch)
1232
+ cmd += ' --detector_options {}'.format(dict_to_kvp_list(options.detector_options))
1233
+ cmd_results = execute_and_print(cmd)
1255
1234
 
1256
- print('\n** Running MD on a folder (with image queue but no preprocessing) (CLI) **\n')
1235
+ # Use compare_results() here rather than output_files_are_identical(), because
1236
+ # batch inference may introduce very small differences. Override the default tolerance,
1237
+ # though, because these differences should be very small compared to, e.g., differences
1238
+ # across library versions.
1239
+ batch_options = copy(options)
1240
+ batch_options.max_coord_error = 0.01
1241
+ batch_options.max_conf_error = 0.01
1242
+ compare_results(inference_output_file,inference_output_file_batch,batch_options)
1257
1243
 
1258
- cmd = base_cmd + ' --use_image_queue'
1259
- inference_output_file_queue = insert_before_extension(inference_output_file,'_queue')
1260
- cmd = cmd.replace(inference_output_file,inference_output_file_queue)
1261
- cmd += ' --detector_options {}'.format(dict_to_kvp_list(options.detector_options))
1262
- cmd_results = execute_and_print(cmd)
1263
1244
 
1264
- assert output_files_are_identical(fn1=inference_output_file,
1265
- fn2=inference_output_file_queue,
1266
- verbose=True)
1245
+ ## Run again with the image queue enabled
1267
1246
 
1247
+ print('\n** Running MD on a folder (with image queue but consumer-side preprocessing) (CLI) **\n')
1268
1248
 
1269
- print('\n** Running MD on a folder (with image queue and preprocessing) (CLI) **\n')
1249
+ cmd = base_cmd + ' --use_image_queue'
1250
+ inference_output_file_queue = insert_before_extension(inference_output_file,'queue')
1251
+ cmd = cmd.replace(inference_output_file,inference_output_file_queue)
1252
+ cmd += ' --detector_options {}'.format(dict_to_kvp_list(options.detector_options))
1253
+ cmd_results = execute_and_print(cmd)
1270
1254
 
1271
- cmd = base_cmd + ' --use_image_queue --preprocess_on_image_queue'
1272
- inference_output_file_queue = insert_before_extension(inference_output_file,'_queue')
1273
- cmd = cmd.replace(inference_output_file,inference_output_file_queue)
1274
- cmd += ' --detector_options {}'.format(dict_to_kvp_list(options.detector_options))
1275
- cmd_results = execute_and_print(cmd)
1255
+ assert output_files_are_identical(fn1=inference_output_file,
1256
+ fn2=inference_output_file_queue,
1257
+ verbose=True)
1276
1258
 
1277
- assert output_files_are_identical(fn1=inference_output_file,
1278
- fn2=inference_output_file_queue,
1279
- verbose=True)
1280
1259
 
1281
- ## Run again on multiple cores, make sure the results are the same
1260
+ ## Run again with the image queue and worker-side preprocessing enabled
1282
1261
 
1283
- if not options.skip_cpu_tests:
1262
+ print('\n** Running MD on a folder (with image queue and worker-side preprocessing) (CLI) **\n')
1284
1263
 
1285
- # First run again on the CPU on a single thread if necessary, so we get a file that
1286
- # *should* be identical to the multicore version.
1287
- gpu_available = is_gpu_available(verbose=False)
1264
+ cmd = base_cmd + ' --use_image_queue --preprocess_on_image_queue'
1265
+ inference_output_file_preprocess_queue = \
1266
+ insert_before_extension(inference_output_file,'preprocess_queue')
1267
+ cmd = cmd.replace(inference_output_file,inference_output_file_preprocess_queue)
1268
+ cmd += ' --detector_options {}'.format(dict_to_kvp_list(options.detector_options))
1269
+ cmd_results = execute_and_print(cmd)
1288
1270
 
1289
- cuda_visible_devices = None
1290
- if 'CUDA_VISIBLE_DEVICES' in os.environ:
1291
- cuda_visible_devices = os.environ['CUDA_VISIBLE_DEVICES']
1292
- os.environ['CUDA_VISIBLE_DEVICES'] = '-1'
1271
+ assert output_files_are_identical(fn1=inference_output_file,
1272
+ fn2=inference_output_file_preprocess_queue,
1273
+ verbose=True)
1293
1274
 
1294
- # If we already ran on the CPU, no need to run again
1295
- if not gpu_available:
1296
1275
 
1297
- inference_output_file_cpu = inference_output_file
1276
+ ## Run again with the image queue and worker-side preprocessing
1298
1277
 
1299
- else:
1278
+ print('\n** Running MD on a folder (with image queue and preprocessing) (CLI) **\n')
1300
1279
 
1301
- print('\n** Running MD on a folder (single CPU) (CLI) **\n')
1280
+ cmd = base_cmd + ' --use_image_queue --preprocess_on_image_queue'
1281
+ inference_output_file_preprocess_queue = \
1282
+ insert_before_extension(inference_output_file,'preprocess_queue')
1283
+ cmd = cmd.replace(inference_output_file,inference_output_file_preprocess_queue)
1284
+ cmd += ' --detector_options {}'.format(dict_to_kvp_list(options.detector_options))
1285
+ cmd_results = execute_and_print(cmd)
1302
1286
 
1303
- inference_output_file_cpu = insert_before_extension(inference_output_file,'cpu')
1304
- cmd = base_cmd
1305
- cmd = cmd.replace(inference_output_file,inference_output_file_cpu)
1306
- cmd += ' --detector_options {}'.format(dict_to_kvp_list(options.detector_options))
1307
- cmd_results = execute_and_print(cmd)
1287
+ assert output_files_are_identical(fn1=inference_output_file,
1288
+ fn2=inference_output_file_preprocess_queue,
1289
+ verbose=True)
1308
1290
 
1309
- print('\n** Running MD on a folder (multiple CPUs) (CLI) **\n')
1310
1291
 
1311
- cpu_string = ' --ncores {}'.format(options.n_cores_for_multiprocessing_tests)
1312
- cmd = base_cmd + cpu_string
1313
- inference_output_file_cpu_multicore = insert_before_extension(inference_output_file,'multicore')
1314
- cmd = cmd.replace(inference_output_file,inference_output_file_cpu_multicore)
1292
+ ## Run again with the worker-side preprocessing and an alternative batch size
1293
+
1294
+ print('\n** Running MD on a folder (with worker-side preprocessing and batched inference) (CLI) **\n')
1295
+
1296
+ batch_string = ' --batch_size {}'.format(options.alternative_batch_size)
1297
+
1298
+ # I reduce the number of loader workers here to force batching to actually appen; with a small
1299
+ # number of images and a few that are intentionally corrupt, with the default number of loader
1300
+ # workers we end up with batches that are mostly just one image.
1301
+ cmd = base_cmd + ' --use_image_queue --preprocess_on_image_queue --loader_workers 2' + batch_string
1302
+ inference_output_file_queue_batch = \
1303
+ insert_before_extension(inference_output_file,'preprocess_queue_batch')
1304
+ cmd = cmd.replace(inference_output_file,inference_output_file_queue_batch)
1315
1305
  cmd += ' --detector_options {}'.format(dict_to_kvp_list(options.detector_options))
1316
1306
  cmd_results = execute_and_print(cmd)
1317
1307
 
1318
- if cuda_visible_devices is not None:
1319
- print('Restoring CUDA_VISIBLE_DEVICES')
1320
- os.environ['CUDA_VISIBLE_DEVICES'] = cuda_visible_devices
1321
- else:
1322
- del os.environ['CUDA_VISIBLE_DEVICES']
1308
+ compare_results(inference_output_file,inference_output_file_queue_batch,batch_options)
1323
1309
 
1324
- assert output_files_are_identical(fn1=inference_output_file_cpu,
1325
- fn2=inference_output_file_cpu_multicore,
1326
- verbose=True)
1327
1310
 
1328
- # ...if we're not skipping the force-cpu tests
1311
+ ## Run again with checkpointing enabled
1329
1312
 
1313
+ print('\n** Running MD on a folder (with checkpoints) (CLI) **\n')
1330
1314
 
1331
- ## Postprocessing
1315
+ checkpoint_string = ' --checkpoint_frequency 5'
1316
+ cmd = base_cmd + checkpoint_string
1317
+ inference_output_file_checkpoint = insert_before_extension(inference_output_file,'checkpoint')
1318
+ cmd = cmd.replace(inference_output_file,inference_output_file_checkpoint)
1319
+ cmd += ' --detector_options {}'.format(dict_to_kvp_list(options.detector_options))
1320
+ cmd_results = execute_and_print(cmd)
1332
1321
 
1333
- print('\n** Testing post-processing (CLI) **\n')
1322
+ assert output_files_are_identical(fn1=inference_output_file,
1323
+ fn2=inference_output_file_checkpoint,
1324
+ verbose=True)
1334
1325
 
1335
- postprocessing_output_dir = os.path.join(options.scratch_dir,'postprocessing_output_cli')
1336
1326
 
1337
- if options.cli_working_dir is None:
1338
- cmd = 'python -m megadetector.postprocessing.postprocess_batch_results'
1339
- else:
1340
- cmd = 'python megadetector/postprocessing/postprocess_batch_results.py'
1341
- cmd += ' "{}" "{}"'.format(
1342
- inference_output_file,postprocessing_output_dir)
1343
- cmd += ' --image_base_dir "{}"'.format(image_folder)
1344
- cmd_results = execute_and_print(cmd)
1327
+ ## Run again with "modern" postprocessing, make sure the results are *not* the same as classic
1345
1328
 
1329
+ print('\n** Running MD on a folder (with modern preprocessing) (CLI) **\n')
1346
1330
 
1347
- ## RDE
1331
+ inference_output_file_modern = insert_before_extension(inference_output_file,'modern')
1332
+ cmd = base_cmd
1333
+ cmd = cmd.replace(inference_output_file,inference_output_file_modern)
1334
+ cmd += ' --detector_options {}'.format(dict_to_kvp_list({'compatibility_mode':'modern'}))
1335
+ cmd_results = execute_and_print(cmd)
1348
1336
 
1349
- print('\n** Running RDE (CLI) **\n')
1337
+ assert not output_files_are_identical(fn1=inference_output_file,
1338
+ fn2=inference_output_file_modern,
1339
+ verbose=True)
1350
1340
 
1351
- rde_output_dir = os.path.join(options.scratch_dir,'rde_output_cli')
1352
1341
 
1353
- if options.cli_working_dir is None:
1354
- cmd = 'python -m megadetector.postprocessing.repeat_detection_elimination.find_repeat_detections'
1355
- else:
1356
- cmd = 'python megadetector/postprocessing/repeat_detection_elimination/find_repeat_detections.py'
1357
- cmd += ' "{}"'.format(inference_output_file)
1358
- cmd += ' --imageBase "{}"'.format(image_folder)
1359
- cmd += ' --outputBase "{}"'.format(rde_output_dir)
1360
- cmd += ' --occurrenceThreshold 1' # Use an absurd number here to make sure we get some suspicious detections
1361
- cmd_results = execute_and_print(cmd)
1362
-
1363
- # Find the latest filtering folder
1364
- filtering_output_dir = os.listdir(rde_output_dir)
1365
- filtering_output_dir = [fn for fn in filtering_output_dir if fn.startswith('filtering_')]
1366
- filtering_output_dir = [os.path.join(rde_output_dir,fn) for fn in filtering_output_dir]
1367
- filtering_output_dir = [fn for fn in filtering_output_dir if os.path.isdir(fn)]
1368
- filtering_output_dir = sorted(filtering_output_dir)[-1]
1369
-
1370
- print('Using RDE filtering folder {}'.format(filtering_output_dir))
1371
-
1372
- filtered_output_file = inference_output_file.replace('.json','_filtered.json')
1373
-
1374
- if options.cli_working_dir is None:
1375
- cmd = 'python -m megadetector.postprocessing.repeat_detection_elimination.remove_repeat_detections'
1376
- else:
1377
- cmd = 'python megadetector/postprocessing/repeat_detection_elimination/remove_repeat_detections.py'
1378
- cmd += ' "{}" "{}" "{}"'.format(inference_output_file,filtered_output_file,filtering_output_dir)
1379
- cmd_results = execute_and_print(cmd)
1342
+ ## Run again with "modern" postprocessing and worker-side preprocessing,
1343
+ ## make sure the results are the same as modern.
1344
+
1345
+ print('\n** Running MD on a folder (with worker-side modern preprocessing) (CLI) **\n')
1380
1346
 
1381
- assert os.path.isfile(filtered_output_file), \
1382
- 'Could not find RDE output file {}'.format(filtered_output_file)
1347
+ inference_output_file_modern_worker_preprocessing = insert_before_extension(inference_output_file,'modern')
1348
+ cmd = base_cmd + ' --use_image_queue --preprocess_on_image_queue'
1349
+ cmd = cmd.replace(inference_output_file,inference_output_file_modern_worker_preprocessing)
1350
+ cmd += ' --detector_options {}'.format(dict_to_kvp_list({'compatibility_mode':'modern'}))
1351
+ cmd_results = execute_and_print(cmd)
1383
1352
 
1353
+ # This should not be the same as the "classic" results
1354
+ assert not output_files_are_identical(fn1=inference_output_file,
1355
+ fn2=inference_output_file_modern_worker_preprocessing,
1356
+ verbose=True)
1384
1357
 
1385
- ## Run inference on a folder (tiled)
1358
+ # ...but it should be the same as the single-threaded "modern" results
1359
+ assert output_files_are_identical(fn1=inference_output_file_modern,
1360
+ fn2=inference_output_file_modern_worker_preprocessing,
1361
+ verbose=True)
1386
1362
 
1387
- # This is a rather esoteric code path that I turn off when I'm testing some
1388
- # features that it doesn't include yet, particularly compatibility mode
1389
- # control.
1390
- skip_tiling_tests = True
1391
1363
 
1392
- if skip_tiling_tests:
1364
+ if not options.skip_cpu_tests:
1393
1365
 
1394
- print('### DEBUG: skipping tiling tests ###')
1366
+ ## Run again on multiple cores
1395
1367
 
1396
- else:
1397
- print('\n** Running tiled inference (CLI) **\n')
1368
+ # First run again on the CPU on a single thread if necessary, so we get a file that
1369
+ # *should* be identical to the multicore version.
1370
+ gpu_available = is_gpu_available(verbose=False)
1371
+
1372
+ cuda_visible_devices = None
1373
+ if 'CUDA_VISIBLE_DEVICES' in os.environ:
1374
+ cuda_visible_devices = os.environ['CUDA_VISIBLE_DEVICES']
1375
+ os.environ['CUDA_VISIBLE_DEVICES'] = '-1'
1376
+
1377
+ # If we already ran on the CPU, no need to run again
1378
+ if not gpu_available:
1379
+
1380
+ inference_output_file_cpu = inference_output_file
1381
+
1382
+ else:
1383
+
1384
+ print('\n** Running MD on a folder (single CPU) (CLI) **\n')
1385
+
1386
+ inference_output_file_cpu = insert_before_extension(inference_output_file,'cpu')
1387
+ cmd = base_cmd
1388
+ cmd = cmd.replace(inference_output_file,inference_output_file_cpu)
1389
+ cmd += ' --detector_options {}'.format(dict_to_kvp_list(options.detector_options))
1390
+ cmd_results = execute_and_print(cmd)
1391
+
1392
+ print('\n** Running MD on a folder (multiple CPUs) (CLI) **\n')
1393
+
1394
+ cpu_string = ' --ncores {}'.format(options.n_cores_for_multiprocessing_tests)
1395
+ cmd = base_cmd + cpu_string
1396
+ inference_output_file_cpu_multicore = insert_before_extension(inference_output_file,'multicore')
1397
+ cmd = cmd.replace(inference_output_file,inference_output_file_cpu_multicore)
1398
+ cmd += ' --detector_options {}'.format(dict_to_kvp_list(options.detector_options))
1399
+ cmd_results = execute_and_print(cmd)
1400
+
1401
+ if cuda_visible_devices is not None:
1402
+ print('Restoring CUDA_VISIBLE_DEVICES')
1403
+ os.environ['CUDA_VISIBLE_DEVICES'] = cuda_visible_devices
1404
+ else:
1405
+ del os.environ['CUDA_VISIBLE_DEVICES']
1406
+
1407
+ assert output_files_are_identical(fn1=inference_output_file_cpu,
1408
+ fn2=inference_output_file_cpu_multicore,
1409
+ verbose=True)
1410
+
1411
+ # ...if we're not skipping the force-cpu tests
1412
+
1413
+
1414
+ ## Postprocessing
1415
+
1416
+ print('\n** Testing post-processing (CLI) **\n')
1417
+
1418
+ postprocessing_output_dir = os.path.join(options.scratch_dir,'postprocessing_output_cli')
1398
1419
 
1399
- image_folder = os.path.join(options.scratch_dir,'md-test-images')
1400
- tiling_folder = os.path.join(options.scratch_dir,'tiling-folder')
1401
- inference_output_file_tiled = os.path.join(options.scratch_dir,'folder_inference_output_tiled.json')
1402
1420
  if options.cli_working_dir is None:
1403
- cmd = 'python -m megadetector.detection.run_tiled_inference'
1421
+ cmd = 'python -m megadetector.postprocessing.postprocess_batch_results'
1404
1422
  else:
1405
- cmd = 'python megadetector/detection/run_tiled_inference.py'
1406
- cmd += ' "{}" "{}" "{}" "{}"'.format(
1407
- options.default_model,image_folder,tiling_folder,inference_output_file_tiled)
1408
- cmd += ' --overwrite_handling overwrite'
1423
+ cmd = 'python megadetector/postprocessing/postprocess_batch_results.py'
1424
+ cmd += ' "{}" "{}"'.format(
1425
+ inference_output_file,postprocessing_output_dir)
1426
+ cmd += ' --image_base_dir "{}"'.format(image_folder)
1409
1427
  cmd_results = execute_and_print(cmd)
1410
1428
 
1411
- with open(inference_output_file_tiled,'r') as f:
1412
- results_from_file = json.load(f) # noqa
1413
1429
 
1430
+ ## RDE
1414
1431
 
1415
- ## Run inference on a folder (augmented, w/YOLOv5 val script)
1432
+ print('\n** Running RDE (CLI) **\n')
1416
1433
 
1417
- if options.yolo_working_dir is None:
1434
+ rde_output_dir = os.path.join(options.scratch_dir,'rde_output_cli')
1418
1435
 
1419
- print('Bypassing YOLOv5 val tests, no yolo folder supplied')
1436
+ if options.cli_working_dir is None:
1437
+ cmd = 'python -m megadetector.postprocessing.repeat_detection_elimination.find_repeat_detections'
1438
+ else:
1439
+ cmd = 'python megadetector/postprocessing/repeat_detection_elimination/find_repeat_detections.py'
1440
+ cmd += ' "{}"'.format(inference_output_file)
1441
+ cmd += ' --imageBase "{}"'.format(image_folder)
1442
+ cmd += ' --outputBase "{}"'.format(rde_output_dir)
1443
+ cmd += ' --occurrenceThreshold 1' # Use an absurd number here to make sure we get some suspicious detections
1444
+ cmd_results = execute_and_print(cmd)
1420
1445
 
1421
- else:
1446
+ # Find the latest filtering folder
1447
+ filtering_output_dir = os.listdir(rde_output_dir)
1448
+ filtering_output_dir = [fn for fn in filtering_output_dir if fn.startswith('filtering_')]
1449
+ filtering_output_dir = [os.path.join(rde_output_dir,fn) for fn in filtering_output_dir]
1450
+ filtering_output_dir = [fn for fn in filtering_output_dir if os.path.isdir(fn)]
1451
+ filtering_output_dir = sorted(filtering_output_dir)[-1]
1422
1452
 
1423
- print('\n** Running YOLOv5 val tests (CLI) **\n')
1453
+ print('Using RDE filtering folder {}'.format(filtering_output_dir))
1454
+
1455
+ filtered_output_file = inference_output_file.replace('.json','_filtered.json')
1424
1456
 
1425
- image_folder = os.path.join(options.scratch_dir,'md-test-images')
1426
- yolo_results_folder = os.path.join(options.scratch_dir,'yolo-output-folder')
1427
- yolo_symlink_folder = os.path.join(options.scratch_dir,'yolo-symlink_folder')
1428
- inference_output_file_yolo_val = os.path.join(options.scratch_dir,'folder_inference_output_yolo_val.json')
1429
1457
  if options.cli_working_dir is None:
1430
- cmd = 'python -m megadetector.detection.run_inference_with_yolov5_val'
1458
+ cmd = 'python -m megadetector.postprocessing.repeat_detection_elimination.remove_repeat_detections'
1431
1459
  else:
1432
- cmd = 'python megadetector/detection/run_inference_with_yolov5_val.py'
1433
- cmd += ' "{}" "{}" "{}"'.format(
1434
- options.default_model,image_folder,inference_output_file_yolo_val)
1435
- cmd += ' --yolo_working_folder "{}"'.format(options.yolo_working_dir)
1436
- cmd += ' --yolo_results_folder "{}"'.format(yolo_results_folder)
1437
- cmd += ' --symlink_folder "{}"'.format(yolo_symlink_folder)
1438
- cmd += ' --augment_enabled 1'
1439
- # cmd += ' --no_use_symlinks'
1440
- cmd += ' --overwrite_handling overwrite'
1460
+ cmd = 'python megadetector/postprocessing/repeat_detection_elimination/remove_repeat_detections.py'
1461
+ cmd += ' "{}" "{}" "{}"'.format(inference_output_file,filtered_output_file,filtering_output_dir)
1441
1462
  cmd_results = execute_and_print(cmd)
1442
1463
 
1443
- # Run again with checkpointing, make sure the outputs are identical
1444
- cmd += ' --checkpoint_frequency 5'
1445
- inference_output_file_yolo_val_checkpoint = \
1446
- os.path.join(options.scratch_dir,'folder_inference_output_yolo_val_checkpoint.json')
1447
- assert inference_output_file_yolo_val_checkpoint != inference_output_file_yolo_val
1448
- cmd = cmd.replace(inference_output_file_yolo_val,inference_output_file_yolo_val_checkpoint)
1449
- cmd_results = execute_and_print(cmd)
1464
+ assert os.path.isfile(filtered_output_file), \
1465
+ 'Could not find RDE output file {}'.format(filtered_output_file)
1450
1466
 
1451
- assert output_files_are_identical(fn1=inference_output_file_yolo_val,
1452
- fn2=inference_output_file_yolo_val_checkpoint,
1453
- verbose=True)
1454
1467
 
1455
- if not options.skip_video_tests:
1468
+ ## Run inference on a folder (tiled)
1456
1469
 
1457
- ## Video test
1470
+ # This is a rather esoteric code path that I turn off when I'm testing some
1471
+ # features that it doesn't include yet, particularly compatibility mode
1472
+ # control.
1473
+ skip_tiling_tests = True
1474
+
1475
+ if skip_tiling_tests:
1476
+
1477
+ print('### DEBUG: skipping tiling tests ###')
1458
1478
 
1459
- print('\n** Testing video rendering (CLI) **\n')
1479
+ else:
1480
+ print('\n** Running tiled inference (CLI) **\n')
1481
+
1482
+ image_folder = os.path.join(options.scratch_dir,'md-test-images')
1483
+ tiling_folder = os.path.join(options.scratch_dir,'tiling-folder')
1484
+ inference_output_file_tiled = os.path.join(options.scratch_dir,'folder_inference_output_tiled.json')
1485
+ if options.cli_working_dir is None:
1486
+ cmd = 'python -m megadetector.detection.run_tiled_inference'
1487
+ else:
1488
+ cmd = 'python megadetector/detection/run_tiled_inference.py'
1489
+ cmd += ' "{}" "{}" "{}" "{}"'.format(
1490
+ options.default_model,image_folder,tiling_folder,inference_output_file_tiled)
1491
+ cmd += ' --overwrite_handling overwrite'
1492
+ cmd_results = execute_and_print(cmd)
1460
1493
 
1461
- video_inference_output_file = os.path.join(options.scratch_dir,'video_inference_output.json')
1462
- output_video_file = os.path.join(options.scratch_dir,'video_scratch/cli_rendered_video.mp4')
1463
- frame_folder = os.path.join(options.scratch_dir,'video_scratch/frame_folder_cli')
1464
- frame_rendering_folder = os.path.join(options.scratch_dir,'video_scratch/rendered_frame_folder_cli')
1494
+ with open(inference_output_file_tiled,'r') as f:
1495
+ results_from_file = json.load(f) # noqa
1465
1496
 
1466
- video_fn = os.path.join(options.scratch_dir,options.test_videos[-1])
1467
- assert os.path.isfile(video_fn), 'Could not find video file {}'.format(video_fn)
1468
1497
 
1469
- output_dir = os.path.join(options.scratch_dir,'single_video_test_cli')
1498
+ ## Run inference on a folder (augmented, w/YOLOv5 val script)
1499
+
1500
+ if options.yolo_working_dir is None:
1501
+
1502
+ print('Bypassing YOLOv5 val tests, no yolo folder supplied')
1503
+
1504
+ else:
1505
+
1506
+ print('\n** Running YOLOv5 val tests (CLI) **\n')
1507
+
1508
+ image_folder = os.path.join(options.scratch_dir,'md-test-images')
1509
+ yolo_results_folder = os.path.join(options.scratch_dir,'yolo-output-folder')
1510
+ yolo_symlink_folder = os.path.join(options.scratch_dir,'yolo-symlink_folder')
1511
+ inference_output_file_yolo_val = os.path.join(options.scratch_dir,'folder_inference_output_yolo_val.json')
1512
+ if options.cli_working_dir is None:
1513
+ cmd = 'python -m megadetector.detection.run_inference_with_yolov5_val'
1514
+ else:
1515
+ cmd = 'python megadetector/detection/run_inference_with_yolov5_val.py'
1516
+ cmd += ' "{}" "{}" "{}"'.format(
1517
+ options.default_model,image_folder,inference_output_file_yolo_val)
1518
+ cmd += ' --yolo_working_folder "{}"'.format(options.yolo_working_dir)
1519
+ cmd += ' --yolo_results_folder "{}"'.format(yolo_results_folder)
1520
+ cmd += ' --symlink_folder "{}"'.format(yolo_symlink_folder)
1521
+ cmd += ' --augment_enabled 1'
1522
+ # cmd += ' --no_use_symlinks'
1523
+ cmd += ' --overwrite_handling overwrite'
1524
+ cmd_results = execute_and_print(cmd)
1525
+
1526
+ # Run again with checkpointing, make sure the outputs are identical
1527
+ cmd += ' --checkpoint_frequency 5'
1528
+ inference_output_file_yolo_val_checkpoint = \
1529
+ os.path.join(options.scratch_dir,'folder_inference_output_yolo_val_checkpoint.json')
1530
+ assert inference_output_file_yolo_val_checkpoint != inference_output_file_yolo_val
1531
+ cmd = cmd.replace(inference_output_file_yolo_val,inference_output_file_yolo_val_checkpoint)
1532
+ cmd_results = execute_and_print(cmd)
1533
+
1534
+ assert output_files_are_identical(fn1=inference_output_file_yolo_val,
1535
+ fn2=inference_output_file_yolo_val_checkpoint,
1536
+ verbose=True)
1537
+
1538
+
1539
+ ## Run inference on a folder (with MDV5B, so we can do a comparison)
1540
+
1541
+ print('\n** Running MDv5b (CLI) **\n')
1542
+
1543
+ image_folder = os.path.join(options.scratch_dir,'md-test-images')
1544
+ inference_output_file_alt = os.path.join(options.scratch_dir,'folder_inference_output_alt.json')
1470
1545
  if options.cli_working_dir is None:
1471
- cmd = 'python -m megadetector.detection.process_video'
1546
+ cmd = 'python -m megadetector.detection.run_detector_batch'
1472
1547
  else:
1473
- cmd = 'python megadetector/detection/process_video.py'
1474
- cmd += ' "{}" "{}"'.format(options.default_model,video_fn)
1475
- cmd += ' --frame_folder "{}" --frame_rendering_folder "{}" --output_json_file "{}" --output_video_file "{}"'.format( #noqa
1476
- frame_folder,frame_rendering_folder,video_inference_output_file,output_video_file)
1477
- cmd += ' --fourcc {}'.format(options.video_fourcc)
1478
- cmd += ' --force_extracted_frame_folder_deletion --force_rendered_frame_folder_deletion'
1479
- cmd += ' --n_cores {}'.format(options.n_cores_for_video_tests)
1480
- cmd += ' --frame_sample 4'
1481
- cmd += ' --verbose'
1548
+ cmd = 'python megadetector/detection/run_detector_batch.py'
1549
+ cmd += ' "{}" "{}" "{}" --recursive'.format(
1550
+ options.alt_model,image_folder,inference_output_file_alt)
1551
+ cmd += ' --output_relative_filenames --quiet --include_image_size'
1552
+ cmd += ' --include_image_timestamp --include_exif_data'
1482
1553
  cmd += ' --detector_options {}'.format(dict_to_kvp_list(options.detector_options))
1554
+ cmd_results = execute_and_print(cmd)
1555
+
1556
+ with open(inference_output_file_alt,'r') as f:
1557
+ results_from_file = json.load(f) # noqa
1483
1558
 
1484
- if not options.skip_video_rendering_tests:
1485
- cmd += ' --render_output_video'
1486
1559
 
1560
+ ## Compare the two files
1561
+
1562
+ comparison_output_folder = os.path.join(options.scratch_dir,'results_comparison')
1563
+ image_folder = os.path.join(options.scratch_dir,'md-test-images')
1564
+ results_files_string = '"{}" "{}"'.format(
1565
+ inference_output_file,inference_output_file_alt)
1566
+ if options.cli_working_dir is None:
1567
+ cmd = 'python -m megadetector.postprocessing.compare_batch_results'
1568
+ else:
1569
+ cmd = 'python megadetector/postprocessing/compare_batch_results.py'
1570
+ cmd += ' "{}" "{}" {}'.format(comparison_output_folder,image_folder,results_files_string)
1487
1571
  cmd_results = execute_and_print(cmd)
1488
1572
 
1489
- # ...if we're not skipping video tests
1573
+ assert cmd_results['status'] == 0, 'Error generating comparison HTML'
1574
+ assert os.path.isfile(os.path.join(comparison_output_folder,'index.html')), \
1575
+ 'Failed to generate comparison HTML'
1490
1576
 
1577
+ # ...if we're not skipping image tests
1491
1578
 
1492
- ## Run inference on a folder (with MDV5B, so we can do a comparison)
1493
1579
 
1494
- print('\n** Running MDv5b (CLI) **\n')
1580
+ if not options.skip_video_tests:
1495
1581
 
1496
- image_folder = os.path.join(options.scratch_dir,'md-test-images')
1497
- inference_output_file_alt = os.path.join(options.scratch_dir,'folder_inference_output_alt.json')
1498
- if options.cli_working_dir is None:
1499
- cmd = 'python -m megadetector.detection.run_detector_batch'
1500
- else:
1501
- cmd = 'python megadetector/detection/run_detector_batch.py'
1502
- cmd += ' "{}" "{}" "{}" --recursive'.format(
1503
- options.alt_model,image_folder,inference_output_file_alt)
1504
- cmd += ' --output_relative_filenames --quiet --include_image_size'
1505
- cmd += ' --include_image_timestamp --include_exif_data'
1506
- cmd += ' --detector_options {}'.format(dict_to_kvp_list(options.detector_options))
1507
- cmd_results = execute_and_print(cmd)
1508
-
1509
- with open(inference_output_file_alt,'r') as f:
1510
- results_from_file = json.load(f) # noqa
1582
+ ## Video test
1511
1583
 
1584
+ print('\n** Testing video processing (CLI) **\n')
1585
+
1586
+ video_inference_output_file = os.path.join(options.scratch_dir,'video_folder_output_cli.json')
1587
+ if options.cli_working_dir is None:
1588
+ cmd = 'python -m megadetector.detection.process_video'
1589
+ else:
1590
+ cmd = 'python megadetector/detection/process_video.py'
1512
1591
 
1513
- ## Compare the two files
1592
+ cmd += ' "{}" "{}"'.format(options.default_model,options.scratch_dir)
1593
+ cmd += ' --output_json_file "{}"'.format(video_inference_output_file)
1594
+ cmd += ' --n_cores {}'.format(options.n_cores_for_video_tests)
1595
+ cmd += ' --frame_sample 4'
1596
+ cmd += ' --verbose'
1597
+ cmd += ' --detector_options {}'.format(dict_to_kvp_list(options.detector_options))
1514
1598
 
1515
- comparison_output_folder = os.path.join(options.scratch_dir,'results_comparison')
1516
- image_folder = os.path.join(options.scratch_dir,'md-test-images')
1517
- results_files_string = '"{}" "{}"'.format(
1518
- inference_output_file,inference_output_file_alt)
1519
- if options.cli_working_dir is None:
1520
- cmd = 'python -m megadetector.postprocessing.compare_batch_results'
1521
- else:
1522
- cmd = 'python megadetector/postprocessing/compare_batch_results.py'
1523
- cmd += ' "{}" "{}" {}'.format(comparison_output_folder,image_folder,results_files_string)
1524
- cmd_results = execute_and_print(cmd)
1599
+ cmd_results = execute_and_print(cmd)
1525
1600
 
1526
- assert cmd_results['status'] == 0, 'Error generating comparison HTML'
1527
- assert os.path.isfile(os.path.join(comparison_output_folder,'index.html')), \
1528
- 'Failed to generate comparison HTML'
1601
+ # ...if we're not skipping video tests
1529
1602
 
1530
1603
  print('\n*** Finished CLI tests ***\n')
1531
1604
 
@@ -1692,7 +1765,8 @@ def run_tests(options):
1692
1765
 
1693
1766
  def test_suite_entry_point():
1694
1767
  """
1695
- Main entry point for the numerical test suite.
1768
+ This is the entry point when running tests via pytest; we run a subset of
1769
+ tests in this environment, e.g. we don't run CLI or video tests.
1696
1770
  """
1697
1771
 
1698
1772
  options = MDTestOptions()
@@ -1713,6 +1787,7 @@ def test_suite_entry_point():
1713
1787
  options.cli_test_pythonpath = None
1714
1788
  options.skip_download_tests = True
1715
1789
  options.skip_localhost_downloads = True
1790
+ options.skip_import_tests = False
1716
1791
 
1717
1792
  options = download_test_data(options)
1718
1793
 
@@ -1727,12 +1802,14 @@ if False:
1727
1802
 
1728
1803
  #%% Test Prep
1729
1804
 
1805
+ from megadetector.utils.md_tests import MDTestOptions, download_test_data
1806
+
1730
1807
  options = MDTestOptions()
1731
1808
 
1732
1809
  options.disable_gpu = False
1733
1810
  options.cpu_execution_is_error = False
1734
- options.skip_video_tests = False
1735
- options.skip_python_tests = False
1811
+ options.skip_video_tests = True
1812
+ options.skip_python_tests = True
1736
1813
  options.skip_cli_tests = False
1737
1814
  options.scratch_dir = None
1738
1815
  options.test_data_url = 'https://lila.science/public/md-test-package.zip'
@@ -1741,18 +1818,19 @@ if False:
1741
1818
  options.warning_mode = False
1742
1819
  options.max_coord_error = 0.01 # 0.001
1743
1820
  options.max_conf_error = 0.01 # 0.005
1821
+ options.skip_cpu_tests = True
1744
1822
  options.skip_video_rendering_tests = True
1745
- options.skip_download_tests = False
1823
+ options.skip_download_tests = True
1746
1824
  options.skip_localhost_downloads = False
1747
1825
 
1748
1826
  # options.iou_threshold_for_file_comparison = 0.7
1749
1827
 
1750
- options.cli_working_dir = r'c:\git\MegaDetector'
1828
+ # options.cli_working_dir = r'c:\git\MegaDetector'
1751
1829
  # When running in the cameratraps-detector environment
1752
1830
  # options.cli_test_pythonpath = r'c:\git\MegaDetector;c:\git\yolov5-md'
1753
1831
 
1754
1832
  # When running in the MegaDetector environment
1755
- options.cli_test_pythonpath = r'c:\git\MegaDetector'
1833
+ # options.cli_test_pythonpath = r'c:\git\MegaDetector'
1756
1834
 
1757
1835
  # options.cli_working_dir = os.path.expanduser('~')
1758
1836
  # options.yolo_working_dir = r'c:\git\yolov5-md'
@@ -1774,11 +1852,13 @@ if False:
1774
1852
 
1775
1853
  #%% Run download tests
1776
1854
 
1855
+ from megadetector.utils.md_tests import run_download_tests
1777
1856
  run_download_tests(options=options)
1778
1857
 
1779
1858
 
1780
1859
  #%% Run all tests
1781
1860
 
1861
+ from megadetector.utils.md_tests import run_tests
1782
1862
  run_tests(options)
1783
1863
 
1784
1864
 
@@ -1849,10 +1929,15 @@ def main(): # noqa
1849
1929
  type=str,
1850
1930
  help='Directory for temporary storage (defaults to system temp dir)')
1851
1931
 
1932
+ parser.add_argument(
1933
+ '--skip_image_tests',
1934
+ action='store_true',
1935
+ help='Skip tests related to still images')
1936
+
1852
1937
  parser.add_argument(
1853
1938
  '--skip_video_tests',
1854
1939
  action='store_true',
1855
- help='Skip tests related to video (which can be slow)')
1940
+ help='Skip tests related to video')
1856
1941
 
1857
1942
  parser.add_argument(
1858
1943
  '--skip_video_rendering_tests',
@@ -1874,6 +1959,11 @@ def main(): # noqa
1874
1959
  action='store_true',
1875
1960
  help='Skip model download tests')
1876
1961
 
1962
+ parser.add_argument(
1963
+ '--skip_import_tests',
1964
+ action='store_true',
1965
+ help='Skip module import tests')
1966
+
1877
1967
  parser.add_argument(
1878
1968
  '--skip_cpu_tests',
1879
1969
  action='store_true',
@@ -1979,35 +2069,3 @@ def main(): # noqa
1979
2069
  if __name__ == '__main__':
1980
2070
  main()
1981
2071
 
1982
-
1983
- #%% Scrap
1984
-
1985
- if False:
1986
-
1987
- pass
1988
-
1989
- #%%
1990
-
1991
- import sys; sys.path.append(r'c:\git\yolov5-md')
1992
-
1993
- #%%
1994
-
1995
- fn1 = r"G:\temp\md-test-package\mdv5a-video-cpu-pt1.10.1.frames.json"
1996
- fn2 = r"G:\temp\md-test-package\mdv5a-video-gpu-pt1.10.1.frames.json"
1997
- fn3 = r"G:\temp\md-test-package\mdv5a-video-cpu-pt2.x.frames.json"
1998
- fn4 = r"G:\temp\md-test-package\mdv5a-video-gpu-pt2.x.frames.json"
1999
-
2000
- assert all([os.path.isfile(fn) for fn in [fn1,fn2,fn3,fn4]])
2001
- print(output_files_are_identical(fn1,fn1,verbose=False))
2002
- print(output_files_are_identical(fn1,fn2,verbose=False))
2003
- print(output_files_are_identical(fn1,fn3,verbose=False))
2004
-
2005
- #%%
2006
-
2007
- fn1 = r"G:\temp\md-test-package\mdv5a-image-gpu-pt1.10.1.json"
2008
- fn2 = r"G:\temp\md-test-package\mdv5a-augment-image-gpu-pt1.10.1.json"
2009
- print(output_files_are_identical(fn1,fn2,verbose=True))
2010
-
2011
- fn1 = r"G:\temp\md-test-package\mdv5a-image-cpu-pt1.10.1.json"
2012
- fn2 = r"G:\temp\md-test-package\mdv5a-augment-image-cpu-pt1.10.1.json"
2013
- print(output_files_are_identical(fn1,fn2,verbose=True))