megadetector 10.0.13__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 (147) hide show
  1. megadetector/__init__.py +0 -0
  2. megadetector/api/__init__.py +0 -0
  3. megadetector/api/batch_processing/integration/digiKam/setup.py +6 -0
  4. megadetector/api/batch_processing/integration/digiKam/xmp_integration.py +465 -0
  5. megadetector/api/batch_processing/integration/eMammal/test_scripts/config_template.py +5 -0
  6. megadetector/api/batch_processing/integration/eMammal/test_scripts/push_annotations_to_emammal.py +125 -0
  7. megadetector/api/batch_processing/integration/eMammal/test_scripts/select_images_for_testing.py +55 -0
  8. megadetector/classification/__init__.py +0 -0
  9. megadetector/classification/aggregate_classifier_probs.py +108 -0
  10. megadetector/classification/analyze_failed_images.py +227 -0
  11. megadetector/classification/cache_batchapi_outputs.py +198 -0
  12. megadetector/classification/create_classification_dataset.py +626 -0
  13. megadetector/classification/crop_detections.py +516 -0
  14. megadetector/classification/csv_to_json.py +226 -0
  15. megadetector/classification/detect_and_crop.py +853 -0
  16. megadetector/classification/efficientnet/__init__.py +9 -0
  17. megadetector/classification/efficientnet/model.py +415 -0
  18. megadetector/classification/efficientnet/utils.py +608 -0
  19. megadetector/classification/evaluate_model.py +520 -0
  20. megadetector/classification/identify_mislabeled_candidates.py +152 -0
  21. megadetector/classification/json_to_azcopy_list.py +63 -0
  22. megadetector/classification/json_validator.py +696 -0
  23. megadetector/classification/map_classification_categories.py +276 -0
  24. megadetector/classification/merge_classification_detection_output.py +509 -0
  25. megadetector/classification/prepare_classification_script.py +194 -0
  26. megadetector/classification/prepare_classification_script_mc.py +228 -0
  27. megadetector/classification/run_classifier.py +287 -0
  28. megadetector/classification/save_mislabeled.py +110 -0
  29. megadetector/classification/train_classifier.py +827 -0
  30. megadetector/classification/train_classifier_tf.py +725 -0
  31. megadetector/classification/train_utils.py +323 -0
  32. megadetector/data_management/__init__.py +0 -0
  33. megadetector/data_management/animl_to_md.py +161 -0
  34. megadetector/data_management/annotations/__init__.py +0 -0
  35. megadetector/data_management/annotations/annotation_constants.py +33 -0
  36. megadetector/data_management/camtrap_dp_to_coco.py +270 -0
  37. megadetector/data_management/cct_json_utils.py +566 -0
  38. megadetector/data_management/cct_to_md.py +184 -0
  39. megadetector/data_management/cct_to_wi.py +293 -0
  40. megadetector/data_management/coco_to_labelme.py +284 -0
  41. megadetector/data_management/coco_to_yolo.py +702 -0
  42. megadetector/data_management/databases/__init__.py +0 -0
  43. megadetector/data_management/databases/add_width_and_height_to_db.py +107 -0
  44. megadetector/data_management/databases/combine_coco_camera_traps_files.py +210 -0
  45. megadetector/data_management/databases/integrity_check_json_db.py +528 -0
  46. megadetector/data_management/databases/subset_json_db.py +195 -0
  47. megadetector/data_management/generate_crops_from_cct.py +200 -0
  48. megadetector/data_management/get_image_sizes.py +164 -0
  49. megadetector/data_management/labelme_to_coco.py +559 -0
  50. megadetector/data_management/labelme_to_yolo.py +349 -0
  51. megadetector/data_management/lila/__init__.py +0 -0
  52. megadetector/data_management/lila/create_lila_blank_set.py +556 -0
  53. megadetector/data_management/lila/create_lila_test_set.py +187 -0
  54. megadetector/data_management/lila/create_links_to_md_results_files.py +106 -0
  55. megadetector/data_management/lila/download_lila_subset.py +182 -0
  56. megadetector/data_management/lila/generate_lila_per_image_labels.py +777 -0
  57. megadetector/data_management/lila/get_lila_annotation_counts.py +174 -0
  58. megadetector/data_management/lila/get_lila_image_counts.py +112 -0
  59. megadetector/data_management/lila/lila_common.py +319 -0
  60. megadetector/data_management/lila/test_lila_metadata_urls.py +164 -0
  61. megadetector/data_management/mewc_to_md.py +344 -0
  62. megadetector/data_management/ocr_tools.py +873 -0
  63. megadetector/data_management/read_exif.py +964 -0
  64. megadetector/data_management/remap_coco_categories.py +195 -0
  65. megadetector/data_management/remove_exif.py +156 -0
  66. megadetector/data_management/rename_images.py +194 -0
  67. megadetector/data_management/resize_coco_dataset.py +663 -0
  68. megadetector/data_management/speciesnet_to_md.py +41 -0
  69. megadetector/data_management/wi_download_csv_to_coco.py +247 -0
  70. megadetector/data_management/yolo_output_to_md_output.py +594 -0
  71. megadetector/data_management/yolo_to_coco.py +876 -0
  72. megadetector/data_management/zamba_to_md.py +188 -0
  73. megadetector/detection/__init__.py +0 -0
  74. megadetector/detection/change_detection.py +840 -0
  75. megadetector/detection/process_video.py +479 -0
  76. megadetector/detection/pytorch_detector.py +1451 -0
  77. megadetector/detection/run_detector.py +1267 -0
  78. megadetector/detection/run_detector_batch.py +2159 -0
  79. megadetector/detection/run_inference_with_yolov5_val.py +1314 -0
  80. megadetector/detection/run_md_and_speciesnet.py +1494 -0
  81. megadetector/detection/run_tiled_inference.py +1038 -0
  82. megadetector/detection/tf_detector.py +209 -0
  83. megadetector/detection/video_utils.py +1379 -0
  84. megadetector/postprocessing/__init__.py +0 -0
  85. megadetector/postprocessing/add_max_conf.py +72 -0
  86. megadetector/postprocessing/categorize_detections_by_size.py +166 -0
  87. megadetector/postprocessing/classification_postprocessing.py +1752 -0
  88. megadetector/postprocessing/combine_batch_outputs.py +249 -0
  89. megadetector/postprocessing/compare_batch_results.py +2110 -0
  90. megadetector/postprocessing/convert_output_format.py +403 -0
  91. megadetector/postprocessing/create_crop_folder.py +629 -0
  92. megadetector/postprocessing/detector_calibration.py +570 -0
  93. megadetector/postprocessing/generate_csv_report.py +522 -0
  94. megadetector/postprocessing/load_api_results.py +223 -0
  95. megadetector/postprocessing/md_to_coco.py +428 -0
  96. megadetector/postprocessing/md_to_labelme.py +351 -0
  97. megadetector/postprocessing/md_to_wi.py +41 -0
  98. megadetector/postprocessing/merge_detections.py +392 -0
  99. megadetector/postprocessing/postprocess_batch_results.py +2077 -0
  100. megadetector/postprocessing/remap_detection_categories.py +226 -0
  101. megadetector/postprocessing/render_detection_confusion_matrix.py +677 -0
  102. megadetector/postprocessing/repeat_detection_elimination/find_repeat_detections.py +206 -0
  103. megadetector/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +82 -0
  104. megadetector/postprocessing/repeat_detection_elimination/repeat_detections_core.py +1665 -0
  105. megadetector/postprocessing/separate_detections_into_folders.py +795 -0
  106. megadetector/postprocessing/subset_json_detector_output.py +964 -0
  107. megadetector/postprocessing/top_folders_to_bottom.py +238 -0
  108. megadetector/postprocessing/validate_batch_results.py +332 -0
  109. megadetector/taxonomy_mapping/__init__.py +0 -0
  110. megadetector/taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +491 -0
  111. megadetector/taxonomy_mapping/map_new_lila_datasets.py +213 -0
  112. megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +165 -0
  113. megadetector/taxonomy_mapping/preview_lila_taxonomy.py +543 -0
  114. megadetector/taxonomy_mapping/retrieve_sample_image.py +71 -0
  115. megadetector/taxonomy_mapping/simple_image_download.py +224 -0
  116. megadetector/taxonomy_mapping/species_lookup.py +1008 -0
  117. megadetector/taxonomy_mapping/taxonomy_csv_checker.py +159 -0
  118. megadetector/taxonomy_mapping/taxonomy_graph.py +346 -0
  119. megadetector/taxonomy_mapping/validate_lila_category_mappings.py +83 -0
  120. megadetector/tests/__init__.py +0 -0
  121. megadetector/tests/test_nms_synthetic.py +335 -0
  122. megadetector/utils/__init__.py +0 -0
  123. megadetector/utils/ct_utils.py +1857 -0
  124. megadetector/utils/directory_listing.py +199 -0
  125. megadetector/utils/extract_frames_from_video.py +307 -0
  126. megadetector/utils/gpu_test.py +125 -0
  127. megadetector/utils/md_tests.py +2072 -0
  128. megadetector/utils/path_utils.py +2832 -0
  129. megadetector/utils/process_utils.py +172 -0
  130. megadetector/utils/split_locations_into_train_val.py +237 -0
  131. megadetector/utils/string_utils.py +234 -0
  132. megadetector/utils/url_utils.py +825 -0
  133. megadetector/utils/wi_platform_utils.py +968 -0
  134. megadetector/utils/wi_taxonomy_utils.py +1759 -0
  135. megadetector/utils/write_html_image_list.py +239 -0
  136. megadetector/visualization/__init__.py +0 -0
  137. megadetector/visualization/plot_utils.py +309 -0
  138. megadetector/visualization/render_images_with_thumbnails.py +243 -0
  139. megadetector/visualization/visualization_utils.py +1940 -0
  140. megadetector/visualization/visualize_db.py +630 -0
  141. megadetector/visualization/visualize_detector_output.py +479 -0
  142. megadetector/visualization/visualize_video_output.py +705 -0
  143. megadetector-10.0.13.dist-info/METADATA +134 -0
  144. megadetector-10.0.13.dist-info/RECORD +147 -0
  145. megadetector-10.0.13.dist-info/WHEEL +5 -0
  146. megadetector-10.0.13.dist-info/licenses/LICENSE +19 -0
  147. megadetector-10.0.13.dist-info/top_level.txt +1 -0
