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
detection/video_utils.py DELETED
@@ -1,606 +0,0 @@
1
- """
2
-
3
- video_utils.py
4
-
5
- Utilities for splitting, rendering, and assembling videos.
6
-
7
- """
8
-
9
- #%% Constants, imports, environment
10
-
11
- import os
12
- import cv2
13
- import glob
14
- import json
15
-
16
- from collections import defaultdict
17
- from multiprocessing.pool import ThreadPool
18
- from multiprocessing.pool import Pool
19
- from tqdm import tqdm
20
- from functools import partial
21
-
22
- from md_utils import path_utils
23
- from md_visualization import visualization_utils as vis_utils
24
-
25
- default_fourcc = 'h264'
26
-
27
-
28
- #%% Path utilities
29
-
30
- VIDEO_EXTENSIONS = ('.mp4','.avi','.mpeg','.mpg')
31
-
32
- def is_video_file(s,video_extensions=VIDEO_EXTENSIONS):
33
- """
34
- Checks a file's extension against a set of known video file
35
- extensions to determine whether it's a video file. Performs a
36
- case-insensitive comparison.
37
-
38
- Args:
39
- s (str): filename to check for probable video-ness
40
- video_extensions (list, optional): list of video file extensions
41
-
42
- Returns:
43
- bool: True if this looks like a video file, else False
44
- """
45
-
46
- ext = os.path.splitext(s)[1]
47
- return ext.lower() in video_extensions
48
-
49
-
50
- def find_video_strings(strings):
51
- """
52
- Given a list of strings that are potentially video file names, looks for
53
- strings that actually look like video file names (based on extension).
54
-
55
- Args:
56
- strings (list): list of strings to check for video-ness
57
-
58
- Returns:
59
- list: a subset of [strings] that looks like they are video filenames
60
- """
61
-
62
- return [s for s in strings if is_video_file(s.lower())]
63
-
64
-
65
- def find_videos(dirname,
66
- recursive=False,
67
- convert_slashes=True,
68
- return_relative_paths=False):
69
- """
70
- Finds all files in a directory that look like video file names.
71
-
72
- Args:
73
- dirname (str): folder to search for video files
74
- recursive (bool, optional): whether to search [dirname] recursively
75
- convert_slashes (bool, optional): forces forward slashes in the returned files,
76
- otherwise uses the native path separator
77
- return_relative_paths (bool, optional): forces the returned filenames to be
78
- relative to [dirname], otherwise returns absolute paths
79
-
80
- Returns:
81
- A list of filenames within [dirname] that appear to be videos
82
- """
83
-
84
- if recursive:
85
- files = glob.glob(os.path.join(dirname, '**', '*.*'), recursive=True)
86
- else:
87
- files = glob.glob(os.path.join(dirname, '*.*'))
88
-
89
- if return_relative_paths:
90
- files = [os.path.relpath(fn,dirname) for fn in files]
91
-
92
- if convert_slashes:
93
- files = [fn.replace('\\', '/') for fn in files]
94
-
95
- return find_video_strings(files)
96
-
97
-
98
- #%% Function for rendering frames to video and vice-versa
99
-
100
- # http://tsaith.github.io/combine-images-into-a-video-with-python-3-and-opencv-3.html
101
-
102
- def frames_to_video(images, Fs, output_file_name, codec_spec=default_fourcc):
103
- """
104
- Given a list of image files and a sample rate, concatenates those images into
105
- a video and writes to a new video file.
106
-
107
- Args:
108
- images (list): a list of frame file names to concatenate into a video
109
- Fs (float): the frame rate in fps
110
- output_file_name (str): the output video file, no checking is performed to make
111
- sure the extension is compatible with the codec
112
- codec_spec (str, optional): codec to use for encoding; h264 is a sensible default
113
- and generally works on Windows, but when this fails (which is around 50% of the time
114
- on Linux), mp4v is a good second choice
115
- """
116
-
117
- if codec_spec is None:
118
- codec_spec = 'h264'
119
-
120
- if len(images) == 0:
121
- return
122
-
123
- # Determine the width and height from the first image
124
- frame = cv2.imread(images[0])
125
- cv2.imshow('video',frame)
126
- height, width, channels = frame.shape
127
-
128
- # Define the codec and create VideoWriter object
129
- fourcc = cv2.VideoWriter_fourcc(*codec_spec)
130
- out = cv2.VideoWriter(output_file_name, fourcc, Fs, (width, height))
131
-
132
- for image in images:
133
- frame = cv2.imread(image)
134
- out.write(frame)
135
-
136
- out.release()
137
- cv2.destroyAllWindows()
138
-
139
-
140
- def get_video_fs(input_video_file):
141
- """
142
- Retrieves the frame rate of [input_video_file].
143
-
144
- Args:
145
- input_video_file (str): video file for which we want the frame rate
146
-
147
- Returns:
148
- float: the frame rate of [input_video_file]
149
- """
150
-
151
- assert os.path.isfile(input_video_file), 'File {} not found'.format(input_video_file)
152
- vidcap = cv2.VideoCapture(input_video_file)
153
- Fs = vidcap.get(cv2.CAP_PROP_FPS)
154
- vidcap.release()
155
- return Fs
156
-
157
-
158
- def _frame_number_to_filename(frame_number):
159
- """
160
- Ensures that frame images are given consistent filenames.
161
- """
162
-
163
- return 'frame{:06d}.jpg'.format(frame_number)
164
-
165
-
166
- def video_to_frames(input_video_file, output_folder, overwrite=True,
167
- every_n_frames=None, verbose=False):
168
- """
169
- Renders frames from [input_video_file] to a .jpg in [output_folder].
170
-
171
- With help from:
172
-
173
- https://stackoverflow.com/questions/33311153/python-extracting-and-saving-video-frames
174
-
175
- Args:
176
- input_video_file (str): video file to split into frames
177
- output_folder (str): folder to put frame images in
178
- overwrite (bool, optional): whether to overwrite existing frame images
179
- every_n_frames (int, optional): sample every Nth frame starting from the first frame;
180
- if this is None or 1, every frame is extracted
181
- verbose (bool, optional): enable additional debug console output
182
-
183
- Returns:
184
- tuple: length-2 tuple containing (list of frame filenames,frame rate)
185
- """
186
-
187
- assert os.path.isfile(input_video_file), 'File {} not found'.format(input_video_file)
188
-
189
- vidcap = cv2.VideoCapture(input_video_file)
190
- n_frames = int(vidcap.get(cv2.CAP_PROP_FRAME_COUNT))
191
- Fs = vidcap.get(cv2.CAP_PROP_FPS)
192
-
193
- # If we're not over-writing, check whether all frame images already exist
194
- if overwrite == False:
195
-
196
- missing_frame_number = None
197
- frame_filenames = []
198
-
199
- for frame_number in range(0,n_frames):
200
-
201
- if every_n_frames is not None:
202
- if frame_number % every_n_frames != 0:
203
- continue
204
-
205
- frame_filename = _frame_number_to_filename(frame_number)
206
- frame_filename = os.path.join(output_folder,frame_filename)
207
- frame_filenames.append(frame_filename)
208
- if os.path.isfile(frame_filename):
209
- continue
210
- else:
211
- missing_frame_number = frame_number
212
- break
213
-
214
- # OpenCV seems to over-report the number of frames by 1 in some cases, or fails
215
- # to read the last frame; either way, I'm allowing one missing frame.
216
- allow_last_frame_missing = True
217
-
218
- if missing_frame_number is None or \
219
- (allow_last_frame_missing and (missing_frame_number == n_frames-1)):
220
- if verbose:
221
- print('Skipping video {}, all output frames exist'.format(input_video_file))
222
- return frame_filenames,Fs
223
- else:
224
- pass
225
- # print("Rendering video {}, couldn't find frame {}".format(
226
- # input_video_file,missing_frame_number))
227
-
228
- # ...if we need to check whether to skip this video entirely
229
-
230
- if verbose:
231
- print('Reading {} frames at {} Hz from {}'.format(n_frames,Fs,input_video_file))
232
-
233
- frame_filenames = []
234
-
235
- # for frame_number in tqdm(range(0,n_frames)):
236
- for frame_number in range(0,n_frames):
237
-
238
- success,image = vidcap.read()
239
- if not success:
240
- assert image is None
241
- if verbose:
242
- print('Read terminating at frame {} of {}'.format(frame_number,n_frames))
243
- break
244
-
245
- if every_n_frames is not None:
246
- if frame_number % every_n_frames != 0:
247
- continue
248
-
249
- frame_filename = _frame_number_to_filename(frame_number)
250
- frame_filename = os.path.join(output_folder,frame_filename)
251
- frame_filenames.append(frame_filename)
252
-
253
- if overwrite == False and os.path.isfile(frame_filename):
254
- # print('Skipping frame {}'.format(frame_filename))
255
- pass
256
- else:
257
- try:
258
- if frame_filename.isascii():
259
- cv2.imwrite(os.path.normpath(frame_filename),image)
260
- else:
261
- is_success, im_buf_arr = cv2.imencode('.jpg', image)
262
- im_buf_arr.tofile(frame_filename)
263
- assert os.path.isfile(frame_filename), \
264
- 'Output frame {} unavailable'.format(frame_filename)
265
- except KeyboardInterrupt:
266
- vidcap.release()
267
- raise
268
- except Exception as e:
269
- print('Error on frame {} of {}: {}'.format(frame_number,n_frames,str(e)))
270
-
271
- if verbose:
272
- print('\nExtracted {} of {} frames'.format(len(frame_filenames),n_frames))
273
-
274
- vidcap.release()
275
- return frame_filenames,Fs
276
-
277
- # ...def video_to_frames(...)
278
-
279
-
280
- def _video_to_frames_for_folder(relative_fn,input_folder,output_folder_base,every_n_frames,overwrite,verbose):
281
- """
282
- Internal function to call video_to_frames in the context of video_folder_to_frames;
283
- makes sure the right output folder exists, then calls video_to_frames.
284
- """
285
-
286
- input_fn_absolute = os.path.join(input_folder,relative_fn)
287
- assert os.path.isfile(input_fn_absolute),\
288
- 'Could not find file {}'.format(input_fn_absolute)
289
-
290
- # Create the target output folder
291
- output_folder_video = os.path.join(output_folder_base,relative_fn)
292
- os.makedirs(output_folder_video,exist_ok=True)
293
-
294
- # Render frames
295
- # input_video_file = input_fn_absolute; output_folder = output_folder_video
296
- frame_filenames,fs = video_to_frames(input_fn_absolute,output_folder_video,
297
- overwrite=overwrite,every_n_frames=every_n_frames,
298
- verbose=verbose)
299
-
300
- return frame_filenames,fs
301
-
302
-
303
- def video_folder_to_frames(input_folder, output_folder_base,
304
- recursive=True, overwrite=True,
305
- n_threads=1, every_n_frames=None,
306
- verbose=False, parallelization_uses_threads=True):
307
- """
308
- For every video file in input_folder, creates a folder within output_folder_base, and
309
- renders frame of that video to images in that folder.
310
-
311
- Args:
312
- input_folder (str): folder to process
313
- output_folder_base (str): root folder for output images; subfolders will be
314
- created for each input video
315
- recursive (bool, optional): whether to recursively process videos in [input_folder]
316
- overwrite (bool, optional): whether to overwrite existing frame images
317
- n_threads (int, optional): number of concurrent workers to use; set to <= 1 to disable
318
- parallelism
319
- every_n_frames (int, optional): sample every Nth frame starting from the first frame;
320
- if this is None or 1, every frame is extracted
321
- verbose (bool, optional): enable additional debug console output
322
- parallelization_uses_threads (bool, optional): whether to use threads (True) or
323
- processes (False) for parallelization; ignored if n_threads <= 1
324
-
325
- Returns:
326
- tuple: a length-3 tuple containing:
327
- - list of lists of frame filenames; the Nth list of frame filenames corresponds to
328
- the Nth video
329
- - list of video frame rates; the Nth value corresponds to the Nth video
330
- - list of video filenames
331
- """
332
-
333
- # Recursively enumerate video files
334
- input_files_full_paths = find_videos(input_folder,recursive=recursive)
335
- print('Found {} videos in folder {}'.format(len(input_files_full_paths),input_folder))
336
- if len(input_files_full_paths) == 0:
337
- return [],[],[]
338
-
339
- input_files_relative_paths = [os.path.relpath(s,input_folder) for s in input_files_full_paths]
340
- input_files_relative_paths = [s.replace('\\','/') for s in input_files_relative_paths]
341
-
342
- os.makedirs(output_folder_base,exist_ok=True)
343
-
344
- frame_filenames_by_video = []
345
- fs_by_video = []
346
-
347
- if n_threads == 1:
348
- # For each video
349
- #
350
- # input_fn_relative = input_files_relative_paths[0]
351
- for input_fn_relative in tqdm(input_files_relative_paths):
352
-
353
- frame_filenames,fs = \
354
- _video_to_frames_for_folder(input_fn_relative,input_folder,output_folder_base,
355
- every_n_frames,overwrite,verbose)
356
- frame_filenames_by_video.append(frame_filenames)
357
- fs_by_video.append(fs)
358
- else:
359
- if parallelization_uses_threads:
360
- print('Starting a worker pool with {} threads'.format(n_threads))
361
- pool = ThreadPool(n_threads)
362
- else:
363
- print('Starting a worker pool with {} processes'.format(n_threads))
364
- pool = Pool(n_threads)
365
- process_video_with_options = partial(_video_to_frames_for_folder,
366
- input_folder=input_folder,
367
- output_folder_base=output_folder_base,
368
- every_n_frames=every_n_frames,
369
- overwrite=overwrite,
370
- verbose=verbose)
371
- results = list(tqdm(pool.imap(
372
- partial(process_video_with_options),input_files_relative_paths),
373
- total=len(input_files_relative_paths)))
374
- frame_filenames_by_video = [x[0] for x in results]
375
- fs_by_video = [x[1] for x in results]
376
-
377
- return frame_filenames_by_video,fs_by_video,input_files_full_paths
378
-
379
- # ...def video_folder_to_frames(...)
380
-
381
-
382
- class FrameToVideoOptions:
383
- """
384
- Options controlling the conversion of frame-level results to video-level results via
385
- frame_results_to_video_results()
386
- """
387
-
388
- #: One-indexed indicator of which frame-level confidence value to use to determine detection confidence
389
- #: for the whole video, i.e. "1" means "use the confidence value from the highest-confidence frame"
390
- nth_highest_confidence = 1
391
-
392
- #: What to do if a file referred to in a .json results file appears not to be a
393
- #: video; can be 'error' or 'skip_with_warning'
394
- non_video_behavior = 'error'
395
-
396
-
397
- def frame_results_to_video_results(input_file,output_file,options=None):
398
- """
399
- Given an MD results file produced at the *frame* level, corresponding to a directory
400
- created with video_folder_to_frames, maps those frame-level results back to the
401
- video level for use in Timelapse.
402
-
403
- Preserves everything in the input .json file other than the images.
404
-
405
- Args:
406
- input_file (str): the frame-level MD results file to convert to video-level results
407
- output_file (str): the .json file to which we should write video-level results
408
- options (FrameToVideoOptions, optional): parameters for converting frame-level results
409
- to video-level results, see FrameToVideoOptions for details
410
- """
411
-
412
- if options is None:
413
- options = FrameToVideoOptions()
414
-
415
- # Load results
416
- with open(input_file,'r') as f:
417
- input_data = json.load(f)
418
-
419
- images = input_data['images']
420
- detection_categories = input_data['detection_categories']
421
-
422
- ## Break into videos
423
-
424
- video_to_frames = defaultdict(list)
425
-
426
- # im = images[0]
427
- for im in tqdm(images):
428
-
429
- fn = im['file']
430
- video_name = os.path.dirname(fn)
431
- if not is_video_file(video_name):
432
- if options.non_video_behavior == 'error':
433
- raise ValueError('{} is not a video file'.format(video_name))
434
- elif options.non_video_behavior == 'skip_with_warning':
435
- print('Warning: {} is not a video file'.format(video_name))
436
- continue
437
- else:
438
- raise ValueError('Unrecognized non-video handling behavior: {}'.format(
439
- options.non_video_behavior))
440
- video_to_frames[video_name].append(im)
441
-
442
- print('Found {} unique videos in {} frame-level results'.format(
443
- len(video_to_frames),len(images)))
444
-
445
- output_images = []
446
-
447
- ## For each video...
448
-
449
- # video_name = list(video_to_frames.keys())[0]
450
- for video_name in tqdm(video_to_frames):
451
-
452
- frames = video_to_frames[video_name]
453
-
454
- all_detections_this_video = []
455
-
456
- # frame = frames[0]
457
- for frame in frames:
458
- if frame['detections'] is not None:
459
- all_detections_this_video.extend(frame['detections'])
460
-
461
- # At most one detection for each category for the whole video
462
- canonical_detections = []
463
-
464
- # category_id = list(detection_categories.keys())[0]
465
- for category_id in detection_categories:
466
-
467
- category_detections = [det for det in all_detections_this_video if \
468
- det['category'] == category_id]
469
-
470
- # Find the nth-highest-confidence video to choose a confidence value
471
- if len(category_detections) >= options.nth_highest_confidence:
472
-
473
- category_detections_by_confidence = sorted(category_detections,
474
- key = lambda i: i['conf'],reverse=True)
475
- canonical_detection = category_detections_by_confidence[options.nth_highest_confidence-1]
476
- canonical_detections.append(canonical_detection)
477
-
478
- # Prepare the output representation for this video
479
- im_out = {}
480
- im_out['file'] = video_name
481
- im_out['detections'] = canonical_detections
482
-
483
- # 'max_detection_conf' is no longer included in output files by default
484
- if False:
485
- im_out['max_detection_conf'] = 0
486
- if len(canonical_detections) > 0:
487
- confidences = [d['conf'] for d in canonical_detections]
488
- im_out['max_detection_conf'] = max(confidences)
489
-
490
- output_images.append(im_out)
491
-
492
- # ...for each video
493
-
494
- output_data = input_data
495
- output_data['images'] = output_images
496
- s = json.dumps(output_data,indent=1)
497
-
498
- # Write the output file
499
- with open(output_file,'w') as f:
500
- f.write(s)
501
-
502
- # ...def frame_results_to_video_results(...)
503
-
504
-
505
- #%% Test driver
506
-
507
- if False:
508
-
509
- #%% Constants
510
-
511
- Fs = 30.01
512
- confidence_threshold = 0.75
513
- input_folder = 'z:\\'
514
- frame_folder_base = r'e:\video_test\frames'
515
- detected_frame_folder_base = r'e:\video_test\detected_frames'
516
- rendered_videos_folder_base = r'e:\video_test\rendered_videos'
517
-
518
- results_file = r'results.json'
519
- os.makedirs(detected_frame_folder_base,exist_ok=True)
520
- os.makedirs(rendered_videos_folder_base,exist_ok=True)
521
-
522
-
523
- #%% Split videos into frames
524
-
525
- frame_filenames_by_video,fs_by_video,video_filenames = \
526
- video_folder_to_frames(input_folder,frame_folder_base,recursive=True)
527
-
528
-
529
- #%% List image files, break into folders
530
-
531
- frame_files = path_utils.find_images(frame_folder_base,True)
532
- frame_files = [s.replace('\\','/') for s in frame_files]
533
- print('Enumerated {} total frames'.format(len(frame_files)))
534
-
535
- Fs = 30.01
536
- # Find unique folders
537
- folders = set()
538
- # fn = frame_files[0]
539
- for fn in frame_files:
540
- folders.add(os.path.dirname(fn))
541
- folders = [s.replace('\\','/') for s in folders]
542
- print('Found {} folders for {} files'.format(len(folders),len(frame_files)))
543
-
544
-
545
- #%% Load detector output
546
-
547
- with open(results_file,'r') as f:
548
- detection_results = json.load(f)
549
- detections = detection_results['images']
550
- detector_label_map = detection_results['detection_categories']
551
- for d in detections:
552
- d['file'] = d['file'].replace('\\','/').replace('video_frames/','')
553
-
554
-
555
- #%% Render detector frames
556
-
557
- # folder = list(folders)[0]
558
- for folder in folders:
559
-
560
- frame_files_this_folder = [fn for fn in frame_files if folder in fn]
561
- folder_relative = folder.replace((frame_folder_base + '/').replace('\\','/'),'')
562
- detection_results_this_folder = [d for d in detections if folder_relative in d['file']]
563
- print('Found {} detections in folder {}'.format(len(detection_results_this_folder),folder))
564
- assert len(frame_files_this_folder) == len(detection_results_this_folder)
565
-
566
- rendered_frame_output_folder = os.path.join(detected_frame_folder_base,folder_relative)
567
- os.makedirs(rendered_frame_output_folder,exist_ok=True)
568
-
569
- # d = detection_results_this_folder[0]
570
- for d in tqdm(detection_results_this_folder):
571
-
572
- input_file = os.path.join(frame_folder_base,d['file'])
573
- output_file = os.path.join(detected_frame_folder_base,d['file'])
574
- os.makedirs(os.path.dirname(output_file),exist_ok=True)
575
- vis_utils.draw_bounding_boxes_on_file(input_file,output_file,d['detections'],
576
- confidence_threshold)
577
-
578
- # ...for each file in this folder
579
-
580
- # ...for each folder
581
-
582
-
583
- #%% Render output videos
584
-
585
- # folder = list(folders)[0]
586
- for folder in tqdm(folders):
587
-
588
- folder_relative = folder.replace((frame_folder_base + '/').replace('\\','/'),'')
589
- rendered_detector_output_folder = os.path.join(detected_frame_folder_base,folder_relative)
590
- assert os.path.isdir(rendered_detector_output_folder)
591
-
592
- frame_files_relative = os.listdir(rendered_detector_output_folder)
593
- frame_files_absolute = [os.path.join(rendered_detector_output_folder,s) \
594
- for s in frame_files_relative]
595
-
596
- output_video_filename = os.path.join(rendered_videos_folder_base,folder_relative)
597
- os.makedirs(os.path.dirname(output_video_filename),exist_ok=True)
598
-
599
- original_video_filename = output_video_filename.replace(
600
- rendered_videos_folder_base,input_folder)
601
- assert os.path.isfile(original_video_filename)
602
- Fs = get_video_fs(original_video_filename)
603
-
604
- frames_to_video(frame_files_absolute, Fs, output_video_filename)
605
-
606
- # ...for each video
docs/source/conf.py DELETED
@@ -1,43 +0,0 @@
1
- import sys
2
- import os
3
-
4
- project = 'MegaDetector'
5
- copyright = '2024, Your friendly neighborhood MegaDetector team'
6
- author = 'Your friendly neighborhood MegaDetector team'
7
-
8
- sys.path.insert(0, os.path.abspath("../.."))
9
-
10
- extensions = [
11
- "sphinx.ext.napoleon",
12
- "sphinx.ext.autodoc",
13
- "sphinx.ext.viewcode",
14
- "sphinx_mdinclude",
15
- "sphinx_argparse_cli"
16
- ]
17
-
18
- autodoc_mock_imports = ["azure", "deepdiff", "magic", "tensorflow", "pytesseract"]
19
-
20
- myst_enable_extensions = [
21
- "colon_fence",
22
- ]
23
-
24
- html_theme = 'sphinx_rtd_theme'
25
-
26
- # collapse_navigation doesn't actually work
27
- html_theme_options = {'navigation_depth': 2, 'collapse_navigation': False}
28
-
29
- # html_theme = 'sphinx_book_theme'
30
- # html_theme_options['show_navbar_depth'] = 2
31
-
32
- # html_static_path = ['_static']
33
-
34
- # Hide "bases: object" from all classes that don't define a base class
35
- from sphinx.ext import autodoc
36
-
37
- class MockedClassDocumenter(autodoc.ClassDocumenter):
38
- def add_line(self, line: str, source: str, *lineno: int) -> None:
39
- if line == " Bases: :py:class:`object`":
40
- return
41
- super().add_line(line, source, *lineno)
42
-
43
- autodoc.ClassDocumenter = MockedClassDocumenter
md_utils/__init__.py DELETED
File without changes