megadetector 5.0.21__py3-none-any.whl → 5.0.23__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 (37) hide show
  1. megadetector/data_management/cct_json_utils.py +143 -7
  2. megadetector/data_management/cct_to_md.py +12 -5
  3. megadetector/data_management/databases/integrity_check_json_db.py +83 -77
  4. megadetector/data_management/importers/raic_csv_to_md_results.py +416 -0
  5. megadetector/data_management/importers/zamba_results_to_md_results.py +1 -2
  6. megadetector/data_management/lila/create_lila_test_set.py +25 -11
  7. megadetector/data_management/lila/download_lila_subset.py +9 -2
  8. megadetector/data_management/lila/generate_lila_per_image_labels.py +3 -2
  9. megadetector/data_management/lila/test_lila_metadata_urls.py +5 -1
  10. megadetector/data_management/read_exif.py +10 -14
  11. megadetector/data_management/rename_images.py +1 -1
  12. megadetector/detection/process_video.py +14 -3
  13. megadetector/detection/pytorch_detector.py +15 -3
  14. megadetector/detection/run_detector.py +4 -3
  15. megadetector/detection/run_detector_batch.py +2 -2
  16. megadetector/detection/run_inference_with_yolov5_val.py +121 -13
  17. megadetector/detection/video_utils.py +21 -10
  18. megadetector/postprocessing/classification_postprocessing.py +1 -1
  19. megadetector/postprocessing/compare_batch_results.py +931 -142
  20. megadetector/postprocessing/detector_calibration.py +243 -45
  21. megadetector/postprocessing/md_to_coco.py +85 -20
  22. megadetector/postprocessing/postprocess_batch_results.py +0 -1
  23. megadetector/postprocessing/validate_batch_results.py +65 -15
  24. megadetector/taxonomy_mapping/map_new_lila_datasets.py +15 -12
  25. megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +1 -1
  26. megadetector/taxonomy_mapping/preview_lila_taxonomy.py +3 -1
  27. megadetector/utils/ct_utils.py +71 -14
  28. megadetector/utils/md_tests.py +9 -1
  29. megadetector/utils/path_utils.py +14 -7
  30. megadetector/utils/process_utils.py +9 -3
  31. megadetector/utils/write_html_image_list.py +5 -1
  32. megadetector/visualization/visualization_utils.py +211 -87
  33. {megadetector-5.0.21.dist-info → megadetector-5.0.23.dist-info}/METADATA +19 -18
  34. {megadetector-5.0.21.dist-info → megadetector-5.0.23.dist-info}/RECORD +37 -36
  35. {megadetector-5.0.21.dist-info → megadetector-5.0.23.dist-info}/WHEEL +1 -1
  36. {megadetector-5.0.21.dist-info → megadetector-5.0.23.dist-info}/LICENSE +0 -0
  37. {megadetector-5.0.21.dist-info → megadetector-5.0.23.dist-info}/top_level.txt +0 -0
