megadetector 5.0.11__py3-none-any.whl → 5.0.12__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 (201) hide show
  1. megadetector/api/__init__.py +0 -0
  2. megadetector/api/batch_processing/__init__.py +0 -0
  3. megadetector/api/batch_processing/api_core/__init__.py +0 -0
  4. megadetector/api/batch_processing/api_core/batch_service/__init__.py +0 -0
  5. megadetector/api/batch_processing/api_core/batch_service/score.py +439 -0
  6. megadetector/api/batch_processing/api_core/server.py +294 -0
  7. megadetector/api/batch_processing/api_core/server_api_config.py +98 -0
  8. megadetector/api/batch_processing/api_core/server_app_config.py +55 -0
  9. megadetector/api/batch_processing/api_core/server_batch_job_manager.py +220 -0
  10. megadetector/api/batch_processing/api_core/server_job_status_table.py +152 -0
  11. megadetector/api/batch_processing/api_core/server_orchestration.py +360 -0
  12. megadetector/api/batch_processing/api_core/server_utils.py +92 -0
  13. megadetector/api/batch_processing/api_core_support/__init__.py +0 -0
  14. megadetector/api/batch_processing/api_core_support/aggregate_results_manually.py +46 -0
  15. megadetector/api/batch_processing/api_support/__init__.py +0 -0
  16. megadetector/api/batch_processing/api_support/summarize_daily_activity.py +152 -0
  17. megadetector/api/batch_processing/data_preparation/__init__.py +0 -0
  18. megadetector/api/batch_processing/integration/digiKam/setup.py +6 -0
  19. megadetector/api/batch_processing/integration/digiKam/xmp_integration.py +465 -0
  20. megadetector/api/batch_processing/integration/eMammal/test_scripts/config_template.py +5 -0
  21. megadetector/api/batch_processing/integration/eMammal/test_scripts/push_annotations_to_emammal.py +126 -0
  22. megadetector/api/batch_processing/integration/eMammal/test_scripts/select_images_for_testing.py +55 -0
  23. megadetector/api/synchronous/__init__.py +0 -0
  24. megadetector/api/synchronous/api_core/animal_detection_api/__init__.py +0 -0
  25. megadetector/api/synchronous/api_core/animal_detection_api/api_backend.py +152 -0
  26. megadetector/api/synchronous/api_core/animal_detection_api/api_frontend.py +266 -0
  27. megadetector/api/synchronous/api_core/animal_detection_api/config.py +35 -0
  28. megadetector/api/synchronous/api_core/tests/__init__.py +0 -0
  29. megadetector/api/synchronous/api_core/tests/load_test.py +110 -0
  30. megadetector/classification/__init__.py +0 -0
  31. megadetector/classification/aggregate_classifier_probs.py +108 -0
  32. megadetector/classification/analyze_failed_images.py +227 -0
  33. megadetector/classification/cache_batchapi_outputs.py +198 -0
  34. megadetector/classification/create_classification_dataset.py +627 -0
  35. megadetector/classification/crop_detections.py +516 -0
  36. megadetector/classification/csv_to_json.py +226 -0
  37. megadetector/classification/detect_and_crop.py +855 -0
  38. megadetector/classification/efficientnet/__init__.py +9 -0
  39. megadetector/classification/efficientnet/model.py +415 -0
  40. megadetector/classification/efficientnet/utils.py +610 -0
  41. megadetector/classification/evaluate_model.py +520 -0
  42. megadetector/classification/identify_mislabeled_candidates.py +152 -0
  43. megadetector/classification/json_to_azcopy_list.py +63 -0
  44. megadetector/classification/json_validator.py +699 -0
  45. megadetector/classification/map_classification_categories.py +276 -0
  46. megadetector/classification/merge_classification_detection_output.py +506 -0
  47. megadetector/classification/prepare_classification_script.py +194 -0
  48. megadetector/classification/prepare_classification_script_mc.py +228 -0
  49. megadetector/classification/run_classifier.py +287 -0
  50. megadetector/classification/save_mislabeled.py +110 -0
  51. megadetector/classification/train_classifier.py +827 -0
  52. megadetector/classification/train_classifier_tf.py +725 -0
  53. megadetector/classification/train_utils.py +323 -0
  54. megadetector/data_management/__init__.py +0 -0
  55. megadetector/data_management/annotations/__init__.py +0 -0
  56. megadetector/data_management/annotations/annotation_constants.py +34 -0
  57. megadetector/data_management/camtrap_dp_to_coco.py +239 -0
  58. megadetector/data_management/cct_json_utils.py +395 -0
  59. megadetector/data_management/cct_to_md.py +176 -0
  60. megadetector/data_management/cct_to_wi.py +289 -0
  61. megadetector/data_management/coco_to_labelme.py +272 -0
  62. megadetector/data_management/coco_to_yolo.py +662 -0
  63. megadetector/data_management/databases/__init__.py +0 -0
  64. megadetector/data_management/databases/add_width_and_height_to_db.py +33 -0
  65. megadetector/data_management/databases/combine_coco_camera_traps_files.py +206 -0
  66. megadetector/data_management/databases/integrity_check_json_db.py +477 -0
  67. megadetector/data_management/databases/subset_json_db.py +115 -0
  68. megadetector/data_management/generate_crops_from_cct.py +149 -0
  69. megadetector/data_management/get_image_sizes.py +189 -0
  70. megadetector/data_management/importers/add_nacti_sizes.py +52 -0
  71. megadetector/data_management/importers/add_timestamps_to_icct.py +79 -0
  72. megadetector/data_management/importers/animl_results_to_md_results.py +158 -0
  73. megadetector/data_management/importers/auckland_doc_test_to_json.py +373 -0
  74. megadetector/data_management/importers/auckland_doc_to_json.py +201 -0
  75. megadetector/data_management/importers/awc_to_json.py +191 -0
  76. megadetector/data_management/importers/bellevue_to_json.py +273 -0
  77. megadetector/data_management/importers/cacophony-thermal-importer.py +796 -0
  78. megadetector/data_management/importers/carrizo_shrubfree_2018.py +269 -0
  79. megadetector/data_management/importers/carrizo_trail_cam_2017.py +289 -0
  80. megadetector/data_management/importers/cct_field_adjustments.py +58 -0
  81. megadetector/data_management/importers/channel_islands_to_cct.py +913 -0
  82. megadetector/data_management/importers/eMammal/copy_and_unzip_emammal.py +180 -0
  83. megadetector/data_management/importers/eMammal/eMammal_helpers.py +249 -0
  84. megadetector/data_management/importers/eMammal/make_eMammal_json.py +223 -0
  85. megadetector/data_management/importers/ena24_to_json.py +276 -0
  86. megadetector/data_management/importers/filenames_to_json.py +386 -0
  87. megadetector/data_management/importers/helena_to_cct.py +283 -0
  88. megadetector/data_management/importers/idaho-camera-traps.py +1407 -0
  89. megadetector/data_management/importers/idfg_iwildcam_lila_prep.py +294 -0
  90. megadetector/data_management/importers/jb_csv_to_json.py +150 -0
  91. megadetector/data_management/importers/mcgill_to_json.py +250 -0
  92. megadetector/data_management/importers/missouri_to_json.py +490 -0
  93. megadetector/data_management/importers/nacti_fieldname_adjustments.py +79 -0
  94. megadetector/data_management/importers/noaa_seals_2019.py +181 -0
  95. megadetector/data_management/importers/pc_to_json.py +365 -0
  96. megadetector/data_management/importers/plot_wni_giraffes.py +123 -0
  97. megadetector/data_management/importers/prepare-noaa-fish-data-for-lila.py +359 -0
  98. megadetector/data_management/importers/prepare_zsl_imerit.py +131 -0
  99. megadetector/data_management/importers/rspb_to_json.py +356 -0
  100. megadetector/data_management/importers/save_the_elephants_survey_A.py +320 -0
  101. megadetector/data_management/importers/save_the_elephants_survey_B.py +329 -0
  102. megadetector/data_management/importers/snapshot_safari_importer.py +758 -0
  103. megadetector/data_management/importers/snapshot_safari_importer_reprise.py +665 -0
  104. megadetector/data_management/importers/snapshot_serengeti_lila.py +1067 -0
  105. megadetector/data_management/importers/snapshotserengeti/make_full_SS_json.py +150 -0
  106. megadetector/data_management/importers/snapshotserengeti/make_per_season_SS_json.py +153 -0
  107. megadetector/data_management/importers/sulross_get_exif.py +65 -0
  108. megadetector/data_management/importers/timelapse_csv_set_to_json.py +490 -0
  109. megadetector/data_management/importers/ubc_to_json.py +399 -0
  110. megadetector/data_management/importers/umn_to_json.py +507 -0
  111. megadetector/data_management/importers/wellington_to_json.py +263 -0
  112. megadetector/data_management/importers/wi_to_json.py +442 -0
  113. megadetector/data_management/importers/zamba_results_to_md_results.py +181 -0
  114. megadetector/data_management/labelme_to_coco.py +547 -0
  115. megadetector/data_management/labelme_to_yolo.py +272 -0
  116. megadetector/data_management/lila/__init__.py +0 -0
  117. megadetector/data_management/lila/add_locations_to_island_camera_traps.py +97 -0
  118. megadetector/data_management/lila/add_locations_to_nacti.py +147 -0
  119. megadetector/data_management/lila/create_lila_blank_set.py +558 -0
  120. megadetector/data_management/lila/create_lila_test_set.py +152 -0
  121. megadetector/data_management/lila/create_links_to_md_results_files.py +106 -0
  122. megadetector/data_management/lila/download_lila_subset.py +178 -0
  123. megadetector/data_management/lila/generate_lila_per_image_labels.py +516 -0
  124. megadetector/data_management/lila/get_lila_annotation_counts.py +170 -0
  125. megadetector/data_management/lila/get_lila_image_counts.py +112 -0
  126. megadetector/data_management/lila/lila_common.py +300 -0
  127. megadetector/data_management/lila/test_lila_metadata_urls.py +132 -0
  128. megadetector/data_management/ocr_tools.py +874 -0
  129. megadetector/data_management/read_exif.py +681 -0
  130. megadetector/data_management/remap_coco_categories.py +84 -0
  131. megadetector/data_management/remove_exif.py +66 -0
  132. megadetector/data_management/resize_coco_dataset.py +189 -0
  133. megadetector/data_management/wi_download_csv_to_coco.py +246 -0
  134. megadetector/data_management/yolo_output_to_md_output.py +441 -0
  135. megadetector/data_management/yolo_to_coco.py +676 -0
  136. megadetector/detection/__init__.py +0 -0
  137. megadetector/detection/detector_training/__init__.py +0 -0
  138. megadetector/detection/detector_training/model_main_tf2.py +114 -0
  139. megadetector/detection/process_video.py +702 -0
  140. megadetector/detection/pytorch_detector.py +341 -0
  141. megadetector/detection/run_detector.py +779 -0
  142. megadetector/detection/run_detector_batch.py +1219 -0
  143. megadetector/detection/run_inference_with_yolov5_val.py +917 -0
  144. megadetector/detection/run_tiled_inference.py +934 -0
  145. megadetector/detection/tf_detector.py +189 -0
  146. megadetector/detection/video_utils.py +606 -0
  147. megadetector/postprocessing/__init__.py +0 -0
  148. megadetector/postprocessing/add_max_conf.py +64 -0
  149. megadetector/postprocessing/categorize_detections_by_size.py +163 -0
  150. megadetector/postprocessing/combine_api_outputs.py +249 -0
  151. megadetector/postprocessing/compare_batch_results.py +958 -0
  152. megadetector/postprocessing/convert_output_format.py +396 -0
  153. megadetector/postprocessing/load_api_results.py +195 -0
  154. megadetector/postprocessing/md_to_coco.py +310 -0
  155. megadetector/postprocessing/md_to_labelme.py +330 -0
  156. megadetector/postprocessing/merge_detections.py +401 -0
  157. megadetector/postprocessing/postprocess_batch_results.py +1902 -0
  158. megadetector/postprocessing/remap_detection_categories.py +170 -0
  159. megadetector/postprocessing/render_detection_confusion_matrix.py +660 -0
  160. megadetector/postprocessing/repeat_detection_elimination/find_repeat_detections.py +211 -0
  161. megadetector/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +83 -0
  162. megadetector/postprocessing/repeat_detection_elimination/repeat_detections_core.py +1631 -0
  163. megadetector/postprocessing/separate_detections_into_folders.py +730 -0
  164. megadetector/postprocessing/subset_json_detector_output.py +696 -0
  165. megadetector/postprocessing/top_folders_to_bottom.py +223 -0
  166. megadetector/taxonomy_mapping/__init__.py +0 -0
  167. megadetector/taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +491 -0
  168. megadetector/taxonomy_mapping/map_new_lila_datasets.py +150 -0
  169. megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +142 -0
  170. megadetector/taxonomy_mapping/preview_lila_taxonomy.py +590 -0
  171. megadetector/taxonomy_mapping/retrieve_sample_image.py +71 -0
  172. megadetector/taxonomy_mapping/simple_image_download.py +219 -0
  173. megadetector/taxonomy_mapping/species_lookup.py +834 -0
  174. megadetector/taxonomy_mapping/taxonomy_csv_checker.py +159 -0
  175. megadetector/taxonomy_mapping/taxonomy_graph.py +346 -0
  176. megadetector/taxonomy_mapping/validate_lila_category_mappings.py +83 -0
  177. megadetector/utils/__init__.py +0 -0
  178. megadetector/utils/azure_utils.py +178 -0
  179. megadetector/utils/ct_utils.py +612 -0
  180. megadetector/utils/directory_listing.py +246 -0
  181. megadetector/utils/md_tests.py +968 -0
  182. megadetector/utils/path_utils.py +1044 -0
  183. megadetector/utils/process_utils.py +157 -0
  184. megadetector/utils/sas_blob_utils.py +509 -0
  185. megadetector/utils/split_locations_into_train_val.py +228 -0
  186. megadetector/utils/string_utils.py +92 -0
  187. megadetector/utils/url_utils.py +323 -0
  188. megadetector/utils/write_html_image_list.py +225 -0
  189. megadetector/visualization/__init__.py +0 -0
  190. megadetector/visualization/plot_utils.py +293 -0
  191. megadetector/visualization/render_images_with_thumbnails.py +275 -0
  192. megadetector/visualization/visualization_utils.py +1536 -0
  193. megadetector/visualization/visualize_db.py +550 -0
  194. megadetector/visualization/visualize_detector_output.py +405 -0
  195. {megadetector-5.0.11.dist-info → megadetector-5.0.12.dist-info}/METADATA +1 -1
  196. megadetector-5.0.12.dist-info/RECORD +199 -0
  197. megadetector-5.0.12.dist-info/top_level.txt +1 -0
  198. megadetector-5.0.11.dist-info/RECORD +0 -5
  199. megadetector-5.0.11.dist-info/top_level.txt +0 -1
  200. {megadetector-5.0.11.dist-info → megadetector-5.0.12.dist-info}/LICENSE +0 -0
  201. {megadetector-5.0.11.dist-info → megadetector-5.0.12.dist-info}/WHEEL +0 -0
