megadetector 10.0.2__py3-none-any.whl → 10.0.3__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.

@@ -16,30 +16,16 @@ repeat detection elimination).
16
16
 
17
17
  import os
18
18
  import sys
19
- import tempfile
20
19
  import argparse
21
- import itertools
22
- import json
23
- import shutil
24
- import getpass
25
-
26
- from uuid import uuid1
27
20
 
28
21
  from megadetector.detection import run_detector_batch
29
- from megadetector.visualization import visualize_detector_output
30
22
  from megadetector.utils.ct_utils import args_to_object
31
23
  from megadetector.utils.ct_utils import dict_to_kvp_list, parse_kvp_list
32
- from megadetector.utils.path_utils import insert_before_extension, clean_path
33
- from megadetector.detection.video_utils import video_to_frames
34
- from megadetector.detection.video_utils import run_callback_on_frames
24
+ from megadetector.detection.video_utils import _filename_to_frame_number
35
25
  from megadetector.detection.video_utils import run_callback_on_frames_for_folder
36
- from megadetector.detection.video_utils import frames_to_video
37
- from megadetector.detection.video_utils import frame_results_to_video_results
38
- from megadetector.detection.video_utils import FrameToVideoOptions
39
- from megadetector.detection.video_utils import _add_frame_numbers_to_results
40
- from megadetector.detection.video_utils import video_folder_to_frames
41
- from megadetector.detection.video_utils import default_fourcc
42
26
  from megadetector.detection.run_detector import load_detector
27
+ from megadetector.postprocessing.validate_batch_results import \
28
+ ValidateBatchResultsOptions, validate_batch_results
43
29
 
44
30
 
45
31
  #%% Classes
@@ -65,82 +51,21 @@ class ProcessVideoOptions:
65
51
  #: .json file to which we should write results
66
52
  self.output_json_file = None
67
53
 
68
- #: File to which we should write a video with boxes, only relevant if
69
- #: render_output_video is True
70
- self.output_video_file = None
71
-
72
- #: Folder to use for extracted frames; will use a folder in system temp space
73
- #: if this is None
74
- self.frame_folder = None
75
-
76
- #: Folder to use for rendered frames (if rendering output video); will use a folder
77
- #: in system temp space if this is None
78
- self.frame_rendering_folder = None
79
-
80
- #: Should we render a video with detection boxes?
81
- #:
82
- #: If processing a folder, this renders each input video to a separate
83
- #: video with detection boxes.
84
- self.render_output_video = False
85
-
86
- #: If we are rendering boxes to a new video, should we keep the temporary
87
- #: rendered frames?
88
- self.keep_rendered_frames = False
89
-
90
- #: Should we keep the extracted frames?
91
- self.keep_extracted_frames = False
92
-
93
- #: Should we delete the entire folder the extracted frames are written to?
94
- #:
95
- #: By default, we delete the frame files but leave the (probably-empty) folder in place,
96
- #: for no reason other than being paranoid about deleting folders.
97
- self.force_extracted_frame_folder_deletion = False
98
-
99
- #: Should we delete the entire folder the rendered frames are written to?
100
- #:
101
- #: By default, we delete the frame files but leave the (probably-empty) folder in place,
102
- #: for no reason other than being paranoid about deleting folders.
103
- self.force_rendered_frame_folder_deletion = False
104
-
105
- #: If we've already run MegaDetector on this video or folder of videos, i.e. if we
106
- #: find a corresponding MD results file, should we re-use it? Defaults to reprocessing.
107
- self.reuse_results_if_available = False
108
-
109
- #: If we've already split this video or folder of videos into frames, should we
110
- #: we re-use those extracted frames? Defaults to reprocessing.
111
- self.reuse_frames_if_available = False
112
-
113
54
  #: If [input_video_file] is a folder, should we search for videos recursively?
114
55
  self.recursive = False
115
56
 
116
57
  #: Enable additional debug console output
117
58
  self.verbose = False
118
59
 
119
- #: fourcc code to use for writing videos; only relevant if render_output_video is True
120
- self.fourcc = None
121
-
122
- #: force a specific frame rate for output videos; only relevant if render_output_video
123
- #: is True
124
- self.rendering_fs = None
125
-
126
- #: Confidence threshold to use for writing videos with boxes, only relevant if
127
- #: if render_output_video is True. Defaults to choosing a reasonable threshold
128
- #: based on the model version.
129
- self.rendering_confidence_threshold = None
130
-
131
60
  #: Detections below this threshold will not be included in the output file.
132
61
  self.json_confidence_threshold = 0.005
133
62
 
134
63
  #: Sample every Nth frame; set to None (default) or 1 to sample every frame. Typically
135
64
  #: we sample down to around 3 fps, so for typical 30 fps videos, frame_sample=10 is a
136
- #: typical value. Mutually exclusive with [frames_to_extract] and [time_sample].
65
+ #: typical value. Mutually exclusive with [time_sample].
137
66
  self.frame_sample = None
138
67
 
139
- #: Extract a specific set of frames (list of ints, or a single int). Mutually exclusive with
140
- #: [frame_sample] and [time_sample].
141
- self.frames_to_extract = None
142
-
143
- # Sample frames every N seconds. Mutually exclusive with [frame_sample] and [frames_to_extract].
68
+ #: Sample frames every N seconds. Mutually exclusive with [frame_sample]
144
69
  self.time_sample = None
145
70
 
146
71
  #: Number of workers to use for parallelization; set to <= 1 to disable parallelization
@@ -149,20 +74,10 @@ class ProcessVideoOptions:
149
74
  #: For debugging only, stop processing after a certain number of frames.
150
75
  self.debug_max_frames = -1
151
76
 
152
- #: For debugging only, force on-disk frame extraction, even if it wouldn't otherwise be
153
- #: necessary
154
- self.force_on_disk_frame_extraction = False
155
-
156
77
  #: File containing non-standard categories, typically only used if you're running a non-MD
157
78
  #: detector.
158
79
  self.class_mapping_filename = None
159
80
 
160
- #: JPEG quality for frame output, from 0-100. Use None or -1 to let opencv decide.
161
- self.quality = 90
162
-
163
- #: Resize frames so they're at most this wide
164
- self.max_width = None
165
-
166
81
  #: Run the model at this image size (don't mess with this unless you know what you're
167
82
  #: getting into)... if you just want to pass smaller frames to MD, use max_width
168
83
  self.image_size = None
@@ -171,14 +86,9 @@ class ProcessVideoOptions:
171
86
  self.augment = False
172
87
 
173
88
  #: By default, a video with no frames (or no frames retrievable with the current parameters)
174
- #: is an error, this makes it a warning. This would apply if you request, e.g., the 100th
175
- #: frame from each video, but a video only has 50 frames.
89
+ #: is treated as a failure; this causes it to be treated as a video with no detections.
176
90
  self.allow_empty_videos = False
