megadetector 5.0.26__py3-none-any.whl → 5.0.28__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 (26) hide show
  1. megadetector/data_management/mewc_to_md.py +1 -1
  2. megadetector/data_management/read_exif.py +2 -0
  3. megadetector/detection/process_video.py +1 -1
  4. megadetector/detection/pytorch_detector.py +4 -4
  5. megadetector/detection/run_detector.py +10 -3
  6. megadetector/detection/run_detector_batch.py +4 -3
  7. megadetector/detection/run_tiled_inference.py +65 -13
  8. megadetector/detection/video_utils.py +2 -2
  9. megadetector/postprocessing/classification_postprocessing.py +517 -20
  10. megadetector/postprocessing/create_crop_folder.py +1 -1
  11. megadetector/postprocessing/generate_csv_report.py +499 -0
  12. megadetector/postprocessing/load_api_results.py +4 -4
  13. megadetector/postprocessing/postprocess_batch_results.py +6 -4
  14. megadetector/taxonomy_mapping/preview_lila_taxonomy.py +0 -3
  15. megadetector/taxonomy_mapping/taxonomy_graph.py +1 -1
  16. megadetector/utils/ct_utils.py +3 -2
  17. megadetector/utils/path_utils.py +75 -29
  18. megadetector/utils/split_locations_into_train_val.py +16 -3
  19. megadetector/utils/wi_utils.py +68 -410
  20. megadetector/visualization/visualization_utils.py +25 -9
  21. megadetector/visualization/visualize_detector_output.py +50 -28
  22. {megadetector-5.0.26.dist-info → megadetector-5.0.28.dist-info}/METADATA +132 -132
  23. {megadetector-5.0.26.dist-info → megadetector-5.0.28.dist-info}/RECORD +26 -25
  24. {megadetector-5.0.26.dist-info → megadetector-5.0.28.dist-info}/WHEEL +1 -1
  25. {megadetector-5.0.26.dist-info → megadetector-5.0.28.dist-info}/licenses/LICENSE +0 -0
  26. {megadetector-5.0.26.dist-info → megadetector-5.0.28.dist-info}/top_level.txt +0 -0
