megadetector 5.0.14__py3-none-any.whl → 5.0.16__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 (29) hide show
  1. megadetector/data_management/importers/import_desert_lion_conservation_camera_traps.py +387 -0
  2. megadetector/data_management/lila/generate_lila_per_image_labels.py +3 -3
  3. megadetector/data_management/lila/test_lila_metadata_urls.py +2 -2
  4. megadetector/data_management/remove_exif.py +61 -36
  5. megadetector/data_management/yolo_to_coco.py +25 -6
  6. megadetector/detection/process_video.py +261 -128
  7. megadetector/detection/pytorch_detector.py +13 -11
  8. megadetector/detection/run_detector.py +9 -2
  9. megadetector/detection/run_detector_batch.py +14 -2
  10. megadetector/detection/run_inference_with_yolov5_val.py +58 -10
  11. megadetector/detection/tf_detector.py +8 -2
  12. megadetector/detection/video_utils.py +204 -16
  13. megadetector/postprocessing/md_to_coco.py +31 -9
  14. megadetector/postprocessing/postprocess_batch_results.py +19 -3
  15. megadetector/postprocessing/subset_json_detector_output.py +22 -12
  16. megadetector/taxonomy_mapping/map_new_lila_datasets.py +3 -3
  17. megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +2 -1
  18. megadetector/taxonomy_mapping/preview_lila_taxonomy.py +1 -1
  19. megadetector/taxonomy_mapping/simple_image_download.py +5 -0
  20. megadetector/taxonomy_mapping/species_lookup.py +1 -1
  21. megadetector/utils/md_tests.py +362 -100
  22. megadetector/utils/path_utils.py +2 -2
  23. megadetector/utils/url_utils.py +7 -1
  24. megadetector/visualization/visualize_db.py +16 -0
  25. {megadetector-5.0.14.dist-info → megadetector-5.0.16.dist-info}/LICENSE +0 -0
  26. {megadetector-5.0.14.dist-info → megadetector-5.0.16.dist-info}/METADATA +2 -2
  27. {megadetector-5.0.14.dist-info → megadetector-5.0.16.dist-info}/RECORD +29 -28
  28. {megadetector-5.0.14.dist-info → megadetector-5.0.16.dist-info}/WHEEL +1 -1
  29. {megadetector-5.0.14.dist-info → megadetector-5.0.16.dist-info}/top_level.txt +0 -0
@@ -949,6 +949,13 @@ def process_batch_results(options):
949
949
  f'negative, {n_positive} positive, {n_unknown} unknown, '
950
950
  f'{n_ambiguous} ambiguous')
951
951
 
952
+ if n_positive == 0:
953
+ print('\n*** Warning: no positives found in ground truth, analysis won\'t be very meaningful ***\n')
954
+ if n_negative == 0:
955
+ print('\n*** Warning: no negatives found in ground truth, analysis won\'t be very meaningful ***\n')
956
+ if n_ambiguous > 0:
957
+ print('\n*** Warning: {} images with ambiguous positive/negative status found in ground truth ***\n'.format(
958
+ n_ambiguous))
952
959
 
953
960
  ##%% Load detection (and possibly classification) results
954
961
 
@@ -1095,25 +1102,34 @@ def process_batch_results(options):
1095
1102
 
1096
1103
  ##%% Detection evaluation: compute precision/recall
1097
1104
 
1098
- # numpy array of detection probabilities
1105
+ # numpy array of maximum confidence values
1099
1106
  p_detection = detections_df['max_detection_conf'].values
1100
- n_detections = len(p_detection)
1107
+ n_detection_values = len(p_detection)
1101
1108
 
1102
1109
  # numpy array of bools (0.0/1.0), and -1 as null value
1103
- gt_detections = np.zeros(n_detections, dtype=float)
1110
+ gt_detections = np.zeros(n_detection_values, dtype=float)
1104
1111
 
1112
+ n_positive = 0
1113
+ n_negative = 0
1114
+
1105
1115
  for i_detection, fn in enumerate(detector_files):
1116
+
1106
1117
  image_id = ground_truth_indexed_db.filename_to_id[fn]
1107
1118
  image = ground_truth_indexed_db.image_id_to_image[image_id]
1108
1119
  detection_status = image['_detection_status']
