megadetector 5.0.10__py3-none-any.whl → 5.0.11__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 (226) hide show
  1. {megadetector-5.0.10.dist-info → megadetector-5.0.11.dist-info}/LICENSE +0 -0
  2. {megadetector-5.0.10.dist-info → megadetector-5.0.11.dist-info}/METADATA +12 -11
  3. megadetector-5.0.11.dist-info/RECORD +5 -0
  4. megadetector-5.0.11.dist-info/top_level.txt +1 -0
  5. api/__init__.py +0 -0
  6. api/batch_processing/__init__.py +0 -0
  7. api/batch_processing/api_core/__init__.py +0 -0
  8. api/batch_processing/api_core/batch_service/__init__.py +0 -0
  9. api/batch_processing/api_core/batch_service/score.py +0 -439
  10. api/batch_processing/api_core/server.py +0 -294
  11. api/batch_processing/api_core/server_api_config.py +0 -98
  12. api/batch_processing/api_core/server_app_config.py +0 -55
  13. api/batch_processing/api_core/server_batch_job_manager.py +0 -220
  14. api/batch_processing/api_core/server_job_status_table.py +0 -152
  15. api/batch_processing/api_core/server_orchestration.py +0 -360
  16. api/batch_processing/api_core/server_utils.py +0 -92
  17. api/batch_processing/api_core_support/__init__.py +0 -0
  18. api/batch_processing/api_core_support/aggregate_results_manually.py +0 -46
  19. api/batch_processing/api_support/__init__.py +0 -0
  20. api/batch_processing/api_support/summarize_daily_activity.py +0 -152
  21. api/batch_processing/data_preparation/__init__.py +0 -0
  22. api/batch_processing/data_preparation/manage_local_batch.py +0 -2391
  23. api/batch_processing/data_preparation/manage_video_batch.py +0 -327
  24. api/batch_processing/integration/digiKam/setup.py +0 -6
  25. api/batch_processing/integration/digiKam/xmp_integration.py +0 -465
  26. api/batch_processing/integration/eMammal/test_scripts/config_template.py +0 -5
  27. api/batch_processing/integration/eMammal/test_scripts/push_annotations_to_emammal.py +0 -126
  28. api/batch_processing/integration/eMammal/test_scripts/select_images_for_testing.py +0 -55
  29. api/batch_processing/postprocessing/__init__.py +0 -0
  30. api/batch_processing/postprocessing/add_max_conf.py +0 -64
  31. api/batch_processing/postprocessing/categorize_detections_by_size.py +0 -163
  32. api/batch_processing/postprocessing/combine_api_outputs.py +0 -249
  33. api/batch_processing/postprocessing/compare_batch_results.py +0 -958
  34. api/batch_processing/postprocessing/convert_output_format.py +0 -397
  35. api/batch_processing/postprocessing/load_api_results.py +0 -195
  36. api/batch_processing/postprocessing/md_to_coco.py +0 -310
  37. api/batch_processing/postprocessing/md_to_labelme.py +0 -330
  38. api/batch_processing/postprocessing/merge_detections.py +0 -401
  39. api/batch_processing/postprocessing/postprocess_batch_results.py +0 -1904
  40. api/batch_processing/postprocessing/remap_detection_categories.py +0 -170
  41. api/batch_processing/postprocessing/render_detection_confusion_matrix.py +0 -661
  42. api/batch_processing/postprocessing/repeat_detection_elimination/find_repeat_detections.py +0 -211
  43. api/batch_processing/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +0 -82
  44. api/batch_processing/postprocessing/repeat_detection_elimination/repeat_detections_core.py +0 -1631
  45. api/batch_processing/postprocessing/separate_detections_into_folders.py +0 -731
  46. api/batch_processing/postprocessing/subset_json_detector_output.py +0 -696
  47. api/batch_processing/postprocessing/top_folders_to_bottom.py +0 -223
  48. api/synchronous/__init__.py +0 -0
  49. api/synchronous/api_core/animal_detection_api/__init__.py +0 -0
  50. api/synchronous/api_core/animal_detection_api/api_backend.py +0 -152
  51. api/synchronous/api_core/animal_detection_api/api_frontend.py +0 -266
  52. api/synchronous/api_core/animal_detection_api/config.py +0 -35
  53. api/synchronous/api_core/animal_detection_api/data_management/annotations/annotation_constants.py +0 -47
  54. api/synchronous/api_core/animal_detection_api/detection/detector_training/copy_checkpoints.py +0 -43
  55. api/synchronous/api_core/animal_detection_api/detection/detector_training/model_main_tf2.py +0 -114
  56. api/synchronous/api_core/animal_detection_api/detection/process_video.py +0 -543
  57. api/synchronous/api_core/animal_detection_api/detection/pytorch_detector.py +0 -304
  58. api/synchronous/api_core/animal_detection_api/detection/run_detector.py +0 -627
  59. api/synchronous/api_core/animal_detection_api/detection/run_detector_batch.py +0 -1029
  60. api/synchronous/api_core/animal_detection_api/detection/run_inference_with_yolov5_val.py +0 -581
  61. api/synchronous/api_core/animal_detection_api/detection/run_tiled_inference.py +0 -754
  62. api/synchronous/api_core/animal_detection_api/detection/tf_detector.py +0 -165
  63. api/synchronous/api_core/animal_detection_api/detection/video_utils.py +0 -495
  64. api/synchronous/api_core/animal_detection_api/md_utils/azure_utils.py +0 -174
  65. api/synchronous/api_core/animal_detection_api/md_utils/ct_utils.py +0 -262
  66. api/synchronous/api_core/animal_detection_api/md_utils/directory_listing.py +0 -251
  67. api/synchronous/api_core/animal_detection_api/md_utils/matlab_porting_tools.py +0 -97
  68. api/synchronous/api_core/animal_detection_api/md_utils/path_utils.py +0 -416
  69. api/synchronous/api_core/animal_detection_api/md_utils/process_utils.py +0 -110
  70. api/synchronous/api_core/animal_detection_api/md_utils/sas_blob_utils.py +0 -509
  71. api/synchronous/api_core/animal_detection_api/md_utils/string_utils.py +0 -59
  72. api/synchronous/api_core/animal_detection_api/md_utils/url_utils.py +0 -144
  73. api/synchronous/api_core/animal_detection_api/md_utils/write_html_image_list.py +0 -226
  74. api/synchronous/api_core/animal_detection_api/md_visualization/visualization_utils.py +0 -841
  75. api/synchronous/api_core/tests/__init__.py +0 -0
  76. api/synchronous/api_core/tests/load_test.py +0 -110
  77. classification/__init__.py +0 -0
  78. classification/aggregate_classifier_probs.py +0 -108
  79. classification/analyze_failed_images.py +0 -227
  80. classification/cache_batchapi_outputs.py +0 -198
  81. classification/create_classification_dataset.py +0 -627
  82. classification/crop_detections.py +0 -516
  83. classification/csv_to_json.py +0 -226
  84. classification/detect_and_crop.py +0 -855
  85. classification/efficientnet/__init__.py +0 -9
  86. classification/efficientnet/model.py +0 -415
  87. classification/efficientnet/utils.py +0 -610
  88. classification/evaluate_model.py +0 -520
  89. classification/identify_mislabeled_candidates.py +0 -152
  90. classification/json_to_azcopy_list.py +0 -63
  91. classification/json_validator.py +0 -695
  92. classification/map_classification_categories.py +0 -276
  93. classification/merge_classification_detection_output.py +0 -506
  94. classification/prepare_classification_script.py +0 -194
  95. classification/prepare_classification_script_mc.py +0 -228
  96. classification/run_classifier.py +0 -286
  97. classification/save_mislabeled.py +0 -110
  98. classification/train_classifier.py +0 -825
  99. classification/train_classifier_tf.py +0 -724
  100. classification/train_utils.py +0 -322
  101. data_management/__init__.py +0 -0
  102. data_management/annotations/__init__.py +0 -0
  103. data_management/annotations/annotation_constants.py +0 -34
  104. data_management/camtrap_dp_to_coco.py +0 -238
  105. data_management/cct_json_utils.py +0 -395
  106. data_management/cct_to_md.py +0 -176
  107. data_management/cct_to_wi.py +0 -289
  108. data_management/coco_to_labelme.py +0 -272
  109. data_management/coco_to_yolo.py +0 -662
  110. data_management/databases/__init__.py +0 -0
  111. data_management/databases/add_width_and_height_to_db.py +0 -33
  112. data_management/databases/combine_coco_camera_traps_files.py +0 -206
  113. data_management/databases/integrity_check_json_db.py +0 -477
  114. data_management/databases/subset_json_db.py +0 -115
  115. data_management/generate_crops_from_cct.py +0 -149
  116. data_management/get_image_sizes.py +0 -188
  117. data_management/importers/add_nacti_sizes.py +0 -52
  118. data_management/importers/add_timestamps_to_icct.py +0 -79
  119. data_management/importers/animl_results_to_md_results.py +0 -158
  120. data_management/importers/auckland_doc_test_to_json.py +0 -372
  121. data_management/importers/auckland_doc_to_json.py +0 -200
  122. data_management/importers/awc_to_json.py +0 -189
  123. data_management/importers/bellevue_to_json.py +0 -273
  124. data_management/importers/cacophony-thermal-importer.py +0 -796
  125. data_management/importers/carrizo_shrubfree_2018.py +0 -268
  126. data_management/importers/carrizo_trail_cam_2017.py +0 -287
  127. data_management/importers/cct_field_adjustments.py +0 -57
  128. data_management/importers/channel_islands_to_cct.py +0 -913
  129. data_management/importers/eMammal/copy_and_unzip_emammal.py +0 -180
  130. data_management/importers/eMammal/eMammal_helpers.py +0 -249
  131. data_management/importers/eMammal/make_eMammal_json.py +0 -223
  132. data_management/importers/ena24_to_json.py +0 -275
  133. data_management/importers/filenames_to_json.py +0 -385
  134. data_management/importers/helena_to_cct.py +0 -282
  135. data_management/importers/idaho-camera-traps.py +0 -1407
  136. data_management/importers/idfg_iwildcam_lila_prep.py +0 -294
  137. data_management/importers/jb_csv_to_json.py +0 -150
  138. data_management/importers/mcgill_to_json.py +0 -250
  139. data_management/importers/missouri_to_json.py +0 -489
  140. data_management/importers/nacti_fieldname_adjustments.py +0 -79
  141. data_management/importers/noaa_seals_2019.py +0 -181
  142. data_management/importers/pc_to_json.py +0 -365
  143. data_management/importers/plot_wni_giraffes.py +0 -123
  144. data_management/importers/prepare-noaa-fish-data-for-lila.py +0 -359
  145. data_management/importers/prepare_zsl_imerit.py +0 -131
  146. data_management/importers/rspb_to_json.py +0 -356
  147. data_management/importers/save_the_elephants_survey_A.py +0 -320
  148. data_management/importers/save_the_elephants_survey_B.py +0 -332
  149. data_management/importers/snapshot_safari_importer.py +0 -758
  150. data_management/importers/snapshot_safari_importer_reprise.py +0 -665
  151. data_management/importers/snapshot_serengeti_lila.py +0 -1067
  152. data_management/importers/snapshotserengeti/make_full_SS_json.py +0 -150
  153. data_management/importers/snapshotserengeti/make_per_season_SS_json.py +0 -153
  154. data_management/importers/sulross_get_exif.py +0 -65
  155. data_management/importers/timelapse_csv_set_to_json.py +0 -490
  156. data_management/importers/ubc_to_json.py +0 -399
  157. data_management/importers/umn_to_json.py +0 -507
  158. data_management/importers/wellington_to_json.py +0 -263
  159. data_management/importers/wi_to_json.py +0 -441
  160. data_management/importers/zamba_results_to_md_results.py +0 -181
  161. data_management/labelme_to_coco.py +0 -548
  162. data_management/labelme_to_yolo.py +0 -272
  163. data_management/lila/__init__.py +0 -0
  164. data_management/lila/add_locations_to_island_camera_traps.py +0 -97
  165. data_management/lila/add_locations_to_nacti.py +0 -147
  166. data_management/lila/create_lila_blank_set.py +0 -557
  167. data_management/lila/create_lila_test_set.py +0 -151
  168. data_management/lila/create_links_to_md_results_files.py +0 -106
  169. data_management/lila/download_lila_subset.py +0 -177
  170. data_management/lila/generate_lila_per_image_labels.py +0 -515
  171. data_management/lila/get_lila_annotation_counts.py +0 -170
  172. data_management/lila/get_lila_image_counts.py +0 -111
  173. data_management/lila/lila_common.py +0 -300
  174. data_management/lila/test_lila_metadata_urls.py +0 -132
  175. data_management/ocr_tools.py +0 -874
  176. data_management/read_exif.py +0 -681
  177. data_management/remap_coco_categories.py +0 -84
  178. data_management/remove_exif.py +0 -66
  179. data_management/resize_coco_dataset.py +0 -189
  180. data_management/wi_download_csv_to_coco.py +0 -246
  181. data_management/yolo_output_to_md_output.py +0 -441
  182. data_management/yolo_to_coco.py +0 -676
  183. detection/__init__.py +0 -0
  184. detection/detector_training/__init__.py +0 -0
  185. detection/detector_training/model_main_tf2.py +0 -114
  186. detection/process_video.py +0 -703
  187. detection/pytorch_detector.py +0 -337
  188. detection/run_detector.py +0 -779
  189. detection/run_detector_batch.py +0 -1219
  190. detection/run_inference_with_yolov5_val.py +0 -917
  191. detection/run_tiled_inference.py +0 -935
  192. detection/tf_detector.py +0 -188
  193. detection/video_utils.py +0 -606
  194. docs/source/conf.py +0 -43
  195. md_utils/__init__.py +0 -0
  196. md_utils/azure_utils.py +0 -174
  197. md_utils/ct_utils.py +0 -612
  198. md_utils/directory_listing.py +0 -246
  199. md_utils/md_tests.py +0 -968
  200. md_utils/path_utils.py +0 -1044
  201. md_utils/process_utils.py +0 -157
  202. md_utils/sas_blob_utils.py +0 -509
  203. md_utils/split_locations_into_train_val.py +0 -228
  204. md_utils/string_utils.py +0 -92
  205. md_utils/url_utils.py +0 -323
  206. md_utils/write_html_image_list.py +0 -225
  207. md_visualization/__init__.py +0 -0
  208. md_visualization/plot_utils.py +0 -293
  209. md_visualization/render_images_with_thumbnails.py +0 -275
  210. md_visualization/visualization_utils.py +0 -1537
  211. md_visualization/visualize_db.py +0 -551
  212. md_visualization/visualize_detector_output.py +0 -406
  213. megadetector-5.0.10.dist-info/RECORD +0 -224
  214. megadetector-5.0.10.dist-info/top_level.txt +0 -8
  215. taxonomy_mapping/__init__.py +0 -0
  216. taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +0 -491
  217. taxonomy_mapping/map_new_lila_datasets.py +0 -154
  218. taxonomy_mapping/prepare_lila_taxonomy_release.py +0 -142
  219. taxonomy_mapping/preview_lila_taxonomy.py +0 -591
  220. taxonomy_mapping/retrieve_sample_image.py +0 -71
  221. taxonomy_mapping/simple_image_download.py +0 -218
  222. taxonomy_mapping/species_lookup.py +0 -834
  223. taxonomy_mapping/taxonomy_csv_checker.py +0 -159
  224. taxonomy_mapping/taxonomy_graph.py +0 -346
  225. taxonomy_mapping/validate_lila_category_mappings.py +0 -83
  226. {megadetector-5.0.10.dist-info → megadetector-5.0.11.dist-info}/WHEEL +0 -0