@@ -242,7 +242,7 @@ def mewc_to_md(mewc_input_folder,
242
242
 
243
243
  # ...for each image
244
244
 
245
- ##%% Map MD reults to the global level
245
+ ##%% Map MD results to the global level
246
246
 
247
247
  if md_results_all['info'] is None:
248
248
  md_results_all['info'] = md_results['info']
@@ -31,6 +31,8 @@ from megadetector.data_management.cct_json_utils import write_object_with_serial
31
31
 
32
32
  debug_max_images = None
33
33
 
34
+ minimal_exif_tags = ['DateTime','Model','Make','ExifImageWidth','ExifImageHeight','DateTimeOriginal','Orientation']
35
+
34
36
 
35
37
  #%% Options
36
38
 
@@ -140,7 +140,7 @@ class ProcessVideoOptions:
140
140
  #: [frame_sample] and [time_sample].
141
141
  self.frames_to_extract = None
142
142
 
143
- # Sample frames every N seconds. Mutally exclusive with [frame_sample] and [frames_to_extract].
143
+ # Sample frames every N seconds. Mutually exclusive with [frame_sample] and [frames_to_extract].
144
144
  self.time_sample = None
145
145
 
146
146
  #: Number of workers to use for parallelization; set to <= 1 to disable parallelization
@@ -107,7 +107,7 @@ def _get_model_type_for_model(model_file,
107
107
  if model_type_from_model_version == model_type_from_model_file_metadata:
108
108
  model_type = model_type_from_model_file_metadata
109
109
  else:
110
- print('Waring: model type from model version is {}, from file metadata is {}'.format(
110
+ print('Warning: model type from model version is {}, from file metadata is {}'.format(
111
111
  model_type_from_model_version,model_type_from_model_file_metadata))
112
112
  if prefer_model_type_source == 'table':
113
113
  model_type = model_type_from_model_file_metadata
@@ -145,7 +145,7 @@ def _initialize_yolo_imports_for_model(model_file,
145
145
  appropriate metadata in the file or in the global table.
146
146
  detector_options (dict, optional): dictionary of detector options that mean
147
147
  different things to different models
148
- verbose (bool, optional): enable additonal debug output
148
+ verbose (bool, optional): enable additional debug output
149
149
 
150
150
  Returns:
151
151
  str: the model type for which we initialized support
@@ -238,7 +238,7 @@ def _initialize_yolo_imports(model_type='yolov5',
238
238
  typically used when the right support library is on the current PYTHONPATH.
239
239
  force_reimport (bool, optional): import the appropriate libraries even if the
240
240
  requested model type matches the current initialization state
241
- verbose (bool, optional): include additonal debug output
241
+ verbose (bool, optional): include additional debug output
242
242
 
243
243
  Returns:
244
244
  str: the model type for which we initialized support
@@ -527,7 +527,7 @@ def read_metadata_from_megadetector_model_file(model_file,
527
527
  default_compatibility_mode = 'classic'
528
528
 
529
529
  # This is a useful hack when I want to verify that my test driver (md_tests.py) is
530
- # correctly forcing a specific compabitility mode (I use "classic-test" in that case)
530
+ # correctly forcing a specific compatibility mode (I use "classic-test" in that case)
531
531
  require_non_default_compatibility_mode = False
532
532
 
533
533
  class PTDetector:
@@ -29,6 +29,7 @@ import os
29
29
  import statistics
30
30
  import sys
31
31
  import time
32
+ import json
32
33
  import warnings
33
34
  import tempfile
34
35
 
@@ -214,8 +215,8 @@ def get_detector_metadata_from_version_string(detector_version):
214
215
  print('Warning: no metadata for unknown detector version {}'.format(detector_version))
215
216
  default_detector_metadata = {
216
217
  'megadetector_version':'unknown',
217
- 'typical_detection_threshold':0.5,
218
- 'conservative_detection_threshold':0.25
218
+ 'typical_detection_threshold':0.2,
219
+ 'conservative_detection_threshold':0.1
219
220
  }
220
221
  return default_detector_metadata
221
222
  else:
@@ -423,12 +424,18 @@ def get_typical_confidence_threshold_from_results(results):
423
424
  threshold based on the detector version.
424
425
 
425
426
  Args:
426
- results (dict): a dict of MD results, as it would be loaded from a MD results .json file
427
+ results (dict or str): a dict of MD results, as it would be loaded from a MD results .json
428
+ file, or a .json filename
427
429
 
428
430
  Returns:
429
431
  float: a sensible default threshold for this model
430
432
  """
431
433
 
434
+ # Load results if necessary
435
+ if isinstance(results,str):
436
+ with open(results,'r') as f:
437
+ results = json.load(f)
438
+
432
439
  if 'detector_metadata' in results['info'] and \
433
440
  'typical_detection_threshold' in results['info']['detector_metadata']:
434
441
  default_threshold = results['info']['detector_metadata']['typical_detection_threshold']
@@ -130,7 +130,7 @@ def _producer_func(q,
130
130
  print('Producer starting: ID {}, preprocessor {}'.format(producer_id,preprocessor))
131
131
  sys.stdout.flush()
132
132
 
133
- if preprocessor is not None:
133
+ if preprocessor is not None:
134
134
  assert isinstance(preprocessor,str)
135
135
  detector_options = deepcopy(detector_options)
136
136
  detector_options['preprocess_only'] = True
@@ -161,8 +161,8 @@ def _producer_func(q,
161
161
 
162
162
  image = image_info
163
163
 
164
- except Exception:
165
- print('Producer process: image {} cannot be loaded'.format(im_file))
164
+ except Exception as e:
165
+ print('Producer process: image {} cannot be loaded:\n{}'.format(im_file,str(e)))
166
166
  image = run_detector.FAILURE_IMAGE_OPEN
167
167
 
168
168
  if verbose:
@@ -354,6 +354,7 @@ def run_detector_with_image_queue(image_files,
354
354
  preprocessor = None
355
355
 
356
356
  if preprocess_on_image_queue:
357
+ print('Enabling image queue preprocessing')
357
358
  preprocessor = model_file
358
359
 
359
360
  n_total_images = len(image_files)
@@ -26,16 +26,22 @@ augmentation); the command-line interface only supports standard inference right
26
26
 
27
27
  import os
28
28
  import json
29
+ import tempfile
30
+ import uuid
29
31
 
30
32
  from tqdm import tqdm
31
33
 
32
34
  import torch
33
35
  from torchvision import ops
34
36
 
35
- from megadetector.detection.run_inference_with_yolov5_val import YoloInferenceOptions,run_inference_with_yolo_val
36
- from megadetector.detection.run_detector_batch import load_and_run_detector_batch,write_results_to_file
37
- from megadetector.detection.run_detector import try_download_known_detector
37
+ from megadetector.detection.run_inference_with_yolov5_val import \
38
+ YoloInferenceOptions,run_inference_with_yolo_val
39
+ from megadetector.detection.run_detector_batch import \
40
+ load_and_run_detector_batch,write_results_to_file
41
+ from megadetector.detection.run_detector import \
42
+ try_download_known_detector, CONF_DIGITS, COORD_DIGITS
38
43
  from megadetector.utils import path_utils
44
+ from megadetector.utils.ct_utils import round_float_array, round_float
39
45
  from megadetector.visualization import visualization_utils as vis_utils
40
46
 
41
47
  default_patch_overlap = 0.5
@@ -380,13 +386,25 @@ def _extract_tiles_for_image(fn_relative,image_folder,tiling_folder,patch_size,p
380
386
 
381
387
  #%% Main function
382
388
 
383
- def run_tiled_inference(model_file, image_folder, tiling_folder, output_file,
384
- tile_size_x=1280, tile_size_y=1280, tile_overlap=0.5,
385
- checkpoint_path=None, checkpoint_frequency=-1, remove_tiles=False,
389
+ def run_tiled_inference(model_file,
390
+ image_folder,
391
+ tiling_folder,
392
+ output_file,
393
+ tile_size_x=1280,
394
+ tile_size_y=1280,
395
+ tile_overlap=0.5,
396
+ checkpoint_path=None,
397
+ checkpoint_frequency=-1,
398
+ remove_tiles=False,
386
399
  yolo_inference_options=None,
387
400
  n_patch_extraction_workers=default_n_patch_extraction_workers,
388
401
  overwrite_tiles=True,
389
- image_list=None):
402
+ image_list=None,
403
+ augment=False,
404
+ detector_options=None,
405
+ use_image_queue=True,
406
+ preprocess_on_image_queue=True,
407
+ inference_size=None):
390
408
  """
391
409
  Runs inference using [model_file] on the images in [image_folder], fist splitting each image up
392
410
  into tiles of size [tile_size_x] x [tile_size_y], writing those tiles to [tiling_folder],
@@ -397,7 +415,8 @@ def run_tiled_inference(model_file, image_folder, tiling_folder, output_file,
397
415
  within that folder, including deleting everything, so it's best if it's a new folder.
398
416
  Conceptually this folder is temporary, it's just helpful in this case to not actually
399
417
  use the system temp folder, because the tile cache may be very large, so the caller may
400
- want it to be on a specific drive.
418
+ want it to be on a specific drive. If this is None, a new folder will be created in
419
+ system temp space.
401
420
 
402
421
  tile_overlap is the fraction of overlap between tiles.
403
422
 
@@ -410,11 +429,12 @@ def run_tiled_inference(model_file, image_folder, tiling_folder, output_file,
410
429
  Args:
411
430
  model_file (str): model filename (ending in .pt), or a well-known model name (e.g. "MDV5A")
412
431
  image_folder (str): the folder of images to proess (always recursive)
413
- tiling_folder (str): folder for temporary tile storage; see caveats above
432
+ tiling_folder (str): folder for temporary tile storage; see caveats above. Can be None
433
+ to use system temp space.
414
434
  output_file (str): .json file to which we should write MD-formatted results
415
435
  tile_size_x (int, optional): tile width
416
436
  tile_size_y (int, optional): tile height
417
- tile_overlap (float, optional): overlap between adjacenet tiles, as a fraction of the
437
+ tile_overlap (float, optional): overlap between adjacent tiles, as a fraction of the
418
438
  tile size
419
439
  checkpoint_path (str, optional): checkpoint path; passed directly to run_detector_batch; see
420
440
  run_detector_batch for details
@@ -427,7 +447,17 @@ def run_tiled_inference(model_file, image_folder, tiling_folder, output_file,
427
447
  set to <= 1 to disable parallelization
428
448
  image_list (list, optional): .json file containing a list of specific images to process. If
429
449
  this is supplied, and the paths are absolute, [image_folder] will be ignored. If this is supplied,
430
- and the paths are relative, they should be relative to [image_folder].
450
+ and the paths are relative, they should be relative to [image_folder]
451
+ augment (bool, optional): apply test-time augmentation, only relevant if yolo_inference_options
452
+ is None
453
+ detector_options (dict, optional): parameters to pass to run_detector, only relevant if
454
+ yolo_inference_options is None
455
+ use_image_queue (bool, optional): whether to use a loader worker queue, only relevant if
456
+ yolo_inference_options is None
457
+ preprocess_on_image_queue (bool, optional): whether the image queue should also be responsible
458
+ for preprocessing
459
+ inference_size (int, optional): override the default inference image size, only relevant if
460
+ yolo_inference_options is None
431
461
 
432
462
  Returns:
433
463
  dict: MD-formatted results dictionary, identical to what's written to [output_file]
@@ -447,6 +477,11 @@ def run_tiled_inference(model_file, image_folder, tiling_folder, output_file,
447
477
  patch_stride = (round(patch_size[0]*(1.0-tile_overlap)),
448
478
  round(patch_size[1]*(1.0-tile_overlap)))
449
479
 
480
+ if tiling_folder is None:
481
+ tiling_folder = \
482
+ os.path.join(tempfile.gettempdir(), 'md-tiling', str(uuid.uuid1()))
483
+ print('Creating temporary tiling folder: {}'.format(tiling_folder))
484
+
450
485
  os.makedirs(tiling_folder,exist_ok=True)
451
486
 
452
487
  ##%% List files
@@ -545,7 +580,7 @@ def run_tiled_inference(model_file, image_folder, tiling_folder, output_file,
545
580
  images_with_patch_errors[patch_info['image_fn']] = patch_info
546
581
 
547
582
 
548
- ##%% Run inference on tiles
583
+ ##%% Run inference on the folder of tiles
549
584
 
550
585
  # When running with run_inference_with_yolov5_val, we'll pass the folder
551
586
  if yolo_inference_options is not None:
@@ -582,7 +617,12 @@ def run_tiled_inference(model_file, image_folder, tiling_folder, output_file,
582
617
  patch_file_names,
583
618
  checkpoint_path=checkpoint_path,
584
619
  checkpoint_frequency=checkpoint_frequency,
585
- quiet=True)
620
+ quiet=True,
621
+ augment=augment,
622
+ detector_options=detector_options,
623
+ use_image_queue=use_image_queue,
624
+ preprocess_on_image_queue=preprocess_on_image_queue,
625
+ image_size=inference_size)
586
626
 
587
627
  patch_level_output_file = os.path.join(tiling_folder,folder_name + '_patch_level_results.json')
588
628
 
@@ -591,6 +631,7 @@ def run_tiled_inference(model_file, image_folder, tiling_folder, output_file,
591
631
  relative_path_base=tiling_folder,
592
632
  detector_file=model_file)
593
633
 
634
+ # ...if we are/aren't using run_inference_with_yolov5_val
594
635
 
595
636
  ##%% Map patch-level detections back to the original images
596
637
 
@@ -708,6 +749,10 @@ def run_tiled_inference(model_file, image_folder, tiling_folder, output_file,
708
749
  w_image_normalized,
709
750
  h_image_normalized]
710
751
 
752
+ bbox_image_normalized = round_float_array(bbox_image_normalized,
753
+ precision=COORD_DIGITS)
754
+ det['conf'] = round_float(det['conf'], precision=CONF_DIGITS)
755
+
711
756
  output_det = {}
712
757
  output_det['bbox'] = bbox_image_normalized
713
758
  output_det['conf'] = det['conf']
@@ -899,7 +944,14 @@ def main():
899
944
  type=str,
900
945
  default=None,
901
946
  help=('A .json list of relative filenames (or absolute paths contained within image_folder) to include'))
947
+ parser.add_argument(
948
+ '--detector_options',
949
+ type=str,
950
+ default=None,
951
+ help=('A list of detector options (key-value pairs) to '))
902
952
 
953
+ # detector_options = parse_kvp_list(args.detector_options)
954
+
903
955
  if len(sys.argv[1:]) == 0:
904
956
  parser.print_help()
905
957
  parser.exit()
@@ -714,8 +714,8 @@ def video_folder_to_frames(input_folder,
714
714
  parallelism
715
715
  every_n_frames (int, optional): sample every Nth frame starting from the first frame;
716
716
  if this is None or 1, every frame is extracted. If this is a negative value, it's
717
- interpreted as a sampling rate in seconds, which is rounded to the nearest frame sampling
718
- rate. Mutually exclusive with frames_to_extract.
717
+ interpreted as a sampling rate in seconds, which is rounded to the nearest frame
718
+ sampling rate. Mutually exclusive with frames_to_extract.
719
719
  verbose (bool, optional): enable additional debug console output
720
720
  parallelization_uses_threads (bool, optional): whether to use threads (True) or
721
721
  processes (False) for parallelization; ignored if n_threads <= 1