1109
1120
 
1110
1121
  if detection_status == DetectionStatus.DS_NEGATIVE:
1111
1122
  gt_detections[i_detection] = 0.0
1123
+ n_negative += 1
1112
1124
  elif detection_status == DetectionStatus.DS_POSITIVE:
1113
1125
  gt_detections[i_detection] = 1.0
1126
+ n_positive += 1
1114
1127
  else:
1115
1128
  gt_detections[i_detection] = -1.0
1116
1129
 
1130
+ print('Of {} ground truth values, found {} positives and {} negatives'.format(
1131
+ len(detections_df),n_positive,n_negative))
1132
+
1117
1133
  # Don't include ambiguous/unknown ground truth in precision/recall analysis
1118
1134
  b_valid_ground_truth = gt_detections >= 0.0
1119
1135
 
@@ -124,7 +124,7 @@ class SubsetJsonDetectorOutputOptions:
124
124
  self.remove_failed_images = False
125
125
 
126
126
  #: Either a list of category IDs (as string-ints) (not names), or a dictionary mapping category *IDs*
127
- #: (as string-ints) (not names) to thresholds. Removes non-matching detections, does not
127
+ #: (as string-ints) (not names) to thresholds. Removes non-matching detections, does not
128
128
  #: remove images. Not technically mutually exclusize with category_names_to_keep, but it's an esoteric
129
129
  #: scenario indeed where you would want to specify both.
130
130
  self.categories_to_keep = None
