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,661 +0,0 @@
1
- """
2
-
3
- render_detection_confusion_matrix.py
4
-
5
- Given a CCT-formatted ground truth file and a MegaDetector-formatted results file,
6
- render an HTML confusion matrix. Typically used for multi-class detectors. Currently
7
- assumes a single class per image.
8
-
9
- """
10
-
11
- #%% Imports and constants
12
-
13
- import os
14
- import json
15
-
16
- import matplotlib.pyplot as plt
17
- import numpy as np
18
-
19
- from tqdm import tqdm
20
- from collections import defaultdict
21
- from functools import partial
22
-
23
- from md_utils.path_utils import find_images
24
- from md_utils.path_utils import flatten_path
25
- from md_visualization import visualization_utils as vis_utils
26
- from md_utils.write_html_image_list import write_html_image_list
27
-
28
- import md_visualization.plot_utils as plot_utils
29
-
30
- from multiprocessing.pool import ThreadPool
31
- from multiprocessing.pool import Pool
32
-
33
-
34
- #%% Support functions
35
-
36
- def _image_to_output_file(im,preview_images_folder):
37
- """
38
- Produces a clean filename from im (if [im] is a str) or im['file'] (if [im] is a dict).
39
- """
40
-
41
- if isinstance(im,str):
42
- filename_relative = im
43
- else:
44
- filename_relative = im['file']
45
-
46
- fn_clean = flatten_path(filename_relative).replace(' ','_')
47
- return os.path.join(preview_images_folder,fn_clean)
48
-
49
-
50
- def _render_image(im,render_image_constants):
51
- """
52
- Internal function for rendering a single image to the confusion matrix preview folder.
53
- """
54
-
55
- filename_to_ground_truth_im = render_image_constants['filename_to_ground_truth_im']
56
- image_folder = render_image_constants['image_folder']
57
- preview_images_folder = render_image_constants['preview_images_folder']
58
- force_render_images = render_image_constants['force_render_images']
59
- results_category_id_to_name = render_image_constants['results_category_id_to_name']
60
- rendering_confidence_thresholds = render_image_constants['rendering_confidence_thresholds']
61
- target_image_size = render_image_constants['target_image_size']
62
-
63
- assert im['file'] in filename_to_ground_truth_im
64
-
65
- output_file = _image_to_output_file(im,preview_images_folder)
66
- if os.path.isfile(output_file) and not force_render_images:
67
- return output_file
68
-
69
- input_file = os.path.join(image_folder,im['file'])
70
- assert os.path.isfile(input_file)
71
-
72
- detections_to_render = []
73
-
74
- for det in im['detections']:
75
- category_name = results_category_id_to_name[det['category']]
76
- detection_threshold = rendering_confidence_thresholds['default']
77
- if category_name in rendering_confidence_thresholds:
78
- detection_threshold = rendering_confidence_thresholds[category_name]
79
- if det['conf'] > detection_threshold:
80
- detections_to_render.append(det)
81
-
82
- vis_utils.draw_bounding_boxes_on_file(input_file, output_file, detections_to_render,
83
- detector_label_map=results_category_id_to_name,
84
- label_font_size=20,target_size=target_image_size)
85
-
86
- return output_file
87
-
88
-
89
- #%% Main function
90
-
91
- def render_detection_confusion_matrix(ground_truth_file,
92
- results_file,
93
- image_folder,
94
- preview_folder,
95
- force_render_images=False,
96
- confidence_thresholds=None,
97
- rendering_confidence_thresholds=None,
98
- target_image_size=(1280,-1),
99
- parallelize_rendering=True,
100
- parallelize_rendering_n_cores=None,
101
- parallelize_rendering_with_threads=False,
102
- job_name='unknown',
103
- model_file=None,
104
- empty_category_name='empty',
105
- html_image_list_options=None):
106
- """
107
- Given a CCT-formatted ground truth file and a MegaDetector-formatted results file,
108
- render an HTML confusion matrix in [preview_folder. Typically used for multi-class detectors.
109
- Currently assumes a single class per image.
110
-
111
- confidence_thresholds and rendering_confidence_thresholds are dictionaries mapping
112
- class names to thresholds. "default" is a special token that will be used for all
113
- classes not otherwise assigned thresholds.
114
-
115
- Args:
116
- ground_truth_file (str): the CCT-formatted .json file with ground truth information
117
- results_file (str): the MegaDetector results .json file
118
- image_folder (str): the folder where images live; filenames in [ground_truth_file] and
119
- [results_file] should be relative to this folder.
120
- preview_folder (str): the output folder, i.e. the folder in which we'll create our nifty
121
- HTML stuff.
122
- force_rendering_images (bool, optional): if False, skips images that already exist
123
- confidence_thresholds (dict, optional): a dictionary mapping class names to thresholds;
124
- all classes not explicitly named here will use the threshold for the "default" category.
125
- rendering_thresholds (dict, optional): a dictionary mapping class names to thresholds;
126
- all classes not explicitly named here will use the threshold for the "default" category.
127
- target_image_size (tuple, optional): output image size, as a pair of ints (width,height). If one
128
- value is -1 and the other is not, aspect ratio is preserved. If both are -1, the original image
129
- sizes are preserved.
130
- parallelize_rendering (bool, optional): enable (default) or disable parallelization when rendering
131
- parallelize_rendering_n_core (int, optional): number of threads or processes to use for rendering, only
132
- used if parallelize_rendering is True
133
- parallelize_rendering_with_threads: whether to use threads (True) or processes (False) when rendering,
134
- only used if parallelize_rendering is True
135
- job_name (str, optional): job name to include in big letters in the output file
136
- model_file (str, optional) model filename to include in HTML output
137
- empty_category_name (str, optional): special category name that we should treat as empty, typically
138
- "empty"
139
- html_image_list_options (dict, optional): options listed passed along to write_html_image_list;
140
- see write_html_image_list for documentation.
141
- """
142
-
143
- ##%% Argument and path handling
144
-
145
- preview_images_folder = os.path.join(preview_folder,'images')
146
- os.makedirs(preview_images_folder,exist_ok=True)
147
-
148
- if confidence_thresholds is None:
149
- confidence_thresholds = {'default':0.5}
150
- if rendering_confidence_thresholds is None:
151
- rendering_confidence_thresholds = {'default':0.4}
152
-
153
-
154
- ##%% Load ground truth
155
-
156
- with open(ground_truth_file,'r') as f:
157
- ground_truth_data_cct = json.load(f)
158
-
159
- filename_to_ground_truth_im = {}
160
- for im in ground_truth_data_cct['images']:
161
- assert im['file_name'] not in filename_to_ground_truth_im
162
- filename_to_ground_truth_im[im['file_name']] = im
163
-
164
-
165
- ##%% Confirm that the ground truth images are present in the image folder
166
-
167
- ground_truth_images = find_images(image_folder,return_relative_paths=True,recursive=True)
168
- assert len(ground_truth_images) == len(ground_truth_data_cct['images'])
169
- del ground_truth_images
170
-
171
-
172
- ##%% Map images to categories
173
-
174
- # gt_image_id_to_image = {im['id']:im for im in ground_truth_data_cct['images']}
175
- gt_image_id_to_annotations = defaultdict(list)
176
-
177
- ground_truth_category_id_to_name = {}
178
- for c in ground_truth_data_cct['categories']:
179
- ground_truth_category_id_to_name[c['id']] = c['name']
180
-
181
- # Add the empty category if necessary
182
- if empty_category_name not in ground_truth_category_id_to_name.values():
183
- empty_category_id = max(ground_truth_category_id_to_name.keys())+1
184
- ground_truth_category_id_to_name[empty_category_id] = empty_category_name
185
-
186
- ground_truth_category_names = sorted(list(ground_truth_category_id_to_name.values()))
187
-
188
- for ann in ground_truth_data_cct['annotations']:
189
- gt_image_id_to_annotations[ann['image_id']].append(ann)
190
-
191
- gt_filename_to_category_names = defaultdict(set)
192
-
193
- for im in ground_truth_data_cct['images']:
194
- annotations_this_image = gt_image_id_to_annotations[im['id']]
195
- for ann in annotations_this_image:
196
- category_name = ground_truth_category_id_to_name[ann['category_id']]
197
- gt_filename_to_category_names[im['file_name']].add(category_name)
198
-
199
- for filename in gt_filename_to_category_names:
200
-
201
- category_names_this_file = gt_filename_to_category_names[filename]
202
-
203
- # The empty category should be exclusive
204
- if empty_category_name in category_names_this_file:
205
- assert len(category_names_this_file) == 1, \
206
- 'Empty category assigned along with another category for {}'.format(filename)
207
- assert len(category_names_this_file) > 0, \
208
- 'No ground truth category assigned to {}'.format(filename)
209
-
210
-
211
- ##%% Load results
212
-
213
- with open(results_file,'r') as f:
214
- md_formatted_results = json.load(f)
215
-
216
- results_category_id_to_name = md_formatted_results['detection_categories']
217
-
218
-
219
- ##%% Render images with detections
220
-
221
- render_image_constants = {}
222
- render_image_constants['filename_to_ground_truth_im'] = filename_to_ground_truth_im
223
- render_image_constants['image_folder'] = image_folder
224
- render_image_constants['preview_images_folder'] = preview_images_folder
225
- render_image_constants['force_render_images'] = force_render_images
226
- render_image_constants['results_category_id_to_name'] = results_category_id_to_name
227
- render_image_constants['rendering_confidence_thresholds'] = rendering_confidence_thresholds
228
- render_image_constants['target_image_size'] = target_image_size
229
-
230
- if parallelize_rendering:
231
-
232
- if parallelize_rendering_n_cores is None:
233
- if parallelize_rendering_with_threads:
234
- pool = ThreadPool()
235
- else:
236
- pool = Pool()
237
- else:
238
- if parallelize_rendering_with_threads:
239
- pool = ThreadPool(parallelize_rendering_n_cores)
240
- worker_string = 'threads'
241
- else:
242
- pool = Pool(parallelize_rendering_n_cores)
243
- worker_string = 'processes'
244
- print('Rendering images with {} {}'.format(parallelize_rendering_n_cores,
245
- worker_string))
246
-
247
- _ = list(tqdm(pool.imap(partial(_render_image,render_image_constants=render_image_constants),
248
- md_formatted_results['images']),
249
- total=len(md_formatted_results['images'])))
250
-
251
- else:
252
-
253
- # im = md_formatted_results['images'][0]
254
- for im in tqdm(md_formatted_results['images']):
255
- _render_image(im,render_image_constants)
256
-
257
-
258
- ##%% Map images to predicted categories, and vice-versa
259
-
260
- filename_to_predicted_categories = defaultdict(set)
261
- predicted_category_name_to_filenames = defaultdict(set)
262
-
263
- # im = md_formatted_results['images'][0]
264
- for im in tqdm(md_formatted_results['images']):
265
-
266
- assert im['file'] in filename_to_ground_truth_im
267
-
268
- # det = im['detections'][0]
269
- for det in im['detections']:
270
- category_name = results_category_id_to_name[det['category']]
271
- detection_threshold = confidence_thresholds['default']
272
- if category_name in confidence_thresholds:
273
- detection_threshold = confidence_thresholds[category_name]
274
- if det['conf'] > detection_threshold:
275
- filename_to_predicted_categories[im['file']].add(category_name)
276
- predicted_category_name_to_filenames[category_name].add(im['file'])
277
-
278
- # ...for each detection
279
-
280
- # ...for each image
281
-
282
-
283
- ##%% Create TP/TN/FP/FN lists
284
-
285
- category_name_to_image_lists = {}
286
-
287
- sub_page_tokens = ['fn','tn','fp','tp']
288
-
289
- for category_name in ground_truth_category_names:
290
-
291
- category_name_to_image_lists[category_name] = {}
292
- for sub_page_token in sub_page_tokens:
293
- category_name_to_image_lists[category_name][sub_page_token] = []
294
-
295
- # filename = next(iter(gt_filename_to_category_names))
296
- for filename in gt_filename_to_category_names.keys():
297
-
298
- ground_truth_categories_this_image = gt_filename_to_category_names[filename]
299
- predicted_categories_this_image = filename_to_predicted_categories[filename]
300
-
301
- for category_name in ground_truth_category_names:
302
-
303
- assignment = None
304
-
305
- if category_name == empty_category_name:
306
- # If this is an empty image
307
- if category_name in ground_truth_categories_this_image:
308
- assert len(ground_truth_categories_this_image) == 1
309
- if len(predicted_categories_this_image) == 0:
310
- assignment = 'tp'
311
- else:
312
- assignment = 'fn'
313
- # If this not an empty image
314
- else:
315
- if len(predicted_categories_this_image) == 0:
316
- assignment = 'fp'
317
- else:
318
- assignment = 'tn'
319
-
320
- else:
321
- if category_name in ground_truth_categories_this_image:
322
- if category_name in predicted_categories_this_image:
323
- assignment = 'tp'
324
- else:
325
- assignment = 'fn'
326
- else:
327
- if category_name in predicted_categories_this_image:
328
- assignment = 'fp'
329
- else:
330
- assignment = 'tn'
331
-
332
- category_name_to_image_lists[category_name][assignment].append(filename)
333
-
334
- # ...for each filename
335
-
336
-
337
- ##%% Create confusion matrix
338
-
339
- gt_category_name_to_category_index = {}
340
-
341
- for i_category,category_name in enumerate(ground_truth_category_names):
342
- gt_category_name_to_category_index[category_name] = i_category
343
-
344
- n_categories = len(gt_category_name_to_category_index)
345
-
346
- # indexed as [true,predicted]
347
- confusion_matrix = np.zeros(shape=(n_categories,n_categories),dtype=int)
348
-
349
- filename_to_results_im = {im['file']:im for im in md_formatted_results['images']}
350
-
351
- true_predicted_to_file_list = defaultdict(list)
352
-
353
- # filename = next(iter(gt_filename_to_category_names.keys()))
354
- for filename in gt_filename_to_category_names.keys():
355
-
356
- ground_truth_categories_this_image = gt_filename_to_category_names[filename]
357
- assert len(ground_truth_categories_this_image) == 1
358
- ground_truth_category_name = next(iter(ground_truth_categories_this_image))
359
-
360
- results_im = filename_to_results_im[filename]
361
-
362
- # If there were no detections at all, call this image empty
363
- if len(results_im['detections']) == 0:
364
- predicted_category_name = empty_category_name
365
- # Otherwise look for above-threshold detections
366
- else:
367
- results_category_name_to_confidence = defaultdict(int)
368
- for det in results_im['detections']:
369
- category_name = results_category_id_to_name[det['category']]
370
- detection_threshold = confidence_thresholds['default']
371
- if category_name in confidence_thresholds:
372
- detection_threshold = confidence_thresholds[category_name]
373
- if det['conf'] > detection_threshold:
374
- results_category_name_to_confidence[category_name] = max(
375
- results_category_name_to_confidence[category_name],det['conf'])
376
- # If there were no detections above threshold
377
- if len(results_category_name_to_confidence) == 0:
378
- predicted_category_name = empty_category_name
379
- else:
380
- predicted_category_name = max(results_category_name_to_confidence,
381
- key=results_category_name_to_confidence.get)
382
-
383
- ground_truth_category_index = gt_category_name_to_category_index[ground_truth_category_name]
384
- predicted_category_index = gt_category_name_to_category_index[predicted_category_name]
385
-
386
- true_predicted_token = ground_truth_category_name + '_' + predicted_category_name
387
- true_predicted_to_file_list[true_predicted_token].append(filename)
388
-
389
- confusion_matrix[ground_truth_category_index,predicted_category_index] += 1
390
-
391
- # ...for each file
392
-
393
- plt.ioff()
394
-
395
- fig_h = 3 + 0.3 * n_categories
396
- fig_w = fig_h
397
- fig = plt.figure(figsize=(fig_w, fig_h),tight_layout=True)
398
-
399
- plot_utils.plot_confusion_matrix(
400
- matrix=confusion_matrix,
401
- classes=ground_truth_category_names,
402
- normalize=False,
403
- title='Confusion matrix',
404
- cmap=plt.cm.Blues,
405
- vmax=1.0,
406
- use_colorbar=False,
407
- y_label=True,
408
- fig=fig)
409
-
410
- cm_figure_fn_relative = 'confusion_matrix.png'
411
- cm_figure_fn_abs = os.path.join(preview_folder, cm_figure_fn_relative)
412
- # fig.show()
413
- fig.savefig(cm_figure_fn_abs,dpi=100)
414
- plt.close(fig)
415
-
416
- # open_file(cm_figure_fn_abs)
417
-
418
-
419
- ##%% Create HTML confusion matrix
420
-
421
- html_confusion_matrix = '<table class="result-table">\n'
422
- html_confusion_matrix += '<tr>\n'
423
- html_confusion_matrix += '<td>{}</td>\n'.format('True category')
424
- for category_name in ground_truth_category_names:
425
- html_confusion_matrix += '<td>{}</td>\n'.format('&nbsp;')
426
- html_confusion_matrix += '</tr>\n'
427
-
428
- for true_category in ground_truth_category_names:
429
-
430
- html_confusion_matrix += '<tr>\n'
431
- html_confusion_matrix += '<td>{}</td>\n'.format(true_category)
432
-
433
- for predicted_category in ground_truth_category_names:
434
-
435
- true_predicted_token = true_category + '_' + predicted_category
436
- image_list = true_predicted_to_file_list[true_predicted_token]
437
- if len(image_list) == 0:
438
- td_content = '0'
439
- else:
440
- if html_image_list_options is None:
441
- html_image_list_options = {}
442
- title_string = 'true: {}, predicted {}'.format(
443
- true_category,predicted_category)
444
- html_image_list_options['headerHtml'] = '<h1>{}</h1>'.format(title_string)
445
-
446
- html_image_info_list = []
447
-
448
- for image_filename_relative in image_list:
449
- html_image_info = {}
450
- detections = filename_to_results_im[image_filename_relative]['detections']
451
- if len(detections) == 0:
452
- max_conf = 0
453
- else:
454
- max_conf = max([d['conf'] for d in detections])
455
-
456
- title = '<b>Image</b>: {}, <b>Max conf</b>: {:0.3f}'.format(
457
- image_filename_relative, max_conf)
458
- image_link = 'images/' + os.path.basename(
459
- _image_to_output_file(image_filename_relative,preview_images_folder))
460
- html_image_info = {
461
- 'filename': image_link,
462
- 'title': title,
463
- 'textStyle':\
464
- 'font-family:verdana,arial,calibri;font-size:80%;' + \
465
- 'text-align:left;margin-top:20;margin-bottom:5'
466
- }
467
-
468
- html_image_info_list.append(html_image_info)
469
-
470
- target_html_file_relative = true_predicted_token + '.html'
471
- target_html_file_abs = os.path.join(preview_folder,target_html_file_relative)
472
- write_html_image_list(
473
- filename=target_html_file_abs,
474
- images=html_image_info_list,
475
- options=html_image_list_options)
476
-
477
- td_content = '<a href="{}">{}</a>'.format(target_html_file_relative,
478
- len(image_list))
479
-
480
- html_confusion_matrix += '<td>{}</td>\n'.format(td_content)
481
-
482
- # ...for each predicted category
483
-
484
- html_confusion_matrix += '</tr>\n'
485
-
486
- # ...for each true category
487
-
488
- html_confusion_matrix += '<tr>\n'
489
- html_confusion_matrix += '<td>&nbsp;</td>\n'
490
-
491
- for category_name in ground_truth_category_names:
492
- html_confusion_matrix += '<td class="rotate"><p style="margin-left:20px;">{}</p></td>\n'.format(
493
- category_name)
494
- html_confusion_matrix += '</tr>\n'
495
-
496
- html_confusion_matrix += '</table>'
497
-
498
-
499
- ##%% Create HTML sub-pages and HTML table
500
-
501
- html_table = '<table class="result-table">\n'
502
-
503
- html_table += '<tr>\n'
504
- html_table += '<td>{}</td>\n'.format('True category')
505
- for sub_page_token in sub_page_tokens:
506
- html_table += '<td>{}</td>'.format(sub_page_token)
507
- html_table += '</tr>\n'
508
-
509
- filename_to_results_im = {im['file']:im for im in md_formatted_results['images']}
510
-
511
- sub_page_token_to_page_name = {
512
- 'fp':'false positives',
513
- 'tp':'true positives',
514
- 'fn':'false negatives',
515
- 'tn':'true negatives'
516
- }
517
-
518
- # category_name = ground_truth_category_names[0]
519
- for category_name in ground_truth_category_names:
520
-
521
- html_table += '<tr>\n'
522
-
523
- html_table += '<td>{}</td>\n'.format(category_name)
524
-
525
- # sub_page_token = sub_page_tokens[0]
526
- for sub_page_token in sub_page_tokens:
527
-
528
- html_table += '<td>\n'
529
-
530
- image_list = category_name_to_image_lists[category_name][sub_page_token]
531
-
532
- if len(image_list) == 0:
533
-
534
- html_table += '0\n'
535
-
536
- else:
537
-
538
- html_image_list_options = {}
539
- title_string = '{}: {}'.format(category_name,sub_page_token_to_page_name[sub_page_token])
540
- html_image_list_options['headerHtml'] = '<h1>{}</h1>'.format(title_string)
541
-
542
- target_html_file_relative = '{}_{}.html'.format(category_name,sub_page_token)
543
- target_html_file_abs = os.path.join(preview_folder,target_html_file_relative)
544
-
545
- html_image_info_list = []
546
-
547
- # image_filename_relative = image_list[0]
548
- for image_filename_relative in image_list:
549
-
550
- source_file = os.path.join(image_folder,image_filename_relative)
551
- assert os.path.isfile(source_file)
552
-
553
- html_image_info = {}
554
- detections = filename_to_results_im[image_filename_relative]['detections']
555
- if len(detections) == 0:
556
- max_conf = 0
557
- else:
558
- max_conf = max([d['conf'] for d in detections])
559
-
560
- title = '<b>Image</b>: {}, <b>Max conf</b>: {:0.3f}'.format(
561
- image_filename_relative, max_conf)
562
- image_link = 'images/' + os.path.basename(
563
- _image_to_output_file(image_filename_relative,preview_images_folder))
564
- html_image_info = {
565
- 'filename': image_link,
566
- 'title': title,
567
- 'linkTarget': source_file,
568
- 'textStyle':\
569
- 'font-family:verdana,arial,calibri;font-size:80%;' + \
570
- 'text-align:left;margin-top:20;margin-bottom:5'
571
- }
572
-
573
- html_image_info_list.append(html_image_info)
574
-
575
- # ...for each image
576
-
577
- write_html_image_list(
578
- filename=target_html_file_abs,
579
- images=html_image_info_list,
580
- options=html_image_list_options)
581
-
582
- html_table += '<a href="{}">{}</a>\n'.format(target_html_file_relative,len(image_list))
583
-
584
- html_table += '</td>\n'
585
-
586
- # ...for each sub-page
587
-
588
- html_table += '</tr>\n'
589
-
590
- # ...for each category
591
-
592
- html_table += '</table>'
593
-
594
- html = '<html>\n'
595
-
596
- style_header = """<head>
597
- <style type="text/css">
598
- a { text-decoration: none; }
599
- body { font-family: segoe ui, calibri, "trebuchet ms", verdana, arial, sans-serif; }
600
- div.contentdiv { margin-left: 20px; }
601
- table.result-table { border:1px solid black; border-collapse: collapse; margin-left:50px;}
602
- td,th { padding:10px; }
603
- .rotate {
604
- padding:0px;
605
- writing-mode:vertical-lr;
606
- -webkit-transform: rotate(-180deg);
607
- -moz-transform: rotate(-180deg);
608
- -ms-transform: rotate(-180deg);
609
- -o-transform: rotate(-180deg);
610
- transform: rotate(-180deg);
611
- }
612
- </style>
613
- </head>"""
614
-
615
- html += style_header + '\n'
616
-
617
- html += '<body>\n'
618
-
619
- html += '<h1>Results summary for {}</h1>\n'.format(job_name)
620
-
621
- if model_file is not None and len(model_file) > 0:
622
- html += '<p><b>Model file</b>: {}</p>'.format(os.path.basename(model_file))
623
-
624
- html += '<p><b>Confidence thresholds</b></p>'
625
-
626
- for c in confidence_thresholds.keys():
627
- html += '<p style="margin-left:15px;">{}: {}</p>'.format(c,confidence_thresholds[c])
628
-
629
- html += '<h2>Confusion matrix</h2>\n'
630
-
631
- html += '<p>...assuming a single category per image.</p>\n'
632
-
633
- html += '<img src="{}"/>\n'.format(cm_figure_fn_relative)
634
-
635
- html += '<h2>Confusion matrix (with links)</h2>\n'
636
-
637
- html += '<p>...assuming a single category per image.</p>\n'
638
-
639
- html += html_confusion_matrix
640
-
641
- html += '<h2>Per-class statistics</h2>\n'
642
-
643
- html += html_table
644
-
645
- html += '</body>\n'
646
- html += '<html>\n'
647
-
648
- target_html_file = os.path.join(preview_folder,'index.html')
649
-
650
- with open(target_html_file,'w') as f:
651
- f.write(html)
652
-
653
-
654
- ##%% Prepare return data
655
-
656
- confusion_matrix_info = {}
657
- confusion_matrix_info['html_file'] = target_html_file
658
-
659
- return confusion_matrix_info
660
-
661
- # ...render_detection_confusion_matrix(...)