megadetector 5.0.28__py3-none-any.whl → 10.0.0__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 (197) hide show
  1. megadetector/api/batch_processing/integration/digiKam/xmp_integration.py +2 -2
  2. megadetector/api/batch_processing/integration/eMammal/test_scripts/push_annotations_to_emammal.py +1 -1
  3. megadetector/api/batch_processing/integration/eMammal/test_scripts/select_images_for_testing.py +1 -1
  4. megadetector/classification/aggregate_classifier_probs.py +3 -3
  5. megadetector/classification/analyze_failed_images.py +5 -5
  6. megadetector/classification/cache_batchapi_outputs.py +5 -5
  7. megadetector/classification/create_classification_dataset.py +11 -12
  8. megadetector/classification/crop_detections.py +10 -10
  9. megadetector/classification/csv_to_json.py +8 -8
  10. megadetector/classification/detect_and_crop.py +13 -15
  11. megadetector/classification/efficientnet/model.py +8 -8
  12. megadetector/classification/efficientnet/utils.py +6 -5
  13. megadetector/classification/evaluate_model.py +7 -7
  14. megadetector/classification/identify_mislabeled_candidates.py +6 -6
  15. megadetector/classification/json_to_azcopy_list.py +1 -1
  16. megadetector/classification/json_validator.py +29 -32
  17. megadetector/classification/map_classification_categories.py +9 -9
  18. megadetector/classification/merge_classification_detection_output.py +12 -9
  19. megadetector/classification/prepare_classification_script.py +19 -19
  20. megadetector/classification/prepare_classification_script_mc.py +26 -26
  21. megadetector/classification/run_classifier.py +4 -4
  22. megadetector/classification/save_mislabeled.py +6 -6
  23. megadetector/classification/train_classifier.py +1 -1
  24. megadetector/classification/train_classifier_tf.py +9 -9
  25. megadetector/classification/train_utils.py +10 -10
  26. megadetector/data_management/annotations/annotation_constants.py +1 -2
  27. megadetector/data_management/camtrap_dp_to_coco.py +79 -46
  28. megadetector/data_management/cct_json_utils.py +103 -103
  29. megadetector/data_management/cct_to_md.py +49 -49
  30. megadetector/data_management/cct_to_wi.py +33 -33
  31. megadetector/data_management/coco_to_labelme.py +75 -75
  32. megadetector/data_management/coco_to_yolo.py +210 -193
  33. megadetector/data_management/databases/add_width_and_height_to_db.py +86 -12
  34. megadetector/data_management/databases/combine_coco_camera_traps_files.py +40 -40
  35. megadetector/data_management/databases/integrity_check_json_db.py +228 -200
  36. megadetector/data_management/databases/subset_json_db.py +33 -33
  37. megadetector/data_management/generate_crops_from_cct.py +88 -39
  38. megadetector/data_management/get_image_sizes.py +54 -49
  39. megadetector/data_management/labelme_to_coco.py +133 -125
  40. megadetector/data_management/labelme_to_yolo.py +159 -73
  41. megadetector/data_management/lila/create_lila_blank_set.py +81 -83
  42. megadetector/data_management/lila/create_lila_test_set.py +32 -31
  43. megadetector/data_management/lila/create_links_to_md_results_files.py +18 -18
  44. megadetector/data_management/lila/download_lila_subset.py +21 -24
  45. megadetector/data_management/lila/generate_lila_per_image_labels.py +365 -107
  46. megadetector/data_management/lila/get_lila_annotation_counts.py +35 -33
  47. megadetector/data_management/lila/get_lila_image_counts.py +22 -22
  48. megadetector/data_management/lila/lila_common.py +73 -70
  49. megadetector/data_management/lila/test_lila_metadata_urls.py +28 -19
  50. megadetector/data_management/mewc_to_md.py +344 -340
  51. megadetector/data_management/ocr_tools.py +262 -255
  52. megadetector/data_management/read_exif.py +249 -227
  53. megadetector/data_management/remap_coco_categories.py +90 -28
  54. megadetector/data_management/remove_exif.py +81 -21
  55. megadetector/data_management/rename_images.py +187 -187
  56. megadetector/data_management/resize_coco_dataset.py +588 -120
  57. megadetector/data_management/speciesnet_to_md.py +41 -41
  58. megadetector/data_management/wi_download_csv_to_coco.py +55 -55
  59. megadetector/data_management/yolo_output_to_md_output.py +248 -122
  60. megadetector/data_management/yolo_to_coco.py +333 -191
  61. megadetector/detection/change_detection.py +832 -0
  62. megadetector/detection/process_video.py +340 -337
  63. megadetector/detection/pytorch_detector.py +358 -278
  64. megadetector/detection/run_detector.py +399 -186
  65. megadetector/detection/run_detector_batch.py +404 -377
  66. megadetector/detection/run_inference_with_yolov5_val.py +340 -327
  67. megadetector/detection/run_tiled_inference.py +257 -249
  68. megadetector/detection/tf_detector.py +24 -24
  69. megadetector/detection/video_utils.py +332 -295
  70. megadetector/postprocessing/add_max_conf.py +19 -11
  71. megadetector/postprocessing/categorize_detections_by_size.py +45 -45
  72. megadetector/postprocessing/classification_postprocessing.py +468 -433
  73. megadetector/postprocessing/combine_batch_outputs.py +23 -23
  74. megadetector/postprocessing/compare_batch_results.py +590 -525
  75. megadetector/postprocessing/convert_output_format.py +106 -102
  76. megadetector/postprocessing/create_crop_folder.py +347 -147
  77. megadetector/postprocessing/detector_calibration.py +173 -168
  78. megadetector/postprocessing/generate_csv_report.py +508 -499
  79. megadetector/postprocessing/load_api_results.py +48 -27
  80. megadetector/postprocessing/md_to_coco.py +133 -102
  81. megadetector/postprocessing/md_to_labelme.py +107 -90
  82. megadetector/postprocessing/md_to_wi.py +40 -40
  83. megadetector/postprocessing/merge_detections.py +92 -114
  84. megadetector/postprocessing/postprocess_batch_results.py +319 -301
  85. megadetector/postprocessing/remap_detection_categories.py +91 -38
  86. megadetector/postprocessing/render_detection_confusion_matrix.py +214 -205
  87. megadetector/postprocessing/repeat_detection_elimination/find_repeat_detections.py +57 -57
  88. megadetector/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +27 -28
  89. megadetector/postprocessing/repeat_detection_elimination/repeat_detections_core.py +704 -679
  90. megadetector/postprocessing/separate_detections_into_folders.py +226 -211
  91. megadetector/postprocessing/subset_json_detector_output.py +265 -262
  92. megadetector/postprocessing/top_folders_to_bottom.py +45 -45
  93. megadetector/postprocessing/validate_batch_results.py +70 -70
  94. megadetector/taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +52 -52
  95. megadetector/taxonomy_mapping/map_new_lila_datasets.py +18 -19
  96. megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +54 -33
  97. megadetector/taxonomy_mapping/preview_lila_taxonomy.py +67 -67
  98. megadetector/taxonomy_mapping/retrieve_sample_image.py +16 -16
  99. megadetector/taxonomy_mapping/simple_image_download.py +8 -8
  100. megadetector/taxonomy_mapping/species_lookup.py +156 -74
  101. megadetector/taxonomy_mapping/taxonomy_csv_checker.py +14 -14
  102. megadetector/taxonomy_mapping/taxonomy_graph.py +10 -10
  103. megadetector/taxonomy_mapping/validate_lila_category_mappings.py +13 -13
  104. megadetector/utils/ct_utils.py +1049 -211
  105. megadetector/utils/directory_listing.py +21 -77
  106. megadetector/utils/gpu_test.py +22 -22
  107. megadetector/utils/md_tests.py +632 -529
  108. megadetector/utils/path_utils.py +1520 -431
  109. megadetector/utils/process_utils.py +41 -41
  110. megadetector/utils/split_locations_into_train_val.py +62 -62
  111. megadetector/utils/string_utils.py +148 -27
  112. megadetector/utils/url_utils.py +489 -176
  113. megadetector/utils/wi_utils.py +2658 -2526
  114. megadetector/utils/write_html_image_list.py +137 -137
  115. megadetector/visualization/plot_utils.py +34 -30
  116. megadetector/visualization/render_images_with_thumbnails.py +39 -74
  117. megadetector/visualization/visualization_utils.py +487 -435
  118. megadetector/visualization/visualize_db.py +232 -198
  119. megadetector/visualization/visualize_detector_output.py +82 -76
  120. {megadetector-5.0.28.dist-info → megadetector-10.0.0.dist-info}/METADATA +5 -2
  121. megadetector-10.0.0.dist-info/RECORD +139 -0
  122. {megadetector-5.0.28.dist-info → megadetector-10.0.0.dist-info}/WHEEL +1 -1
  123. megadetector/api/batch_processing/api_core/__init__.py +0 -0
  124. megadetector/api/batch_processing/api_core/batch_service/__init__.py +0 -0
  125. megadetector/api/batch_processing/api_core/batch_service/score.py +0 -439
  126. megadetector/api/batch_processing/api_core/server.py +0 -294
  127. megadetector/api/batch_processing/api_core/server_api_config.py +0 -97
  128. megadetector/api/batch_processing/api_core/server_app_config.py +0 -55
  129. megadetector/api/batch_processing/api_core/server_batch_job_manager.py +0 -220
  130. megadetector/api/batch_processing/api_core/server_job_status_table.py +0 -149
  131. megadetector/api/batch_processing/api_core/server_orchestration.py +0 -360
  132. megadetector/api/batch_processing/api_core/server_utils.py +0 -88
  133. megadetector/api/batch_processing/api_core_support/__init__.py +0 -0
  134. megadetector/api/batch_processing/api_core_support/aggregate_results_manually.py +0 -46
  135. megadetector/api/batch_processing/api_support/__init__.py +0 -0
  136. megadetector/api/batch_processing/api_support/summarize_daily_activity.py +0 -152
  137. megadetector/api/batch_processing/data_preparation/__init__.py +0 -0
  138. megadetector/api/synchronous/__init__.py +0 -0
  139. megadetector/api/synchronous/api_core/animal_detection_api/__init__.py +0 -0
  140. megadetector/api/synchronous/api_core/animal_detection_api/api_backend.py +0 -151
  141. megadetector/api/synchronous/api_core/animal_detection_api/api_frontend.py +0 -263
  142. megadetector/api/synchronous/api_core/animal_detection_api/config.py +0 -35
  143. megadetector/api/synchronous/api_core/tests/__init__.py +0 -0
  144. megadetector/api/synchronous/api_core/tests/load_test.py +0 -110
  145. megadetector/data_management/importers/add_nacti_sizes.py +0 -52
  146. megadetector/data_management/importers/add_timestamps_to_icct.py +0 -79
  147. megadetector/data_management/importers/animl_results_to_md_results.py +0 -158
  148. megadetector/data_management/importers/auckland_doc_test_to_json.py +0 -373
  149. megadetector/data_management/importers/auckland_doc_to_json.py +0 -201
  150. megadetector/data_management/importers/awc_to_json.py +0 -191
  151. megadetector/data_management/importers/bellevue_to_json.py +0 -272
  152. megadetector/data_management/importers/cacophony-thermal-importer.py +0 -793
  153. megadetector/data_management/importers/carrizo_shrubfree_2018.py +0 -269
  154. megadetector/data_management/importers/carrizo_trail_cam_2017.py +0 -289
  155. megadetector/data_management/importers/cct_field_adjustments.py +0 -58
  156. megadetector/data_management/importers/channel_islands_to_cct.py +0 -913
  157. megadetector/data_management/importers/eMammal/copy_and_unzip_emammal.py +0 -180
  158. megadetector/data_management/importers/eMammal/eMammal_helpers.py +0 -249
  159. megadetector/data_management/importers/eMammal/make_eMammal_json.py +0 -223
  160. megadetector/data_management/importers/ena24_to_json.py +0 -276
  161. megadetector/data_management/importers/filenames_to_json.py +0 -386
  162. megadetector/data_management/importers/helena_to_cct.py +0 -283
  163. megadetector/data_management/importers/idaho-camera-traps.py +0 -1407
  164. megadetector/data_management/importers/idfg_iwildcam_lila_prep.py +0 -294
  165. megadetector/data_management/importers/import_desert_lion_conservation_camera_traps.py +0 -387
  166. megadetector/data_management/importers/jb_csv_to_json.py +0 -150
  167. megadetector/data_management/importers/mcgill_to_json.py +0 -250
  168. megadetector/data_management/importers/missouri_to_json.py +0 -490
  169. megadetector/data_management/importers/nacti_fieldname_adjustments.py +0 -79
  170. megadetector/data_management/importers/noaa_seals_2019.py +0 -181
  171. megadetector/data_management/importers/osu-small-animals-to-json.py +0 -364
  172. megadetector/data_management/importers/pc_to_json.py +0 -365
  173. megadetector/data_management/importers/plot_wni_giraffes.py +0 -123
  174. megadetector/data_management/importers/prepare_zsl_imerit.py +0 -131
  175. megadetector/data_management/importers/raic_csv_to_md_results.py +0 -416
  176. megadetector/data_management/importers/rspb_to_json.py +0 -356
  177. megadetector/data_management/importers/save_the_elephants_survey_A.py +0 -320
  178. megadetector/data_management/importers/save_the_elephants_survey_B.py +0 -329
  179. megadetector/data_management/importers/snapshot_safari_importer.py +0 -758
  180. megadetector/data_management/importers/snapshot_serengeti_lila.py +0 -1067
  181. megadetector/data_management/importers/snapshotserengeti/make_full_SS_json.py +0 -150
  182. megadetector/data_management/importers/snapshotserengeti/make_per_season_SS_json.py +0 -153
  183. megadetector/data_management/importers/sulross_get_exif.py +0 -65
  184. megadetector/data_management/importers/timelapse_csv_set_to_json.py +0 -490
  185. megadetector/data_management/importers/ubc_to_json.py +0 -399
  186. megadetector/data_management/importers/umn_to_json.py +0 -507
  187. megadetector/data_management/importers/wellington_to_json.py +0 -263
  188. megadetector/data_management/importers/wi_to_json.py +0 -442
  189. megadetector/data_management/importers/zamba_results_to_md_results.py +0 -180
  190. megadetector/data_management/lila/add_locations_to_island_camera_traps.py +0 -101
  191. megadetector/data_management/lila/add_locations_to_nacti.py +0 -151
  192. megadetector/utils/azure_utils.py +0 -178
  193. megadetector/utils/sas_blob_utils.py +0 -509
  194. megadetector-5.0.28.dist-info/RECORD +0 -209
  195. /megadetector/{api/batch_processing/__init__.py → __init__.py} +0 -0
  196. {megadetector-5.0.28.dist-info → megadetector-10.0.0.dist-info}/licenses/LICENSE +0 -0
  197. {megadetector-5.0.28.dist-info → megadetector-10.0.0.dist-info}/top_level.txt +0 -0