177
91
 
178
- #: When processing a folder of videos, should we include just a single representative
179
- #: frame result for each video (default), or every frame that was processed?
180
- self.include_all_processed_frames = False
181
-
182
92
  #: Detector-specific options
183
93
  self.detector_options = None
184
94
 
@@ -197,655 +107,150 @@ def _validate_video_options(options):
197
107
  n_sampling_options_configured += 1
198
108
  if options.time_sample is not None:
199
109
  n_sampling_options_configured += 1
200
- if options.frames_to_extract is not None:
201
- n_sampling_options_configured += 1
202
110
 
203
111
  if n_sampling_options_configured > 1:
204
- raise ValueError('frame_sample, time_sample, and frames_to_extract are mutually exclusive')
112
+ raise ValueError('frame_sample and time_sample are mutually exclusive')
205
113
 
206
114
  return True
207
115
 
208
116
 
209
- def _select_temporary_output_folders(options):
210
- """
211
- Choose folders in system temp space for writing temporary frames. Does not create folders,
212
- just defines them.
213
- """
214
-
215
- tempdir = os.path.join(tempfile.gettempdir(), 'process_camera_trap_video')
216
-
217
- # If we create a folder like "process_camera_trap_video" in the system temp dir, it may
218
- # be the case that no one else can write to it, even to create user-specific subfolders.
219
- # If we create a uuid-named folder in the system temp dir, we make a mess.
220
- #
221
- # Compromise with "process_camera_trap_video-[user]".
222
- user_tempdir = tempdir + '-' + getpass.getuser()
223
-
224
- # I don't know whether it's possible for a username to contain characters that are
225
- # not valid filename characters, but just to be sure...
226
- user_tempdir = clean_path(user_tempdir)
227
-
228
- frame_output_folder = os.path.join(
229
- user_tempdir, os.path.basename(options.input_video_file) + '_frames_' + str(uuid1()))
230
-
231
- rendering_output_folder = os.path.join(
232
- tempdir, os.path.basename(options.input_video_file) + '_detections_' + str(uuid1()))
233
-
234
- temporary_folder_info = \
235
- {
236
- 'temp_folder_base':user_tempdir,
237
- 'frame_output_folder':frame_output_folder,
238
- 'rendering_output_folder':rendering_output_folder
239
- }
240
-
241
- return temporary_folder_info
242
-
243
- # ...def _create_frame_output_folders(...)
244
-
245
-
246
- def _clean_up_rendered_frames(options,rendering_output_folder,detected_frame_files):
247
- """
248
- If necessary, delete rendered frames and/or the entire rendering output folder.
249
- """
250
-
251
- if rendering_output_folder is None:
252
- return
253
-
254
- caller_provided_rendering_output_folder = (options.frame_rendering_folder is not None)
255
-
256
- # (Optionally) delete the temporary directory we used for rendered detection images
257
- if not options.keep_rendered_frames:
258
-
259
- try:
260
-
261
- # If (a) we're supposed to delete the temporary rendering folder no
262
- # matter where it is and (b) we created it in temp space, delete the
263
- # whole tree
264
- if options.force_rendered_frame_folder_deletion and \
265
- (not caller_provided_rendering_output_folder):
266
-
267
- if options.verbose:
268
- print('Recursively deleting rendered frame folder {}'.format(
269
- rendering_output_folder))
270
-
271
- shutil.rmtree(rendering_output_folder)
272
-
273
- # ...otherwise just delete the frames, but leave the folder in place
274
- else:
275
-
276
- if options.force_rendered_frame_folder_deletion:
277
- assert caller_provided_rendering_output_folder
278
- print('Warning: force_rendered_frame_folder_deletion supplied with a ' + \
279
- 'user-provided folder, only removing frames')
280
-
281
- for rendered_frame_fn in detected_frame_files:
282
- os.remove(rendered_frame_fn)
283
-
284
- except Exception as e:
285
- print('Warning: error deleting rendered frames from folder {}:\n{}'.format(
286
- rendering_output_folder,str(e)))
287
- pass
288
-
289
- elif options.force_rendered_frame_folder_deletion:
290
-
291
- print('Warning: keep_rendered_frames and force_rendered_frame_folder_deletion both ' + \
292
- 'specified, not deleting')
293
-
294
- # ...def _clean_up_rendered_frames(...)
295
-
296
-
297
- def _clean_up_extracted_frames(options,frame_output_folder,frame_filenames):
298
- """
299
- If necessary, delete extracted frames and/or the entire temporary frame folder.
300
- """
301
-
302
- if frame_output_folder is None:
303
- return
304
-
305
- caller_provided_frame_output_folder = (options.frame_folder is not None)
306
-
307
- if not options.keep_extracted_frames:
308
-
309
- try:
310
-
311
- # If (a) we're supposed to delete the temporary frame folder no
312
- # matter where it is and (b) we created it in temp space, delete the
313
- # whole tree.
314
- if options.force_extracted_frame_folder_deletion and \
315
- (not caller_provided_frame_output_folder):
316
-
317
- if options.verbose:
318
- print('Recursively deleting frame output folder {}'.format(frame_output_folder))
319
-
320
- shutil.rmtree(frame_output_folder)
321
-
322
- # ...otherwise just delete the frames, but leave the folder in place
323
- else:
324
-
325
- if frame_filenames is None:
326
- return
327
-
328
- if options.force_extracted_frame_folder_deletion:
329
- assert caller_provided_frame_output_folder
330
- print('Warning: force_extracted_frame_folder_deletion supplied with a ' + \
331
- 'user-provided folder, only removing frames')
332
-
333
- for extracted_frame_fn in frame_filenames:
334
- os.remove(extracted_frame_fn)
335
-
336
- except Exception as e:
337
- print('Warning: error removing extracted frames from folder {}:\n{}'.format(
338
- frame_output_folder,str(e)))
339
- pass
340
-
341
- elif options.force_extracted_frame_folder_deletion:
342
-
343
- print('Warning: keep_extracted_frames and force_extracted_frame_folder_deletion both ' + \
344
- 'specified, not deleting')
345
-
346
- # ...def _clean_up_extracted_frames
347
-
348
-
349
- def process_video(options):
117
+ def process_videos(options):
350
118
  """
351
- Process a single video through MD, optionally writing a new video with boxes.
352
- Can also be used just to split a video into frames, without running a model.
119
+ Process a video or folder of videos through MD.
353
120
 
354
121
  Args:
355
122
  options (ProcessVideoOptions): all the parameters used to control this process,
356
123
  including filenames; see ProcessVideoOptions for details
357
-
358
- Returns:
359
- dict: frame-level MegaDetector results, identical to what's in the output .json file
360
124
  """
