megadetector 5.0.23__py3-none-any.whl → 5.0.24__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 (38) hide show
  1. megadetector/api/synchronous/api_core/animal_detection_api/api_backend.py +2 -3
  2. megadetector/classification/merge_classification_detection_output.py +2 -2
  3. megadetector/data_management/coco_to_labelme.py +2 -1
  4. megadetector/data_management/databases/integrity_check_json_db.py +15 -14
  5. megadetector/data_management/databases/subset_json_db.py +49 -21
  6. megadetector/data_management/mewc_to_md.py +340 -0
  7. megadetector/data_management/wi_to_md.py +41 -0
  8. megadetector/data_management/yolo_output_to_md_output.py +15 -8
  9. megadetector/detection/process_video.py +24 -7
  10. megadetector/detection/pytorch_detector.py +841 -160
  11. megadetector/detection/run_detector.py +340 -146
  12. megadetector/detection/run_detector_batch.py +304 -68
  13. megadetector/detection/run_inference_with_yolov5_val.py +61 -4
  14. megadetector/detection/tf_detector.py +6 -1
  15. megadetector/postprocessing/{combine_api_outputs.py → combine_batch_outputs.py} +10 -13
  16. megadetector/postprocessing/compare_batch_results.py +68 -6
  17. megadetector/postprocessing/md_to_labelme.py +7 -7
  18. megadetector/postprocessing/md_to_wi.py +40 -0
  19. megadetector/postprocessing/merge_detections.py +1 -1
  20. megadetector/postprocessing/postprocess_batch_results.py +10 -3
  21. megadetector/postprocessing/separate_detections_into_folders.py +32 -4
  22. megadetector/postprocessing/validate_batch_results.py +9 -4
  23. megadetector/utils/ct_utils.py +165 -45
  24. megadetector/utils/gpu_test.py +107 -0
  25. megadetector/utils/md_tests.py +355 -108
  26. megadetector/utils/path_utils.py +9 -2
  27. megadetector/utils/wi_utils.py +1794 -0
  28. megadetector/visualization/visualization_utils.py +82 -16
  29. megadetector/visualization/visualize_db.py +25 -7
  30. megadetector/visualization/visualize_detector_output.py +60 -13
  31. {megadetector-5.0.23.dist-info → megadetector-5.0.24.dist-info}/METADATA +10 -24
  32. {megadetector-5.0.23.dist-info → megadetector-5.0.24.dist-info}/RECORD +35 -33
  33. megadetector/detection/detector_training/__init__.py +0 -0
  34. megadetector/detection/detector_training/model_main_tf2.py +0 -114
  35. megadetector/utils/torch_test.py +0 -32
  36. {megadetector-5.0.23.dist-info → megadetector-5.0.24.dist-info}/LICENSE +0 -0
  37. {megadetector-5.0.23.dist-info → megadetector-5.0.24.dist-info}/WHEEL +0 -0
  38. {megadetector-5.0.23.dist-info → megadetector-5.0.24.dist-info}/top_level.txt +0 -0
@@ -28,8 +28,8 @@ image_extensions = ['.jpg', '.jpeg', '.gif', '.png']
28
28
 
29
29
  def truncate_float_array(xs, precision=3):
30
30
  """
31
- Vectorized version of truncate_float(...), truncates the fractional portion of each
32
- floating-point value to a specific number of floating-point digits.
31
+ Truncates the fractional portion of each floating-point value in the array [xs]
32
+ to a specific number of floating-point digits.
33
33
 
34
34
  Args:
35
35
  xs (list): list of floats to truncate
@@ -42,6 +42,37 @@ def truncate_float_array(xs, precision=3):
42
42
  return [truncate_float(x, precision=precision) for x in xs]
43
43
 
44
44
 
45
+ def round_float_array(xs, precision=3):
46
+ """
47
+ Truncates the fractional portion of each floating-point value in the array [xs]
48
+ to a specific number of floating-point digits.
49
+
50
+ Args:
51
+ xs (list): list of floats to round
52
+ precision (int, optional): the number of significant digits to preserve, should be >= 1
53
+
54
+ Returns:
55
+ list: list of rounded floats
56
+ """
57
+
58
+ return [round_float(x,precision) for x in xs]
59
+
60
+
61
+ def round_float(x, precision=3):
62
+ """
63
+ Convenience wrapper for the native Python round()
64
+
65
+ Args:
66
+ x (float): number to truncate
67
+ precision (int, optional): the number of significant digits to preserve, should be >= 1
68
+
69
+ Returns:
70
+ float: rounded value
71
+ """
72
+
73
+ return round(x,precision)
74
+
75
+
45
76
  def truncate_float(x, precision=3):
46
77
  """
