megadetector 5.0.27__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.
- megadetector/data_management/mewc_to_md.py +1 -1
- megadetector/data_management/read_exif.py +2 -0
- megadetector/detection/process_video.py +1 -1
- megadetector/detection/pytorch_detector.py +4 -4
- megadetector/detection/run_detector.py +10 -3
- megadetector/detection/run_detector_batch.py +4 -3
- megadetector/detection/run_tiled_inference.py +65 -13
- megadetector/detection/video_utils.py +2 -2
- megadetector/postprocessing/classification_postprocessing.py +517 -20
- megadetector/postprocessing/create_crop_folder.py +1 -1
- megadetector/postprocessing/generate_csv_report.py +499 -0
- megadetector/postprocessing/load_api_results.py +4 -4
- megadetector/postprocessing/postprocess_batch_results.py +6 -4
- megadetector/taxonomy_mapping/preview_lila_taxonomy.py +0 -3
- megadetector/taxonomy_mapping/taxonomy_graph.py +1 -1
- megadetector/utils/ct_utils.py +3 -2
- megadetector/utils/path_utils.py +75 -29
- megadetector/utils/split_locations_into_train_val.py +16 -3
- megadetector/utils/wi_utils.py +68 -410
- megadetector/visualization/visualization_utils.py +25 -9
- megadetector/visualization/visualize_detector_output.py +50 -28
- {megadetector-5.0.27.dist-info → megadetector-5.0.28.dist-info}/METADATA +132 -132
- {megadetector-5.0.27.dist-info → megadetector-5.0.28.dist-info}/RECORD +26 -25
- {megadetector-5.0.27.dist-info → megadetector-5.0.28.dist-info}/WHEEL +1 -1
- {megadetector-5.0.27.dist-info → megadetector-5.0.28.dist-info}/licenses/LICENSE +0 -0
- {megadetector-5.0.27.dist-info → megadetector-5.0.28.dist-info}/top_level.txt +0 -0
|
@@ -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.
|
|
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('
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
218
|
-
'conservative_detection_threshold':0.
|
|
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
|
|
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
|
|
36
|
-
|
|
37
|
-
from megadetector.detection.
|
|
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,
|
|
384
|
-
|
|
385
|
-
|
|
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
|
|
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
|
|
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
|