361
125
 
126
+ ## Validate options
127
+
362
128
  # Check for incompatible options
363
129
  _validate_video_options(options)
364
130
 
365
- if options.output_json_file is None:
366
- options.output_json_file = options.input_video_file + '.json'
367
-
368
- if options.render_output_video and (options.output_video_file is None):
369
- options.output_video_file = options.input_video_file + '.detections.mp4'
131
+ assert options.output_json_file.endswith('.json'), \
132
+ 'Illegal output file {}'.format(options.output_json_file)
370
133
 
371
134
  if options.time_sample is not None:
372
- raise ValueError('Time-based sampling is not supported when processing a single video; ' + \
373
- 'consider processing a folder, or using frame_sample')
374
-
375
- if options.model_file == 'no_detection' and not options.keep_extracted_frames:
376
- print('Warning: you asked for no detection, but did not specify keep_extracted_frames, this is a no-op')
377
- return
378
-
379
- # Track whether frame and rendering folders were created by this script
380
- caller_provided_frame_output_folder = (options.frame_folder is not None)
381
- caller_provided_rendering_output_folder = (options.frame_rendering_folder is not None)
382
-
383
- frame_output_folder = None
384
- frame_filenames = None
385
-
386
- # If we should re-use existing results, and the output file exists, don't bother running MD
387
- if (options.reuse_results_if_available and os.path.isfile(options.output_json_file)):
388
-
389
- print('Loading results from {}'.format(options.output_json_file))
390
- with open(options.output_json_file,'r') as f:
391
- results = json.load(f)
392
-
393
- # Run MD in memory if we don't need to generate frames
394
- #
395
- # Currently if we're generating an output video, we need to generate frames on disk first.
396
- elif (not options.keep_extracted_frames and \
397
- not options.render_output_video and \
398
- not options.force_on_disk_frame_extraction):
399
-
400
- # Run MegaDetector in memory
401
-
402
- if options.verbose:
403
- print('Running MegaDetector in memory for {}'.format(options.input_video_file))
404
-
405
- if options.frame_folder is not None:
406
- print('Warning: frame_folder specified, but keep_extracted_frames is ' + \
407
- 'not; no raw frames will be written')
408
-
409
- detector = load_detector(options.model_file,detector_options=options.detector_options)
410
-
411
- def frame_callback(image_np,image_id):
412
- return detector.generate_detections_one_image(image_np,
413
- image_id,
414
- detection_threshold=options.json_confidence_threshold,
415
- augment=options.augment)
416
-
417
- frame_results = run_callback_on_frames(options.input_video_file,
418
- frame_callback,
419
- every_n_frames=options.frame_sample,
420
- verbose=options.verbose,
421
- frames_to_process=options.frames_to_extract)
422
-
423
- frame_results['results'] = _add_frame_numbers_to_results(frame_results['results'])
424
-
425
- run_detector_batch.write_results_to_file(
426
- frame_results['results'],
427
- options.output_json_file,
428
- relative_path_base=None,
429
- detector_file=options.model_file,
430
- custom_metadata={'video_frame_rate':frame_results['frame_rate']})
431
-
432
- # Extract frames and optionally run MegaDetector on those frames
135
+ every_n_frames_param = -1 * options.time_sample
433
136
  else:
137
+ every_n_frames_param = options.frame_sample
434
138
 
435
- if options.verbose:
436
- print('Extracting frames for {}'.format(options.input_video_file))
437
-
438
- # This does not create any folders, just defines temporary folder names in
439
- # case we need them.
440
- temporary_folder_info = _select_temporary_output_folders(options)
441
-
442
- if (caller_provided_frame_output_folder):
443
- frame_output_folder = options.frame_folder
444
- else:
445
- frame_output_folder = temporary_folder_info['frame_output_folder']
446
-
447
- os.makedirs(frame_output_folder, exist_ok=True)
448
-
449
-
450
- ## Extract frames
451
-
452
- frame_filenames, fs = video_to_frames(
453
- options.input_video_file,
454
- frame_output_folder,
455
- every_n_frames=options.frame_sample,
456
- overwrite=(not options.reuse_frames_if_available),
457
- quality=options.quality,
458
- max_width=options.max_width,
459
- verbose=options.verbose,
460
- frames_to_extract=options.frames_to_extract,
461
- allow_empty_videos=options.allow_empty_videos)
462
-
463
- image_file_names = frame_filenames
464
- if options.debug_max_frames > 0:
465
- image_file_names = image_file_names[0:options.debug_max_frames]
466
-
467
- ## Run MegaDetector on those frames
468
-
469
- if options.model_file != 'no_detection':
470
-
471
- if options.verbose:
472
- print('Running MD for {}'.format(options.input_video_file))
473
-
474
- results = run_detector_batch.load_and_run_detector_batch(
475
- options.model_file,
476
- image_file_names,
477
- confidence_threshold=options.json_confidence_threshold,
478
- n_cores=options.n_cores,
479
- class_mapping_filename=options.class_mapping_filename,
480
- quiet=True,
481
- augment=options.augment,
482
- image_size=options.image_size,
483
- detector_options=options.detector_options)
484
-
485
- results = _add_frame_numbers_to_results(results)
486
-
487
- run_detector_batch.write_results_to_file(
488
- results,
489
- options.output_json_file,
490
- relative_path_base=frame_output_folder,
491
- detector_file=options.model_file,
492
- custom_metadata={'video_frame_rate':fs})
493
-
494
- # ...if we are/aren't keeping raw frames on disk
495
-
496
-
497
- ## (Optionally) render output video
498
-
499
- if options.render_output_video:
500
-
501
- ## Render detections to images
502
-
503
- if (caller_provided_rendering_output_folder):
504
- rendering_output_dir = options.frame_rendering_folder
505
- else:
506
- rendering_output_dir = temporary_folder_info['rendering_output_folder']
507
-
508
- os.makedirs(rendering_output_dir,exist_ok=True)
509
-
510
- detected_frame_files = visualize_detector_output.visualize_detector_output(
511
- detector_output_path=options.output_json_file,
512
- out_dir=rendering_output_dir,
513
- images_dir=frame_output_folder,
514
- confidence_threshold=options.rendering_confidence_threshold)
515
-
516
-
517
- ## Choose the frame rate at which we should render the output video
518
-
519
- if options.rendering_fs is not None:
520
- rendering_fs = options.rendering_fs
521
- elif options.frame_sample is None and options.time_sample is None:
522
- rendering_fs = fs
523
- elif options.frame_sample is not None:
524
- assert options.time_sample is None
525
- # If the original video was 30fps and we sampled every 10th frame,
526
- # render at 3fps
527
- rendering_fs = fs / options.frame_sample
528
- elif options.time_sample is not None:
529
- rendering_fs = options.time_sample
530
-
531
-
532
- ## Render the output video
533
-
534
- print('Rendering {} frames to {} at {} fps (original video {} fps)'.format(
535
- len(detected_frame_files), options.output_video_file,rendering_fs,fs))
536
- frames_to_video(detected_frame_files,
537
- rendering_fs,
538
- options.output_video_file,
539
- codec_spec=options.fourcc)
540
-
541
- # Possibly clean up rendered frames
542
- _clean_up_rendered_frames(options,rendering_output_dir,detected_frame_files)
543
-
544
- # ...if we're rendering video
545
-
546
-
547
- ## (Optionally) delete the extracted frames
548
-
549
- _clean_up_extracted_frames(options, frame_output_folder, frame_filenames)
139
+ if options.verbose:
140
+ print('Running MegaDetector for folder {}'.format(options.input_video_file))
550
141
 