47
78
  Truncates the fractional portion of a floating-point value to a specific number of
@@ -63,26 +94,7 @@ def truncate_float(x, precision=3):
63
94
  float: truncated version of [x]
64
95
  """
65
96
 
66
- assert precision > 0
67
-
68
- if np.isclose(x, 0):
69
-
70
- return 0
71
-
72
- elif (x > 1):
73
-
74
- fractional_component = x - 1.0
75
- return 1 + truncate_float(fractional_component)
76
-
77
- else:
78
-
79
- # Determine the factor, which shifts the decimal point of x
80
- # just behind the last significant digit.
81
- factor = math.pow(10, precision - 1 - math.floor(math.log10(abs(x))))
82
-
83
- # Shift decimal point by multiplication with factor, flooring, and
84
- # division by factor.
85
- return math.floor(x * factor)/factor
97
+ return math.floor(x * (10 ** precision)) / (10 ** precision)
86
98
 
87
99
 
88
100
  def args_to_object(args, obj):
@@ -187,7 +199,8 @@ def write_json(path, content, indent=1):
187
199
 
188
200
  def convert_yolo_to_xywh(yolo_box):
189
201
  """
190
- Converts a YOLO format bounding box to [x_min, y_min, width_of_box, height_of_box].
202
+ Converts a YOLO format bounding box [x_center, y_center, w, h] to
203
+ [x_min, y_min, width_of_box, height_of_box].
191
204
 
192
205
  Args:
193
206
  yolo_box (list): bounding box of format [x_center, y_center, width_of_box, height_of_box]
@@ -202,37 +215,21 @@ def convert_yolo_to_xywh(yolo_box):
202
215
  return [x_min, y_min, width_of_box, height_of_box]
203
216
 
204
217
 
205
- def convert_xywh_to_tf(api_box):
218
+ def convert_xywh_to_xyxy(api_box):
206
219
  """
207
- Converts an xywh bounding box (the format used in MD output) to the [y_min, x_min, y_max, x_max]
208
- format that the TensorFlow Object Detection API uses.
220
+ Converts an xywh bounding box (the MD output format) to an xyxy bounding box (the format
221
+ produced by TF-based MD models).
209
222
 
210
223
  Args:
211
- api_box: bbox output by the batch processing API [x_min, y_min, width_of_box, height_of_box]
224
+ api_box (list): bbox formatted as [x_min, y_min, width_of_box, height_of_box]
212
225
 
213
226
  Returns:
214
- list: bbox with coordinates represented as [y_min, x_min, y_max, x_max]
227
+ list: bbox formatted as [x_min, y_min, x_max, y_max]
215
228
  """
216
-
229
+
217
230
  x_min, y_min, width_of_box, height_of_box = api_box
218
231
  x_max = x_min + width_of_box
219
232
  y_max = y_min + height_of_box
220
- return [y_min, x_min, y_max, x_max]
221
-
222
-
223
- def convert_xywh_to_xyxy(api_bbox):
224
- """
225
- Converts an xywh bounding box (the MD output format) to an xyxy bounding box.
226
-
227
- Args:
228
- api_bbox (list): bbox formatted as [x_min, y_min, width_of_box, height_of_box]
229
-
230
- Returns:
231
- list: bbox formatted as [x_min, y_min, x_max, y_max]
232
- """
233
-
234
- x_min, y_min, width_of_box, height_of_box = api_bbox
235
- x_max, y_max = x_min + width_of_box, y_min + height_of_box
236
233
  return [x_min, y_min, x_max, y_max]
237
234
 
238
235
 
@@ -706,7 +703,130 @@ def is_function_name(s,calling_namespace):
706
703
  callable(locals().get(s)) or \