@@ -0,0 +1,479 @@
1
+ """
2
+
3
+ process_video.py
4
+
5
+ Splits a video (or folder of videos) into frames, runs the frames through run_detector_batch.py,
6
+ and optionally stitches together results into a new video with detection boxes.
7
+
8
+ When possible, video processing happens in memory, without writing intermediate frames to disk.
9
+ If the caller requests that frames be saved, frames are written before processing, and the MD
10
+ results correspond to the frames that were written to disk (which simplifies, for example,
11
+ repeat detection elimination).
12
+
13
+ """
14
+
15
+ #%% Imports
16
+
17
+ import os
18
+ import sys
19
+ import argparse
20
+
21
+ from megadetector.detection import run_detector_batch
22
+ from megadetector.utils.ct_utils import args_to_object
23
+ from megadetector.utils.ct_utils import dict_to_kvp_list, parse_kvp_list
24
+ from megadetector.detection.video_utils import _filename_to_frame_number
25
+ from megadetector.detection.video_utils import find_videos
26
+ from megadetector.detection.video_utils import run_callback_on_frames_for_folder
27
+ from megadetector.detection.run_detector import load_detector
28
+ from megadetector.postprocessing.validate_batch_results import \
29
+ ValidateBatchResultsOptions, validate_batch_results
30
+
31
+ # Notes to self re: upcoming work on checkpointing
32
+ from megadetector.utils.ct_utils import split_list_into_fixed_size_chunks # noqa
33
+ from megadetector.detection.run_detector_batch import write_checkpoint, load_checkpoint # noqa
34
+
35
+
36
+ #%% Classes
37
+
38
+ class ProcessVideoOptions:
39
+ """
40
+ Options controlling the behavior of process_video()
41
+ """
42
+
43
+ def __init__(self):
44
+
45
+ #: Can be a model filename (.pt or .pb) or a model name (e.g. "MDV5A")
46
+ #:
47
+ #: Use the string "no_detection" to indicate that you only want to extract frames,
48
+ #: not run a model. If you do this, you almost definitely want to set
49
+ #: keep_extracted_frames to "True", otherwise everything in this module is a no-op.
50
+ #: I.e., there's no reason to extract frames, do nothing with them, then delete them.
51
+ self.model_file = 'MDV5A'
52
+
53
+ #: Video (of folder of videos) to process
54
+ self.input_video_file = ''
55
+
56
+ #: .json file to which we should write results
57
+ self.output_json_file = None
58
+
59
+ #: If [input_video_file] is a folder, should we search for videos recursively?
60
+ self.recursive = False
61
+
62
+ #: Enable additional debug console output
63
+ self.verbose = False
64
+
65
+ #: Detections below this threshold will not be included in the output file.
66
+ self.json_confidence_threshold = 0.005
67
+
68
+ #: Sample every Nth frame; set to None (default) or 1 to sample every frame. Typically
69
+ #: we sample down to around 3 fps, so for typical 30 fps videos, frame_sample=10 is a
70
+ #: typical value. Mutually exclusive with [time_sample].
71
+ self.frame_sample = None
72
+
73
+ #: Sample frames every N seconds. Mutually exclusive with [frame_sample]
74
+ self.time_sample = None
75
+
76
+ #: Run the model at this image size (don't mess with this unless you know what you're
77
+ #: getting into)... if you just want to pass smaller frames to MD, use max_width
78
+ self.image_size = None
79
+
80
+ #: Enable image augmentation
81
+ self.augment = False
82
+
83
+ #: By default, a video with no frames (or no frames retrievable with the current parameters)
84
+ #: is silently stored as a failure; this causes it to halt execution.
85
+ self.exit_on_empty_video = False
86
+
87
+ #: Detector-specific options
88
+ self.detector_options = None
89
+
90
+ #: Write a checkpoint file (to resume processing later) every N videos;
91
+ #: set to -1 (default) to disable checkpointing
92
+ self.checkpoint_frequency = -1
93
+
94
+ #: Path to checkpoint file; None (default) for auto-generation based on output filename
95
+ self.checkpoint_path = None
96
+
97
+ #: Resume from a checkpoint file, or "auto" to use the most recent checkpoint in the
98
+ #: output directory
99
+ self.resume_from_checkpoint = None
100
+
101
+ # ...class ProcessVideoOptions
102
+
103
+
104
+ #%% Functions
105
+
106
+ def _validate_video_options(options):
107
+ """
108
+ Consistency checking for ProcessVideoOptions objects.
109
+ """
110
+
111
+ n_sampling_options_configured = 0
112
+ if options.frame_sample is not None:
113
+ n_sampling_options_configured += 1
114
+ if options.time_sample is not None:
115
+ n_sampling_options_configured += 1
116
+
117
+ if n_sampling_options_configured > 1:
118
+ raise ValueError('frame_sample and time_sample are mutually exclusive')
119
+
120
+ return True
121
+
122
+
123
+ def process_videos(options):
124
+ """
125
+ Process a video or folder of videos through MD.
126
+
127
+ Args:
128
+ options (ProcessVideoOptions): all the parameters used to control this process,
129
+ including filenames; see ProcessVideoOptions for details
130
+ """
131
+
132
+ ## Validate options
133
+
134
+ # Check for incompatible options
135
+ _validate_video_options(options)
136
+
137
+ if options.output_json_file is None:
138
+ video_file = options.input_video_file.replace('\\','/')
139
+ if video_file.endswith('/'):
140
+ video_file = video_file[:-1]
141
+ options.output_json_file = video_file + '.json'
142
+ print('Output file not specified, defaulting to {}'.format(
143
+ options.output_json_file))
144
+
145
+ assert options.output_json_file.endswith('.json'), \
146
+ 'Illegal output file {}'.format(options.output_json_file)
147
+
148
+ if options.time_sample is not None:
149
+ every_n_frames_param = -1 * options.time_sample
150
+ else:
151
+ every_n_frames_param = options.frame_sample
152
+
153
+ if options.verbose:
154
+ print('Processing videos from input source {}'.format(options.input_video_file))
155
+
156
+ detector = load_detector(options.model_file,detector_options=options.detector_options)
157
+
158
+ def frame_callback(image_np,image_id):
159
+ return detector.generate_detections_one_image(image_np,
160
+ image_id,
161
+ detection_threshold=options.json_confidence_threshold,
162
+ augment=options.augment,
163
+ image_size=options.image_size,
164
+ verbose=options.verbose)
165
+
166
+ """
167
+ [md_results] will be dict with keys 'video_filenames' (list of str), 'frame_rates' (list of floats),
168
+ 'results' (list of list of dicts). 'video_filenames' will contain *relative* filenames.
169
+ 'results' is a list (one element per video) of lists (one element per frame) of whatever the
170
+ callback returns, typically (but not necessarily) dicts in the MD results format.
171
+
172
+ For failed videos, the frame rate will be represented by -1, and "results"
173
+ will be a dict with at least the key "failure".
174
+ """
175
+ if os.path.isfile(options.input_video_file):
176
+
177
+ video_folder = os.path.dirname(options.input_video_file)
178
+ video_bn = os.path.basename(options.input_video_file)
179
+ md_results = run_callback_on_frames_for_folder(input_video_folder=video_folder,
180
+ frame_callback=frame_callback,
181
+ every_n_frames=every_n_frames_param,
182
+ verbose=options.verbose,
183
+ files_to_process_relative=[video_bn],
184
+ error_on_empty_video=options.exit_on_empty_video)
185
+
186
+ else:
187
+
188
+ assert os.path.isdir(options.input_video_file), \
189
+ '{} is neither a file nor a folder'.format(options.input_video_file)
190
+
191
+ video_folder = options.input_video_file
192
+
193
+ md_results = run_callback_on_frames_for_folder(input_video_folder=options.input_video_file,
194
+ frame_callback=frame_callback,
195
+ every_n_frames=every_n_frames_param,
196
+ verbose=options.verbose,
197
+ recursive=options.recursive,
198
+ error_on_empty_video=options.exit_on_empty_video)
199
+
200
+ # ...whether we're processing a file or a folder
201
+
202
+ print('Finished running MD on videos')
203
+
204
+ video_results = md_results['results']
205
+ video_filenames = md_results['video_filenames']
206
+ video_frame_rates = md_results['frame_rates']
207
+
208
+ assert len(video_results) == len(video_filenames)
209
+ assert len(video_results) == len(video_frame_rates)
210
+
211
+ video_list_md_format = []
212
+
213
+ # i_video = 0; results_this_video = video_results[i_video]
214
+ for i_video,results_this_video in enumerate(video_results):
215
+
216
+ video_fn = video_filenames[i_video]
217
+
218
+ im = {}
219
+ im['file'] = video_fn
220
+ im['frame_rate'] = video_frame_rates[i_video]
221
+ im['frames_processed'] = []
222
+
223
+ if isinstance(results_this_video,dict):
224
+
225
+ assert 'failure' in results_this_video
226
+ im['failure'] = results_this_video['failure']
227
+ im['detections'] = None
228
+
229
+ else:
230
+
231
+ im['detections'] = []
232
+
233
+ # results_one_frame = results_this_video[0]
234
+ for results_one_frame in results_this_video:
235
+
236
+ assert results_one_frame['file'].startswith(video_fn)
237
+
238
+ frame_number = _filename_to_frame_number(results_one_frame['file'])
239
+
240
+ assert frame_number not in im['frames_processed'], \
241
+ 'Received the same frame twice for video {}'.format(im['file'])
242
+
243
+ im['frames_processed'].append(frame_number)
244
+
245
+ for det in results_one_frame['detections']:
246
+ det['frame_number'] = frame_number
247
+
248
+ # This is a no-op if there were no above-threshold detections
249
+ # in this frame
250
+ im['detections'].extend(results_one_frame['detections'])
251
+
252
+ # ...for each frame
253
+
254
+ # ...was this a failed video?
255
+
256
+ im['frames_processed'] = sorted(im['frames_processed'])
257
+
258
+ video_list_md_format.append(im)
259
+
260
+ # ...for each video
261
+
262
+ run_detector_batch.write_results_to_file(
263
+ video_list_md_format,
264
+ options.output_json_file,
265
+ relative_path_base=None,
266
+ detector_file=options.model_file)
267
+
268
+ validation_options = ValidateBatchResultsOptions()
269
+ validation_options.raise_errors = True
270
+ validation_options.check_image_existence = True
271
+ validation_options.return_data = False
272
+ validation_options.relative_path_base = video_folder
273
+ validate_batch_results(options.output_json_file,options=validation_options)
274
+
275
+ # ...process_videos()
276
+
277
+
278
+ def options_to_command(options):
279
+ """
280
+ Convert a ProcessVideoOptions object to a corresponding command line.
281
+
282
+ Args:
283
+ options (ProcessVideoOptions): the options set to render as a command line
284
+
285
+ Returns:
286
+ str: the command line corresponding to [options]
287
+
288
+ :meta private:
289
+ """
290
+
291
+ cmd = 'python process_video.py'
292
+ cmd += ' "' + options.model_file + '"'
293
+ cmd += ' "' + options.input_video_file + '"'
294
+
295
+ if options.recursive:
296
+ cmd += ' --recursive'
297
+ if options.output_json_file is not None:
298
+ cmd += ' --output_json_file' + ' "' + options.output_json_file + '"'
299
+ if options.json_confidence_threshold is not None:
300
+ cmd += ' --json_confidence_threshold ' + str(options.json_confidence_threshold)
301
+ if options.frame_sample is not None:
302
+ cmd += ' --frame_sample ' + str(options.frame_sample)
303
+ if options.verbose:
304
+ cmd += ' --verbose'
305
+ if options.detector_options is not None and len(options.detector_options) > 0:
306
+ cmd += ' --detector_options {}'.format(dict_to_kvp_list(options.detector_options))
307
+
308
+ return cmd
309
+
310
+
311
+ #%% Interactive driver
312
+
313
+ if False:
314
+
315
+ pass
316
+
317
+ #%% Process a folder of videos
318
+
319
+ import os
320
+ from megadetector.detection.process_video import \
321
+ process_videos, ProcessVideoOptions
322
+
323
+ model_file = 'MDV5A'
324
+ input_dir = r"G:\temp\md-test-images\video-samples"
325
+ assert os.path.isdir(input_dir)
326
+
327
+ output_json_file = os.path.join(input_dir,'mdv5a-video.json')
328
+
329
+ print('Processing folder {}'.format(input_dir))
330
+
331
+ options = ProcessVideoOptions()
332
+ options.json_confidence_threshold = 0.05
333
+ options.model_file = model_file
334
+ options.input_video_file = input_dir
335
+ options.output_json_file = output_json_file
336
+ options.recursive = True
337
+ # options.frame_sample = 10
338
+ options.time_sample = 2
339
+ options.verbose = True
340
+
341
+ process_videos(options)
342
+
343
+
344
+ #%% Process a single video
345
+
346
+ import os
347
+ from megadetector.detection.process_video import \
348
+ process_videos, ProcessVideoOptions
349
+ from megadetector.detection.video_utils import find_videos
350
+
351
+ model_file = 'MDV5A'
352
+ input_dir = r"G:\temp\md-test-images\video-samples"
353
+ assert os.path.isdir(input_dir)
354
+ video_fn_abs = find_videos(input_dir)[0]
355
+
356
+ output_json_file = os.path.join(input_dir,'mdv5a-single-video.json')
357
+
358
+ print('Processing video {}'.format(video_fn_abs))
359
+
360
+ options = ProcessVideoOptions()
361
+ options.json_confidence_threshold = 0.05
362
+ options.model_file = model_file
363
+ options.input_video_file = video_fn_abs
364
+ options.output_json_file = output_json_file
365
+ options.recursive = True
366
+ # options.frame_sample = 10
367
+ options.time_sample = 2
368
+ options.verbose = True
369
+
370
+ process_videos(options)
371
+
372
+
373
+ #%% Command-line driver
374
+
375
+ def main(): # noqa
376
+
377
+ default_options = ProcessVideoOptions()
378
+
379
+ parser = argparse.ArgumentParser(description=(
380
+ 'Run MegaDetector on each frame (or every Nth frame) in a video (or folder of videos), optionally '\
381
+ 'producing a new video with detections annotated'))
382
+
383
+ parser.add_argument('model_file', type=str,
384
+ help='MegaDetector model file (.pt or .pb) or model name (e.g. "MDV5A"), '\
385
+ 'or the string "no_detection" to run just frame extraction')
386
+
387
+ parser.add_argument('input_video_file', type=str,
388
+ help='video file (or folder) to process')
389
+
390
+ parser.add_argument('--recursive', action='store_true',
391
+ help='recurse into [input_video_file]; only meaningful if a folder '\
392
+ 'is specified as input')
393
+
394
+ parser.add_argument('--output_json_file', type=str,
395
+ default=None, help='.json output file, defaults to [video file].json')
396
+
397
+ parser.add_argument('--json_confidence_threshold', type=float,
398
+ default=default_options.json_confidence_threshold,
399
+ help="don't include boxes in the .json file with confidence "\
400
+ 'below this threshold (default {})'.format(
401
+ default_options.json_confidence_threshold))
402
+
403
+ parser.add_argument('--frame_sample', type=int,
404
+ default=None, help='process every Nth frame (defaults to every frame), mutually exclusive '\
405
+ 'with --time_sample.')
406
+
407
+ parser.add_argument('--time_sample', type=float,
408
+ default=None, help='process frames every N seconds; this is converted to a '\
409
+ 'frame sampling rate, so it may not be exactly the requested interval in seconds. '\
410
+ 'mutually exclusive with --frame_sample')
411
+
412
+ parser.add_argument('--verbose', action='store_true',
413
+ help='Enable additional debug output')
414
+
415
+ parser.add_argument('--image_size',
416
+ type=int,
417
+ default=None,
418
+ help=('Force image resizing to a specific integer size on the long '\
419
+ 'axis (not recommended to change this)'))
420
+
421
+ parser.add_argument('--augment',
422
+ action='store_true',
423
+ help='Enable image augmentation')
424
+
425
+ parser.add_argument('--exit_on_empty_video',
426
+ action='store_true',
427
+ help=('By default, videos with no retrievable frames are stored as failures; this' \
428
+ 'causes them to halt execution'))
429
+
430
+ parser.add_argument(
431
+ '--detector_options',
432
+ nargs='*',
433
+ metavar='KEY=VALUE',
434
+ default='',
435
+ help='Detector-specific options, as a space-separated list of key-value pairs')
436
+
437
+ parser.add_argument(
438
+ '--checkpoint_frequency',
439
+ type=int,
440
+ default=default_options.checkpoint_frequency,
441
+ help='Write a checkpoint file (to resume processing later) every N videos; ' + \
442
+ 'set to -1 to disable checkpointing (default {})'.format(
443
+ default_options.checkpoint_frequency))
444
+
445
+ parser.add_argument(
446
+ '--checkpoint_path',
447
+ type=str,
448
+ default=None,
449
+ help='Path to checkpoint file; defaults to a file in the same directory ' + \
450
+ 'as the output file')
451
+
452
+ parser.add_argument(
453
+ '--resume_from_checkpoint',
454
+ type=str,
455
+ default=None,
456
+ help='Resume from a specific checkpoint file, or "auto" to resume from the ' + \
457
+ 'most recent checkpoint in the output directory')
458
+
459
+ if len(sys.argv[1:]) == 0:
460
+ parser.print_help()
461
+ parser.exit()
462
+
463
+ args = parser.parse_args()
464
+ options = ProcessVideoOptions()
465
+ args_to_object(args,options)
466
+
467
+ options.detector_options = parse_kvp_list(args.detector_options)
468
+
469
+ if os.path.isdir(options.input_video_file):
470
+ process_videos(options)
471
+ else:
472
+ assert os.path.isfile(options.input_video_file), \
473
+ '{} is not a valid file or folder name'.format(options.input_video_file)
474
+ assert not options.recursive, \
475
+ '--recursive is only meaningful when processing a folder'
476
+ process_videos(options)
477
+
478
+ if __name__ == '__main__':
479
+ main()