551
- # ...process_video()
142
+ detector = load_detector(options.model_file,detector_options=options.detector_options)
552
143
 
144
+ def frame_callback(image_np,image_id):
145
+ return detector.generate_detections_one_image(image_np,
146
+ image_id,
147
+ detection_threshold=options.json_confidence_threshold,
148
+ augment=options.augment)
553
149
 
554
- def process_video_folder(options):
555
150
  """
556
- Process a folder of videos through MD. Can also be used just to split a folder of
557
- videos into frames, without running a model.
558
-
559
- When this function is used to run MD, two .json files will get written, one with
560
- an entry for each *frame* (identical to what's created by process_video()), and
561
- one with an entry for each *video* (which is more suitable for, e.g., reading into
562
- Timelapse).
151
+ [md_results] will be dict with keys 'video_filenames' (list of str), 'frame_rates' (list of floats),
152
+ 'results' (list of list of dicts). 'video_filenames' will contain *relative* filenames.
153
+ 'results' is a list (one element per video) of lists (one element per frame) of whatever the
154
+ callback returns, typically (but not necessarily) dicts in the MD results format.
563
155
 
564
- Args:
565
- options (ProcessVideoOptions): all the parameters used to control this process,
566
- including filenames; see ProcessVideoOptions for details
156
+ For failed videos, the frame rate will be represented by -1, and "results"
157
+ will be a dict with at least the key "failure".
567
158
  """
159
+ if os.path.isfile(options.input_video_file):
568
160
 
569
- ## Validate options
570
-
571
- # Check for incompatible options
572
- _validate_video_options(options)
573
-
574
- assert os.path.isdir(options.input_video_file), \
575
- '{} is not a folder'.format(options.input_video_file)
576
-
577
- if options.model_file == 'no_detection' and not options.keep_extracted_frames:
578
- print('Warning: you asked for no detection, but did not specify keep_extracted_frames, this is a no-op')
579
- return
580
-
581
- if options.model_file != 'no_detection':
582
- assert options.output_json_file is not None, \
583
- 'When processing a folder, you must specify an output .json file'
584
- assert options.output_json_file.endswith('.json')
585
- video_json = options.output_json_file
586
- frames_json = options.output_json_file.replace('.json','.frames.json')
587
- os.makedirs(os.path.dirname(video_json),exist_ok=True)
588
-
589
- # Track whether frame and rendering folders were created by this script
590
- caller_provided_frame_output_folder = (options.frame_folder is not None)
591
- caller_provided_rendering_output_folder = (options.frame_rendering_folder is not None)
592
-
593
- # This does not create any folders, just defines temporary folder names in
594
- # case we need them.
595
- temporary_folder_info = _select_temporary_output_folders(options)
596
-
597
- frame_output_folder = None
598
- image_file_names = None
599
- video_filename_to_fs = {}
161
+ video_folder = os.path.dirname(options.input_video_file)
162
+ video_bn = os.path.basename(options.input_video_file)
163
+ md_results = run_callback_on_frames_for_folder(input_video_folder=video_folder,
164
+ frame_callback=frame_callback,
165
+ every_n_frames=every_n_frames_param,
166
+ verbose=options.verbose,
167
+ files_to_process_relative=[video_bn])
600
168
 
601
- if options.time_sample is not None:
602
- every_n_frames_param = -1 * options.time_sample
603
169
  else:
604
- every_n_frames_param = options.frame_sample
605
-
606
- # Run MD in memory if we don't need to generate frames
607
- #
608
- # Currently if we're generating an output video, we need to generate frames on disk first.
609
- if (not options.keep_extracted_frames and \
610
- not options.render_output_video and \
611
- not options.force_on_disk_frame_extraction):
612
-
613
- if options.verbose:
614
- print('Running MegaDetector in memory for folder {}'.format(options.input_video_file))
615
-
616
- if options.frame_folder is not None:
617
- print('Warning: frame_folder specified, but keep_extracted_frames is ' + \
618
- 'not; no raw frames will be written')
619
-
620
- detector = load_detector(options.model_file,detector_options=options.detector_options)
621
170
 
622
- def frame_callback(image_np,image_id):
623
- return detector.generate_detections_one_image(image_np,
624
- image_id,
625
- detection_threshold=options.json_confidence_threshold,
626
- augment=options.augment)
171
+ assert os.path.isdir(options.input_video_file), \
172
+ '{} is neither a file nor a folder'.format(options.input_video_file)
627
173
 
174
+ video_folder = options.input_video_file
628
175
  md_results = run_callback_on_frames_for_folder(input_video_folder=options.input_video_file,
629
176
  frame_callback=frame_callback,
630
177
  every_n_frames=every_n_frames_param,
631
178
  verbose=options.verbose)
632
179
 
633
- video_results = md_results['results']
180
+ print('Finished running MD on videos')
634
181
 
635
- for i_video,video_filename in enumerate(md_results['video_filenames']):
636
- video_filename = video_filename.replace('\\','/')
637
- assert video_filename not in video_filename_to_fs
638
- video_filename_to_fs[video_filename] = md_results['frame_rates'][i_video]
182
+ video_results = md_results['results']
183
+ video_filenames = md_results['video_filenames']
184
+ video_frame_rates = md_results['frame_rates']
639
185
 
640
- all_frame_results = []
186
+ assert len(video_results) == len(video_filenames)
187
+ assert len(video_results) == len(video_frame_rates)
641
188
 
642
- # r = video_results[0]
643
- for frame_results in video_results:
644
- _add_frame_numbers_to_results(frame_results)
645
- all_frame_results.extend(frame_results)
189
+ video_list_md_format = []
646
190
 
647
- run_detector_batch.write_results_to_file(
648
- all_frame_results,
649
- frames_json,
650
- relative_path_base=None,
651
- detector_file=options.model_file)
191
+ # i_video = 0; results_this_video = video_results[i_video]
192
+ for i_video,results_this_video in enumerate(video_results):
652
193
 