707
704
  callable(calling_namespace.get(s)) or \
708
705
  callable(getattr(builtins, s, None))
706
+
709
707
 
708
+ # From https://gist.github.com/fralau/061a4f6c13251367ef1d9a9a99fb3e8d
709
+ def parse_kvp(s,kv_separator='='):
710
+ """
711
+ Parse a key/value pair, separated by [kv_separator]. Errors if s is not
712
+ a valid key/value pair string.
713
+
714
+ Args:
715
+ s (str): the string to parse
716
+ kv_separator (str, optional): the string separating keys from values.
717
+
718
+ Returns:
719
+ tuple: a 2-tuple formatted as (key,value)
720
+ """
721
+
722
+ items = s.split(kv_separator)
723
+ assert len(items) > 1, 'Illegal key-value pair'
724
+ key = items[0].strip()
725
+ if len(items) > 1:
726
+ value = kv_separator.join(items[1:])
727
+ return (key, value)
728
+
729
+
730
+ def parse_kvp_list(items,kv_separator='=',d=None):
731
+ """
732
+ Parse a list key-value pairs into a dictionary. If items is None or [],
733
+ returns {}.
734
+
735
+ Args:
736
+ items (list): the list of KVPs to parse
737
+ kv_separator (str, optional): the string separating keys from values.
738
+ d (dict, optional): the initial dictionary, defaults to {}
739
+
740
+ Returns:
741
+ dict: a dict mapping keys to values
742
+ """
743
+
744
+ if d is None:
745
+ d = {}
746
+
747
+ if items is None or len(items) == 0:
748
+ return d
749
+
750
+ for item in items:
751
+ key, value = parse_kvp(item)
752
+ d[key] = value
753
+
754
+ return d
755
+
756
+
757
+ def dict_to_kvp_list(d,
758
+ item_separator=' ',
759
+ kv_separator='=',
760
+ non_string_value_handling='error'):
761
+ """
762
+ Convert a string <--> string dict into a string containing list of list of
763
+ key-value pairs. I.e., converts {'a':'dog','b':'cat'} to 'a=dog b=cat'. If
764
+ d is None, returns None. If d is empty, returns ''.
765
+
766
+ Args:
767
+ d (dict): the dictionary to convert, must contain only strings
768
+ item_separator (str, optional): the delimiter between KV pairs
769
+ kv_separator (str, optional): the separator betweena a key and its value
770
+ non_string_value_handling (str, optional): what do do with non-string values,
771
+ can be "omit", "error", or "convert"
772
+
773
+ Returns:
774
+ str: the string representation of [d]
775
+ """
776
+
777
+ if d is None:
778
+ return None
779
+
780
+ if len(d) == 0:
781
+ return ''
782
+
783
+ s = ''
784
+ for k in d.keys():
785
+ assert isinstance(k,str), 'Input {} is not a str <--> str dict'.format(str(d))
786
+ v = d[k]
787
+ if not isinstance(v,str):
788
+ if non_string_value_handling == 'error':
789
+ raise ValueError('Input {} is not a str <--> str dict'.format(str(d)))
790
+ elif non_string_value_handling == 'omit':
791
+ continue
792
+ elif non_string_value_handling == 'convert':
793
+ v = str(v)
794
+ else:
795
+ raise ValueError('Unrecognized non_string_value_handling value: {}'.format(
796
+ non_string_value_handling))
797
+ if s is None:
798
+ s = ''
799
+ else:
800
+ s += item_separator
801
+ s += k + kv_separator + v
802
+
803
+ return s
804
+
805
+
806
+ def parse_bool_string(s):
807
+ """
808
+ Convert the strings "true" or "false" to boolean values. Case-insensitive, discards
809
+ leading and trailing whitespace. If s is already a bool, returns s.
810
+
811
+ Args:
812
+ s (str or bool): the string to parse, or the bool to return
813
+
814
+ Returns:
815
+ bool: the parsed value
816
+ """
817
+
818
+ if isinstance(s,bool):
819
+ return s
820
+ s = s.lower().strip()
821
+ if s == 'true':
822
+ return True
823
+ elif s == 'false':
824
+ return False
825
+ else:
826
+ raise ValueError('Cannot parse bool from string {}'.format(str(s)))
827
+
828
+
829
+ #%% Test driver
710
830
 