@@ -517,7 +517,7 @@ def subset_json_detector_output(input_filename, output_filename, options, data=N
517
517
  else:
518
518
 
519
519
  # Map images to unique folders
520
- print('Finding unique folders')
520
+ print('Finding unique folders')
521
521
 
522
522
  folders_to_images = {}
523
523
 
@@ -670,16 +670,26 @@ def main():
670
670
  parser = argparse.ArgumentParser()
671
671
  parser.add_argument('input_file', type=str, help='Input .json filename')
672
672
  parser.add_argument('output_file', type=str, help='Output .json filename')
673
- parser.add_argument('--query', type=str, default=None, help='Query string to search for (omitting this matches all)')
674
- parser.add_argument('--replacement', type=str, default=None, help='Replace [query] with this')
675
- parser.add_argument('--confidence_threshold', type=float, default=None, help='Remove detections below this confidence level')
676
- parser.add_argument('--split_folders', action='store_true', help='Split .json files by leaf-node folder')
677
- parser.add_argument('--split_folder_param', type=int, help='Directory level count for n_from_bottom and n_from_top splitting')
678
- parser.add_argument('--split_folder_mode', type=str, help='Folder level to use for splitting ("top" or "bottom")')
679
- parser.add_argument('--make_folder_relative', action='store_true', help='Make image paths relative to their containing folder (only meaningful with split_folders)')
680
- parser.add_argument('--overwrite_json_files', action='store_true', help='Overwrite output files')
681
- parser.add_argument('--copy_jsons_to_folders', action='store_true', help='When using split_folders and make_folder_relative, copy jsons to their corresponding folders (relative to output_file)')
682
- parser.add_argument('--create_folders', action='store_true', help='When using copy_jsons_to_folders, create folders that don''t exist')
673
+ parser.add_argument('--query', type=str, default=None,
674
+ help='Query string to search for (omitting this matches all)')
675
+ parser.add_argument('--replacement', type=str, default=None,
676
+ help='Replace [query] with this')
677
+ parser.add_argument('--confidence_threshold', type=float, default=None,
678
+ help='Remove detections below this confidence level')
679
+ parser.add_argument('--split_folders', action='store_true',
680
+ help='Split .json files by leaf-node folder')
681
+ parser.add_argument('--split_folder_param', type=int,
682
+ help='Directory level count for n_from_bottom and n_from_top splitting')
683
+ parser.add_argument('--split_folder_mode', type=str,
684
+ help='Folder level to use for splitting ("top" or "bottom")')
685
+ parser.add_argument('--make_folder_relative', action='store_true',
686
+ help='Make image paths relative to their containing folder (only meaningful with split_folders)')
687
+ parser.add_argument('--overwrite_json_files', action='store_true',
688
+ help='Overwrite output files')
689
+ parser.add_argument('--copy_jsons_to_folders', action='store_true',
690
+ help='When using split_folders and make_folder_relative, copy jsons to their corresponding folders (relative to output_file)')
691
+ parser.add_argument('--create_folders', action='store_true',
692
+ help='When using copy_jsons_to_folders, create folders that don''t exist')
683
693
 
684
694
  if len(sys.argv[1:]) == 0:
685
695
  parser.print_help()
@@ -15,10 +15,10 @@ import json
15
15
  # Created by get_lila_category_list.py
16
16
  input_lila_category_list_file = os.path.expanduser('~/lila/lila_categories_list/lila_dataset_to_categories.json')
17
17
 
18
- output_file = os.path.expanduser('~/lila/lila_additions_2023.12.29.csv')
18
+ output_file = os.path.expanduser('~/lila/lila_additions_2024.07.16.csv')
19
19
 
20
20
  datasets_to_map = [
21
- 'Trail Camera Images of New Zealand Animals'
21
+ 'Desert Lion Conservation Camera Traps'
22
22
  ]
23
23
 
24
24
 
@@ -133,7 +133,7 @@ if False:
133
133
  # q = 'white-throated monkey'
134
134
  # q = 'cingulata'
135
135
  # q = 'notamacropus'
136
- q = 'porzana'
136
+ q = 'aves'
137
137
  taxonomy_preference = 'inat'
138
138
  m = get_preferred_taxonomic_match(q,taxonomy_preference)
139
139
  # print(m.scientific_name); import clipboard; clipboard.copy(m.scientific_name)
@@ -24,7 +24,7 @@ if False:
24
24
  release_taxonomy_file = os.path.expanduser('~/lila/lila-taxonomy-mapping_release.csv')
25
25
  # import clipboard; clipboard.copy(release_taxonomy_file)
26
26
 
27
- # Created by get_lila_category_list.py... contains counts for each category
27
+ # Created by get_lila_annotation_counts.py... contains counts for each category
28
28
  lila_dataset_to_categories_file = os.path.expanduser('~/lila/lila_categories_list/lila_dataset_to_categories.json')
29
29
 
30
30
  assert os.path.isfile(lila_dataset_to_categories_file)
@@ -140,3 +140,4 @@ if False:
140
140
 
141
141
  print('Wrote final output to {}'.format(release_taxonomy_file))
142
142
 
143
+ # ...if False
@@ -16,7 +16,7 @@ import os
16
16
  import pandas as pd
17
17
 
18
18
  # lila_taxonomy_file = r"c:\git\agentmorrisprivate\lila-taxonomy\lila-taxonomy-mapping.csv"
19
- lila_taxonomy_file = os.path.expanduser('~/lila/lila_additions_2023.12.29.csv')
19
+ lila_taxonomy_file = os.path.expanduser('~/lila/lila_additions_2024.07.16.csv')
20
20
 
21
21
  preview_base = os.path.expanduser('~/lila/lila_taxonomy_preview')
22
22
  os.makedirs(preview_base,exist_ok=True)
@@ -8,6 +8,11 @@ Slightly modified from:
8
8
 
9
9
  https://github.com/RiddlerQ/simple_image_download
10
10
 
11
+ pip install python-magic
12
+
13
+ # On Windows, also run:
14
+ pip install python-magic-bin
15
+
11
16
  """
12
17
 
13
18
  #%% Imports
@@ -208,7 +208,7 @@ def initialize_taxonomy_lookup(force_init=False) -> None:
208
208
  # Load GBIF taxonomy
209
209
  gbif_taxonomy_file = os.path.join(taxonomy_download_dir, 'GBIF', 'Taxon.tsv')
210
210
  print('Loading GBIF taxonomy from {}'.format(gbif_taxonomy_file))
211
- gbif_taxonomy = pd.read_csv(gbif_taxonomy_file, sep='\t')
211
+ gbif_taxonomy = pd.read_csv(gbif_taxonomy_file, sep='\t', encoding='utf-8',on_bad_lines='warn')
212
212
  gbif_taxonomy['scientificName'] = gbif_taxonomy['scientificName'].fillna('').str.strip()
213
213
  gbif_taxonomy['canonicalName'] = gbif_taxonomy['canonicalName'].fillna('').str.strip()
214
214