653
- else:
194
+ video_fn = video_filenames[i_video]
654
195
 
655
- ## Split every video into frames
196
+ im = {}
197
+ im['file'] = video_fn
198
+ im['frame_rate'] = video_frame_rates[i_video]
199
+ im['frames_processed'] = []
656
200
 
657
- if options.verbose:
658
- print('Extracting frames for folder {}'.format(options.input_video_file))
201
+ if isinstance(results_this_video,dict):
659
202
 
660
- if caller_provided_frame_output_folder:
661
- frame_output_folder = options.frame_folder
662
- else:
663
- frame_output_folder = temporary_folder_info['frame_output_folder']
664
-
665
- os.makedirs(frame_output_folder, exist_ok=True)
666
-
667
- frame_filenames, fs, video_filenames = \
668
- video_folder_to_frames(input_folder=options.input_video_file,
669
- output_folder_base=frame_output_folder,
670
- recursive=options.recursive,
671
- overwrite=(not options.reuse_frames_if_available),
672
- n_threads=options.n_cores,
673
- every_n_frames=every_n_frames_param,
674
- verbose=options.verbose,
675
- quality=options.quality,
676
- max_width=options.max_width,
677
- frames_to_extract=options.frames_to_extract,
678
- allow_empty_videos=options.allow_empty_videos)
679
-
680
- for i_video,video_filename_abs in enumerate(video_filenames):
681
- video_filename_relative = os.path.relpath(video_filename_abs,options.input_video_file)
682
- video_filename_relative = video_filename_relative.replace('\\','/')
683
- assert video_filename_relative not in video_filename_to_fs
684
- video_filename_to_fs[video_filename_relative] = fs[i_video]
685
-
686
- print('Extracted frames for {} videos'.format(len(set(video_filenames))))
687
- image_file_names = list(itertools.chain.from_iterable(frame_filenames))
688
-
689
- if len(image_file_names) == 0:
690
- if len(video_filenames) == 0:
691
- print('No videos found in folder {}'.format(options.input_video_file))
692
- else:
693
- print('No frames extracted from folder {}, this may be due to an '\
694
- 'unsupported video codec'.format(options.input_video_file))
695
- return
696
-
697
- if options.debug_max_frames is not None and options.debug_max_frames > 0:
698
- image_file_names = image_file_names[0:options.debug_max_frames]
699
-
700
- if options.model_file == 'no_detection':
701
- assert options.keep_extracted_frames, \
702
- 'Internal error: keep_extracted_frames not set, but no model specified'
703
- return
704
-
705
-
706
- ## Run MegaDetector on the extracted frames
707
-
708
- if options.reuse_results_if_available and \
709
- os.path.isfile(frames_json):
710
-
711
- print('Bypassing inference, loading results from {}'.format(frames_json))
712
- with open(frames_json,'r') as f:
713
- results = json.load(f)
203
+ assert 'failure' in results_this_video
204
+ im['failure'] = results_this_video['failure']
205
+ im['detections'] = None
714
206
 
715
207
  else:
716
208
 
717
- print('Running MegaDetector')
718
-
719
- results = run_detector_batch.load_and_run_detector_batch(
720
- options.model_file,
721
- image_file_names,
722
- confidence_threshold=options.json_confidence_threshold,
723
- n_cores=options.n_cores,
724
- class_mapping_filename=options.class_mapping_filename,
725
- quiet=True,
726
- augment=options.augment,
727
- image_size=options.image_size,
728
- detector_options=options.detector_options)
729
-
730
- _add_frame_numbers_to_results(results)
731
-
732
- run_detector_batch.write_results_to_file(
733
- results,
734
- frames_json,
735
- relative_path_base=frame_output_folder,
736
- detector_file=options.model_file)
737
-
738
- # ...if we're re-using existing results / running MD
739
-
740
- # ...if we're running MD on in-memory frames vs. extracting frames to disk
209
+ im['detections'] = []
741
210
 
742
- ## Convert frame-level results to video-level results
211
+ # results_one_frame = results_this_video[0]
212
+ for results_one_frame in results_this_video:
743
213
 
744
- frame_to_video_options = FrameToVideoOptions()
745
- frame_to_video_options.include_all_processed_frames = options.include_all_processed_frames
214
+ assert results_one_frame['file'].startswith(video_fn)
746
215
 
747
- print('Converting frame-level results to video-level results')
748
- frame_results_to_video_results(frames_json,
749
- video_json,
750
- options=frame_to_video_options,
751
- video_filename_to_frame_rate=video_filename_to_fs)
752
-
753
-
754
- ## (Optionally) render output videos
755
-
756
- if options.render_output_video:
757
-
758
- # Render detections to images
759
- if (caller_provided_rendering_output_folder):
760
- rendering_output_dir = options.frame_rendering_folder
761
- else:
762
- rendering_output_dir = temporary_folder_info['rendering_output_folder']
763
-
764
- os.makedirs(rendering_output_dir,exist_ok=True)
765
-
766
- detected_frame_files = visualize_detector_output.visualize_detector_output(
767
- detector_output_path=frames_json,
768
- out_dir=rendering_output_dir,
769
- images_dir=frame_output_folder,
770
- confidence_threshold=options.rendering_confidence_threshold,
771
- preserve_path_structure=True,
772
- output_image_width=-1)
773
- detected_frame_files = [s.replace('\\','/') for s in detected_frame_files]
774
-
775
- # Choose an output folder
776
- output_folder_is_input_folder = False
777
- if options.output_video_file is not None:
778
- if os.path.isfile(options.output_video_file):
779
- raise ValueError('Rendering videos for a folder, but an existing file was specified as output')
780
- elif options.output_video_file == options.input_video_file:
781
- output_folder_is_input_folder = True
782
- output_video_folder = options.input_video_file
783
- else:
784
- os.makedirs(options.output_video_file,exist_ok=True)
785
- output_video_folder = options.output_video_file
786
- else:
787
- output_folder_is_input_folder = True
788
- output_video_folder = options.input_video_file
216
+ frame_number = _filename_to_frame_number(results_one_frame['file'])
789
217
 
790
- # For each video
791
- #
792
- # TODO: parallelize this loop
793
- #
794
- # i_video=0; input_video_file_abs = video_filenames[i_video]
795
- for i_video,input_video_file_abs in enumerate(video_filenames):
218
+ assert frame_number not in im['frames_processed'], \
219
+ 'Received the same frame twice for video {}'.format(im['file'])
796
220
 
797
- video_fs = fs[i_video]
221
+ im['frames_processed'].append(frame_number)
798
222
 