711
831
  def __module_test__():
712
832
  """
@@ -0,0 +1,107 @@
1
+ """
2
+
3
+ gpu_test.py
4
+
5
+ Simple script to verify CUDA availability, used to verify a CUDA environment
6
+ for TF or PyTorch
7
+
8
+ """
9
+
10
+ #%% Torch/TF test functions
11
+
12
+ def torch_test():
13
+ """
14
+ Print diagnostic information about Torch/CUDA status, including Torch/CUDA versions
15
+ and all available CUDA device names.
16
+
17
+ Returns:
18
+ int: The number of CUDA devices reported by PyTorch.
19
+ """
20
+
21
+ try:
22
+ import torch
23
+ except Exception as e: #noqa
24
+ print('PyTorch unavailable, not running PyTorch tests. PyTorch import error was:\n{}'.format(
25
+ str(e)))
26
+ return
27
+
28
+ print('Torch version: {}'.format(str(torch.__version__)))
29
+ print('CUDA available (according to PyTorch): {}'.format(torch.cuda.is_available()))
30
+ if torch.cuda.is_available():
31
+ print('CUDA version (according to PyTorch): {}'.format(torch.version.cuda))
32
+ print('CuDNN version (according to PyTorch): {}'.format(torch.backends.cudnn.version()))
33
+
34
+ device_ids = list(range(torch.cuda.device_count()))
35
+
36
+ if len(device_ids) > 0:
37
+ cuda_str = 'Found {} CUDA devices:'.format(len(device_ids))
38
+ print(cuda_str)
39
+
40
+ for device_id in device_ids:
41
+ device_name = 'unknown'
42
+ try:
43
+ device_name = torch.cuda.get_device_name(device=device_id)
44
+ except Exception as e: #noqa
45
+ pass
46
+ print('{}: {}'.format(device_id,device_name))
47
+ else:
48
+ print('No CUDA GPUs reported by PyTorch')
49
+
50
+ try:
51
+ if torch.backends.mps.is_built and torch.backends.mps.is_available():
52
+ print('PyTorch reports that Metal Performance Shaders are available')
53
+ except Exception:
54
+ pass
55
+ return len(device_ids)
56
+
57
+
58
+ def tf_test():
59
+ """
60
+ Print diagnostic information about TF/CUDA status.
61
+
62
+ Returns:
63
+ int: The number of CUDA devices reported by PyTorch.
64
+ """
65
+
66
+ try:
67
+ import tensorflow as tf
68
+ except Exception as e: #noqa
69
+ print('TensorFlow unavailable, not running TF tests. TF import error was:\n{}'.format(
70
+ str(e)))
71
+ return
72
+
73
+ from tensorflow.python.platform import build_info as build
74
+ print(f"TF version: {tf.__version__}")
75
+ print(f"CUDA build version reported by TensorFlow: {build.build_info['cuda_version']}")
76
+ print(f"CuDNN build version reported by TensorFlow: {build.build_info['cudnn_version']}")
77
+
78
+ try:
79
+ from tensorflow.python.compiler.tensorrt import trt_convert as trt
80
+ print("Linked TensorRT version: {}".format(trt.trt_utils._pywrap_py_utils.get_linked_tensorrt_version()))
81
+ except Exception:
82
+ print('Could not probe TensorRT version')
83
+
84
+ gpus = tf.config.list_physical_devices('GPU')
85
+ if gpus is None:
86
+ gpus = []
87
+
88
+ if len(gpus) > 0:
89
+ print('TensorFlow found the following GPUs:')
90
+ for gpu in gpus:
91
+ print(gpu.name)
92
+
93
+ else:
94
+ print('No GPUs reported by TensorFlow')
95
+
96
+ return len(gpus)
97
+
98
+
99
+ #%% Command-line driver
100
+
101
+ if __name__ == '__main__':
102
+
103
+ print('*** Running Torch tests ***\n')
104
+ torch_test()
105
+
106
+ print('\n*** Running TF tests ***\n')
107
+ tf_test()