@@ -0,0 +1,158 @@
1
+ """
2
+
3
+ animl_results_to_md_results.py
4
+
5
+ Convert a .csv file produced by the Animl package:
6
+
7
+ https://github.com/conservationtechlab/animl-py
8
+
9
+ ...to a MD results file suitable for import into Timelapse.
10
+
11
+ Columns are expected to be:
12
+
13
+ file
14
+ category (MD category identifies: 1==animal, 2==person, 3==vehicle)
15
+ detection_conf
16
+ bbox1,bbox2,bbox3,bbox4
17
+ class
18
+ classification_conf
19
+
20
+ """
21
+
22
+ #%% Imports and constants
23
+
24
+ import pandas as pd
25
+ import json
26
+
27
+ # It's a little icky to hard-code this here rather than importing from elsewhere
28
+ # in the MD repo, but it seemed silly to take a dependency on lots of MD code
29
+ # just for this, so, hard-coding.
30
+ detection_category_id_to_name = {'1':'animal','2':'person','3':'vehicle'}
31
+
32
+
33
+ #%% Main function
34
+
35
+ def animl_results_to_md_results(input_file,output_file=None):
36
+ """
37
+ Converts the .csv file [input_file] to the MD-formatted .json file [output_file].
38
+
39
+ If [output_file] is None, '.json' will be appended to the input file.
40
+ """
41
+
42
+ if output_file is None:
43
+ output_file = input_file + '.json'
44
+
45
+ df = pd.read_csv(input_file)
46
+
47
+ expected_columns = ('file','category','detection_conf',
48
+ 'bbox1','bbox2','bbox3','bbox4','class','classification_conf')
49
+
50
+ for s in expected_columns:
51
+ assert s in df.columns,\
52
+ 'Expected column {} not found'.format(s)
53
+
54
+ classification_category_name_to_id = {}
55
+ filename_to_results = {}
56
+
57
+ # i_row = 0; row = df.iloc[i_row]
58
+ for i_row,row in df.iterrows():
59
+
60
+ # Is this the first detection we've seen for this file?
61
+ if row['file'] in filename_to_results:
62
+ im = filename_to_results[row['file']]
63
+ else:
64
+ im = {}
65
+ im['detections'] = []
66
+ im['file'] = row['file']
67
+ filename_to_results[im['file']] = im
68
+
69
+ assert isinstance(row['category'],int),'Invalid category identifier in row {}'.format(im['file'])
70
+ detection_category_id = str(row['category'])
71
+ assert detection_category_id in detection_category_id_to_name,\
72
+ 'Unrecognized detection category ID {}'.format(detection_category_id)
73
+
74
+ detection = {}
75
+ detection['category'] = detection_category_id
76
+ detection['conf'] = row['detection_conf']
77
+ bbox = [row['bbox1'],row['bbox2'],row['bbox3'],row['bbox4']]
78
+ detection['bbox'] = bbox
79
+ classification_category_name = row['class']
80
+
81
+ # Have we seen this classification category before?
82
+ if classification_category_name in classification_category_name_to_id:
83
+ classification_category_id = \
84
+ classification_category_name_to_id[classification_category_name]
85
+ else:
86
+ classification_category_id = str(len(classification_category_name_to_id))
87
+ classification_category_name_to_id[classification_category_name] = \
88
+ classification_category_id
89
+
90
+ classifications = [[classification_category_id,row['classification_conf']]]
91
+ detection['classifications'] = classifications
92
+
93
+ im['detections'].append(detection)
94
+
95
+ # ...for each row
96
+
97
+ info = {}
98
+ info['format_version'] = '1.3'
99
+ info['detector'] = 'Animl'
100
+ info['classifier'] = 'Animl'
101
+
102
+ results = {}
103
+ results['info'] = info
104
+ results['detection_categories'] = detection_category_id_to_name
105
+ results['classification_categories'] = \
106
+ {v: k for k, v in classification_category_name_to_id.items()}
107
+ results['images'] = list(filename_to_results.values())
108
+
109
+ with open(output_file,'w') as f:
110
+ json.dump(results,f,indent=1)
111
+
112
+ # ...animl_results_to_md_results(...)
113
+
114
+
115
+ #%% Interactive driver
116
+
117
+ if False:
118
+
119
+ pass
120
+
121
+ #%%
122
+
123
+ input_file = r"G:\temp\animl-runs\animl-runs\Coati_v2\manifest.csv"
124
+ output_file = None
125
+ animl_results_to_md_results(input_file,output_file)
126
+
127
+
128
+ #%% Command-line driver
129
+
130
+ import sys,argparse
131
+
132
+ def main():
133
+
134
+ parser = argparse.ArgumentParser(
135
+ description='Convert an Animl-formatted .csv results file to MD-formatted .json results file')
136
+
137
+ parser.add_argument(
138
+ 'input_file',
139
+ type=str,
140
+ help='input .csv file')
141
+
142
+ parser.add_argument(
143
+ '--output_file',
144
+ type=str,
145
+ default=None,
146
+ help='output .json file (defaults to input file appended with ".json")')
147
+
148
+ if len(sys.argv[1:]) == 0:
149
+ parser.print_help()
150
+ parser.exit()
151
+
152
+ args = parser.parse_args()
153
+
154
+ animl_results_to_md_results(args.input_file,args.output_file)
155
+
156
+ if __name__ == '__main__':
157
+ main()
158
+
@@ -0,0 +1,373 @@
1
+ """
2
+
3
+ auckland_doc_test_to_json.py
4
+
5
+ Convert Auckland DOC data set to COCO camera traps format. This was
6
+ for a testing data set where a .csv file was provided with class
7
+ information.
8
+
9
+ """
10
+
11
+ #%% Constants and imports
12
+
13
+ import json
14
+ import os
15
+ import uuid
16
+ import pandas as pd
17
+ import datetime
18
+ import ntpath
19
+ import re
20
+ import numpy as np
21
+
22
+ from tqdm import tqdm
23
+
24
+ from megadetector.visualization import visualize_db
25
+ from megadetector.data_management.databases import integrity_check_json_db
26
+ from megadetector.utils.path_utils import find_images
27
+
28
+ input_base_dir = r'e:\auckland-test\2_Testing'
29
+
30
+ input_metadata_file = r'G:\auckland-doc\Maukahuka - Auckland Island - Cat camera data Master April 2019 - DOC-5924483.xlsx'
31
+
32
+ # Filenames will be stored in the output .json relative to this base dir
33
+ output_base_dir = r'g:\auckland-doc'
34
+ output_json_filename = os.path.join(output_base_dir, 'auckland-doc-test.json')
35
+
36
+ assert os.path.isdir(input_base_dir)
37
+ os.makedirs(output_base_dir,exist_ok=True)
38
+
39
+ output_encoding = 'utf-8'
40
+ read_image_sizes = True
41
+
42
+ info = {}
43
+ info['year'] = 2020
44
+ info['version'] = '1.0'
45
+ info['description'] = 'Auckaland DOC Camera Traps (test)'
46
+ info['contributor'] = 'Auckland DOC'
47
+ info['date_created'] = str(datetime.date.today())
48
+
49
+
50
+ #%% Enumerate files
51
+
52
+ print('Enumerating files from {}'.format(input_base_dir))
53
+ absolute_image_paths = find_images(input_base_dir, recursive=True)
54
+ print('Enumerated {} images'.format(len(absolute_image_paths)))
55
+
56
+ relative_image_paths = []
57
+ for fn in absolute_image_paths:
58
+ relative_image_paths.append(os.path.relpath(fn,input_base_dir).replace('\\','/'))
59
+
60
+ relative_image_paths_set = set(relative_image_paths)
61
+
62
+ assert len(relative_image_paths_set) == len(relative_image_paths)
63
+
64
+
65
+ #%% Create unique identifier for each image
66
+
67
+ # The ground truth doesn't have full paths in it; create unique identifiers for each image
68
+ # based on the camera name and filename.
69
+ #
70
+ # We store file identifiers as cameraname_filename.
71
+ file_identifier_to_relative_paths = {}
72
+ camera_names = set()
73
+
74
+ # relative_path = relative_image_paths[0]
75
+ for relative_path in relative_image_paths:
76
+
77
+ # Example relative paths
78
+ #
79
+ # Summer_Trial_2019/A1_1_42_SD114_20190210/AucklandIsland_A1_1_42_SD114_20190210_01300001.jpg
80
+ # Winter_Trial_2019/Installation/10_F4/10_F4_tmp_201908210001.JPG
81
+ fn = ntpath.basename(relative_path)
82
+
83
+ # Find the camera name
84
+ tokens = relative_path.split('/')
85
+
86
+ if tokens[1] == 'Installation' or 'Rebait' in tokens[1]:
87
+ camera_name = tokens[2]
88
+
89
+ else:
90
+ # E..g. "A1_1_42_SD114_20190210" in the above example
91
+ camera_token = tokens[1]
92
+ camera_name = None
93
+ m = re.search('^(.+)_SD',camera_token)
94
+ if m:
95
+ camera_name = m.group(1)
96
+ else:
97
+ # For camera tokens like C1_5_D_190207
98
+ m = re.search('^(.+_.+_.+)',camera_token)
99
+ camera_name = m.group(1)
100
+
101
+ assert camera_name
102
+ camera_names.add(camera_name)
103
+
104
+ file_identifier = camera_name + '_' + fn
105
+ if file_identifier not in file_identifier_to_relative_paths:
106
+ file_identifier_to_relative_paths[file_identifier] = [relative_path]
107
+ else:
108
+ file_identifier_to_relative_paths[file_identifier].append(relative_path)
109
+
110
+ print('Found {} unique camera names'.format(len(camera_names)))
111
+
112
+
113
+ #%% Load input data
114
+
115
+ input_metadata = pd.read_excel(input_metadata_file)
116
+
117
+ print('Read {} columns and {} rows from metadata file'.format(len(input_metadata.columns),
118
+ len(input_metadata)))
119
+
120
+ # The spreadsheet has a space after "Camera"
121
+ input_metadata = input_metadata.rename(columns={'Camera ':'Camera'})
122
+
123
+
124
+ #%% Assemble dictionaries
125
+
126
+ image_id_to_image = {}
127
+ category_name_to_category = {}
128
+ annotations = []
129
+
130
+ # Force the empty category to be ID 0
131
+ empty_category = {}
132
+ empty_category['name'] = 'empty'
133
+ empty_category['id'] = 0
134
+ category_name_to_category['empty'] = empty_category
135
+
136
+ rows_not_found_in_folder = []
137
+ rows_ambiguous = []
138
+ rows_no_filename = []
139
+ rows_no_annotation = []
140
+
141
+ image_id_to_rows = {}
142
+
143
+ next_id = 1
144
+
145
+ category_names = ['cat','mouse','unknown','human','pig','sealion','penguin','dog','openadjusted']
146
+
147
+ # array([nan, 'Blackbird', 'Bellbird', 'Tomtit', 'Song thrush', 'Pippit',
148
+ # 'Pippet', '?', 'Dunnock', 'Song thursh', 'Kakariki', 'Tui', ' ',
149
+ # 'Silvereye', 'NZ Pipit', 'Blackbird and Dunnock', 'Unknown',
150
+ # 'Pipit', 'Songthrush'], dtype=object)
151
+
152
+ def bird_name_to_category_name(bird_name):
153
+ bird_name = bird_name.lower().strip().replace(' ','_').replace('song_thursh','song_thrush')
154
+ bird_name = bird_name.replace('pippet','pipt').replace('pippit','pipit').replace('nz_pipit','pipit')
155
+ if bird_name == '?' or bird_name == '' or bird_name == 'unknown':
156
+ category_name = 'unknown_bird'
157
+ else:
158
+ category_name = bird_name
159
+ return category_name
160
+
161
+ bird_names = input_metadata.Bird_ID.unique()
162
+ for bird_name in bird_names:
163
+ if isinstance(bird_name,float):
164
+ continue
165
+ category_name = bird_name_to_category_name(bird_name)
166
+ if category_name not in category_names:
167
+ category_names.append(category_name)
168
+
169
+ for category_name in category_names:
170
+ cat = {}
171
+ cat['name'] = category_name
172
+ cat['id'] = next_id
173
+ next_id = next_id +1
174
+ category_name_to_category[category_name] = cat
175
+
176
+ def create_annotation(image_id,category_name,count):
177
+ assert isinstance(image_id,str)
178
+ assert isinstance(category_name,str)
179
+ assert isinstance(count,int) or isinstance(count,float)
180
+ if isinstance(count,float):
181
+ count = int(count)
182
+ ann = {}
183
+ ann['id'] = str(uuid.uuid1())
184
+ ann['image_id'] = image_id
185
+ category = category_name_to_category[category_name]
186
+ category_id = category['id']
187
+ ann['category_id'] = category_id
188
+ ann['count'] = count
189
+ return ann
190
+
191
+ # i_row = 0; row = input_metadata.iloc[i_row]
192
+ for i_row,row in tqdm(input_metadata.iterrows(),total=len(input_metadata)):
193
+
194
+ # E.g.: AucklandIsland_A1_1_42_SD114_20190210_01300009.jpg
195
+ filename = row['File']
196
+ if isinstance(filename,float):
197
+ rows_no_filename.append(i_row)
198
+ continue
199
+
200
+ camera_name = row['Camera']
201
+ file_identifier = camera_name + '_' + filename
202
+ if not file_identifier in file_identifier_to_relative_paths:
203
+ rows_not_found_in_folder.append(i_row)
204
+ continue
205
+
206
+ relative_paths_this_file_id = file_identifier_to_relative_paths[file_identifier]
207
+
208
+ if len(relative_paths_this_file_id) == 1:
209
+ relative_path = relative_paths_this_file_id[0]
210
+ else:
211
+
212
+ # We have multiple files matching this identifier, can we uniquely resolve this
213
+ # to one of those files based on the camera ID?
214
+ matches = [s for s in relative_paths_this_file_id if camera_name in s]
215
+ assert len(matches) > 0
216
+ if len(matches) > 1:
217
+ rows_ambiguous.append(i_row)
218
+ continue
219
+ relative_path = matches[0]
220
+
221
+ assert filename.endswith('.jpg') or filename.endswith('.JPG')
222
+ image_id = filename.lower().replace('.jpg','')
223
+
224
+ if image_id in image_id_to_rows:
225
+ image_id_to_rows[image_id].append(i_row)
226
+ continue
227
+
228
+ image_id_to_rows[image_id] = [i_row]
229
+
230
+ im = {}
231
+ im['id'] = image_id
232
+ im['file_name'] = relative_path
233
+ im['datetime'] = str(row['Date and time'])
234
+ im['camera'] = row['Camera']
235
+ im['sd_card'] = row['SD_Card']
236
+ im['sd_change'] = row['SD_Change']
237
+ im['comments'] = row['Comments']
238
+
239
+ image_id_to_image[im['id']] = im
240
+
241
+ # create_annotation(image_id,category_name,count)
242
+
243
+ # 'SD_Change', 'Cat', 'Mouse', 'Bird', 'Bird_ID', 'False_trig', 'Unknown',
244
+ # 'Human', 'Collared_cat', 'Cat_ID', 'Pig', 'Sea_lion', 'Open_adjusted',
245
+ # 'Penguin', 'Dog', 'Comments', 'Unnamed: 22']
246
+
247
+ # Each of these categories is handled a little differently...
248
+
249
+ annotations_this_image = []
250
+ if (not np.isnan(row['Cat'])):
251
+ assert np.isnan(row['Collared_cat'] )
252
+ annotations_this_image.append(create_annotation(im['id'],'cat',row['Cat']))
253
+
254
+ if (not np.isnan(row['Collared_cat'])):
255
+ assert np.isnan(row['Cat'] )
256
+ annotations_this_image.append(create_annotation(im['id'],'cat',row['Collared_cat']))
257
+
258
+ if (not np.isnan(row['Bird'])):
259
+ if isinstance(row['Bird_ID'],str):
260
+ category_name = bird_name_to_category_name(row['Bird_ID'])
261
+ else:
262
+ assert np.isnan(row['Bird_ID'])
263
+ category_name = 'unknown_bird'
264
+ annotations_this_image.append(create_annotation(im['id'],category_name,row['Bird']))
265
+
266
+ if (not np.isnan(row['False_trig'])):
267
+ annotations_this_image.append(create_annotation(im['id'],'empty',-1))
268
+
269
+ # These are straightforward
270
+ for s in ['Mouse','Unknown','Pig','Human','Sea_lion','Penguin','Dog','Open_adjusted']:
271
+ if isinstance(row[s],float) or isinstance(row[s],int):
272
+ if not np.isnan(row[s]):
273
+ annotations_this_image.append(create_annotation(im['id'],s.lower().replace('_',''),row[s]))
274
+ elif isinstance(row[s],str):
275
+ print('File {}, label {}, value {}'.format(filename,s,row[s]))
276
+ else:
277
+ raise ValueError('Error handling count value {}'.format(row[s]))
278
+
279
+ if len(annotations_this_image) > 1:
280
+ print('Multiple annotations for filename {}'.format(filename))
281
+
282
+ if len(annotations_this_image) == 0:
283
+ rows_no_annotation.append(i_row)
284
+
285
+ annotations.extend(annotations_this_image)
286
+
287
+ # ...for each image
288
+
289
+
290
+ #%% Summarize errors
291
+
292
+ print('Of {} rows:\n'.format(len(input_metadata)))
293
+
294
+ print('{} images not found in folder'.format(len(rows_not_found_in_folder)))
295
+ print('{} images ambiguously mapped'.format(len(rows_ambiguous)))
296
+ print('{} images no filename'.format(len(rows_no_filename)))
297
+ print('{} images no annotation'.format(len(rows_no_annotation)))
298
+ print('{} images handled successfully, {} total annotations'.format(len(image_id_to_image),len(annotations)))
299
+
300
+
301
+ #%% Write output .json
302
+
303
+ images = list(image_id_to_image.values())
304
+ categories = list(category_name_to_category.values())
305
+
306
+ data = {}
307
+ data['info'] = info
308
+ data['images'] = images
309
+ data['annotations'] = annotations
310
+ data['categories'] = categories
311
+
312
+ json.dump(data, open(output_json_filename, 'w'), indent=1)
313
+ print('Finished writing json to {}'.format(output_json_filename))
314
+
315
+
316
+ #%% Validate .json file
317
+
318
+ options = integrity_check_json_db.IntegrityCheckOptions()
319
+ options.baseDir = input_base_dir
320
+ options.bCheckImageSizes = False
321
+ options.bCheckImageExistence = False
322
+ options.bFindUnusedImages = False
323
+
324
+ sortedCategories, data, _ = integrity_check_json_db.integrity_check_json_db(output_json_filename, options)
325
+
326
+
327
+ #%% Preview labels
328
+
329
+ viz_options = visualize_db.DbVizOptions()
330
+ viz_options.num_to_visualize = 2000
331
+ viz_options.trim_to_images_with_bboxes = False
332
+ viz_options.add_search_links = False
333
+ viz_options.sort_by_filename = False
334
+ viz_options.parallelize_rendering = True
335
+ viz_options.classes_to_exclude = ['empty']
336
+ html_output_file, image_db = visualize_db.visualize_db(db_path=output_json_filename,
337
+ output_dir=os.path.join(
338
+ output_base_dir, 'preview'),
339
+ image_base_dir=input_base_dir,
340
+ options=viz_options)
341
+ os.startfile(html_output_file)
342
+
343
+
344
+ #%% Precision-recall analysis
345
+
346
+ from megadetector.postprocessing.postprocess_batch_results import PostProcessingOptions
347
+ from megadetector.postprocessing.postprocess_batch_results import process_batch_results
348
+
349
+ api_output_file = r'g:\auckland-doc\auckland-doc_20200801\combined_api_outputs\auckland-doc_202008012020.08.01_reformatMaukahuka_Auckland_Island2_TestingSummer_Trial_2019_detections.filtered_rde_0.60_0.85_5_0.05.json'
350
+ postprocessing_output_folder = r'G:\auckland-doc\auckland-doc_20200801\postprocessing'
351
+ image_base = r'E:\auckland-test\2_Testing'
352
+ ground_truth_json_file = output_json_filename
353
+
354
+ output_base = os.path.join(postprocessing_output_folder,'pr_analysis')
355
+ os.makedirs(output_base,exist_ok=True)
356
+
357
+ options = PostProcessingOptions()
358
+ options.unlabeled_classes.append('openadjusted')
359
+ options.image_base_dir = image_base
360
+ options.parallelize_rendering = True
361
+ options.include_almost_detections = True
362
+ options.num_images_to_sample = 2500
363
+ options.confidence_threshold = 0.75
364
+ options.almost_detection_confidence_threshold = 0.7
365
+ options.ground_truth_json_file = ground_truth_json_file
366
+ options.allow_missing_images = True
367
+ options.ground_truth_filename_replacements = {}
368
+ options.api_output_filename_replacements = {'2020.08.01_reformat\\Maukahuka_Auckland_Island\\2_Testing\\':''}
369
+ options.api_output_file = api_output_file
370
+ options.output_dir = output_base
371
+ ppresults = process_batch_results(options)
372
+ os.startfile(ppresults.output_html_file)
373
+