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.

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.27.dist-info → megadetector-5.0.28.dist-info}/METADATA +132 -132
  23. {megadetector-5.0.27.dist-info → megadetector-5.0.28.dist-info}/RECORD +26 -25
  24. {megadetector-5.0.27.dist-info → megadetector-5.0.28.dist-info}/WHEEL +1 -1
  25. {megadetector-5.0.27.dist-info → megadetector-5.0.28.dist-info}/licenses/LICENSE +0 -0
  26. {megadetector-5.0.27.dist-info → megadetector-5.0.28.dist-info}/top_level.txt +0 -0
@@ -72,7 +72,7 @@ def recursive_file_list(base_dir,
72
72
  assert os.path.isdir(base_dir), '{} is not a folder'.format(base_dir)
73
73
 
74
74
  all_files = []
75
-
75
+
76
76
  if recursive:
77
77
  for root, _, filenames in os.walk(base_dir):
78
78
  for filename in filenames:
@@ -454,6 +454,25 @@ def top_level_folder(p):
454
454
  # ...top_level_folder()
455
455
 
456
456
 
457
+ def path_join(*paths, convert_slashes=True):
458
+ r"""
459
+ Wrapper for os.path.join that optionally converts backslashes to forward slashes.
460
+
461
+ Args:
462
+ *paths (variable-length set of strings): Path components to be joined.
463
+ convert_slashes (bool, optional): whether to convert \\ to /
464
+
465
+ Returns:
466
+ A string with the joined path components.
467
+ """
468
+
469
+ joined_path = os.path.join(*paths)
470
+ if convert_slashes:
471
+ return joined_path.replace('\\', '/')
472
+ else:
473
+ return joined_path
474
+
475
+
457
476
  #%% Test driver for top_level_folder
458
477
 
459
478
  if False:
@@ -665,10 +684,9 @@ def environment_is_wsl():
665
684
  return 'microsoft' in platform_string and 'wsl' in platform_string
666
685
 
667
686
 
668
- def wsl_path_to_windows_path(filename):
687
+ def wsl_path_to_windows_path(filename, failure_behavior='none'):
669
688
  r"""
670
- Converts a WSL path to a Windows path, or returns None if that's not possible. E.g.
671
- converts:
689
+ Converts a WSL path to a Windows path. For example, converts:
672
690
 
673
691
  /mnt/e/a/b/c
674
692
 
@@ -678,27 +696,42 @@ def wsl_path_to_windows_path(filename):
678
696
 
679
697
  Args:
680
698
  filename (str): filename to convert
699
+ failure_behavior (str): what to do if the path can't be processed as a WSL path.
700
+ 'none' to return None in this case, 'original' to return the original path.
681
701
 
682
702
  Returns:
683
- str: Windows equivalent to the WSL path [filename], or [filename] if the current
684
- environment is neither Windows nor WSL.
703
+ str: Windows equivalent to the WSL path [filename]
685
704
  """
686
705
 
687
- if (not environment_is_wsl()) and (os.name != 'nt'):
688
- return filename
706
+ assert failure_behavior in ('none','original'), \
707
+ 'Unrecognized failure_behavior value {}'.format(failure_behavior)
689
708
 
690
- if environment_is_wsl():
691
- result = subprocess.run(['wslpath', '-w', filename], text=True, capture_output=True)
692
- else:
693
- result = subprocess.run(['wsl', 'wslpath', '-w', filename], text=True, capture_output=True)
694
- if result.returncode != 0:
695
- print('Could not convert path {} from WSL to Windows'.format(filename))
696
- return None
709
+ # Check whether the path follows the standard WSL mount pattern
710
+ wsl_path_pattern = r'^/mnt/([a-zA-Z])(/.*)?$'
711
+ match = re.match(wsl_path_pattern, filename)
697
712
 
698
- return result.stdout.strip()
713
+ if match:
714
+
715
+ # Extract the drive letter and the rest of the path
716
+ drive_letter = match.group(1)
717
+ path_remainder = match.group(2) if match.group(2) else ''
718
+
719
+ # Convert forward slashes to backslashes for Windows
720
+ path_remainder = path_remainder.replace('/', '\\')
721
+
722
+ # Format the Windows path
723
+ windows_path = f"{drive_letter}:{path_remainder}"
724
+ return windows_path
699
725
 
726
+ if failure_behavior == 'none':
727
+ return None
728
+ else:
729
+ return filename
700
730
 
701
- def windows_path_to_wsl_path(filename):
731
+ # ...def wsl_path_to_windows_path(...)
732
+
733
+
734
+ def windows_path_to_wsl_path(filename, failure_behavior='none'):
702
735
  r"""
703
736
  Converts a Windows path to a WSL path, or returns None if that's not possible. E.g.
704
737
  converts:
@@ -711,25 +744,38 @@ def windows_path_to_wsl_path(filename):
711
744
 
712
745
  Args:
713
746
  filename (str): filename to convert
747
+ failure_behavior (str): what to do if the path can't be processed as a Windows path.
748
+ 'none' to return None in this case, 'original' to return the original path.
714
749
 
715
750
  Returns:
716
- str: WSL equivalent to the Windows path [filename], or [filename] if the current
717
- environment is neither Windows nor WSL.
751
+ str: WSL equivalent to the Windows path [filename]
718
752
  """
719
753
 
720
- if (not environment_is_wsl()) and (os.name != 'nt'):
721
- return filename
754
+ assert failure_behavior in ('none','original'), \
755
+ 'Unrecognized failure_behavior value {}'.format(failure_behavior)
722
756
 
723
- if environment_is_wsl():
724
- result = subprocess.run(['wslpath', '-u', filename], text=True, capture_output=True)
725
- else:
726
- result = subprocess.run(['wsl', 'wslpath', '-u', filename], text=True, capture_output=True)
727
- if result.returncode != 0:
728
- print('Could not convert path {} from Windows to WSL'.format(filename))
757
+ filename = filename.replace('\\', '/')
758
+
759
+ # Check whether the path follows a Windows drive letter pattern
760
+ windows_path_pattern = r'^([a-zA-Z]):(/.*)?$'
761
+ match = re.match(windows_path_pattern, filename)
762
+
763
+ if match:
764
+ # Extract the drive letter and the rest of the path
765
+ drive_letter = match.group(1).lower() # Convert to lowercase for WSL
766
+ path_remainder = match.group(2) if match.group(2) else ''
767
+
768
+ # Format the WSL path
769
+ wsl_path = f"/mnt/{drive_letter}{path_remainder}"
770
+ return wsl_path
771
+
772
+ if failure_behavior == 'none':
729
773
  return None
774
+ else:
775
+ return filename
730
776
 
731
- return result.stdout.strip()
732
-
777
+ # ...def window_path_to_wsl_path(...)
778
+
733
779
 
734
780
  def open_file_in_chrome(filename):
735
781
  """
@@ -28,7 +28,8 @@ def split_locations_into_train_val(location_to_category_counts,
28
28
  target_val_fraction=0.15,
29
29
  category_to_max_allowable_error=None,
30
30
  category_to_error_weight=None,
31
- default_max_allowable_error=0.1):
31
+ default_max_allowable_error=0.1,
32
+ require_complete_coverage=True):
32
33
  """
33
34
  Splits a list of location IDs into training and validation, targeting a specific
34
35
  train/val split for each category, but allowing some categories to be tighter or looser
@@ -63,6 +64,8 @@ def split_locations_into_train_val(location_to_category_counts,
63
64
  default_max_allowable_error (float, optional): the maximum allowable error for categories not
64
65
  present in [category_to_max_allowable_error]. Set to None (or >= 1.0) to disable hard
65
66
  constraints for categories not present in [category_to_max_allowable_error]
67
+ require_complete_coverage (bool, optional): require that every category appear in both train and
68
+ val
66
69
 
67
70
  Returns:
68
71
  tuple: A two-element tuple:
@@ -125,7 +128,7 @@ def split_locations_into_train_val(location_to_category_counts,
125
128
  category_val_fraction = category_val_count / (category_val_count + category_train_count)
126
129
  category_to_val_fraction[category_id] = category_val_fraction
127
130
 
128
- # Absolute deviation from the target val fraction for each categorys
131
+ # Absolute deviation from the target val fraction for each category
129
132
  category_errors = {}
130
133
  weighted_category_errors = {}
131
134
 
@@ -161,18 +164,28 @@ def split_locations_into_train_val(location_to_category_counts,
161
164
  seed_satisfies_hard_constraints = True
162
165
 
163
166
  for category in category_to_val_fraction:
164
- if category in category_to_max_allowable_error:
167
+ if category in category_to_max_allowable_error:
165
168
  max_allowable_error = category_to_max_allowable_error[category]
166
169
  else:
167
170
  if default_max_allowable_error is None:
168
171
  continue
169
172
  max_allowable_error = default_max_allowable_error
170
173
  val_fraction = category_to_val_fraction[category]
174
+
175
+ # If necessary, verify that this category doesn't *only* appear in train or val
176
+ if require_complete_coverage:
177
+ if (val_fraction == 0.0) or (val_fraction == 1.0):
178
+ seed_satisfies_hard_constraints = False
179
+ break
180
+
181
+ # Check whether this category exceeds the hard maximum deviation
171
182
  category_error = abs(val_fraction - target_val_fraction)
172
183
  if category_error > max_allowable_error:
173
184
  seed_satisfies_hard_constraints = False
174
185
  break
175
186
 
187
+ # ...for each category
188
+
176
189
  if seed_satisfies_hard_constraints:
177
190
  random_seed_to_weighted_average_error[random_seed] = weighted_average_error
178
191