@@ -1,917 +0,0 @@
1
- """
2
-
3
- run_inference_with_yolov5_val.py
4
-
5
- Runs a folder of images through MegaDetector (or another YOLOv5/YOLOv8 model) with YOLO's
6
- val.py, converting the output to the standard MD format. The reasons this script exists,
7
- as an alternative to the standard run_detector_batch.py are:
8
-
9
- * This script provides access to YOLO's test-time augmentation tools.
10
- * This script serves a reference implementation: by any reasonable definition, YOLOv5's
11
- val.py produces the "correct" result for any image, since it matches what was used in
12
- training.
13
- * This script works for any Ultralytics detection model, including YOLOv8 models
14
-
15
- YOLOv5's val.py uses each file's base name as a unique identifier, which doesn't work
16
- when you have typical camera trap images like:
17
-
18
- * a/b/c/RECONYX0001.JPG
19
- * d/e/f/RECONYX0001.JPG
20
-
21
- ...both of which would just be "RECONYX0001.JPG". So this script jumps through a bunch of
22
- hoops to put a symlinks in a flat folder, run YOLOv5 on that folder, and map the results back
23
- to the real files.
24
-
25
- If you are running a YOLOv5 model, this script currently requires the caller to supply the path
26
- where a working YOLOv5 install lives, and assumes that the current conda environment is all set up for
27
- YOLOv5. If you are running a YOLOv8 model, the folder doesn't matter, but it assumes that ultralytics
28
- tools are available in the current environment.
29
-
30
- By default, this script uses symlinks to format the input images in a way that YOLO's
31
- val.py likes, as per above. This requires admin privileges on Windows... actually technically this
32
- only requires permissions to create symbolic links, but I've never seen a case where someone has
33
- that permission and *doesn't* have admin privileges. If you are running this script on
34
- Windows and you don't have admin privileges, use --no_use_symlinks, which will make copies of images,
35
- rather than using symlinks.
36
-
37
- TODO:
38
-
39
- * Multiple GPU support
40
- * Checkpointing
41
- * Support alternative class names at the command line (currently defaults to MD classes,
42
- though other class names can be supplied programmatically)
43
-
44
- """
45
-
46
- #%% Imports
47
-
48
- import os
49
- import sys
50
- import uuid
51
- import glob
52
- import tempfile
53
- import shutil
54
- import json
55
-
56
- from tqdm import tqdm
57
-
58
- from md_utils import path_utils
59
- from md_utils import process_utils
60
- from md_utils import string_utils
61
- from data_management import yolo_output_to_md_output
62
- from detection.run_detector import try_download_known_detector
63
-
64
- default_image_size_with_augmentation = int(1280 * 1.3)
65
- default_image_size_with_no_augmentation = 1280
66
-
67
-
68
- #%% Options class
69
-
70
- class YoloInferenceOptions:
71
- """
72
- Parameters that control the behavior of run_inference_with_yolov5_val(), including
73
- the input/output filenames.
74
- """
75
-
76
- ## Required ##
77
-
78
- #: Folder of images to process
79
- input_folder = None
80
-
81
- #: Model filename (ending in .pt), or a well-known model name (e.g. "MDV5A")
82
- model_filename = None
83
-
84
- #: .json output file, in MD results format
85
- output_file = None
86
-
87
-
88
- ## Optional ##
89
-
90
- #: Required for older YOLOv5 inference, not for newer ulytralytics/YOLOv8 inference
91
- yolo_working_folder = None
92
-
93
- #: Currently 'yolov5' and 'ultralytics' are supported, and really these are proxies for
94
- #: "the yolov5 repo" and "the ultralytics repo".
95
- model_type = 'yolov5'
96
-
97
- #: Image size to use; this is a single int, which in ultralytics's terminology means
98
- #: "scale the long side of the image to this size, and preserve aspect ratio".
99
- image_size = default_image_size_with_augmentation
100
-
101
- #: Detections below this threshold will not be included in the output file
102
- conf_thres = '0.001'
103
-
104
- #: Batch size... has no impact on results, but may create memory issues if you set
105
- #: this to large values
106
- batch_size = 1
107
-
108
- #: Device string: typically '0' for GPU 0, '1' for GPU 1, etc., or 'cpu'
109
- device_string = '0'
110
-
111
- #: Should we enable test-time augmentation?
112
- augment = True
113
-
114
- #: Should we enable half-precision inference?
115
- half_precision_enabled = None
116
-
117
- #: Where should we stash the temporary symlinks used to give unique identifiers to image files?
118
- #:
119
- #: If this is None, we'll create a folder in system temp space.
120
- symlink_folder = None
121
-
122
- #: Should we use symlinks to give unique identifiers to image files (vs. copies)?
123
- use_symlinks = True
124
-
125
- #: Temporary folder to stash intermediate YOLO results.
126
- #:
127
- #: If this is None, we'll create a folder in system temp space.
128
- yolo_results_folder = None
129
-
130
- #: Should we remove the symlink folder when we're done?
131
- remove_symlink_folder = True
132
-
133
- #: Should we remove the intermediate results folder when we're done?
134
- remove_yolo_results_folder = True
135
-
136
- #: These are deliberately offset from the standard MD categories; YOLOv5
137
- #: needs categories IDs to start at 0.
138
- #:
139
- #: This can also be a string that points to a YOLO dataset.yaml file.
140
- yolo_category_id_to_name = {0:'animal',1:'person',2:'vehicle'}
141
-
142
- #: What should we do if the output file already exists?
143
- #:
144
- #: Can be 'error', 'skip', or 'overwrite'.
145
- overwrite_handling = 'skip'
146
-
147
- #: If True, we'll do a dry run that lets you preview the YOLO val command, without
148
- #: actually running it.
149
- preview_yolo_command_only = False
150
-
151
- #: By default, if any errors occur while we're copying images or creating symlinks, it's
152
- #: game over. If this is True, those errors become warnings, and we plow ahead.
153
- treat_copy_failures_as_warnings = False
154
-
155
- #: Save YOLO console output
156
- save_yolo_debug_output = False
157
-
158
- #: Whether to search for images recursively within [input_folder]
159
- recursive = True
160
-
161
-
162
- # ...YoloInferenceOptions()
163
-
164
-
165
- #%% Main function
166
-
167
- def run_inference_with_yolo_val(options):
168
- """
169
- Runs a folder of images through MegaDetector (or another YOLOv5/YOLOv8 model) with YOLO's
170
- val.py, converting the output to the standard MD format.
171
-
172
- Args:
173
- options (YoloInferenceOptions): all the parameters used to control this process,
174
- including filenames; see YoloInferenceOptions for details
175
- """
176
-
177
- ##%% Input and path handling
178
-
179
- if options.model_type == 'yolov8':
180
-
181
- print('Warning: model type "yolov8" supplied, "ultralytics" is the preferred model type string for YOLOv8 models')
182
- options.model_type = 'ultralytics'
183
-
184
- if (options.model_type == 'yolov5') and ('yolov8' in options.model_filename.lower()):
185
- print('\n\n*** Warning: model type set as "yolov5", but your model filename contains "yolov8"... did you mean to use --model_type yolov8?" ***\n\n')
186
-
187
- if options.yolo_working_folder is None:
188
- assert options.model_type == 'ultralytics', \
189
- 'A working folder is required to run YOLOv5 val.py'
190
- else:
191
- assert os.path.isdir(options.yolo_working_folder), \
192
- 'Could not find working folder {}'.format(options.yolo_working_folder)
193
-
194
- assert os.path.isdir(options.input_folder) or os.path.isfile(options.input_folder), \
195
- 'Could not find input {}'.format(options.input_folder)
196
-
197
- if options.half_precision_enabled is not None:
198
- assert options.half_precision_enabled in (0,1), \
199
- 'Invalid value {} for --half_precision_enabled (should be 0 or 1)'.format(
200
- options.half_precision_enabled)
201
-
202
- # If the model filename is a known model string (e.g. "MDv5A", download the model if necessary)
203
- model_filename = try_download_known_detector(options.model_filename)
204
-
205
- assert os.path.isfile(model_filename), \
206
- 'Could not find model file {}'.format(model_filename)
207
-
208
- if os.path.exists(options.output_file):
209
- if options.overwrite_handling == 'skip':
210
- print('Warning: output file {} exists, skipping'.format(options.output_file))
211
- return
212
- elif options.overwrite_handling == 'overwrite':
213
- print('Warning: output file {} exists, overwriting'.format(options.output_file))
214
- elif options.overwrite_handling == 'error':
215
- raise ValueError('Output file {} exists'.format(options.output_file))
216
- else:
217
- raise ValueError('Unknown output handling method {}'.format(options.overwrite_handling))
218
-
219
- os.makedirs(os.path.dirname(options.output_file),exist_ok=True)
220
-
221
-
222
- ##%% Other input handling
223
-
224
- if isinstance(options.yolo_category_id_to_name,str):
225
- assert os.path.isfile(options.yolo_category_id_to_name)
226
- yolo_dataset_file = options.yolo_category_id_to_name
227
- options.yolo_category_id_to_name = \
228
- yolo_output_to_md_output.read_classes_from_yolo_dataset_file(yolo_dataset_file)
229
- print('Loaded {} category mappings from {}'.format(
230
- len(options.yolo_category_id_to_name),yolo_dataset_file))
231
-
232
- temporary_folder = None
233
- symlink_folder_is_temp_folder = False
234
- yolo_folder_is_temp_folder = False
235
-
236
- job_id = str(uuid.uuid1())
237
-
238
- def get_job_temporary_folder(tf):
239
- if tf is not None:
240
- return tf
241
- tempdir_base = tempfile.gettempdir()
242
- tf = os.path.join(tempdir_base,'md_to_yolo','md_to_yolo_' + job_id)
243
- os.makedirs(tf,exist_ok=True)
244
- return tf
245
-
246
- symlink_folder = options.symlink_folder
247
- yolo_results_folder = options.yolo_results_folder
248
-
249
- if symlink_folder is None:
250
- temporary_folder = get_job_temporary_folder(temporary_folder)
251
- symlink_folder = os.path.join(temporary_folder,'symlinks')
252
- symlink_folder_is_temp_folder = True
253
-
254
- if yolo_results_folder is None:
255
- temporary_folder = get_job_temporary_folder(temporary_folder)
256
- yolo_results_folder = os.path.join(temporary_folder,'yolo_results')
257
- yolo_folder_is_temp_folder = True
258
-
259
- # Attach a GUID to the symlink folder, regardless of whether we created it
260
- symlink_folder_inner = os.path.join(symlink_folder,job_id)
261
-
262
- os.makedirs(symlink_folder_inner,exist_ok=True)
263
- os.makedirs(yolo_results_folder,exist_ok=True)
264
-
265
-
266
- ##%% Enumerate images
267
-
268
- if os.path.isdir(options.input_folder):
269
- image_files_absolute = path_utils.find_images(options.input_folder,recursive=options.recursive)
270
- else:
271
- assert os.path.isfile(options.input_folder)
272
- with open(options.input_folder,'r') as f:
273
- image_files_absolute = json.load(f)
274
- assert isinstance(image_files_absolute,list)
275
- for fn in image_files_absolute:
276
- assert os.path.isfile(fn), 'Could not find image file {}'.format(fn)
277
-
278
-
279
- ##%% Create symlinks to give a unique ID to each image
280
-
281
- image_id_to_file = {}
282
- image_id_to_error = {}
283
-
284
- if options.use_symlinks:
285
- print('Creating {} symlinks in {}'.format(len(image_files_absolute),symlink_folder_inner))
286
- else:
287
- print('Symlinks disabled, copying {} images to {}'.format(len(image_files_absolute),symlink_folder_inner))
288
-
289
- # i_image = 0; image_fn = image_files_absolute[i_image]
290
- for i_image,image_fn in tqdm(enumerate(image_files_absolute),total=len(image_files_absolute)):
291
-
292
- ext = os.path.splitext(image_fn)[1]
293
-
294
- image_id = str(i_image).zfill(10)
295
- image_id_to_file[image_id] = image_fn
296
- symlink_name = image_id + ext
297
- symlink_full_path = os.path.join(symlink_folder_inner,symlink_name)
298
-
299
- try:
300
- if options.use_symlinks:
301
- path_utils.safe_create_link(image_fn,symlink_full_path)
302
- else:
303
- shutil.copyfile(image_fn,symlink_full_path)
304
- except Exception as e:
305
- error_string = str(e)
306
- image_id_to_error[image_id] = error_string
307
- # Always break if the user is trying to create symlinks on Windows without
308
- # permission, 100% of images will always fail in this case.
309
- if ('a required privilege is not held by the client' in error_string.lower()) or \
310
- (not options.treat_copy_failures_as_warnings):
311
- print('\nError copying/creating link for input file {}: {}'.format(
312
- image_fn,error_string))
313
-
314
- raise
315
- else:
316
- print('Warning: error copying/creating link for input file {}: {}'.format(
317
- image_fn,error_string))
318
- continue
319
-
320
- # ...for each image
321
-
322
-
323
- ##%% Create the dataset file if necessary
324
-
325
- # This may have been passed in as a string, but at this point, we should have
326
- # loaded the dataset file.
327
- assert isinstance(options.yolo_category_id_to_name,dict)
328
-
329
- # Category IDs need to be continuous integers starting at 0
330
- category_ids = sorted(list(options.yolo_category_id_to_name.keys()))
331
- assert category_ids[0] == 0
332
- assert len(category_ids) == 1 + category_ids[-1]
333
-
334
- yolo_dataset_file = os.path.join(yolo_results_folder,'dataset.yaml')
335
-
336
- with open(yolo_dataset_file,'w') as f:
337
- f.write('path: {}\n'.format(symlink_folder_inner))
338
- f.write('train: .\n')
339
- f.write('val: .\n')
340
- f.write('test: .\n')
341
- f.write('\n')
342
- f.write('nc: {}\n'.format(len(options.yolo_category_id_to_name)))
343
- f.write('\n')
344
- f.write('names:\n')
345
- for category_id in category_ids:
346
- assert isinstance(category_id,int)
347
- f.write(' {}: {}\n'.format(category_id,
348
- options.yolo_category_id_to_name[category_id]))
349
-
350
-
351
- ##%% Prepare Python command or YOLO CLI command
352
-
353
- image_size_string = str(round(options.image_size))
354
-
355
- if options.model_type == 'yolov5':
356
-
357
- cmd = 'python val.py --task test --data "{}"'.format(yolo_dataset_file)
358
- cmd += ' --weights "{}"'.format(model_filename)
359
- cmd += ' --batch-size {} --imgsz {} --conf-thres {}'.format(
360
- options.batch_size,image_size_string,options.conf_thres)
361
- cmd += ' --device "{}" --save-json'.format(options.device_string)
362
- cmd += ' --project "{}" --name "{}" --exist-ok'.format(yolo_results_folder,'yolo_results')
363
-
364
- if options.augment:
365
- cmd += ' --augment'
366
-
367
- # --half is a store_true argument for YOLOv5's val.py
368
- if (options.half_precision_enabled is not None) and (options.half_precision_enabled == 1):
369
- cmd += ' --half'
370
-
371
- # Sometimes useful for debugging
372
- # cmd += ' --save_conf --save_txt'
373
-
374
- elif options.model_type == 'ultralytics':
375
-
376
- if options.augment:
377
- augment_string = 'augment'
378
- else:
379
- augment_string = ''
380
-
381
- cmd = 'yolo val {} model="{}" imgsz={} batch={} data="{}" project="{}" name="{}" device="{}"'.\
382
- format(augment_string,model_filename,image_size_string,options.batch_size,
383
- yolo_dataset_file,yolo_results_folder,'yolo_results',options.device_string)
384
- cmd += ' save_json exist_ok'
385
-
386
- if (options.half_precision_enabled is not None):
387
- if options.half_precision_enabled == 1:
388
- cmd += ' --half=True'
389
- else:
390
- assert options.half_precision_enabled == 0
391
- cmd += ' --half=False'
392
-
393
- # Sometimes useful for debugging
394
- # cmd += ' save_conf save_txt'
395
-
396
- else:
397
-
398
- raise ValueError('Unrecognized model type {}'.format(options.model_type))
399
-
400
- # print(cmd); import clipboard; clipboard.copy(cmd)
401
-
402
-
403
- ##%% Run YOLO command
404
-
405
- if options.yolo_working_folder is not None:
406
- current_dir = os.getcwd()
407
- os.chdir(options.yolo_working_folder)
408
-
409
- print('Running YOLO inference command:\n{}\n'.format(cmd))
410
-
411
- if options.preview_yolo_command_only:
412
-
413
- if options.remove_symlink_folder:
414
- try:
415
- print('Removing YOLO symlink folder {}'.format(symlink_folder))
416
- shutil.rmtree(symlink_folder)
417
- except Exception:
418
- print('Warning: error removing symlink folder {}'.format(symlink_folder))
419
- pass
420
- if options.remove_yolo_results_folder:
421
- try:
422
- print('Removing YOLO results folder {}'.format(yolo_results_folder))
423
- shutil.rmtree(yolo_results_folder)
424
- except Exception:
425
- print('Warning: error removing YOLO results folder {}'.format(yolo_results_folder))
426
- pass
427
-
428
- sys.exit()
429
-
430
- execution_result = process_utils.execute_and_print(cmd,encoding='utf-8',verbose=True)
431
- assert execution_result['status'] == 0, 'Error running {}'.format(options.model_type)
432
- yolo_console_output = execution_result['output']
433
-
434
- if options.save_yolo_debug_output:
435
- with open(os.path.join(yolo_results_folder,'yolo_console_output.txt'),'w') as f:
436
- for s in yolo_console_output:
437
- f.write(s + '\n')
438
- with open(os.path.join(yolo_results_folder,'image_id_to_file.json'),'w') as f:
439
- json.dump(image_id_to_file,f,indent=1)
440
- with open(os.path.join(yolo_results_folder,'image_id_to_error.json'),'w') as f:
441
- json.dump(image_id_to_error,f,indent=1)
442
-
443
-
444
- # YOLO console output contains lots of ANSI escape codes, remove them for easier parsing
445
- yolo_console_output = [string_utils.remove_ansi_codes(s) for s in yolo_console_output]
446
-
447
- # Find errors that occurred during the initial corruption check; these will not be included in the
448
- # output. Errors that occur during inference will be handled separately.
449
- yolo_read_failures = []
450
-
451
- for line in yolo_console_output:
452
- # Lines look like:
453
- #
454
- # For ultralytics val:
455
- #
456
- # val: WARNING ⚠️ /a/b/c/d.jpg: ignoring corrupt image/label: [Errno 13] Permission denied: '/a/b/c/d.jpg'
457
- # line = "val: WARNING ⚠️ /a/b/c/d.jpg: ignoring corrupt image/label: [Errno 13] Permission denied: '/a/b/c/d.jpg'"
458
- #
459
- # For yolov5 val.py:
460
- #
461
- # test: WARNING: a/b/c/d.jpg: ignoring corrupt image/label: cannot identify image file '/a/b/c/d.jpg'
462
- # line = "test: WARNING: a/b/c/d.jpg: ignoring corrupt image/label: cannot identify image file '/a/b/c/d.jpg'"
463
- if 'cannot identify image file' in line:
464
- tokens = line.split('cannot identify image file')
465
- image_name = tokens[-1].strip()
466
- assert image_name[0] == "'" and image_name [-1] == "'"
467
- image_name = image_name[1:-1]
468
- yolo_read_failures.append(image_name)
469
- elif 'ignoring corrupt image/label' in line:
470
- assert 'WARNING' in line
471
- if '⚠️' in line:
472
- assert line.startswith('val'), \
473
- 'Unrecognized line in YOLO output: {}'.format(line)
474
- tokens = line.split('ignoring corrupt image/label')
475
- image_name = tokens[0].split('⚠️')[-1].strip()
476
- else:
477
- assert line.startswith('test'), \
478
- 'Unrecognized line in YOLO output: {}'.format(line)
479
- tokens = line.split('ignoring corrupt image/label')
480
- image_name = tokens[0].split('WARNING:')[-1].strip()
481
- assert image_name.endswith(':')
482
- image_name = image_name[0:-1]
483
- yolo_read_failures.append(image_name)
484
-
485
- # image_file = yolo_read_failures[0]
486
- for image_file in yolo_read_failures:
487
- image_id = os.path.splitext(os.path.basename(image_file))[0]
488
- assert image_id in image_id_to_file
489
- if image_id not in image_id_to_error:
490
- image_id_to_error[image_id] = 'YOLO read failure'
491
-
492
- if options.yolo_working_folder is not None:
493
- os.chdir(current_dir)
494
-
495
-
496
- ##%% Convert results to MD format
497
-
498
- json_files = glob.glob(yolo_results_folder + '/yolo_results/*.json')
499
- assert len(json_files) == 1
500
- yolo_json_file = json_files[0]
501
-
502
- image_id_to_relative_path = {}
503
- for image_id in image_id_to_file:
504
- fn = image_id_to_file[image_id]
505
- if os.path.isdir(options.input_folder):
506
- assert options.input_folder in fn
507
- relative_path = os.path.relpath(fn,options.input_folder)
508
- else:
509
- assert os.path.isfile(options.input_folder)
510
- # We'll use the absolute path as a relative path, and pass '/'
511
- # as the base path in this case.
512
- relative_path = fn
513
- image_id_to_relative_path[image_id] = relative_path
514
-
515
- if os.path.isdir(options.input_folder):
516
- image_base = options.input_folder
517
- else:
518
- assert os.path.isfile(options.input_folder)
519
- image_base = '/'
520
-
521
- yolo_output_to_md_output.yolo_json_output_to_md_output(
522
- yolo_json_file=yolo_json_file,
523
- image_folder=image_base,
524
- output_file=options.output_file,
525
- yolo_category_id_to_name=options.yolo_category_id_to_name,
526
- detector_name=os.path.basename(model_filename),
527
- image_id_to_relative_path=image_id_to_relative_path,
528
- image_id_to_error=image_id_to_error)
529
-
530
-
531
- ##%% Clean up
532
-
533
- if options.remove_symlink_folder:
534
- shutil.rmtree(symlink_folder)
535
- elif symlink_folder_is_temp_folder:
536
- print('Warning: using temporary symlink folder {}, but not removing it'.format(
537
- symlink_folder))
538
-
539
- if options.remove_yolo_results_folder:
540
- shutil.rmtree(yolo_results_folder)
541
- elif yolo_folder_is_temp_folder:
542
- print('Warning: using temporary YOLO results folder {}, but not removing it'.format(
543
- yolo_results_folder))
544
-
545
- # ...def run_inference_with_yolo_val()
546
-
547
-
548
- #%% Command-line driver
549
-
550
- import argparse
551
- from md_utils.ct_utils import args_to_object
552
-
553
- def main():
554
-
555
- options = YoloInferenceOptions()
556
-
557
- parser = argparse.ArgumentParser()
558
- parser.add_argument(
559
- 'model_filename',type=str,
560
- help='model file name')
561
- parser.add_argument(
562
- 'input_folder',type=str,
563
- help='folder on which to recursively run the model, or a .json list of filenames')
564
- parser.add_argument(
565
- 'output_file',type=str,
566
- help='.json file where output will be written')
567
-
568
- parser.add_argument(
569
- '--yolo_working_folder',type=str,default=None,
570
- help='folder in which to execute val.py (not necessary for YOLOv8 inference)')
571
- parser.add_argument(
572
- '--image_size', default=None, type=int,
573
- help='image size for model execution (default {} when augmentation is enabled, else {})'.format(
574
- default_image_size_with_augmentation,default_image_size_with_no_augmentation))
575
- parser.add_argument(
576
- '--conf_thres', default=options.conf_thres, type=float,
577
- help='confidence threshold for including detections in the output file (default {})'.format(
578
- options.conf_thres))
579
- parser.add_argument(
580
- '--batch_size', default=options.batch_size, type=int,
581
- help='inference batch size (default {})'.format(options.batch_size))
582
- parser.add_argument(
583
- '--half_precision_enabled', default=None, type=int,
584
- help='use half-precision-inference (1 or 0) (default is the underlying model\'s default, probably full for YOLOv8 and half for YOLOv5')
585
- parser.add_argument(
586
- '--device_string', default=options.device_string, type=str,
587
- help='CUDA device specifier, typically "0" or "1" for CUDA devices, "mps" for M1/M2 devices, or "cpu" (default {})'.format(options.device_string))
588
- parser.add_argument(
589
- '--overwrite_handling', default=options.overwrite_handling, type=str,
590
- help='action to take if the output file exists (skip, error, overwrite) (default {})'.format(
591
- options.overwrite_handling))
592
- parser.add_argument(
593
- '--yolo_dataset_file', default=None, type=str,
594
- help='YOLOv5 dataset.yml file from which we should load category information ' + \
595
- '(otherwise defaults to MD categories)')
596
- parser.add_argument(
597
- '--model_type', default=options.model_type, type=str,
598
- help='Model type ("yolov5" or "ultralytics" ("yolov8" behaves the same as "ultralytics")) (default {})'.format(options.model_type))
599
-
600
- parser.add_argument(
601
- '--symlink_folder', type=str,
602
- help='temporary folder for symlinks (defaults to a folder in the system temp dir)')
603
- parser.add_argument(
604
- '--yolo_results_folder', type=str,
605
- help='temporary folder for YOLO intermediate output (defaults to a folder in the system temp dir)')
606
- parser.add_argument(
607
- '--no_use_symlinks', action='store_true',
608
- help='copy files instead of creating symlinks when preparing the yolo input folder')
609
- parser.add_argument(
610
- '--no_remove_symlink_folder', action='store_true',
611
- help='don\'t remove the temporary folder full of symlinks')
612
- parser.add_argument(
613
- '--no_remove_yolo_results_folder', action='store_true',
614
- help='don\'t remove the temporary folder full of YOLO intermediate files')
615
- parser.add_argument(
616
- '--save_yolo_debug_output', action='store_true',
617
- help='write yolo console output to a text file in the results folder, along with additional debug files')
618
-
619
- parser.add_argument(
620
- '--nonrecursive', action='store_true',
621
- help='Disable recursive folder processing')
622
-
623
- parser.add_argument(
624
- '--preview_yolo_command_only', action='store_true',
625
- help='don\'t run inference, just preview the YOLO inference command (still creates symlinks)')
626
-
627
- if options.augment:
628
- default_augment_enabled = 1
629
- else:
630
- default_augment_enabled = 0
631
-
632
- parser.add_argument(
633
- '--augment_enabled', default=default_augment_enabled, type=int,
634
- help='enable/disable augmentation (default {})'.format(default_augment_enabled))
635
-
636
- if len(sys.argv[1:]) == 0:
637
- parser.print_help()
638
- parser.exit()
639
-
640
- args = parser.parse_args()
641
-
642
- # If the caller hasn't specified an image size, choose one based on whether augmentation
643
- # is enabled.
644
- if args.image_size is None:
645
- assert args.augment_enabled in (0,1), \
646
- 'Illegal augment_enabled value {}'.format(args.augment_enabled)
647
- if args.augment_enabled == 1:
648
- args.image_size = default_image_size_with_augmentation
649
- else:
650
- args.image_size = default_image_size_with_no_augmentation
651
- augment_enabled_string = 'enabled'
652
- if not args.augment_enabled:
653
- augment_enabled_string = 'disabled'
654
- print('Augmentation is {}, using default image size {}'.format(
655
- augment_enabled_string,args.image_size))
656
-
657
- args_to_object(args, options)
658
-
659
- if args.yolo_dataset_file is not None:
660
- options.yolo_category_id_to_name = args.yolo_dataset_file
661
-
662
- options.recursive = (not options.nonrecursive)
663
- options.remove_symlink_folder = (not options.no_remove_symlink_folder)
664
- options.remove_yolo_results_folder = (not options.no_remove_yolo_results_folder)
665
- options.use_symlinks = (not options.no_use_symlinks)
666
- options.augment = (options.augment_enabled > 0)
667
-
668
- print(options.__dict__)
669
-
670
- run_inference_with_yolo_val(options)
671
-
672
- if __name__ == '__main__':
673
- main()
674
-
675
-
676
- #%% Scrap
677
-
678
- if False:
679
-
680
- #%% Test driver (folder)
681
-
682
- project_name = 'KRU-test-corrupted'
683
- input_folder = os.path.expanduser(f'~/data/{project_name}')
684
- output_folder = os.path.expanduser(f'~/tmp/{project_name}')
685
- model_filename = os.path.expanduser('~/models/camera_traps/megadetector/md_v5.0.0/md_v5a.0.0.pt')
686
- yolo_working_folder = os.path.expanduser('~/git/yolov5')
687
- model_name = os.path.splitext(os.path.basename(model_filename))[0]
688
-
689
- symlink_folder = os.path.join(output_folder,'symlinks')
690
- yolo_results_folder = os.path.join(output_folder,'yolo_results')
691
-
692
- output_file = os.path.join(output_folder,'{}_{}-md_format.json'.format(
693
- project_name,model_name))
694
-
695
- options = YoloInferenceOptions()
696
-
697
- options.yolo_working_folder = yolo_working_folder
698
-
699
- options.output_file = output_file
700
-
701
- options.augment = False
702
- options.conf_thres = '0.001'
703
- options.batch_size = 1
704
- options.device_string = '0'
705
-
706
- if options.augment:
707
- options.image_size = round(1280 * 1.3)
708
- else:
709
- options.image_size = 1280
710
-
711
- options.input_folder = input_folder
712
- options.model_filename = model_filename
713
-
714
- options.yolo_results_folder = yolo_results_folder # os.path.join(output_folder + 'yolo_results')
715
- options.symlink_folder = symlink_folder # os.path.join(output_folder,'symlinks')
716
- options.use_symlinks = False
717
-
718
- options.remove_temporary_symlink_folder = False
719
- options.remove_yolo_results_file = False
720
-
721
- cmd = f'python run_inference_with_yolov5_val.py {model_filename} {input_folder} ' + \
722
- f'{output_file} --yolo_working_folder {yolo_working_folder} ' + \
723
- f' --image_size {options.image_size} --conf_thres {options.conf_thres} ' + \
724
- f' --batch_size {options.batch_size} ' + \
725
- f' --symlink_folder {options.symlink_folder} --yolo_results_folder {options.yolo_results_folder} ' + \
726
- ' --no_remove_symlink_folder --no_remove_yolo_results_folder'
727
-
728
- if not options.use_symlinks:
729
- cmd += ' --no_use_symlinks'
730
- if not options.augment:
731
- cmd += ' --augment_enabled 0'
732
-
733
- print(cmd)
734
- execute_in_python = False
735
- if execute_in_python:
736
- run_inference_with_yolo_val(options)
737
- else:
738
- import clipboard; clipboard.copy(cmd)
739
-
740
-
741
- #%% Test driver (folder) (YOLOv8 model)
742
-
743
- project_name = 'yolov8-inference-test'
744
- input_folder = os.path.expanduser('~/data/usgs-kissel-training-resized/val')
745
- dataset_file = os.path.expanduser('~/data/usgs-kissel-training-yolo/dataset.yaml')
746
- output_folder = os.path.expanduser(f'~/tmp/{project_name}')
747
- model_filename = os.path.expanduser(
748
- '~/models/usgs-tegus/usgs-tegus-yolov8x-2023.10.25-b-1-img640-e200-best.pt')
749
- model_name = os.path.splitext(os.path.basename(model_filename))[0]
750
-
751
- assert os.path.isdir(input_folder)
752
- assert os.path.isfile(dataset_file)
753
- assert os.path.isfile(model_filename)
754
-
755
- symlink_folder = os.path.join(output_folder,'symlinks')
756
- yolo_results_folder = os.path.join(output_folder,'yolo_results')
757
-
758
- output_file = os.path.join(output_folder,'{}_{}-md_format.json'.format(
759
- project_name,model_name))
760
-
761
- options = YoloInferenceOptions()
762
-
763
- options.model_type = 'yolov8'
764
- options.yolo_category_id_to_name = dataset_file
765
- options.yolo_working_folder = None
766
- options.output_file = output_file
767
-
768
- options.augment = False
769
- options.conf_thres = '0.001'
770
- options.batch_size = 1
771
- options.device_string = '0'
772
-
773
- if options.augment:
774
- options.image_size = round(640 * 1.3)
775
- else:
776
- options.image_size = 640
777
-
778
- options.input_folder = input_folder
779
- options.model_filename = model_filename
780
-
781
- options.yolo_results_folder = yolo_results_folder
782
- options.symlink_folder = symlink_folder
783
- options.use_symlinks = False
784
-
785
- options.remove_temporary_symlink_folder = False
786
- options.remove_yolo_results_file = False
787
-
788
- cmd = f'python run_inference_with_yolov5_val.py {model_filename} ' + \
789
- f'{input_folder} {output_file}' + \
790
- f' --image_size {options.image_size} --conf_thres {options.conf_thres} ' + \
791
- f' --batch_size {options.batch_size} --symlink_folder {options.symlink_folder} ' + \
792
- f'--yolo_results_folder {options.yolo_results_folder} --model_type {options.model_type}' + \
793
- f' --yolo_dataset_file {options.yolo_category_id_to_name}' + \
794
- ' --no_remove_symlink_folder --no_remove_yolo_results_folder'
795
-
796
- if not options.use_symlinks:
797
- cmd += ' --no_use_symlinks'
798
- if not options.augment:
799
- cmd += ' --augment_enabled 0'
800
-
801
- print(cmd)
802
- execute_in_python = False
803
- if execute_in_python:
804
- run_inference_with_yolo_val(options)
805
- else:
806
- import clipboard; clipboard.copy(cmd)
807
-
808
-
809
- #%% Preview results
810
-
811
- postprocessing_output_folder = os.path.join(output_folder,'yolo-val-preview')
812
- md_json_file = options.output_file
813
-
814
- from api.batch_processing.postprocessing.postprocess_batch_results import (
815
- PostProcessingOptions, process_batch_results)
816
-
817
- with open(md_json_file,'r') as f:
818
- d = json.load(f)
819
-
820
- base_task_name = os.path.basename(md_json_file)
821
-
822
- pp_options = PostProcessingOptions()
823
- pp_options.image_base_dir = input_folder
824
- pp_options.include_almost_detections = True
825
- pp_options.num_images_to_sample = None
826
- pp_options.confidence_threshold = 0.1
827
- pp_options.almost_detection_confidence_threshold = pp_options.confidence_threshold - 0.025
828
- pp_options.ground_truth_json_file = None
829
- pp_options.separate_detections_by_category = True
830
- # pp_options.sample_seed = 0
831
-
832
- pp_options.parallelize_rendering = True
833
- pp_options.parallelize_rendering_n_cores = 16
834
- pp_options.parallelize_rendering_with_threads = False
835
-
836
- output_base = os.path.join(postprocessing_output_folder,
837
- base_task_name + '_{:.3f}'.format(pp_options.confidence_threshold))
838
-
839
- os.makedirs(output_base, exist_ok=True)
840
- print('Processing to {}'.format(output_base))
841
-
842
- pp_options.api_output_file = md_json_file
843
- pp_options.output_dir = output_base
844
- ppresults = process_batch_results(pp_options)
845
- html_output_file = ppresults.output_html_file
846
-
847
- path_utils.open_file(html_output_file)
848
-
849
- # ...for each prediction file
850
-
851
-
852
- #%% Compare results
853
-
854
- import itertools
855
-
856
- from api.batch_processing.postprocessing.compare_batch_results import (
857
- BatchComparisonOptions,PairwiseBatchComparisonOptions,compare_batch_results)
858
-
859
- options = BatchComparisonOptions()
860
-
861
- organization_name = ''
862
- project_name = ''
863
-
864
- options.job_name = f'{organization_name}-comparison'
865
- options.output_folder = os.path.join(output_folder,'model_comparison')
866
- options.image_folder = input_folder
867
-
868
- options.pairwise_options = []
869
-
870
- filenames = [
871
- f'/home/user/tmp/{project_name}/{project_name}_md_v5a.0.0-md_format.json',
872
- f'/home/user/postprocessing/{organization_name}/{organization_name}-2023-04-06-v5a.0.0/combined_api_outputs/{organization_name}-2023-04-06-v5a.0.0_detections.json',
873
- f'/home/user/postprocessing/{organization_name}/{organization_name}-2023-04-06-v5b.0.0/combined_api_outputs/{organization_name}-2023-04-06-v5b.0.0_detections.json'
874
- ]
875
-
876
- descriptions = ['YOLO w/augment','MDv5a','MDv5b']
877
-
878
- if False:
879
- results = []
880
-
881
- for fn in filenames:
882
- with open(fn,'r') as f:
883
- d = json.load(f)
884
- results.append(d)
885
-
886
- detection_thresholds = [0.1,0.1,0.1]
887
-
888
- assert len(detection_thresholds) == len(filenames)
889
-
890
- rendering_thresholds = [(x*0.6666) for x in detection_thresholds]
891
-
892
- # Choose all pairwise combinations of the files in [filenames]
893
- for i, j in itertools.combinations(list(range(0,len(filenames))),2):
894
-
895
- pairwise_options = PairwiseBatchComparisonOptions()
896
-
897
- pairwise_options.results_filename_a = filenames[i]
898
- pairwise_options.results_filename_b = filenames[j]
899
-
900
- pairwise_options.results_description_a = descriptions[i]
901
- pairwise_options.results_description_b = descriptions[j]
902
-
903
- pairwise_options.rendering_confidence_threshold_a = rendering_thresholds[i]
904
- pairwise_options.rendering_confidence_threshold_b = rendering_thresholds[j]
905
-
906
- pairwise_options.detection_thresholds_a = {'animal':detection_thresholds[i],
907
- 'person':detection_thresholds[i],
908
- 'vehicle':detection_thresholds[i]}
909
- pairwise_options.detection_thresholds_b = {'animal':detection_thresholds[j],
910
- 'person':detection_thresholds[j],
911
- 'vehicle':detection_thresholds[j]}
912
- options.pairwise_options.append(pairwise_options)
913
-
914
- results = compare_batch_results(options)
915
-
916
- from md_utils.path_utils import open_file
917
- open_file(results.html_output_file)