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,551 +0,0 @@
1
- """
2
-
3
- visualize_db.py
4
-
5
- Outputs an HTML page visualizing annotations (class labels and/or bounding boxes)
6
- on a sample of images in a database in the COCO Camera Traps format.
7
-
8
- """
9
-
10
- #%% Imports
11
-
12
- import argparse
13
- import inspect
14
- import random
15
- import json
16
- import math
17
- import os
18
- import sys
19
- import time
20
-
21
- import pandas as pd
22
- import numpy as np
23
-
24
- import humanfriendly
25
-
26
- from itertools import compress
27
- from multiprocessing.pool import ThreadPool
28
- from multiprocessing.pool import Pool
29
- from tqdm import tqdm
30
-
31
- from md_utils.write_html_image_list import write_html_image_list
32
- from data_management.cct_json_utils import IndexedJsonDb
33
-
34
- import md_visualization.visualization_utils as vis_utils
35
-
36
-
37
- #%% Settings
38
-
39
- class DbVizOptions:
40
- """
41
- Parameters controlling the behavior of visualize_db().
42
- """
43
-
44
- #: Number of images to sample from the database, or None to visualize all images
45
- num_to_visualize = None
46
-
47
- #: Target size for rendering; set either dimension to -1 to preserve aspect ratio.
48
- #:
49
- #: If viz_size is None or (-1,-1), the original image size is used.
50
- viz_size = (800, -1)
51
-
52
- #: HTML rendering options; see write_html_image_list for details
53
- #:
54
- #:The most relevant option one might want to set here is:
55
- #:
56
- #: htmlOptions['maxFiguresPerHtmlFile']
57
- #:
58
- #: ...which can be used to paginate previews to a number of images that will load well
59
- #: in a browser (5000 is a reasonable limit).
60
- htmlOptions = write_html_image_list()
61
-
62
- #: Whether to sort images by filename (True) or randomly (False)
63
- sort_by_filename = True
64
-
65
- #: Only show images that contain bounding boxes
66
- trim_to_images_with_bboxes = False
67
-
68
- #: Random seed to use for sampling images
69
- random_seed = 0
70
-
71
- #: Should we include Web search links for each category name?
72
- add_search_links = False
73
-
74
- #: Should each thumbnail image link back to the original image?
75
- include_image_links = False
76
-
77
- #: Should there be a text link back to each original image?
78
- include_filename_links = False
79
-
80
- #: Line width in pixels
81
- box_thickness = 4
82
-
83
- #: Number of pixels to expand each bounding box
84
- box_expansion = 0
85
-
86
- #: Only include images that contain annotations with these class names (not IDs)
87
- #:
88
- #: Mutually exclusive with classes_to_exclude
89
- classes_to_include = None
90
-
91
- #: Exclude images that contain annotations with these class names (not IDs)
92
- #:
93
- #: Mutually exclusive with classes_to_include
94
- classes_to_exclude = None
95
-
96
- #: Special tag used to say "show me all images with multiple categories"
97
- #:
98
- #: :meta private:
99
- multiple_categories_tag = '*multiple*'
100
-
101
- #: We sometimes flatten image directories by replacing a path separator with
102
- #: another character. Leave blank for the typical case where this isn't necessary.
103
- pathsep_replacement = '' # '~'
104
-
105
- #: Parallelize rendering across multiple workers
106
- parallelize_rendering = False
107
-
108
- #: In theory, whether to parallelize with threads (True) or processes (False), but
109
- #: process-based parallelization in this function is currently unsupported
110
- parallelize_rendering_with_threads = True
111
-
112
- #: Number of workers to use for parallelization; ignored if parallelize_rendering
113
- #: is False
114
- parallelize_rendering_n_cores = 25
115
-
116
- #: Should we show absolute (True) or relative (False) paths for each image?
117
- show_full_paths = False
118
-
119
- #: Set to False to skip existing images
120
- force_rendering = True
121
-
122
- #: Enable additionald debug console output
123
- verbose = False
124
-
125
-
126
- #%% Helper functions
127
-
128
- def _image_filename_to_path(image_file_name, image_base_dir, pathsep_replacement=''):
129
- """
130
- Translates the file name in an image entry in the json database to a path, possibly doing
131
- some manipulation of path separators.
132
- """
133
-
134
- if len(pathsep_replacement) > 0:
135
- image_file_name = os.path.normpath(image_file_name).replace(os.pathsep,pathsep_replacement)
136
- return os.path.join(image_base_dir, image_file_name)
137
-
138
-
139
- #%% Core functions
140
-
141
- def visualize_db(db_path, output_dir, image_base_dir, options=None):
142
- """
143
- Writes images and html to output_dir to visualize the annotations in a .json file.
144
-
145
- Args:
146
- db_path (str or dict): the .json filename to load, or a previously-loaded database
147
- image_base_dir (str): the folder where the images live; filenames in [db_path] should
148
- be relative to this folder.
149
- options (DbVizOptions, optional): See DbVizOptions for details
150
-
151
- Returns:
152
- tuple: A length-two tuple containing (the html filename) and (the loaded database).
153
- """
154
-
155
- if options is None:
156
- options = DbVizOptions()
157
-
158
- if not options.parallelize_rendering_with_threads:
159
- print('Warning: process-based parallelization is not yet supported by visualize_db')
160
- options.parallelize_rendering_with_threads = True
161
-
162
- if image_base_dir.startswith('http'):
163
- if not image_base_dir.endswith('/'):
164
- image_base_dir += '/'
165
- else:
166
- assert(os.path.isdir(image_base_dir))
167
-
168
- os.makedirs(os.path.join(output_dir, 'rendered_images'), exist_ok=True)
169
-
170
- if isinstance(db_path,str):
171
- assert(os.path.isfile(db_path))
172
- print('Loading database from {}...'.format(db_path))
173
- image_db = json.load(open(db_path))
174
- print('...done')
175
- elif isinstance(db_path,dict):
176
- print('Using previously-loaded DB')
177
- image_db = db_path
178
- else:
179
- raise ValueError('Illegal dictionary or filename')
180
-
181
- annotations = image_db['annotations']
182
- images = image_db['images']
183
- categories = image_db['categories']
184
-
185
- # Optionally remove all images without bounding boxes, *before* sampling
186
- if options.trim_to_images_with_bboxes:
187
-
188
- bHasBbox = [False] * len(annotations)
189
- for iAnn,ann in enumerate(annotations):
190
- if 'bbox' in ann:
191
- assert isinstance(ann['bbox'],list)
192
- bHasBbox[iAnn] = True
193
- annotationsWithBboxes = list(compress(annotations, bHasBbox))
194
-
195
- imageIDsWithBboxes = [x['image_id'] for x in annotationsWithBboxes]
196
- imageIDsWithBboxes = set(imageIDsWithBboxes)
197
-
198
- bImageHasBbox = [False] * len(images)
199
- for iImage,image in enumerate(images):
200
- imageID = image['id']
201
- if imageID in imageIDsWithBboxes:
202
- bImageHasBbox[iImage] = True
203
- imagesWithBboxes = list(compress(images, bImageHasBbox))
204
- images = imagesWithBboxes
205
-
206
- # Optionally include/remove images with specific labels, *before* sampling
207
-
208
- assert (not ((options.classes_to_exclude is not None) and \
209
- (options.classes_to_include is not None))), \
210
- 'Cannot specify an inclusion and exclusion list'
211
-
212
- if (options.classes_to_exclude is not None) or (options.classes_to_include is not None):
213
-
214
- print('Indexing database')
215
- indexed_db = IndexedJsonDb(image_db)
216
- bValidClass = [True] * len(images)
217
- for iImage,image in enumerate(images):
218
- classes = indexed_db.get_classes_for_image(image)
219
- if options.classes_to_exclude is not None:
220
- for excludedClass in options.classes_to_exclude:
221
- if excludedClass in classes:
222
- bValidClass[iImage] = False
223
- break
224
- elif options.classes_to_include is not None:
225
- bValidClass[iImage] = False
226
- if options.multiple_categories_tag in options.classes_to_include:
227
- if len(classes) > 1:
228
- bValidClass[iImage] = True
229
- if not bValidClass[iImage]:
230
- for c in classes:
231
- if c in options.classes_to_include:
232
- bValidClass[iImage] = True
233
- break
234
- else:
235
- raise ValueError('Illegal include/exclude combination')
236
-
237
- imagesWithValidClasses = list(compress(images, bValidClass))
238
- images = imagesWithValidClasses
239
-
240
- # ...if we need to include/exclude categories
241
-
242
- # Put the annotations in a dataframe so we can select all annotations for a given image
243
- print('Creating data frames')
244
- df_anno = pd.DataFrame(annotations)
245
- df_img = pd.DataFrame(images)
246
-
247
- # Construct label map
248
- label_map = {}
249
- for cat in categories:
250
- label_map[int(cat['id'])] = cat['name']
251
-
252
- # Take a sample of images
253
- if options.num_to_visualize is not None:
254
- if options.num_to_visualize > len(df_img):
255
- print('Warning: asked to visualize {} images, but only {} are available, keeping them all'.\
256
- format(options.num_to_visualize,len(df_img)))
257
- else:
258
- df_img = df_img.sample(n=options.num_to_visualize,random_state=options.random_seed)
259
-
260
- images_html = []
261
-
262
- # Set of dicts representing inputs to render_db_bounding_boxes:
263
- #
264
- # bboxes, boxClasses, image_path
265
- rendering_info = []
266
-
267
- print('Preparing rendering list')
268
-
269
- for iImage,img in tqdm(df_img.iterrows(),total=len(df_img)):
270
-
271
- img_id = img['id']
272
- assert img_id is not None
273
-
274
- img_relative_path = img['file_name']
275
-
276
- if image_base_dir.startswith('http'):
277
- img_path = image_base_dir + img_relative_path
278
- else:
279
- img_path = os.path.join(image_base_dir,
280
- _image_filename_to_path(img_relative_path, image_base_dir))
281
-
282
- annos_i = df_anno.loc[df_anno['image_id'] == img_id, :] # all annotations on this image
283
-
284
- bboxes = []
285
- boxClasses = []
286
-
287
- # All the class labels we've seen for this image (with out without bboxes)
288
- imageCategories = set()
289
-
290
- annotationLevelForImage = ''
291
-
292
- # Iterate over annotations for this image
293
- # iAnn = 0; anno = annos_i.iloc[iAnn]
294
- for iAnn,anno in annos_i.iterrows():
295
-
296
- if 'sequence_level_annotation' in anno:
297
- bSequenceLevelAnnotation = anno['sequence_level_annotation']
298
- if bSequenceLevelAnnotation:
299
- annLevel = 'sequence'
300
- else:
301
- annLevel = 'image'
302
- if annotationLevelForImage == '':
303
- annotationLevelForImage = annLevel
304
- elif annotationLevelForImage != annLevel:
305
- annotationLevelForImage = 'mixed'
306
-
307
- categoryID = anno['category_id']
308
- categoryName = label_map[categoryID]
309
- if options.add_search_links:
310
- categoryName = categoryName.replace('"','')
311
- categoryName = '<a href="https://www.google.com/search?tbm=isch&q={}">{}</a>'.format(
312
- categoryName,categoryName)
313
- imageCategories.add(categoryName)
314
-
315
- if 'bbox' in anno:
316
- bbox = anno['bbox']
317
- if isinstance(bbox,float):
318
- assert math.isnan(bbox), "I shouldn't see a bbox that's neither a box nor NaN"
319
- continue
320
- bboxes.append(bbox)
321
- boxClasses.append(anno['category_id'])
322
-
323
- # ...for each of this image's annotations
324
-
325
- imageClasses = ', '.join(imageCategories)
326
-
327
- img_id_string = str(img_id).lower()
328
- file_name = '{}_gt.jpg'.format(os.path.splitext(img_id_string)[0])
329
-
330
- # Replace characters that muck up image links
331
- illegal_characters = ['/','\\',':','\t','#',' ','%']
332
- for c in illegal_characters:
333
- file_name = file_name.replace(c,'~')
334
-
335
- rendering_info.append({'bboxes':bboxes, 'boxClasses':boxClasses, 'img_path':img_path,
336
- 'output_file_name':file_name})
337
-
338
- labelLevelString = ' '
339
- if len(annotationLevelForImage) > 0:
340
- labelLevelString = ' (annotation level: {})'.format(annotationLevelForImage)
341
-
342
- if 'frame_num' in img and 'seq_num_frames' in img:
343
- frameString = ' frame: {} of {}, '.format(img['frame_num'],img['seq_num_frames'])
344
- elif 'frame_num' in img:
345
- frameString = ' frame: {}, '.format(img['frame_num'])
346
- else:
347
- frameString = ' '
348
-
349
- if options.show_full_paths:
350
- filename_text = img_path
351
- else:
352
- filename_text = img_relative_path
353
- if options.include_filename_links:
354
- filename_text = '<a href="{}">{}</a>'.format(img_path,filename_text)
355
-
356
- flagString = ''
357
-
358
- def isnan(x):
359
- return (isinstance(x,float) and np.isnan(x))
360
-
361
- if ('flags' in img) and (not isnan(img['flags'])):
362
- flagString = ', flags: {}'.format(str(img['flags']))
363
-
364
- # We're adding html for an image before we render it, so it's possible this image will
365
- # fail to render. For applications where this script is being used to debua a database
366
- # (the common case?), this is useful behavior, for other applications, this is annoying.
367
- image_dict = \
368
- {
369
- 'filename': '{}/{}'.format('rendered_images', file_name),
370
- 'title': '{}<br/>{}, num boxes: {}, {}class labels: {}{}{}'.format(
371
- filename_text, img_id, len(bboxes), frameString, imageClasses, labelLevelString, flagString),
372
- 'textStyle': 'font-family:verdana,arial,calibri;font-size:80%;' + \
373
- 'text-align:left;margin-top:20;margin-bottom:5'
374
- }
375
- if options.include_image_links:
376
- image_dict['linkTarget'] = img_path
377
-
378
- images_html.append(image_dict)
379
-
380
- # ...for each image
381
-
382
- def render_image_info(rendering_info):
383
-
384
- img_path = rendering_info['img_path']
385
- bboxes = rendering_info['bboxes']
386
- bboxClasses = rendering_info['boxClasses']
387
- output_file_name = rendering_info['output_file_name']
388
- output_full_path = os.path.join(output_dir, 'rendered_images', output_file_name)
389
-
390
- if (os.path.isfile(output_full_path)) and (not options.force_rendering):
391
- if options.verbose:
392
- print('Skipping existing image {}'.format(output_full_path))
393
- return True
394
-
395
- if not img_path.startswith('http'):
396
- if not os.path.exists(img_path):
397
- print('Image {} cannot be found'.format(img_path))
398
- return False
399
-
400
- try:
401
- original_image = vis_utils.open_image(img_path)
402
- original_size = original_image.size
403
- if (options.viz_size is None) or (options.viz_size[0] == -1 and options.viz_size[1] == -1):
404
- image = original_image
405
- else:
406
- image = vis_utils.resize_image(original_image, options.viz_size[0],
407
- options.viz_size[1])
408
- except Exception as e:
409
- print('Image {} failed to open, error: {}'.format(img_path, e))
410
- return False
411
-
412
- vis_utils.render_db_bounding_boxes(boxes=bboxes, classes=bboxClasses,
413
- image=image, original_size=original_size,
414
- label_map=label_map,
415
- thickness=options.box_thickness,
416
- expansion=options.box_expansion)
417
-
418
- image.save(output_full_path)
419
-
420
- return True
421
-
422
- # ...def render_image_info
423
-
424
- print('Rendering images')
425
- start_time = time.time()
426
-
427
- if options.parallelize_rendering:
428
-
429
- if options.parallelize_rendering_with_threads:
430
- worker_string = 'threads'
431
- else:
432
- worker_string = 'processes'
433
-
434
- if options.parallelize_rendering_n_cores is None:
435
- if options.parallelize_rendering_with_threads:
436
- pool = ThreadPool()
437
- else:
438
- pool = Pool()
439
- else:
440
- if options.parallelize_rendering_with_threads:
441
- pool = ThreadPool(options.parallelize_rendering_n_cores)
442
- else:
443
- pool = Pool(options.parallelize_rendering_n_cores)
444
- print('Rendering images with {} {}'.format(options.parallelize_rendering_n_cores,
445
- worker_string))
446
- rendering_success = list(tqdm(pool.imap(render_image_info, rendering_info),
447
- total=len(rendering_info)))
448
-
449
- else:
450
-
451
- rendering_success = []
452
- for file_info in tqdm(rendering_info):
453
- rendering_success.append(render_image_info(file_info))
454
-
455
- elapsed = time.time() - start_time
456
-
457
- print('Rendered {} images in {} ({} successful)'.format(
458
- len(rendering_info),humanfriendly.format_timespan(elapsed),sum(rendering_success)))
459
-
460
- if options.sort_by_filename:
461
- images_html = sorted(images_html, key=lambda x: x['filename'])
462
- else:
463
- random.shuffle(images_html)
464
-
465
- htmlOutputFile = os.path.join(output_dir, 'index.html')
466
-
467
- htmlOptions = options.htmlOptions
468
- if isinstance(db_path,str):
469
- htmlOptions['headerHtml'] = '<h1>Sample annotations from {}</h1>'.format(db_path)
470
- else:
471
- htmlOptions['headerHtml'] = '<h1>Sample annotations</h1>'
472
-
473
- write_html_image_list(
474
- filename=htmlOutputFile,
475
- images=images_html,
476
- options=htmlOptions)
477
-
478
- print('Visualized {} images, wrote results to {}'.format(len(images_html),htmlOutputFile))
479
-
480
- return htmlOutputFile,image_db
481
-
482
- # def visualize_db(...)
483
-
484
-
485
- #%% Command-line driver
486
-
487
- # Copy all fields from a Namespace (i.e., the output from parse_args) to an object.
488
- #
489
- # Skips fields starting with _. Does not check existence in the target object.
490
- def args_to_object(args, obj):
491
-
492
- for n, v in inspect.getmembers(args):
493
- if not n.startswith('_'):
494
- setattr(obj, n, v)
495
-
496
-
497
- def main():
498
-
499
- parser = argparse.ArgumentParser()
500
- parser.add_argument('db_path', action='store', type=str,
501
- help='.json file to visualize')
502
- parser.add_argument('output_dir', action='store', type=str,
503
- help='Output directory for html and rendered images')
504
- parser.add_argument('image_base_dir', action='store', type=str,
505
- help='Base directory (or URL) for input images')
506
-
507
- parser.add_argument('--num_to_visualize', action='store', type=int, default=None,
508
- help='Number of images to visualize (randomly drawn) (defaults to all)')
509
- parser.add_argument('--random_sort', action='store_true',
510
- help='Sort randomly (rather than by filename) in output html')
511
- parser.add_argument('--trim_to_images_with_bboxes', action='store_true',
512
- help='Only include images with bounding boxes (defaults to false)')
513
- parser.add_argument('--random_seed', action='store', type=int, default=None,
514
- help='Random seed for image selection')
515
- parser.add_argument('--pathsep_replacement', action='store', type=str, default='',
516
- help='Replace path separators in relative filenames with another ' + \
517
- 'character (frequently ~)')
518
-
519
- if len(sys.argv[1:]) == 0:
520
- parser.print_help()
521
- parser.exit()
522
-
523
- args = parser.parse_args()
524
-
525
- # Convert to an options object
526
- options = DbVizOptions()
527
- args_to_object(args, options)
528
- if options.random_sort:
529
- options.sort_by_filename = False
530
-
531
- visualize_db(options.db_path,options.output_dir,options.image_base_dir,options)
532
-
533
- if __name__ == '__main__':
534
- main()
535
-
536
-
537
- #%% Interactive driver
538
-
539
- if False:
540
-
541
- #%%
542
-
543
- db_path = r'e:\wildlife_data\missouri_camera_traps\missouri_camera_traps_set1.json'
544
- output_dir = r'e:\wildlife_data\missouri_camera_traps\preview'
545
- image_base_dir = r'e:\wildlife_data\missouri_camera_traps'
546
-
547
- options = DbVizOptions()
548
- options.num_to_visualize = 100
549
-
550
- htmlOutputFile,db = visualize_db(db_path,output_dir,image_base_dir,options)
551
- # os.startfile(htmlOutputFile)