@@ -40,6 +40,10 @@ EXIF_IMAGE_ROTATIONS = {
40
40
 
41
41
  TEXTALIGN_LEFT = 0
42
42
  TEXTALIGN_RIGHT = 1
43
+ TEXTALIGN_CENTER = 2
44
+
45
+ VTEXTALIGN_TOP = 0
46
+ VTEXTALIGN_BOTTOM = 1
43
47
 
44
48
  # Convert category ID from int to str
45
49
  DEFAULT_DETECTOR_LABEL_MAP = {
@@ -413,6 +417,7 @@ def render_detection_bounding_boxes(detections,
413
417
  max_classifications=3,
414
418
  colormap=None,
415
419
  textalign=TEXTALIGN_LEFT,
420
+ vtextalign=VTEXTALIGN_TOP,
416
421
  label_font_size=DEFAULT_LABEL_FONT_SIZE,
417
422
  custom_strings=None):
418
423
  """
@@ -469,37 +474,27 @@ def render_detection_bounding_boxes(detections,
469
474
  ]
470
475
 
471
476
  image (PIL.Image.Image): image on which we should render detections
472
-
473
477
  label_map (dict, optional): optional, mapping the numeric label to a string name. The type of the
474
478
  numeric label (typically strings) needs to be consistent with the keys in label_map; no casting is
475
479
  carried out. If [label_map] is None, no labels are shown (not even numbers and confidence values).
476
480
  If you want category numbers and confidence values without class labels, use the default value,
477
481
  the string 'show_categories'.
478
-
479
482
  classification_label_map (dict, optional): optional, mapping of the string class labels to the actual
480
483
  class names. The type of the numeric label (typically strings) needs to be consistent with the keys
481
484
  in label_map; no casting is carried out. If [label_map] is None, no labels are shown (not even numbers
482
485
  and confidence values).
483
-
484
486
  confidence_threshold (float or dict, optional), threshold above which boxes are rendered. Can also be a
485
- dictionary mapping category IDs to thresholds.
486
-
487
- thickness (int, optional): line thickness in pixels
488
-
489
- expansion (int, optional): number of pixels to expand bounding boxes on each side
490
-
487
+ dictionary mapping category IDs to thresholds.
488
+ thickness (int, optional): line thickness in pixels
489
+ expansion (int, optional): number of pixels to expand bounding boxes on each side
491
490
  classification_confidence_threshold (float, optional): confidence above which classification results
492
- are displayed
493
-
494
- max_classifications (int, optional): maximum number of classification results rendered for one image
495
-
491
+ are displayed
492
+ max_classifications (int, optional): maximum number of classification results rendered for one image
496
493
  colormap (list, optional): list of color names, used to choose colors for categories by
497
- indexing with the values in [classes]; defaults to a reasonable set of colors
498
-
499
- textalign (int, optional): TEXTALIGN_LEFT or TEXTALIGN_RIGHT
500
-
501
- label_font_size (float, optional): font size for labels
502
-
494
+ indexing with the values in [classes]; defaults to a reasonable set of colors
495
+ textalign (int, optional): TEXTALIGN_LEFT, TEXTALIGN_CENTER, or TEXTALIGN_RIGHT
496
+ vtextalign (int, optional): VTEXTALIGN_TOP or VTEXTALIGN_BOTTOM
497
+ label_font_size (float, optional): font size for labels
503
498
  custom_strings: optional set of strings to append to detection labels, should have the
504
499
  same length as [detections]. Appended before any classification labels.
505
500
  """
@@ -613,7 +608,8 @@ def render_detection_bounding_boxes(detections,
613
608
 
614
609
  draw_bounding_boxes_on_image(image, display_boxes, classes,
615
610
  display_strs=display_strs, thickness=thickness,
616
- expansion=expansion, colormap=colormap, textalign=textalign,
611
+ expansion=expansion, colormap=colormap,
612
+ textalign=textalign, vtextalign=vtextalign,
617
613
  label_font_size=label_font_size)
618
614
 
619
615
  # ...render_detection_bounding_boxes(...)
@@ -627,6 +623,8 @@ def draw_bounding_boxes_on_image(image,
627
623
  display_strs=None,
628
624
  colormap=None,
629
625
  textalign=TEXTALIGN_LEFT,
626
+ vtextalign=VTEXTALIGN_TOP,
627
+ text_rotation=None,
630
628
  label_font_size=DEFAULT_LABEL_FONT_SIZE):
631
629
  """
632
630
  Draws bounding boxes on an image. Modifies the image in place.
@@ -647,7 +645,9 @@ def draw_bounding_boxes_on_image(image,
647
645
  or classification categories and/or confidence values.
648
646
  colormap (list, optional): list of color names, used to choose colors for categories by
649
647
  indexing with the values in [classes]; defaults to a reasonable set of colors
650
- textalign (int, optional): TEXTALIGN_LEFT or TEXTALIGN_RIGHT
648
+ textalign (int, optional): TEXTALIGN_LEFT, TEXTALIGN_CENTER, or TEXTALIGN_RIGHT
649
+ vtextalign (int, optional): VTEXTALIGN_TOP or VTEXTALIGN_BOTTOM
650
+ text_rotation (float, optional): rotation to apply to text
651
651
  label_font_size (float, optional): font size for labels
652
652
  """
653
653
 
@@ -655,19 +655,21 @@ def draw_bounding_boxes_on_image(image,
655
655
  if not boxes_shape:
656
656
  return
657
657
  if len(boxes_shape) != 2 or boxes_shape[1] != 4:
658
- # print('Input must be of size [N, 4], but is ' + str(boxes_shape))
659
- return # no object detection on this image, return
658
+ return
660
659
  for i in range(boxes_shape[0]):
660
+ display_str_list = None
661
661
  if display_strs:
662
662
  display_str_list = display_strs[i]
663
- draw_bounding_box_on_image(image,
664
- boxes[i, 0], boxes[i, 1], boxes[i, 2], boxes[i, 3],
665
- classes[i],
666
- thickness=thickness, expansion=expansion,
667
- display_str_list=display_str_list,
668
- colormap=colormap,
669
- textalign=textalign,
670
- label_font_size=label_font_size)
663
+ draw_bounding_box_on_image(image,
664
+ boxes[i, 0], boxes[i, 1], boxes[i, 2], boxes[i, 3],
665
+ classes[i],
666
+ thickness=thickness, expansion=expansion,
667
+ display_str_list=display_str_list,
668
+ colormap=colormap,
669
+ textalign=textalign,
670
+ vtextalign=vtextalign,
671
+ text_rotation=text_rotation,
672
+ label_font_size=label_font_size)
671
673
 
672
674
  # ...draw_bounding_boxes_on_image(...)
673
675
 
@@ -682,7 +684,7 @@ def get_text_size(font,s):
682
684
  s (str): the string whose size we should query
683
685
 
684
686
  Returns:
685
- tuple: (w,h), both floats in pixel coordinatess
687
+ tuple: (w,h), both floats in pixel coordinates
686
688
  """
687
689
 
688
690
  # This is what we did w/Pillow 9
@@ -714,7 +716,9 @@ def draw_bounding_box_on_image(image,
714
716
  use_normalized_coordinates=True,
715
717
  label_font_size=DEFAULT_LABEL_FONT_SIZE,
716
718
  colormap=None,
717
- textalign=TEXTALIGN_LEFT):
719
+ textalign=TEXTALIGN_LEFT,
720
+ vtextalign=VTEXTALIGN_TOP,
721
+ text_rotation=None):
718
722
  """
719
723
  Adds a bounding box to an image. Modifies the image in place.
720
724
 
@@ -747,7 +751,9 @@ def draw_bounding_box_on_image(image,
747
751
  label_font_size (float, optional): font size
748
752
  colormap (list, optional): list of color names, used to choose colors for categories by
749
753
  indexing with the values in [classes]; defaults to a reasonable set of colors
750
- textalign (int, optional): TEXTALIGN_LEFT or TEXTALIGN_RIGHT
754
+ textalign (int, optional): TEXTALIGN_LEFT, TEXTALIGN_CENTER, or TEXTALIGN_RIGHT
755
+ vtextalign (int, optional): VTEXTALIGN_TOP or VTEXTALIGN_BOTTOM
756
+ text_rotation (float, optional): rotation to apply to text
751
757
  """
752
758
 
753
759
  if colormap is None:
@@ -798,53 +804,98 @@ def draw_bounding_box_on_image(image,
798
804
  draw.line([(left, top), (left, bottom), (right, bottom),
799
805
  (right, top), (left, top)], width=thickness, fill=color)
800
806
 
801
- try:
802
- font = ImageFont.truetype('arial.ttf', label_font_size)
803
- except IOError:
804
- font = ImageFont.load_default()
805
-
806
- # If the total height of the display strings added to the top of the bounding
807
- # box exceeds the top of the image, stack the strings below the bounding box
808
- # instead of above.
809
- display_str_heights = [get_text_size(font,ds)[1] for ds in display_str_list]
810
-
811
- # Each display_str has a top and bottom margin of 0.05x.
812
- total_display_str_height = (1 + 2 * 0.05) * sum(display_str_heights)
813
-
814
- if top > total_display_str_height:
815
- text_bottom = top
816
- else:
817
- text_bottom = bottom + total_display_str_height
818
-
819
- # Reverse list and print from bottom to top.
820
- for display_str in display_str_list[::-1]:
807
+ if display_str_list is not None:
821
808
 
822
- # Skip empty strings
823
- if len(display_str) == 0:
824
- continue
825
-
826
- text_width, text_height = get_text_size(font,display_str)
827
-
828
- text_left = left
829
-
830
- if textalign == TEXTALIGN_RIGHT:
831
- text_left = right - text_width
809
+ try:
810
+ font = ImageFont.truetype('arial.ttf', label_font_size)
811
+ except IOError:
812
+ font = ImageFont.load_default()
813
+
814
+ display_str_heights = [get_text_size(font,ds)[1] for ds in display_str_list]
815
+
816
+ # Each display_str has a top and bottom margin of 0.05x.
817
+ total_display_str_height = (1 + 2 * 0.05) * sum(display_str_heights)
818
+
819
+ # Reverse list and print from bottom to top
820
+ for i_str,display_str in enumerate(display_str_list[::-1]):
821
+
822
+ # Skip empty strings
823
+ if len(display_str) == 0:
824
+ continue
832
825
 
833
- margin = np.ceil(0.05 * text_height)
834
-
835
- draw.rectangle(
836
- [(text_left, text_bottom - text_height - 2 * margin), (text_left + text_width,
837
- text_bottom)],
838
- fill=color)
839
-
840
- draw.text(
841
- (text_left + margin, text_bottom - text_height - margin),
842
- display_str,
843
- fill='black',
844
- font=font)
845
-
846
- text_bottom -= (text_height + 2 * margin)
826
+ text_width, text_height = get_text_size(font,display_str)
827
+ margin = int(np.ceil(0.05 * text_height))
828
+
829
+ if text_rotation is not None and text_rotation != 0:
830
+
831
+ assert text_rotation == -90, \
832
+ 'Only -90-degree text rotation is supported'
833
+
834
+ image_tmp = Image.new('RGB',(text_width+2*margin,text_height+2*margin))
835
+ image_tmp_draw = ImageDraw.Draw(image_tmp)
836
+ image_tmp_draw.rectangle([0,0,text_width+2*margin,text_height+2*margin],fill=color)
837
+ image_tmp_draw.text( (margin,margin), display_str, font=font, fill='black')
838
+ rotated_text = image_tmp.rotate(text_rotation,expand=1)
839
+
840
+ if textalign == TEXTALIGN_RIGHT:
841
+ text_left = right
842
+ else:
843
+ text_left = left
844
+ text_left = int(text_left + (text_height) * i_str)
845
+
846
+ if vtextalign == VTEXTALIGN_BOTTOM:
847
+ text_top = bottom - text_width
848
+ else:
849
+ text_top = top
850
+ text_left = int(text_left)
851
+ text_top = int(text_top)
852
+
853
+ image.paste(rotated_text,[text_left,text_top])
854
+
855
+ else:
856
+
857
+ # If the total height of the display strings added to the top of the bounding
858
+ # box exceeds the top of the image, stack the strings below the bounding box
859
+ # instead of above, and vice-versa if we're bottom-aligning.
860
+ #
861
+ # If the text just doesn't fit outside the box, we don't try anything fancy,
862
+ # it will just appear outside the image.
863
+ if vtextalign == VTEXTALIGN_TOP:
864
+ text_bottom = top
865
+ if (text_bottom - total_display_str_height) < 0:
866
+ text_bottom = bottom + total_display_str_height
867
+ else:
868
+ assert vtextalign == VTEXTALIGN_BOTTOM, \
869
+ 'Unrecognized vertical text alignment {}'.format(vtextalign)
870
+ text_bottom = bottom + total_display_str_height
871
+ if (text_bottom + total_display_str_height) > im_height:
872
+ text_bottom = top
873
+
874
+ text_bottom = int(text_bottom) - i_str * (int(text_height + (2 * margin)))
875
+
876
+ text_left = left
877
+
878
+ if textalign == TEXTALIGN_RIGHT:
879
+ text_left = right - text_width
880
+ elif textalign == TEXTALIGN_CENTER:
881
+ text_left = ((right + left) / 2.0) - (text_width / 2.0)
882
+ text_left = int(text_left)
883
+
884
+ draw.rectangle(
885
+ [(text_left, (text_bottom - text_height) - (2 * margin)),
886
+ (text_left + text_width, text_bottom)],
887
+ fill=color)
888
+
889
+ draw.text(
890
+ (text_left + margin, text_bottom - text_height - margin),
891
+ display_str,
892
+ fill='black',
893
+ font=font)
894
+
895
+ # ...if we're rotating text
847
896
 
897
+ # ...if we're rendering text
898
+
848
899
  # ...def draw_bounding_box_on_image(...)
849
900
 
850
901
 
@@ -897,7 +948,12 @@ def render_db_bounding_boxes(boxes,
897
948
  original_size=None,
898
949
  label_map=None,
899
950
  thickness=DEFAULT_BOX_THICKNESS,
900
- expansion=0):
951
+ expansion=0,
952
+ colormap=None,
953
+ textalign=TEXTALIGN_LEFT,
954
+ vtextalign=VTEXTALIGN_TOP,
955
+ text_rotation=None,
956
+ label_font_size=DEFAULT_LABEL_FONT_SIZE):
901
957
  """
902
958
  Render bounding boxes (with class labels) on an image. This is a wrapper for
903
959
  draw_bounding_boxes_on_image, allowing the caller to operate on a resized image
@@ -919,6 +975,12 @@ def render_db_bounding_boxes(boxes,
919
975
  thickness (int, optional): line width
920
976
  expansion (int, optional): a number of pixels to include on each side of a cropped
921
977
  detection
978
+ colormap (list, optional): list of color names, used to choose colors for categories by
979
+ indexing with the values in [classes]; defaults to a reasonable set of colors
980
+ textalign (int, optional): TEXTALIGN_LEFT, TEXTALIGN_CENTER, or TEXTALIGN_RIGHT
981
+ vtextalign (int, optional): VTEXTALIGN_TOP or VTEXTALIGN_BOTTOM
982
+ text_rotation (float, optional): rotation to apply to text
983
+ label_font_size (float, optional): font size for labels
922
984
  """
923
985
 
924
986
  display_boxes = []
@@ -956,7 +1018,12 @@ def render_db_bounding_boxes(boxes,
956
1018
  classes,
957
1019
  display_strs=display_strs,
958
1020
  thickness=thickness,
959
- expansion=expansion)
1021
+ expansion=expansion,
1022
+ colormap=colormap,
1023
+ textalign=textalign,
1024
+ vtextalign=vtextalign,
1025
+ text_rotation=text_rotation,
1026
+ label_font_size=label_font_size)
960
1027
 
961
1028
  # ...def render_db_bounding_boxes(...)
962
1029
 
@@ -980,9 +1047,9 @@ def draw_bounding_boxes_on_file(input_file,
980
1047
  Args:
981
1048
  input_file (str): filename or URL to load
982
1049
  output_file (str, optional): filename to which we should write the rendered image
983
- detections (list): a list of dictionaries with keys 'conf' and 'bbox';
1050
+ detections (list): a list of dictionaries with keys 'conf', 'bbox', and 'category';
984
1051
  boxes are length-four arrays formatted as [x,y,w,h], normalized,
985
- upper-left origin (this is the standard MD detection format)
1052
+ upper-left origin (this is the standard MD detection format). 'category' is a string-int.
986
1053
  detector_label_map (dict, optional): a dict mapping category IDs to strings. If this
987
1054
  is None, no confidence values or identifiers are shown. If this is {}, just category
988
1055
  indices and confidence values are shown.
@@ -1422,10 +1489,10 @@ def get_image_size(im,verbose=False):
1422
1489
 
1423
1490
 
1424
1491
  def parallel_get_image_sizes(filenames,
1425
- max_workers=16,
1426
- use_threads=True,
1427
- recursive=True,
1428
- verbose=False):
1492
+ max_workers=16,
1493
+ use_threads=True,
1494
+ recursive=True,
1495
+ verbose=False):
1429
1496
  """
1430
1497
  Retrieve image sizes for a list or folder of images
1431
1498
 
@@ -1440,7 +1507,7 @@ def parallel_get_image_sizes(filenames,
1440
1507
  verbose (bool, optional): enable additional debug output
1441
1508
 
1442
1509
  Returns:
1443
- dict: a dict mapping filenames to (w,h) tuples; values will be None for images that fail
1510
+ dict: a dict mapping filenames to (w,h) tuples; the value will be None for images that fail
1444
1511
  to load.
1445
1512
  """
1446
1513
 
@@ -1630,6 +1697,63 @@ def parallel_check_image_integrity(filenames,
1630
1697
 
1631
1698
  if False:
1632
1699
 
1700
+ #%% Text rendering tests
1701
+
1702
+ import os # noqa
1703
+ import numpy as np # noqa
1704
+ from megadetector.visualization.visualization_utils import \
1705
+ draw_bounding_boxes_on_image, exif_preserving_save, load_image, \
1706
+ TEXTALIGN_LEFT,TEXTALIGN_RIGHT,VTEXTALIGN_BOTTOM,VTEXTALIGN_TOP, \
1707
+ DEFAULT_LABEL_FONT_SIZE
1708
+
1709
+ fn = os.path.expanduser('~\AppData\Local\Temp\md-tests\md-test-images\ena24_7904.jpg')
1710
+ output_fn = r'g:\temp\test.jpg'
1711
+
1712
+ image = load_image(fn)
1713
+
1714
+ w = 0.2; h = 0.2
1715
+ all_boxes = [[0.05, 0.05, 0.25, 0.25],
1716
+ [0.05, 0.35, 0.25, 0.6],
1717
+ [0.35, 0.05, 0.6, 0.25],
1718
+ [0.35, 0.35, 0.6, 0.6]]
1719
+
1720
+ alignments = [
1721
+ [TEXTALIGN_LEFT,VTEXTALIGN_TOP],
1722
+ [TEXTALIGN_LEFT,VTEXTALIGN_BOTTOM],
1723
+ [TEXTALIGN_RIGHT,VTEXTALIGN_TOP],
1724
+ [TEXTALIGN_RIGHT,VTEXTALIGN_BOTTOM]
1725
+ ]
1726
+
1727
+ labels = ['left_top','left_bottom','right_top','right_bottom']
1728
+
1729
+ text_rotation = -90
1730
+ n_label_copies = 2
1731
+
1732
+ for i_box,box in enumerate(all_boxes):
1733
+
1734
+ boxes = [box]
1735
+ boxes = np.array(boxes)
1736
+ classes = [i_box]
1737
+ display_strs = [[labels[i_box]]*n_label_copies]
1738
+ textalign = alignments[i_box][0]
1739
+ vtextalign = alignments[i_box][1]
1740
+ draw_bounding_boxes_on_image(image,
1741
+ boxes,
1742
+ classes,
1743
+ thickness=2,
1744
+ expansion=0,
1745
+ display_strs=display_strs,
1746
+ colormap=None,
1747
+ textalign=textalign,
1748
+ vtextalign=vtextalign,
1749
+ label_font_size=DEFAULT_LABEL_FONT_SIZE,
1750
+ text_rotation=text_rotation)
1751
+
1752
+ exif_preserving_save(image,output_fn)
1753
+ from megadetector.utils.path_utils import open_file
1754
+ open_file(output_fn)
1755
+
1756
+
1633
1757
  #%% Recursive resize test
1634
1758
 
1635
1759
  from megadetector.visualization.visualization_utils import resize_image_folder # noqa
@@ -1,10 +1,10 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: megadetector
3
- Version: 5.0.21
3
+ Version: 5.0.23
4
4
  Summary: MegaDetector is an AI model that helps conservation folks spend less time doing boring things with camera trap images.
5
5
  Author-email: Your friendly neighborhood MegaDetector team <cameratraps@lila.science>
6
6
  Maintainer-email: Your friendly neighborhood MegaDetector team <cameratraps@lila.science>
7
- License: MIT License
7
+ License: MIT License
8
8
 
9
9
  Permission is hereby granted, free of charge, to any person obtaining a copy
10
10
  of this software and associated documentation files (the "Software"), to deal
@@ -32,23 +32,24 @@ Keywords: camera traps,conservation,wildlife,ai,megadetector
32
32
  Classifier: Development Status :: 3 - Alpha
33
33
  Classifier: License :: OSI Approved :: MIT License
34
34
  Classifier: Programming Language :: Python :: 3
35
- Requires-Python: <3.12,>=3.9
35
+ Requires-Python: <=3.13,>=3.9
36
36
  Description-Content-Type: text/markdown
37
37
  License-File: LICENSE
38
- Requires-Dist: Pillow >=9.5
39
- Requires-Dist: tqdm >=4.64.0
40
- Requires-Dist: jsonpickle >=3.0.2
41
- Requires-Dist: humanfriendly >=10.0
42
- Requires-Dist: numpy <1.24,>=1.22
43
- Requires-Dist: matplotlib >=3.8.0
44
- Requires-Dist: opencv-python >=4.8.0
45
- Requires-Dist: requests >=2.31.0
46
- Requires-Dist: pyqtree >=1.0.0
47
- Requires-Dist: seaborn >=0.12.2
48
- Requires-Dist: scikit-learn >=1.3.1
49
- Requires-Dist: pandas >=2.1.1
50
- Requires-Dist: PyYAML >=6.0.1
51
- Requires-Dist: ultralytics-yolov5 ==0.1.1
38
+ Requires-Dist: Pillow>=9.5
39
+ Requires-Dist: tqdm>=4.64.0
40
+ Requires-Dist: jsonpickle>=3.0.2
41
+ Requires-Dist: humanfriendly>=10.0
42
+ Requires-Dist: numpy<2.0,>=1.26.4
43
+ Requires-Dist: matplotlib>=3.8.0
44
+ Requires-Dist: opencv-python>=4.8.0
45
+ Requires-Dist: requests>=2.31.0
46
+ Requires-Dist: pyqtree>=1.0.0
47
+ Requires-Dist: seaborn>=0.12.2
48
+ Requires-Dist: scikit-learn>=1.3.1
49
+ Requires-Dist: pandas>=2.1.1
50
+ Requires-Dist: PyYAML>=6.0.1
51
+ Requires-Dist: ultralytics-yolov5==0.1.1
52
+ Requires-Dist: python-dateutil
52
53
 
53
54
  # MegaDetector
54
55