@@ -1,416 +0,0 @@
1
- """
2
-
3
- raic_csv_to_md_results.py
4
-
5
- Converts classification+detection results in the .csv format provided to the Snapshot
6
- Serengeti program by the RAIC team to the MD results format.
7
-
8
- The input format is two .csv files:
9
-
10
- * One with results, with columns [unnamed], filename, category, x_center, y_center,
11
- width, height, confidence, datetime
12
-
13
- * One with class IDs and names, with columns CLASS, SPECIES
14
-
15
- Filenames are relative paths to .txt files, but with slashes replaced by underscores, e.g. this
16
- file:
17
-
18
- B04_R1/I__00122.JPG
19
-
20
- ...appears in the .csv file as:
21
-
22
- B04_R1_I__00122.txt
23
-
24
- Image coordinates are in absolute floating-point units, with an upper-left origin.
25
-
26
- Unknowns at the time I'm writing this:
27
-
28
- * I don't know what the unnamed column is, but it looks like an ID I can safely ignore.
29
-
30
- * I believe that MegaDetector was run, then a classifier was run, but there is a
31
- single "confidence" column in the output. I am writing out the results as if they were a
32
- single multi-class detector. This is suspicious given the lack of a human class, which suggests
33
- that this is intended to be run in conjunection with MD.
34
-
35
- * There is no concept of "empty" in this file format, so by default I assume that images with
36
- no annotations in the .csv file were processed and determine to have no detections above some
37
- (unknown) threshold.
38
-
39
- * I'm not currently handling EXIF rotations, as part of the effort to simplify this file
40
- for conversion to R (see below).
41
-
42
- Note to self: this file should not take dependencies on other components of the MD
43
- repo, at the risk of creating some redundancy. I am going to convert this to R,
44
- which will be easier if it's not using any non-standard libraries. Anything in the
45
- "interactive driver" cells gets a pass.
46
-
47
- """
48
-
49
- #%% Imports and constants
50
-
51
- import os
52
- import glob
53
- import json
54
- import sys
55
- import argparse
56
-
57
- import pandas as pd
58
- from PIL import Image
59
-
60
-
61
- #%% Functions from the MD python package
62
-
63
- # ...that I'm choosing to copy and paste to facilitate a conversion of this
64
- # script to R.
65
-
66
- # Should all be lower-case
67
- IMG_EXTENSIONS = ('.jpg', '.jpeg', '.gif', '.png', '.tif', '.tiff', '.bmp')
68
-
69
- def _is_image_file(s, img_extensions=IMG_EXTENSIONS):
70
- """
71
- Checks a file's extension against a hard-coded set of image file
72
- extensions. Uses case-insensitive comparison.
73
-
74
- Does not check whether the file exists, only determines whether the filename
75
- implies it's an image file.
76
-
77
- Args:
78
- s (str): filename to evaluate for image-ness
79
- img_extensions (list, optional): list of known image file extensions
80
-
81
- Returns:
82
- bool: True if [s] appears to be an image file, else False
83
- """
84
-
85
- ext = os.path.splitext(s)[1]
86
- return ext.lower() in img_extensions
87
-
88
-
89
- def _find_image_strings(strings):
90
- """
91
- Given a list of strings that are potentially image file names, looks for
92
- strings that actually look like image file names (based on extension).
93
-
94
- Args:
95
- strings (list): list of filenames to check for image-ness
96
-
97
- Returns:
98
- list: the subset of [strings] that appear to be image filenames
99
- """
100
-
101
- return [s for s in strings if _is_image_file(s)]
102
-
103
-
104
- def _find_images(dirname,
105
- recursive=False,
106
- return_relative_paths=False,
107
- convert_slashes=True):
108
- """
109
- Finds all files in a directory that look like image file names. Returns
110
- absolute paths unless return_relative_paths is set. Uses the OS-native
111
- path separator unless convert_slashes is set, in which case will always
112
- use '/'.
113
-
114
- Args:
115
- dirname (str): the folder to search for images
116
- recursive (bool, optional): whether to search recursively
117
- return_relative_paths (str, optional): return paths that are relative
118
- to [dirname], rather than absolute paths
119
- convert_slashes (bool, optional): force forward slashes in return values
120
-
121
- Returns:
122
- list: list of image filenames found in [dirname]
123
- """
124
-
125
- assert os.path.isdir(dirname), '{} is not a folder'.format(dirname)
126
-
127
- if recursive:
128
- strings = glob.glob(os.path.join(dirname, '**', '*.*'), recursive=True)
129
- else:
130
- strings = glob.glob(os.path.join(dirname, '*.*'))
131
-
132
- image_files = _find_image_strings(strings)
133
-
134
- if return_relative_paths:
135
- image_files = [os.path.relpath(fn,dirname) for fn in image_files]
136
-
137
- image_files = sorted(image_files)
138
-
139
- if convert_slashes:
140
- image_files = [fn.replace('\\', '/') for fn in image_files]
141
-
142
- return image_files
143
-
144
-
145
- #%% Main conversion function
146
-
147
- def raic_csv_to_md_results(result_csv_file,
148
- class_mapping_csv_file,
149
- image_folder,
150
- output_file=None,
151
- unannotated_image_handling='empty'):
152
- """
153
- Converts a pair of .csv files (see file header for details) to MD results format.
154
-
155
- Currently errors if image filenames are ambiguous, or if any images referred to in
156
- the results are not available.
157
-
158
- Args:
159
- result_csv_file (str): the results file to read (.csv)
160
- class_mapping_csv_file (str): the class mapping file (.csv)
161
- image_folder (str): the folder containing all the images referred to in
162
- [result_csv_file]
163
- output_file (str, optional): the .json file to which we should write results. Defaults
164
- to [result_csv_file].json
165
- unannotated_image_handling (str, optional): can be "empty" (default) to assume
166
- that images without annotations are empty, "warning", "error", or "skip"
167
-
168
- Returns:
169
- str: the output file written, identical to [output_file] if [output_file] was not None
170
- """
171
-
172
- # Validate arguments
173
- assert os.path.isfile(result_csv_file), \
174
- 'Result file {} not found'.format(result_csv_file)
175
- assert os.path.isfile(class_mapping_csv_file), \
176
- 'Class mapping file {} not found'.format(class_mapping_csv_file)
177
- assert os.path.isdir(image_folder), \
178
- 'Image folder {} not found'.format(image_folder)
179
-
180
- if output_file is None:
181
- output_file = result_csv_file + '.json'
182
-
183
- image_files_relative = _find_images(image_folder,
184
- recursive=True,
185
- return_relative_paths=True,
186
- convert_slashes=True)
187
- image_file_base_flattened_to_image_file_relative = {}
188
- for fn in image_files_relative:
189
- # Convert, e.g. B04_R1/I__00108.JPG to B04_R1_I__00108
190
- fn_flattened = fn.replace('/','_')
191
- fn_flattened_base = os.path.splitext(fn_flattened)[0]
192
- image_file_base_flattened_to_image_file_relative[fn_flattened_base] = \
193
- fn
194
-
195
- # Read the .csv files
196
- df_results = pd.read_csv(result_csv_file)
197
- df_class_mapping = pd.read_csv(class_mapping_csv_file)
198
-
199
- assert 'CLASS' in df_class_mapping.columns and 'SPECIES' in df_class_mapping.columns, \
200
- 'Unexpected column names in class mapping file {}'.format(class_mapping_csv_file)
201
-
202
- category_id_to_name = {}
203
- for i_row,row in df_class_mapping.iterrows():
204
- class_id = int(row['CLASS'])
205
- assert class_id not in category_id_to_name, \
206
- 'Class ID {} occurs more than once in class mapping file {}'.format(
207
- class_id,class_mapping_csv_file)
208
- category_id_to_name[class_id] = row['SPECIES']
209
-
210
- if len(category_id_to_name) != len(set(category_id_to_name.values())):
211
- print('Warning: one or more categories are used more than once in class mapping file {}'.format(
212
- class_mapping_csv_file))
213
-
214
- # Convert results
215
-
216
- fn_relative_to_im = {}
217
-
218
- # i_row = 0; row = df_results.iloc[i_row]
219
- for i_row,row in df_results.iterrows():
220
-
221
- # Map the .txt filename base to a relative path
222
- bn = row['filename']
223
- assert bn.lower().endswith('.txt')
224
- bn_no_ext = os.path.splitext(bn)[0]
225
- assert bn_no_ext in image_file_base_flattened_to_image_file_relative, \
226
- 'No image found for result row {}'.format(row['filename'])
227
-
228
- image_fn_relative = image_file_base_flattened_to_image_file_relative[bn_no_ext]
229
-
230
- # Have we seen another detection for this image?
231
- if image_fn_relative in fn_relative_to_im:
232
-
233
- im = fn_relative_to_im[image_fn_relative]
234
-
235
- # If not, load this image so we can read its size
236
- else:
237
-
238
- image_fn_abs = os.path.join(image_folder,image_fn_relative)
239
- image = Image.open(image_fn_abs)
240
- w = image.size[0]
241
- h = image.size[1]
242
-
243
- im = {}
244
- im['file'] = image_fn_relative
245
- im['width'] = w
246
- im['height'] = h
247
- im['detections'] = []
248
- im['datetime'] = str(row['datetime'])
249
- fn_relative_to_im[image_fn_relative] = im
250
-
251
- # Convert annotation
252
- x_center_abs = row['x_center']
253
- y_center_abs = row['y_center']
254
- box_width_abs = row['width']
255
- box_height_abs = row['height']
256
-
257
- # Convert to relative coordinates
258
- box_left_abs = x_center_abs - (box_width_abs/2.0)
259
- box_top_abs = y_center_abs - (box_height_abs/2.0)
260
- bbox_normalized = [box_left_abs/im['width'],
261
- box_top_abs/im['height'],
262
- box_width_abs/im['width'],
263
- box_height_abs/im['height']]
264
-
265
- category_id = str(int(row['category']))
266
- confidence = row['confidence']
267
- assert isinstance(confidence,float) and confidence <= 1.0 and confidence >= 0.0
268
-
269
- det = {}
270
- im['detections'].append(det)
271
- det['category'] = category_id
272
- det['conf'] = confidence
273
- det['bbox'] = bbox_normalized
274
-
275
- # ...for each row
276
-
277
- n_empty_images = 0
278
-
279
- # Handle images without annotations
280
- for fn_relative in image_files_relative:
281
-
282
- if fn_relative not in fn_relative_to_im:
283
- if unannotated_image_handling == 'empty':
284
- im = {}
285
- im['file'] = fn_relative
286
- im['detections'] = []
287
- fn_relative_to_im[fn_relative] = im
288
- n_empty_images += 1
289
- # Don't bother to read width and height here
290
- elif unannotated_image_handling == 'warning':
291
- print('Warning: image {} is not represented in the .csv results file'.format(fn_relative))
292
- elif unannotated_image_handling == 'error':
293
- raise ValueError('Image {} is not represented in the .csv results file'.format(fn_relative))
294
- elif unannotated_image_handling == 'skip':
295
- continue
296
-
297
- # ...for each image file
298
-
299
- if n_empty_images > 0:
300
- print('Warning: assuming {} of {} images without annotations are empty'.format(
301
- n_empty_images,len(image_files_relative)))
302
-
303
- images = list(fn_relative_to_im.values())
304
-
305
- # The MD output format uses string-ints for category IDs, right now we have ints
306
- detection_categories = {}
307
- for category_id_int in category_id_to_name:
308
- detection_categories[str(category_id_int)] = category_id_to_name[category_id_int]
309
-
310
- info = {}
311
- info['format_version'] = '1.4'
312
- info['detector'] = 'RAIC .csv converter'
313
-
314
- d = {}
315
- d['images'] = images
316
- d['detection_categories'] = detection_categories
317
- d['info'] = info
318
-
319
- with open(output_file,'w') as f:
320
- json.dump(d,f,indent=1)
321
-
322
- return output_file
323
-
324
- # ...def raic_csv_to_md_results(...)
325
-
326
-
327
- #%% Interactive driver
328
-
329
- if False:
330
-
331
- pass
332
-
333
- #%% Test conversion
334
-
335
- base_folder = r'G:\temp\S24_B04_R1_output_annotations_for_Dan'
336
- result_csv_file = os.path.join(base_folder,'S24_B04_R1_output_annotations_for_Dan.csv')
337
- class_mapping_csv_file = os.path.join(base_folder,'categories_key.csv')
338
-
339
- # This is wrong, B04_R1 has to be part of the image paths
340
- # image_folder = os.path.join(base_folder,'B04_R1')
341
-
342
- image_folder = base_folder
343
-
344
- output_file = None
345
- unannotated_image_handling='empty'
346
-
347
- output_file = raic_csv_to_md_results(result_csv_file=result_csv_file,
348
- class_mapping_csv_file=class_mapping_csv_file,
349
- image_folder=image_folder,
350
- output_file=output_file,
351
- unannotated_image_handling=unannotated_image_handling)
352
-
353
- #%% Validate results file
354
-
355
- from megadetector.postprocessing.validate_batch_results import \
356
- ValidateBatchResultsOptions, validate_batch_results
357
-
358
- validation_options = ValidateBatchResultsOptions()
359
- validation_options.check_image_existence = True
360
- validation_options.relative_path_base = image_folder
361
- validation_options.return_data = True
362
-
363
- results = validate_batch_results(output_file,validation_options)
364
- assert len(results['validation_results']['errors']) == 0
365
- assert len(results['validation_results']['warnings']) == 0
366
-
367
-
368
- #%% Preview results
369
-
370
- from megadetector.postprocessing.postprocess_batch_results import \
371
- PostProcessingOptions, process_batch_results
372
-
373
- postprocessing_options = PostProcessingOptions()
374
-
375
- postprocessing_options.md_results_file = output_file
376
- postprocessing_options.output_dir = r'g:\temp\serengeti-conversion-preview'
377
- postprocessing_options.image_base_dir = image_folder
378
- postprocessing_options.confidence_threshold = 0.2
379
- postprocessing_options.num_images_to_sample = None
380
- postprocessing_options.viz_target_width = 1280
381
- postprocessing_options.line_thickness = 4
382
- postprocessing_options.parallelize_rendering_n_cores = 10
383
- postprocessing_options.parallelize_rendering_with_threads = True
384
-
385
- postprocessing_results = process_batch_results(postprocessing_options)
386
-
387
- from megadetector.utils.path_utils import open_file
388
- open_file(postprocessing_results.output_html_file)
389
-
390
-
391
- #%% Command-line driver
392
-
393
- def main():
394
-
395
- parser = argparse.ArgumentParser()
396
- parser.add_argument('result_csv_file', type=str,
397
- help='csv file containing AI results')
398
- parser.add_argument('class_mapping_csv_file', type=str,
399
- help='csv file containing class mappings (with columns CLASS, SPECIES)')
400
- parser.add_argument('image_folder', type=str,
401
- help='folder containing the images referred to in [result_csv_file]')
402
- parser.add_argument('--output_file', type=str, default=None,
403
- help='.json file to which we should write results (defaults to [result_csv_file].json)')
404
-
405
- if len(sys.argv[1:])==0:
406
- parser.print_help()
407
- parser.exit()
408
-
409
- args = parser.parse_args()
410
- raic_csv_to_md_results(result_csv_file=args.result_csv_file,
411
- class_mapping_csv_file=args.class_mapping_csv_file,
412
- image_folder=args.image_folder,
413
- output_file=args.output_file)
414
-
415
- if __name__ == '__main__':
416
- main()