megadetector 5.0.9__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.9.dist-info → megadetector-5.0.11.dist-info}/LICENSE +0 -0
  2. {megadetector-5.0.9.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.9.dist-info/RECORD +0 -224
  214. megadetector-5.0.9.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.9.dist-info → megadetector-5.0.11.dist-info}/WHEEL +0 -0
@@ -1,958 +0,0 @@
1
- """
2
-
3
- compare_batch_results.py
4
-
5
- Compare sets of batch results; typically used to compare:
6
-
7
- * Results from different MegaDetector versions
8
- * Results before/after RDE
9
- * Results with/without augmentation
10
-
11
- Makes pairwise comparisons, but can take lists of results files (will perform
12
- all pairwise comparisons). Results are written to an HTML page that shows the number
13
- and nature of disagreements (in the sense of each image being a detection or non-detection),
14
- with sample images for each category.
15
-
16
- """
17
-
18
- #%% Imports
19
-
20
- import json
21
- import os
22
- import random
23
- import copy
24
- import urllib
25
- import itertools
26
-
27
- from tqdm import tqdm
28
- from functools import partial
29
-
30
- from multiprocessing.pool import ThreadPool
31
- from multiprocessing.pool import Pool
32
-
33
- from md_visualization import visualization_utils
34
- from md_utils.write_html_image_list import write_html_image_list
35
- from md_utils import path_utils
36
-
37
-
38
- #%% Constants and support classes
39
-
40
- class PairwiseBatchComparisonOptions:
41
- """
42
- Defines the options used for a single pairwise comparison; a list of these
43
- pairwise options sets is stored in the BatchComparisonsOptions class.
44
- """
45
-
46
- #: First filename to compare
47
- results_filename_a = None
48
-
49
- #: Second filename to compare
50
- results_filename_b = None
51
-
52
- #: Description to use in the output HTML for filename A
53
- results_description_a = None
54
-
55
- #: Description to use in the output HTML for filename B
56
- results_description_b = None
57
-
58
- #: Per-class detection thresholds to use for filename A (including a 'default' threshold)
59
- detection_thresholds_a = {'animal':0.15,'person':0.15,'vehicle':0.15,'default':0.15}
60
-
61
- #: Per-class detection thresholds to use for filename B (including a 'default' threshold)
62
- detection_thresholds_b = {'animal':0.15,'person':0.15,'vehicle':0.15,'default':0.15}
63
-
64
- #: Rendering threshold to use for all categories for filename A
65
- rendering_confidence_threshold_a = 0.1
66
-
67
- #: Rendering threshold to use for all categories for filename B
68
- rendering_confidence_threshold_b = 0.1
69
-
70
- # ...class PairwiseBatchComparisonOptions
71
-
72
-
73
- class BatchComparisonOptions:
74
- """
75
- Defines the options for a set of (possibly many) pairwise comparisons.
76
- """
77
-
78
- #: Folder to which we should write HTML output
79
- output_folder = None
80
-
81
- #: Base folder for images (which are specified as relative files)
82
- image_folder = None
83
-
84
- #: Job name to use in the HTML output file
85
- job_name = ''
86
-
87
- #: Maximum number of images to render for each category, where a "category" here is
88
- #: "detections_a_only", "detections_b_only", etc., or None to render all images.
89
- max_images_per_category = 1000
90
-
91
- #: Maximum number of images per HTML page (paginates if a category page goes beyond this),
92
- #: or None to disable pagination.
93
- max_images_per_page = None
94
-
95
- #: Colormap to use for detections in file A (maps detection categories to colors)
96
- colormap_a = ['Red']
97
-
98
- #: Colormap to use for detections in file B (maps detection categories to colors)
99
- colormap_b = ['RoyalBlue']
100
-
101
- #: Process-based parallelization isn't supported yet; this must be "True"
102
- parallelize_rendering_with_threads = True
103
-
104
- #: List of filenames to include in the comparison, or None to use all files
105
- filenames_to_include = None
106
-
107
- #: Compare only detections/non-detections, ignore categories (still renders categories)
108
- class_agnostic_comparison = False
109
-
110
- #: Width of images to render in the output HTML
111
- target_width = 800
112
-
113
- #: Number of workers to use for rendering, or <=1 to disable parallelization
114
- n_rendering_workers = 20
115
-
116
- #: Random seed for image sampling (not used if max_images_per_category is None)
117
- random_seed = 0
118
-
119
- #: Whether to sort results by confidence; if this is False, sorts by filename
120
- sort_by_confidence = False
121
-
122
- #: The expectation is that all results sets being compared will refer to the same images; if this
123
- #: is True (default), we'll error if that's not the case, otherwise non-matching lists will just be
124
- #: a warning.
125
- error_on_non_matching_lists = True
126
-
127
- #: List of PairwiseBatchComparisonOptions that defines the comparisons we'll render.
128
- pairwise_options = []
129
-
130
- # ...class BatchComparisonOptions
131
-
132
-
133
- class PairwiseBatchComparisonResults:
134
- """
135
- The results from a single pairwise comparison.
136
- """
137
-
138
- #: String of HTML content suitable for rendering to an HTML file
139
- html_content = None
140
-
141
- #: Possibly-modified version of the PairwiseBatchComparisonOptions supplied as input.
142
- pairwise_options = None
143
-
144
- #: A dictionary with keys including:
145
- #:
146
- #: common_detections
147
- #: common_non_detections
148
- #: detections_a_only
149
- #: detections_b_only
150
- #: class_transitions
151
- #
152
- #: Each of these maps a filename to a two-element list (the image in set A, the image in set B).
153
- categories_to_image_pairs = None
154
-
155
- # ...class PairwiseBatchComparisonResults
156
-
157
-
158
- class BatchComparisonResults:
159
- """
160
- The results from a set of pairwise comparisons
161
- """
162
-
163
- #: Filename containing HTML output
164
- html_output_file = None
165
-
166
- #: A list of PairwiseBatchComparisonResults
167
- pairwise_results = None
168
-
169
- # ...class BatchComparisonResults
170
-
171
-
172
- main_page_style_header = """<head>
173
- <style type="text/css">
174
- a { text-decoration: none; }
175
- body { font-family: segoe ui, calibri, "trebuchet ms", verdana, arial, sans-serif; }
176
- div.contentdiv { margin-left: 20px; }
177
- </style>
178
- </head>"""
179
-
180
- main_page_header = '<html>\n{}\n<body>\n'.format(main_page_style_header)
181
- main_page_footer = '<br/><br/><br/></body></html>\n'
182
-
183
-
184
- #%% Comparison functions
185
-
186
- def _render_image_pair(fn,image_pairs,category_folder,options,pairwise_options):
187
- """
188
- Render two sets of results (i.e., a comparison) for a single image.
189
-
190
- Args:
191
- fn (str): image filename
192
- image_pairs (dict): dict mapping filenames to pairs of image dicts
193
- category_folder (str): folder to which to render this image, typically
194
- "detections_a_only", "detections_b_only", etc.
195
- options (BatchComparisonOptions): job options
196
- pairwise_options (PairwiseBatchComparisonOptions): pairwise comparison options
197
-
198
- Returns:
199
- str: rendered image filename
200
- """
201
-
202
- input_image_path = os.path.join(options.image_folder,fn)
203
- assert os.path.isfile(input_image_path), 'Image {} does not exist'.format(input_image_path)
204
-
205
- im = visualization_utils.open_image(input_image_path)
206
- image_pair = image_pairs[fn]
207
- detections_a = image_pair[0]['detections']
208
- detections_b = image_pair[1]['detections']
209
-
210
- custom_strings_a = [''] * len(detections_a)
211
- custom_strings_b = [''] * len(detections_b)
212
-
213
- # This function is often used to compare results before/after various merging
214
- # steps, so we have some special-case formatting based on the "transferred_from"
215
- # field generated in merge_detections.py.
216
- for i_det,det in enumerate(detections_a):
217
- if 'transferred_from' in det:
218
- custom_strings_a[i_det] = '({})'.format(
219
- det['transferred_from'].split('.')[0])
220
-
221
- for i_det,det in enumerate(detections_b):
222
- if 'transferred_from' in det:
223
- custom_strings_b[i_det] = '({})'.format(
224
- det['transferred_from'].split('.')[0])
225
-
226
- if options.target_width is not None:
227
- im = visualization_utils.resize_image(im, options.target_width)
228
-
229
- visualization_utils.render_detection_bounding_boxes(detections_a,im,
230
- confidence_threshold=pairwise_options.rendering_confidence_threshold_a,
231
- thickness=4,expansion=0,
232
- colormap=options.colormap_a,
233
- textalign=visualization_utils.TEXTALIGN_LEFT,
234
- custom_strings=custom_strings_a)
235
- visualization_utils.render_detection_bounding_boxes(detections_b,im,
236
- confidence_threshold=pairwise_options.rendering_confidence_threshold_b,
237
- thickness=2,expansion=0,
238
- colormap=options.colormap_b,
239
- textalign=visualization_utils.TEXTALIGN_RIGHT,
240
- custom_strings=custom_strings_b)
241
-
242
- output_image_fn = path_utils.flatten_path(fn)
243
- output_image_path = os.path.join(category_folder,output_image_fn)
244
- im.save(output_image_path)
245
- return output_image_path
246
-
247
- # ...def _render_image_pair()
248
-
249
-
250
- def _pairwise_compare_batch_results(options,output_index,pairwise_options):
251
- """
252
- The main entry point for this module is compare_batch_results(), which calls
253
- this function for each pair of comparisons the caller has requested. Generates an
254
- HTML page for this comparison. Returns a BatchComparisonResults object.
255
-
256
- Args:
257
- options (BatchComparisonOptions): overall job options for this comparison group
258
- output_index (int): a numeric index used for generating HTML titles
259
- pairwise_options (PairwiseBatchComparisonOptions): job options for this comparison
260
-
261
- Returns:
262
- PairwiseBatchComparisonResults: the results of this pairwise comparison
263
- """
264
-
265
- # pairwise_options is passed as a parameter here, and should not be specified
266
- # in the options object.
267
- assert options.pairwise_options is None
268
-
269
- if options.random_seed is not None:
270
- random.seed(options.random_seed)
271
-
272
- # Warn the user if some "detections" might not get rendered
273
- max_classification_threshold_a = max(list(pairwise_options.detection_thresholds_a.values()))
274
- max_classification_threshold_b = max(list(pairwise_options.detection_thresholds_b.values()))
275
-
276
- if pairwise_options.rendering_confidence_threshold_a > max_classification_threshold_a:
277
- print('*** Warning: rendering threshold A ({}) is higher than max confidence threshold A ({}) ***'.format(
278
- pairwise_options.rendering_confidence_threshold_a,max_classification_threshold_a))
279
-
280
- if pairwise_options.rendering_confidence_threshold_b > max_classification_threshold_b:
281
- print('*** Warning: rendering threshold B ({}) is higher than max confidence threshold B ({}) ***'.format(
282
- pairwise_options.rendering_confidence_threshold_b,max_classification_threshold_b))
283
-
284
-
285
- ##%% Validate inputs
286
-
287
- assert os.path.isfile(pairwise_options.results_filename_a), \
288
- "Can't find results file {}".format(pairwise_options.results_filename_a)
289
- assert os.path.isfile(pairwise_options.results_filename_b), \
290
- "Can't find results file {}".format(pairwise_options.results_filename_b)
291
- assert os.path.isdir(options.image_folder), \
292
- "Can't find image folder {}".format(pairwise_options.image_folder)
293
- os.makedirs(options.output_folder,exist_ok=True)
294
-
295
-
296
- ##%% Load both result sets
297
-
298
- with open(pairwise_options.results_filename_a,'r') as f:
299
- results_a = json.load(f)
300
-
301
- with open(pairwise_options.results_filename_b,'r') as f:
302
- results_b = json.load(f)
303
-
304
- # Don't let path separators confuse things
305
- for im in results_a['images']:
306
- if 'file' in im:
307
- im['file'] = im['file'].replace('\\','/')
308
- for im in results_b['images']:
309
- if 'file' in im:
310
- im['file'] = im['file'].replace('\\','/')
311
-
312
- if not options.class_agnostic_comparison:
313
- assert results_a['detection_categories'] == results_b['detection_categories'], \
314
- "Cannot perform a class-sensitive comparison across results with different categories"
315
-
316
- detection_categories_a = results_a['detection_categories']
317
- detection_categories_b = results_b['detection_categories']
318
-
319
- if pairwise_options.results_description_a is None:
320
- if 'detector' not in results_a['info']:
321
- print('No model metadata supplied for results-A, assuming MDv4')
322
- pairwise_options.results_description_a = 'MDv4 (assumed)'
323
- else:
324
- pairwise_options.results_description_a = results_a['info']['detector']
325
-
326
- if pairwise_options.results_description_b is None:
327
- if 'detector' not in results_b['info']:
328
- print('No model metadata supplied for results-B, assuming MDv4')
329
- pairwise_options.results_description_b = 'MDv4 (assumed)'
330
- else:
331
- pairwise_options.results_description_b = results_b['info']['detector']
332
-
333
- images_a = results_a['images']
334
- images_b = results_b['images']
335
-
336
- filename_to_image_a = {im['file']:im for im in images_a}
337
- filename_to_image_b = {im['file']:im for im in images_b}
338
-
339
-
340
- ##%% Make sure they represent the same set of images
341
-
342
- filenames_a = [im['file'] for im in images_a]
343
- filenames_b_set = set([im['file'] for im in images_b])
344
-
345
- if len(images_a) != len(images_b):
346
- s = 'set A has {} images, set B has {}'.format(len(images_a),len(images_b))
347
- if options.error_on_non_matching_lists:
348
- raise ValueError(s)
349
- else:
350
- print('Warning: ' + s)
351
- else:
352
- if options.error_on_non_matching_lists:
353
- for fn in filenames_a:
354
- assert fn in filenames_b_set
355
-
356
- assert len(filenames_a) == len(images_a)
357
- assert len(filenames_b_set) == len(images_b)
358
-
359
- if options.filenames_to_include is None:
360
- filenames_to_compare = filenames_a
361
- else:
362
- filenames_to_compare = options.filenames_to_include
363
-
364
- ##%% Find differences
365
-
366
- # Each of these maps a filename to a two-element list (the image in set A, the image in set B)
367
- #
368
- # Right now, we only handle a very simple notion of class transition, where the detection
369
- # of maximum confidence changes class *and* both images have an above-threshold detection.
370
- common_detections = {}
371
- common_non_detections = {}
372
- detections_a_only = {}
373
- detections_b_only = {}
374
- class_transitions = {}
375
-
376
- # fn = filenames_to_compare[0]
377
- for fn in tqdm(filenames_to_compare):
378
-
379
- if fn not in filename_to_image_b:
380
-
381
- # We shouldn't have gotten this far if error_on_non_matching_lists is set
382
- assert not options.error_on_non_matching_lists
383
-
384
- print('Skipping filename {}, not in image set B'.format(fn))
385
- continue
386
-
387
- im_a = filename_to_image_a[fn]
388
- im_b = filename_to_image_b[fn]
389
-
390
- categories_above_threshold_a = set()
391
-
392
- if not 'detections' in im_a or im_a['detections'] is None:
393
- assert 'failure' in im_a and im_a['failure'] is not None
394
- continue
395
-
396
- if not 'detections' in im_b or im_b['detections'] is None:
397
- assert 'failure' in im_b and im_b['failure'] is not None
398
- continue
399
-
400
- invalid_category_error = False
401
-
402
- # det = im_a['detections'][0]
403
- for det in im_a['detections']:
404
-
405
- category_id = det['category']
406
-
407
- if category_id not in detection_categories_a:
408
- print('Warning: unexpected category {} for model A on file {}'.format(category_id,fn))
409
- invalid_category_error = True
410
- break
411
-
412
- conf = det['conf']
413
-
414
- if detection_categories_a[category_id] in pairwise_options.detection_thresholds_a:
415
- conf_thresh = pairwise_options.detection_thresholds_a[detection_categories_a[category_id]]
416
- else:
417
- conf_thresh = pairwise_options.detection_thresholds_a['default']
418
-
419
- if conf >= conf_thresh:
420
- categories_above_threshold_a.add(category_id)
421
-
422
- if invalid_category_error:
423
- continue
424
-
425
- categories_above_threshold_b = set()
426
-
427
- for det in im_b['detections']:
428
-
429
- category_id = det['category']
430
-
431
- if category_id not in detection_categories_b:
432
- print('Warning: unexpected category {} for model B on file {}'.format(category_id,fn))
433
- invalid_category_error = True
434
- break
435
-
436
- conf = det['conf']
437
-
438
- if detection_categories_b[category_id] in pairwise_options.detection_thresholds_b:
439
- conf_thresh = pairwise_options.detection_thresholds_b[detection_categories_b[category_id]]
440
- else:
441
- conf_thresh = pairwise_options.detection_thresholds_a['default']
442
-
443
- if conf >= conf_thresh:
444
- categories_above_threshold_b.add(category_id)
445
-
446
- if invalid_category_error:
447
- continue
448
-
449
- im_pair = (im_a,im_b)
450
-
451
- detection_a = (len(categories_above_threshold_a) > 0)
452
- detection_b = (len(categories_above_threshold_b) > 0)
453
-
454
- if detection_a and detection_b:
455
- if (categories_above_threshold_a == categories_above_threshold_b) or \
456
- options.class_agnostic_comparison:
457
- common_detections[fn] = im_pair
458
- else:
459
- class_transitions[fn] = im_pair
460
- elif (not detection_a) and (not detection_b):
461
- common_non_detections[fn] = im_pair
462
- elif detection_a and (not detection_b):
463
- detections_a_only[fn] = im_pair
464
- else:
465
- assert detection_b and (not detection_a)
466
- detections_b_only[fn] = im_pair
467
-
468
- # ...for each filename
469
-
470
- print('Of {} files:\n{} common detections\n{} common non-detections\n{} A only\n{} B only\n{} class transitions'.format(
471
- len(filenames_to_compare),len(common_detections),
472
- len(common_non_detections),len(detections_a_only),
473
- len(detections_b_only),len(class_transitions)))
474
-
475
-
476
- ##%% Sample and plot differences
477
-
478
- if options.n_rendering_workers > 1:
479
- worker_type = 'processes'
480
- if options.parallelize_rendering_with_threads:
481
- worker_type = 'threads'
482
- print('Rendering images with {} {}'.format(options.n_rendering_workers,worker_type))
483
- if options.parallelize_rendering_with_threads:
484
- pool = ThreadPool(options.n_rendering_workers)
485
- else:
486
- pool = Pool(options.n_rendering_workers)
487
-
488
- categories_to_image_pairs = {
489
- 'common_detections':common_detections,
490
- 'common_non_detections':common_non_detections,
491
- 'detections_a_only':detections_a_only,
492
- 'detections_b_only':detections_b_only,
493
- 'class_transitions':class_transitions
494
- }
495
-
496
- categories_to_page_titles = {
497
- 'common_detections':'Detections common to both models',
498
- 'common_non_detections':'Non-detections common to both models',
499
- 'detections_a_only':'Detections reported by model A only',
500
- 'detections_b_only':'Detections reported by model B only',
501
- 'class_transitions':'Detections reported as different classes by models A and B'
502
- }
503
-
504
- local_output_folder = os.path.join(options.output_folder,'cmp_' + \
505
- str(output_index).zfill(3))
506
-
507
- def render_detection_comparisons(category,image_pairs,image_filenames):
508
-
509
- print('Rendering detections for category {}'.format(category))
510
-
511
- category_folder = os.path.join(local_output_folder,category)
512
- os.makedirs(category_folder,exist_ok=True)
513
-
514
- # fn = image_filenames[0]
515
- if options.n_rendering_workers <= 1:
516
- output_image_paths = []
517
- for fn in tqdm(image_filenames):
518
- output_image_paths.append(_render_image_pair(fn,image_pairs,category_folder,
519
- options,pairwise_options))
520
- else:
521
- output_image_paths = list(tqdm(pool.imap(
522
- partial(_render_image_pair, image_pairs=image_pairs,
523
- category_folder=category_folder,options=options,
524
- pairwise_options=pairwise_options),
525
- image_filenames),
526
- total=len(image_filenames)))
527
-
528
- return output_image_paths
529
-
530
- # ...def render_detection_comparisons()
531
-
532
- # For each category, generate comparison images and the
533
- # comparison HTML page.
534
- #
535
- # category = 'common_detections'
536
- for category in categories_to_image_pairs.keys():
537
-
538
- # Choose detection pairs we're going to render for this category
539
- image_pairs = categories_to_image_pairs[category]
540
- image_filenames = list(image_pairs.keys())
541
-
542
- if options.max_images_per_category is not None and options.max_images_per_category > 0:
543
- if len(image_filenames) > options.max_images_per_category:
544
- print('Sampling {} of {} image pairs for category {}'.format(
545
- options.max_images_per_category,
546
- len(image_filenames),
547
- category))
548
- image_filenames = random.sample(image_filenames,
549
- options.max_images_per_category)
550
- assert len(image_filenames) <= options.max_images_per_category
551
-
552
- input_image_absolute_paths = [os.path.join(options.image_folder,fn) for fn in image_filenames]
553
-
554
- category_image_output_paths = render_detection_comparisons(category,
555
- image_pairs,image_filenames)
556
-
557
- category_html_filename = os.path.join(local_output_folder,
558
- category + '.html')
559
- category_image_output_paths_relative = [os.path.relpath(s,local_output_folder) \
560
- for s in category_image_output_paths]
561
-
562
- image_info = []
563
-
564
- assert len(category_image_output_paths_relative) == len(input_image_absolute_paths)
565
-
566
- for i_fn,fn in enumerate(category_image_output_paths_relative):
567
-
568
- input_path_relative = image_filenames[i_fn]
569
- image_pair = image_pairs[input_path_relative]
570
- assert len(image_pair) == 2; image_a = image_pair[0]; image_b = image_pair[1]
571
-
572
- def maxempty(L):
573
- if len(L) == 0:
574
- return 0
575
- else:
576
- return max(L)
577
-
578
- max_conf_a = maxempty([det['conf'] for det in image_a['detections']])
579
- max_conf_b = maxempty([det['conf'] for det in image_b['detections']])
580
-
581
- title = input_path_relative + ' (max conf {:.2f},{:.2f})'.format(max_conf_a,max_conf_b)
582
-
583
- # Only used if sort_by_confidence is True
584
- if category == 'common_detections':
585
- sort_conf = max(max_conf_a,max_conf_b)
586
- elif category == 'common_non_detections':
587
- sort_conf = max(max_conf_a,max_conf_b)
588
- elif category == 'detections_a_only':
589
- sort_conf = max_conf_a
590
- elif category == 'detections_b_only':
591
- sort_conf = max_conf_b
592
- elif category == 'class_transitions':
593
- sort_conf = max(max_conf_a,max_conf_b)
594
- else:
595
- print('Warning: unknown sort category {}'.format(category))
596
- sort_conf = max(max_conf_a,max_conf_b)
597
-
598
- info = {
599
- 'filename': fn,
600
- 'title': title,
601
- 'textStyle': 'font-family:verdana,arial,calibri;font-size:' + \
602
- '80%;text-align:left;margin-top:20;margin-bottom:5',
603
- 'linkTarget': urllib.parse.quote(input_image_absolute_paths[i_fn]),
604
- 'sort_conf':sort_conf
605
- }
606
- image_info.append(info)
607
-
608
- # ...for each image
609
-
610
- category_page_header_string = '<h1>{}</h1>'.format(categories_to_page_titles[category])
611
- category_page_header_string += '<p style="font-weight:bold;">\n'
612
- category_page_header_string += 'Model A: {}<br/>\n'.format(
613
- pairwise_options.results_description_a)
614
- category_page_header_string += 'Model B: {}'.format(pairwise_options.results_description_b)
615
- category_page_header_string += '</p>\n'
616
-
617
- category_page_header_string += '<p>\n'
618
- category_page_header_string += 'Detection thresholds for A ({}):\n{}<br/>'.format(
619
- pairwise_options.results_description_a,str(pairwise_options.detection_thresholds_a))
620
- category_page_header_string += 'Detection thresholds for B ({}):\n{}<br/>'.format(
621
- pairwise_options.results_description_b,str(pairwise_options.detection_thresholds_b))
622
- category_page_header_string += 'Rendering threshold for A ({}):\n{}<br/>'.format(
623
- pairwise_options.results_description_a,
624
- str(pairwise_options.rendering_confidence_threshold_a))
625
- category_page_header_string += 'Rendering threshold for B ({}):\n{}<br/>'.format(
626
- pairwise_options.results_description_b,
627
- str(pairwise_options.rendering_confidence_threshold_b))
628
- category_page_header_string += '</p>\n'
629
-
630
- # Default to sorting by filename
631
- if options.sort_by_confidence:
632
- image_info = sorted(image_info, key=lambda d: d['sort_conf'], reverse=True)
633
- else:
634
- image_info = sorted(image_info, key=lambda d: d['filename'])
635
-
636
- write_html_image_list(
637
- category_html_filename,
638
- images=image_info,
639
- options={
640
- 'headerHtml': category_page_header_string,
641
- 'maxFiguresPerHtmlFile': options.max_images_per_page
642
- })
643
-
644
- # ...for each category
645
-
646
-
647
- ##%% Write the top-level HTML file content
648
-
649
- html_output_string = ''
650
-
651
- html_output_string += '<p>Comparing <b>{}</b> (A, red) to <b>{}</b> (B, blue)</p>'.format(
652
- pairwise_options.results_description_a,pairwise_options.results_description_b)
653
- html_output_string += '<div class="contentdiv">\n'
654
- html_output_string += 'Detection thresholds for {}:\n{}<br/>'.format(
655
- pairwise_options.results_description_a,
656
- str(pairwise_options.detection_thresholds_a))
657
- html_output_string += 'Detection thresholds for {}:\n{}<br/>'.format(
658
- pairwise_options.results_description_b,
659
- str(pairwise_options.detection_thresholds_b))
660
- html_output_string += 'Rendering threshold for {}:\n{}<br/>'.format(
661
- pairwise_options.results_description_a,
662
- str(pairwise_options.rendering_confidence_threshold_a))
663
- html_output_string += 'Rendering threshold for {}:\n{}<br/>'.format(
664
- pairwise_options.results_description_b,
665
- str(pairwise_options.rendering_confidence_threshold_b))
666
-
667
- html_output_string += '<br/>'
668
-
669
- html_output_string += 'Rendering a maximum of {} images per category<br/>'.format(
670
- options.max_images_per_category)
671
-
672
- html_output_string += '<br/>'
673
-
674
- html_output_string += ('Of {} total files:<br/><br/><div style="margin-left:15px;">{} common detections<br/>{} common non-detections<br/>{} A only<br/>{} B only<br/>{} class transitions</div><br/>'.format(
675
- len(filenames_to_compare),len(common_detections),
676
- len(common_non_detections),len(detections_a_only),
677
- len(detections_b_only),len(class_transitions)))
678
-
679
- html_output_string += 'Comparison pages:<br/><br/>\n'
680
- html_output_string += '<div style="margin-left:15px;">\n'
681
-
682
- comparison_path_relative = os.path.relpath(local_output_folder,options.output_folder)
683
- for category in categories_to_image_pairs.keys():
684
- category_html_filename = os.path.join(comparison_path_relative,category + '.html')
685
- html_output_string += '<a href="{}">{}</a><br/>\n'.format(
686
- category_html_filename,category)
687
-
688
- html_output_string += '</div>\n'
689
- html_output_string += '</div>\n'
690
-
691
- pairwise_results = PairwiseBatchComparisonResults()
692
-
693
- pairwise_results.html_content = html_output_string
694
- pairwise_results.pairwise_options = pairwise_options
695
- pairwise_results.categories_to_image_pairs = categories_to_image_pairs
696
-
697
- return pairwise_results
698
-
699
- # ...def _pairwise_compare_batch_results()
700
-
701
-
702
- def compare_batch_results(options):
703
- """
704
- The main entry point for this module. Runs one or more batch results comparisons,
705
- writing results to an html page. Most of the work is deferred to _pairwise_compare_batch_results().
706
-
707
- Args:
708
- options (BatchComparisonOptions): job options to use for this comparison task, including the
709
- list of specific pairswise comparisons to make (in the pairwise_options field)
710
-
711
- Returns:
712
- BatchComparisonResults: the results of this comparison task
713
- """
714
-
715
- assert options.output_folder is not None
716
- assert options.image_folder is not None
717
- assert options.pairwise_options is not None
718
-
719
- options = copy.deepcopy(options)
720
-
721
- if not isinstance(options.pairwise_options,list):
722
- options.pairwise_options = [options.pairwise_options]
723
-
724
- pairwise_options_list = options.pairwise_options
725
- n_comparisons = len(pairwise_options_list)
726
-
727
- options.pairwise_options = None
728
-
729
- html_content = ''
730
- all_pairwise_results = []
731
-
732
- # i_comparison = 0; pairwise_options = pairwise_options_list[i_comparison]
733
- for i_comparison,pairwise_options in enumerate(pairwise_options_list):
734
- print('Running comparison {} of {}'.format(i_comparison,n_comparisons))
735
- pairwise_results = \
736
- _pairwise_compare_batch_results(options,i_comparison,pairwise_options)
737
- html_content += pairwise_results.html_content
738
- all_pairwise_results.append(pairwise_results)
739
-
740
- html_output_string = main_page_header
741
- job_name_string = ''
742
- if len(options.job_name) > 0:
743
- job_name_string = ' for {}'.format(options.job_name)
744
- html_output_string += '<h2>Comparison of results{}</h2>\n'.format(
745
- job_name_string)
746
- html_output_string += html_content
747
- html_output_string += main_page_footer
748
-
749
- html_output_file = os.path.join(options.output_folder,'index.html')
750
- with open(html_output_file,'w') as f:
751
- f.write(html_output_string)
752
-
753
- results = BatchComparisonResults()
754
- results.html_output_file = html_output_file
755
- results.pairwise_results = all_pairwise_results
756
- return results
757
-
758
-
759
- def n_way_comparison(filenames,options,detection_thresholds=None,rendering_thresholds=None):
760
- """
761
- Performs N pairwise comparisons for the list of results files in [filenames], by generating
762
- sets of pairwise options and calling compare_batch_results.
763
-
764
- Args:
765
- filenames (list): list of MD results filenames to compare
766
- options (BatchComparisonOptions): task options set in which pairwise_options is still
767
- empty; that will get populated from [filenames]
768
- detection_thresholds (list, optional): list of detection thresholds with the same length
769
- as [filenames], or None to use sensible defaults
770
- rendering_thresholds (list, optional): list of rendering thresholds with the same length
771
- as [filenames], or None to use sensible defaults
772
-
773
- Returns:
774
- BatchComparisonResults: the results of this comparison task
775
- """
776
-
777
- if detection_thresholds is None:
778
- detection_thresholds = [0.15] * len(filenames)
779
- assert len(detection_thresholds) == len(filenames)
780
-
781
- if rendering_thresholds is not None:
782
- assert len(rendering_thresholds) == len(detection_thresholds)
783
- else:
784
- rendering_thresholds = [(x*0.6666) for x in detection_thresholds]
785
-
786
- # Choose all pairwise combinations of the files in [filenames]
787
- for i, j in itertools.combinations(list(range(0,len(filenames))),2):
788
-
789
- pairwise_options = PairwiseBatchComparisonOptions()
790
-
791
- pairwise_options.results_filename_a = filenames[i]
792
- pairwise_options.results_filename_b = filenames[j]
793
-
794
- pairwise_options.rendering_confidence_threshold_a = rendering_thresholds[i]
795
- pairwise_options.rendering_confidence_threshold_b = rendering_thresholds[j]
796
-
797
- pairwise_options.detection_thresholds_a = {'default':detection_thresholds[i]}
798
- pairwise_options.detection_thresholds_b = {'default':detection_thresholds[j]}
799
-
800
- options.pairwise_options.append(pairwise_options)
801
-
802
- return compare_batch_results(options)
803
-
804
- # ...n_way_comparison()
805
-
806
-
807
- #%% Interactive driver
808
-
809
- if False:
810
-
811
- #%% Test two-way comparison
812
-
813
- options = BatchComparisonOptions()
814
-
815
- options.parallelize_rendering_with_threads = True
816
-
817
- options.job_name = 'BCT'
818
- options.output_folder = r'g:\temp\comparisons'
819
- options.image_folder = r'g:\camera_traps\camera_trap_images'
820
- options.max_images_per_category = 100
821
- options.sort_by_confidence = True
822
-
823
- options.pairwise_options = []
824
-
825
- results_base = os.path.expanduser('~/postprocessing/bellevue-camera-traps')
826
- filenames = [
827
- os.path.join(results_base,r'bellevue-camera-traps-2023-12-05-v5a.0.0\combined_api_outputs\bellevue-camera-traps-2023-12-05-v5a.0.0_detections.json'),
828
- os.path.join(results_base,r'bellevue-camera-traps-2023-12-05-aug-v5a.0.0\combined_api_outputs\bellevue-camera-traps-2023-12-05-aug-v5a.0.0_detections.json')
829
- ]
830
-
831
- detection_thresholds = [0.15,0.15]
832
- rendering_thresholds = None
833
-
834
- results = n_way_comparison(filenames,options,detection_thresholds,rendering_thresholds=rendering_thresholds)
835
-
836
- from md_utils.path_utils import open_file
837
- open_file(results.html_output_file)
838
-
839
-
840
- #%% Test three-way comparison
841
-
842
- options = BatchComparisonOptions()
843
-
844
- options.parallelize_rendering_with_threads = False
845
-
846
- options.job_name = 'KGA-test'
847
- options.output_folder = os.path.expanduser('~/tmp/md-comparison-test')
848
- options.image_folder = os.path.expanduser('~/data/KGA')
849
-
850
- options.pairwise_options = []
851
-
852
- filenames = [
853
- os.path.expanduser('~/data/KGA-4.json'),
854
- os.path.expanduser('~/data/KGA-5a.json'),
855
- os.path.expanduser('~/data/KGA-5b.json')
856
- ]
857
-
858
- detection_thresholds = [0.7,0.15,0.15]
859
-
860
- results = n_way_comparison(filenames,options,detection_thresholds,rendering_thresholds=None)
861
-
862
- from md_utils.path_utils import open_file
863
- open_file(results.html_output_file)
864
-
865
-
866
- #%% Command-line driver
867
-
868
- """
869
- python compare_batch_results.py ~/tmp/comparison-test ~/data/KGA ~/data/KGA-5a.json ~/data/KGA-5b.json ~/data/KGA-4.json --detection_thresholds 0.15 0.15 0.7 --rendering_thresholds 0.1 0.1 0.6 --use_processes
870
- """
871
-
872
- import sys,argparse,textwrap
873
-
874
- def main():
875
-
876
- options = BatchComparisonOptions()
877
-
878
- parser = argparse.ArgumentParser(
879
- formatter_class=argparse.RawDescriptionHelpFormatter,
880
- epilog=textwrap.dedent('''\
881
- Example:
882
-
883
- python compare_batch_results.py output_folder image_folder mdv5a.json mdv5b.json mdv4.json --detection_thresholds 0.15 0.15 0.7
884
- '''))
885
-
886
- parser.add_argument('output_folder', type=str, help='folder to which to write html results')
887
-
888
- parser.add_argument('image_folder', type=str, help='image source folder')
889
-
890
- parser.add_argument('results_files', nargs='*', type=str, help='list of .json files to be compared')
891
-
892
- parser.add_argument('--detection_thresholds', nargs='*', type=float,
893
- help='list of detection thresholds, same length as the number of .json files, ' + \
894
- 'defaults to 0.15 for all files')
895
-
896
- parser.add_argument('--rendering_thresholds', nargs='*', type=float,
897
- help='list of rendering thresholds, same length as the number of .json files, ' + \
898
- 'defaults to 0.10 for all files')
899
-
900
- parser.add_argument('--max_images_per_category', type=int, default=options.max_images_per_category,
901
- help='number of images to sample for each agreement category (common detections, etc.)')
902
-
903
- parser.add_argument('--target_width', type=int, default=options.target_width,
904
- help='output image width, defaults to {}'.format(options.target_width))
905
-
906
- parser.add_argument('--use_processes', action='store_true',
907
- help='use processes rather than threads for parallelization')
908
-
909
- parser.add_argument('--open_results', action='store_true',
910
- help='open the output html file when done')
911
-
912
- parser.add_argument('--n_rendering_workers', type=int, default=options.n_rendering_workers,
913
- help='number of workers for parallel rendering, defaults to {}'.format(
914
- options.n_rendering_workers))
915
-
916
- if len(sys.argv[1:])==0:
917
- parser.print_help()
918
- parser.exit()
919
-
920
- args = parser.parse_args()
921
-
922
- print('Output folder:')
923
- print(args.output_folder)
924
-
925
- print('\nResults files:')
926
- print(args.results_files)
927
-
928
- print('\nDetection thresholds:')
929
- print(args.detection_thresholds)
930
-
931
- print('\nRendering thresholds:')
932
- print(args.rendering_thresholds)
933
-
934
- # Convert to options objects
935
- options = BatchComparisonOptions()
936
-
937
- options.output_folder = args.output_folder
938
- options.image_folder = args.image_folder
939
- options.target_width = args.target_width
940
- options.n_rendering_workers = args.n_rendering_workers
941
- options.max_images_per_category = args.max_images_per_category
942
-
943
- if args.use_processes:
944
- options.parallelize_rendering_with_threads = False
945
-
946
- results = n_way_comparison(args.results_files,options,args.detection_thresholds,args.rendering_thresholds)
947
-
948
- if args.open_results:
949
- path_utils.open_file(results.html_output_file)
950
-
951
- print('Wrote results to {}'.format(results.html_output_file))
952
-
953
- # ...main()
954
-
955
-
956
- if __name__ == '__main__':
957
-
958
- main()