799
- if options.rendering_fs is not None:
800
- rendering_fs = options.rendering_fs
801
- elif options.frame_sample is None:
802
- rendering_fs = video_fs
803
- else:
804
- # If the original video was 30fps and we sampled every 10th frame,
805
- # render at 3fps
806
- rendering_fs = video_fs / options.frame_sample
223
+ for det in results_one_frame['detections']:
224
+ det['frame_number'] = frame_number
807
225
 
808
- input_video_file_relative = os.path.relpath(input_video_file_abs,options.input_video_file)
809
- video_frame_output_folder = os.path.join(rendering_output_dir,input_video_file_relative)
226
+ # This is a no-op if there were no above-threshold detections
227
+ # in this frame
228
+ im['detections'].extend(results_one_frame['detections'])
810
229
 
811
- video_frame_output_folder = video_frame_output_folder.replace('\\','/')
812
- assert os.path.isdir(video_frame_output_folder), \
813
- 'Could not find frame folder for video {}'.format(input_video_file_relative)
230
+ # ...for each frame
814
231
 
815
- # Find the corresponding rendered frame folder
816
- video_frame_files = [fn for fn in detected_frame_files if \
817
- fn.startswith(video_frame_output_folder)]
818
- assert len(video_frame_files) > 0, 'Could not find rendered frames for video {}'.format(
819
- input_video_file_relative)
232
+ # ...was this a failed video?
820
233
 
821
- # Select the output filename for the rendered video
822
- if output_folder_is_input_folder:
823
- video_output_file = insert_before_extension(input_video_file_abs,'annotated','_')
824
- else:
825
- video_output_file = os.path.join(output_video_folder,input_video_file_relative)
234
+ video_list_md_format.append(im)
826
235
 
827
- os.makedirs(os.path.dirname(video_output_file),exist_ok=True)
236
+ # ...for each video
828
237
 
829
- # Create the output video
830
- print('Rendering detections for video {} to {} at {} fps (original video {} fps)'.format(
831
- input_video_file_relative,video_output_file,rendering_fs,video_fs))
832
- frames_to_video(video_frame_files,
833
- rendering_fs,
834
- video_output_file,
835
- codec_spec=options.fourcc)
238
+ im['frames_processed'] = sorted(im['frames_processed'])
836
239
 
837
- # ...for each video
240
+ run_detector_batch.write_results_to_file(
241
+ video_list_md_format,
242
+ options.output_json_file,
243
+ relative_path_base=None,
244
+ detector_file=options.model_file)
838
245
 
839
- # Possibly clean up rendered frames
840
- _clean_up_rendered_frames(options,rendering_output_dir,detected_frame_files)
246
+ validation_options = ValidateBatchResultsOptions()
247
+ validation_options.raise_errors = True
248
+ validation_options.check_image_existence = True
249
+ validation_options.return_data = False
250
+ validation_options.relative_path_base = video_folder
251
+ validate_batch_results(options.output_json_file,options=validation_options)
841
252
 
842
- # ...if we're rendering video
843
-
844
-
845
- ## (Optionally) delete the extracted frames
846
- _clean_up_extracted_frames(options, frame_output_folder, image_file_names)
847
-
848
- # ...process_video_folder()
253
+ # ...process_videos()
849
254
 
850
255
 
851
256
  def options_to_command(options):
@@ -860,62 +265,27 @@ def options_to_command(options):
860
265
 
861
266
  :meta private:
862
267
  """
268
+
863
269
  cmd = 'python process_video.py'
864
270
  cmd += ' "' + options.model_file + '"'
865
271
  cmd += ' "' + options.input_video_file + '"'
866
272
 
867
273
  if options.recursive:
868
274
  cmd += ' --recursive'
869
- if options.frame_folder is not None:
870
- cmd += ' --frame_folder' + ' "' + options.frame_folder + '"'
871
- if options.frame_rendering_folder is not None:
872
- cmd += ' --frame_rendering_folder' + ' "' + options.frame_rendering_folder + '"'
873
275
  if options.output_json_file is not None:
874
276
  cmd += ' --output_json_file' + ' "' + options.output_json_file + '"'
875
- if options.output_video_file is not None:
876
- cmd += ' --output_video_file' + ' "' + options.output_video_file + '"'
877
- if options.keep_extracted_frames:
878
- cmd += ' --keep_extracted_frames'
879
- if options.reuse_results_if_available:
880
- cmd += ' --reuse_results_if_available'
881
- if options.reuse_frames_if_available:
882
- cmd += ' --reuse_frames_if_available'
883
- if options.render_output_video:
884
- cmd += ' --render_output_video'
885
- if options.keep_rendered_frames:
886
- cmd += ' --keep_rendered_frames'
887
- if options.rendering_confidence_threshold is not None:
888
- cmd += ' --rendering_confidence_threshold ' + str(options.rendering_confidence_threshold)
889
277
  if options.json_confidence_threshold is not None:
890
278
  cmd += ' --json_confidence_threshold ' + str(options.json_confidence_threshold)
891
279
  if options.n_cores is not None:
892
280
  cmd += ' --n_cores ' + str(options.n_cores)
893
281
  if options.frame_sample is not None:
894
282
  cmd += ' --frame_sample ' + str(options.frame_sample)
895
- if options.frames_to_extract is not None:
896
- cmd += ' --frames_to_extract '
897
- if isinstance(options.frames_to_extract,int):
898
- frames_to_extract = [options.frames_to_extract]
899
- else:
900
- frames_to_extract = options.frames_to_extract
901
- for frame_number in frames_to_extract:
902
- cmd += ' {}'.format(frame_number)
903
283
  if options.debug_max_frames is not None:
904
284
  cmd += ' --debug_max_frames ' + str(options.debug_max_frames)
905
285
  if options.class_mapping_filename is not None:
906
286
  cmd += ' --class_mapping_filename ' + str(options.class_mapping_filename)
907
- if options.fourcc is not None:
908
- cmd += ' --fourcc ' + options.fourcc
909
- if options.quality is not None:
910
- cmd += ' --quality ' + str(options.quality)
911
- if options.max_width is not None:
912
- cmd += ' --max_width ' + str(options.max_width)
913
287
  if options.verbose:
914
288
  cmd += ' --verbose'
915
- if options.force_extracted_frame_folder_deletion:
916
- cmd += ' --force_extracted_frame_folder_deletion'
917
- if options.force_rendered_frame_folder_deletion:
918
- cmd += ' --force_rendered_frame_folder_deletion'
919
289
  if options.detector_options is not None and len(options.detector_options) > 0:
920
290
  cmd += '--detector_options {}'.format(dict_to_kvp_list(options.detector_options))
921
291
 
@@ -930,145 +300,58 @@ if False:
930
300
 
931
301
  #%% Process a folder of videos
932
302
 
303
+ import os
304
+ from megadetector.detection.process_video import \
305
+ process_videos, ProcessVideoOptions
306
+
933
307
  model_file = 'MDV5A'
934
- # input_dir = r'g:\temp\test-videos'
935
- # input_dir = r'G:\temp\md-test-package\md-test-images\video-samples'
936
- input_dir = os.path.expanduser('~/AppData/Local/Temp/md-tests/md-test-images/video-samples')
308
+ input_dir = r"G:\temp\md-test-images\video-samples"
937
309
  assert os.path.isdir(input_dir)
938
310
 
939
- output_base = r'g:\temp\video_test'
940
- os.makedirs(output_base,exist_ok=True)
941
-
942
- frame_folder = os.path.join(output_base,'frames')
943
- rendering_folder = os.path.join(output_base,'rendered-frames')
944
- output_json_file = os.path.join(output_base,'video-test.json')
945
- output_video_folder = os.path.join(output_base,'output_videos')
946
-
311
+ output_json_file = os.path.join(input_dir,'mdv5a-video.json')
947
312
 
948
313
  print('Processing folder {}'.format(input_dir))
949
314
 
950
315
  options = ProcessVideoOptions()
316
+ options.json_confidence_threshold = 0.05
951
317
  options.model_file = model_file
952
318
  options.input_video_file = input_dir
953
- options.output_video_file = output_video_folder
954
319
  options.output_json_file = output_json_file
955
320
  options.recursive = True
956
- options.reuse_frames_if_available = False
957
- options.reuse_results_if_available = False
958
- options.quality = None # 90
959
- options.frame_sample = 10
960
- options.max_width = None # 1280
961
- options.n_cores = 4
321
+ # options.frame_sample = 10
322
+ options.time_sample = 2
962
323
  options.verbose = True
963
- options.render_output_video = False
964
- options.frame_folder = frame_folder
965
- options.frame_rendering_folder = rendering_folder
966
- options.keep_extracted_frames = False
967
- options.keep_rendered_frames = False
968
- options.force_extracted_frame_folder_deletion = False
969
- options.force_rendered_frame_folder_deletion = False
970
- options.fourcc = 'mp4v'
971
- options.force_on_disk_frame_extraction = False
972
- # options.rendering_confidence_threshold = 0.15
973
-
974
- cmd = options_to_command(options); print(cmd)
975
324
 
976
- # import clipboard; clipboard.copy(cmd)
977
- process_video_folder(options)
325
+ process_videos(options)
978
326
 
979
327
 
980
328
  #%% Process a single video
981
329
 
982
- fn = r'g:\temp\test-videos\person_and_dog\DSCF0056.AVI'
983
- assert os.path.isfile(fn)
984
- model_file = 'MDV5A'
985
- input_video_file = fn
986
-
987
- output_base = r'g:\temp\video_test'
988
- frame_folder = os.path.join(output_base,'frames')
989
- rendering_folder = os.path.join(output_base,'rendered-frames')
990
- output_json_file = os.path.join(output_base,'video-test.json')
991
- output_video_file = os.path.join(output_base,'output_video.mp4')
992
-
993
- options = ProcessVideoOptions()
994
- options.model_file = model_file
995
- options.input_video_file = input_video_file
996
- options.render_output_video = True
997
- options.output_video_file = output_video_file
998
- options.output_json_file = output_json_file
999
- options.verbose = True
1000
- options.quality = 75
1001
- options.frame_sample = 10
1002
- options.max_width = 1600
1003
- options.frame_folder = frame_folder
1004
- options.frame_rendering_folder = rendering_folder
1005
- options.keep_extracted_frames = False
1006
- options.keep_rendered_frames = False
1007
- options.force_extracted_frame_folder_deletion = True
1008
- options.force_rendered_frame_folder_deletion = True
1009
- options.fourcc = 'mp4v'
1010
- # options.rendering_confidence_threshold = 0.15
1011
-
1012
- cmd = options_to_command(options); print(cmd)
1013
-
1014
- # import clipboard; clipboard.copy(cmd)
1015
- process_video(options)
1016
-
1017
-
1018
- #%% Extract specific frames from a single video, no detection
1019
-
1020
- fn = r'g:\temp\test-videos\person_and_dog\DSCF0064.AVI'
1021
- assert os.path.isfile(fn)
1022
- model_file = 'no_detection'
1023
- input_video_file = fn
1024
-
1025
- output_base = r'g:\temp\video_test'
1026
- frame_folder = os.path.join(output_base,'frames')
1027
- output_video_file = os.path.join(output_base,'output_videos.mp4')
1028
-
1029
- options = ProcessVideoOptions()
1030
- options.model_file = model_file
1031
- options.input_video_file = input_video_file
1032
- options.verbose = True
1033
- options.quality = 90
1034
- options.frame_sample = None
1035
- options.frames_to_extract = [0,100]
1036
- options.max_width = None
1037
- options.frame_folder = frame_folder
1038
- options.keep_extracted_frames = True
1039
-
1040
- cmd = options_to_command(options); print(cmd)
330
+ import os
331
+ from megadetector.detection.process_video import \
332
+ process_videos, ProcessVideoOptions
333
+ from megadetector.detection.video_utils import find_videos
1041
334
 
1042
- # import clipboard; clipboard.copy(cmd)
1043
- process_video(options)
1044
-
1045
-
1046
- #%% Extract specific frames from a folder, no detection
335
+ model_file = 'MDV5A'
336
+ input_dir = r"G:\temp\md-test-images\video-samples"
337
+ assert os.path.isdir(input_dir)
338
+ video_fn_abs = find_videos(input_dir)[0]
1047
339
 
1048
- fn = r'g:\temp\test-videos\person_and_dog'
1049
- assert os.path.isdir(fn)
1050
- model_file = 'no_detection'
1051
- input_video_file = fn
340
+ output_json_file = os.path.join(input_dir,'mdv5a-single-video.json')
1052
341
 
1053
- output_base = r'g:\temp\video_test'
1054
- frame_folder = os.path.join(output_base,'frames')
1055
- output_video_file = os.path.join(output_base,'output_videos.mp4')
342
+ print('Processing video {}'.format(video_fn_abs))
1056
343
 
1057
344
  options = ProcessVideoOptions()
345
+ options.json_confidence_threshold = 0.05
1058
346
  options.model_file = model_file
1059
- options.input_video_file = input_video_file
347
+ options.input_video_file = video_fn_abs
348
+ options.output_json_file = output_json_file
349
+ options.recursive = True
350
+ # options.frame_sample = 10
351
+ options.time_sample = 2
1060
352
  options.verbose = True
1061
- options.quality = 90
1062
- options.frame_sample = None
1063
- options.frames_to_extract = [0,100]
1064
- options.max_width = None
1065
- options.frame_folder = frame_folder
1066
- options.keep_extracted_frames = True
1067
-
1068
- cmd = options_to_command(options); print(cmd)
1069
353
 
1070
- # import clipboard; clipboard.copy(cmd)
1071
- process_video(options)
354
+ process_videos(options)
1072
355
 
1073
356
 
1074
357
  #%% Command-line driver
@@ -1092,65 +375,9 @@ def main(): # noqa
1092
375
  help='recurse into [input_video_file]; only meaningful if a folder '\
1093
376
  'is specified as input')
1094
377
 
1095
- parser.add_argument('--frame_folder', type=str, default=None,
1096
- help='folder to use for intermediate frame storage, defaults to a folder '\
1097
- 'in the system temporary folder')
1098
-
1099
- parser.add_argument('--frame_rendering_folder', type=str, default=None,
1100
- help='folder to use for rendered frame storage, defaults to a folder in '\
1101
- 'the system temporary folder')
1102
-
1103
378
  parser.add_argument('--output_json_file', type=str,
1104
379
  default=None, help='.json output file, defaults to [video file].json')
1105
380
 
1106
- parser.add_argument('--output_video_file', type=str,
1107
- default=None, help='video output file (or folder), defaults to '\
1108
- '[video file].mp4 for files, or [video file]_annotated for folders')
1109
-
1110
- parser.add_argument('--keep_extracted_frames',
1111
- action='store_true', help='Disable the deletion of extracted frames')
1112
-
1113
- parser.add_argument('--reuse_frames_if_available',
1114
- action='store_true',
1115
- help="Don't extract frames that are already available in the frame extraction folder")
1116
-
1117
- parser.add_argument('--reuse_results_if_available',
1118
- action='store_true',
1119
- help='If the output .json files exists, and this flag is set,'\
1120
- 'we\'ll skip running MegaDetector')
1121
-
1122
- parser.add_argument('--render_output_video', action='store_true',
1123
- help='enable video output rendering (not rendered by default)')
1124
-
1125
- parser.add_argument('--fourcc', default=default_fourcc,
1126
- help=f'fourcc code to use for video encoding (default {default_fourcc}), ' + \
1127
- 'only used if render_output_video is True')
1128
-
1129
- parser.add_argument('--keep_rendered_frames',
1130
- action='store_true', help='Disable the deletion of rendered (w/boxes) frames')
1131
-
1132
- parser.add_argument('--force_extracted_frame_folder_deletion',
1133
- action='store_true', help='By default, when keep_extracted_frames is False, we '\
1134
- 'delete the frames, but leave the (probably-empty) folder in place. This option '\
1135
- 'forces deletion of the folder as well. Use at your own risk; does not check '\
1136
- 'whether other files were present in the folder.')
1137
-
1138
- parser.add_argument('--force_rendered_frame_folder_deletion',
1139
- action='store_true', help='By default, when keep_rendered_frames is False, we '\
1140
- 'delete the frames, but leave the (probably-empty) folder in place. This option '\
1141
- 'forces deletion of the folder as well. Use at your own risk; does not check '\
1142
- 'whether other files were present in the folder.')
1143
-
1144
- parser.add_argument('--rendering_confidence_threshold', type=float,
1145
- default=None,
1146
- help="don't render boxes with confidence below this threshold " + \
1147
- "(defaults to choosing based on the MD version)")
1148
-
1149
- parser.add_argument('--rendering_fs', type=float,
1150
- default=None,
1151
- help='force a specific frame rate for output videos (only relevant when using '\
1152
- '--render_output_video) (defaults to the original frame rate)')
1153
-
1154
381
  parser.add_argument('--json_confidence_threshold', type=float,
1155
382
  default=default_options.json_confidence_threshold,
1156
383
  help="don't include boxes in the .json file with confidence "\
@@ -1166,26 +393,12 @@ def main(): # noqa
1166
393
 
1167
394
  parser.add_argument('--frame_sample', type=int,
1168
395
  default=None, help='process every Nth frame (defaults to every frame), mutually exclusive '\
1169
- 'with --frames_to_extract and --time_sample.')
1170
-
1171
- parser.add_argument('--frames_to_extract', nargs='+', type=int,
1172
- default=None, help='extract specific frames (one or more ints), mutually exclusive '\
1173
- 'with --frame_sample and --time_sample.')
396
+ 'with --time_sample.')
1174
397
 
1175
398
  parser.add_argument('--time_sample', type=float,
1176
399
  default=None, help='process frames every N seconds; this is converted to a '\
1177
400
  'frame sampling rate, so it may not be exactly the requested interval in seconds. '\
1178
- 'mutually exclusive with --frame_sample and --frames_to_extract.')
1179
-
1180
- parser.add_argument('--quality', type=int,
1181
- default=default_options.quality,
1182
- help=f'JPEG quality for extracted frames (defaults to {default_options.quality}), ' + \
1183
- 'use -1 to force no quality setting')
1184
-
1185
- parser.add_argument('--max_width', type=int,
1186
- default=default_options.max_width,
1187
- help='Resize frames larger than this before writing (defaults to {})'.format(
1188
- default_options.max_width))
401
+ 'mutually exclusive with --frame_sample')
1189
402
 
1190
403
  parser.add_argument('--debug_max_frames', type=int,
1191
404
  default=-1, help='Trim to N frames for debugging (impacts model execution, '\
@@ -1211,12 +424,6 @@ def main(): # noqa
1211
424
  action='store_true',
1212
425
  help='Enable image augmentation')
1213
426
 
1214
- parser.add_argument('--include_all_processed_frames',
1215
- action='store_true',
1216
- help='When processing a folder of videos, this flag indicates that the output '\
1217
- 'should include results for every frame that was processed, rather than just '\
1218
- 'one representative frame for each detection category per video.')
1219
-
1220
427
  parser.add_argument('--allow_empty_videos',
1221
428
  action='store_true',
1222
429
  help='By default, videos with no retrievable frames cause an error, this makes it a warning')
@@ -1239,13 +446,13 @@ def main(): # noqa
1239
446
  options.detector_options = parse_kvp_list(args.detector_options)
1240
447
 
1241
448
  if os.path.isdir(options.input_video_file):
1242
- process_video_folder(options)
449
+ process_videos(options)
1243
450
  else:
1244
451
  assert os.path.isfile(options.input_video_file), \
1245
452
  '{} is not a valid file or folder name'.format(options.input_video_file)
1246
453
  assert not options.recursive, \
1247
454
  '--recursive is only meaningful when processing a folder'
1248
- process_video(options)
455
+ process_videos(options)
1249
456
 
1250
457
  if __name__ == '__main__':
1251
458
  main()