megadetector 5.0.13__py3-none-any.whl → 5.0.14__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.
- megadetector/data_management/read_exif.py +11 -5
- megadetector/detection/process_video.py +228 -68
- megadetector/detection/pytorch_detector.py +16 -11
- megadetector/detection/run_detector.py +17 -5
- megadetector/detection/run_detector_batch.py +179 -65
- megadetector/detection/tf_detector.py +11 -3
- megadetector/detection/video_utils.py +174 -43
- megadetector/postprocessing/convert_output_format.py +12 -5
- megadetector/utils/md_tests.py +80 -24
- megadetector/utils/path_utils.py +38 -6
- megadetector/utils/process_utils.py +8 -2
- megadetector/visualization/visualization_utils.py +7 -2
- megadetector/visualization/visualize_detector_output.py +0 -1
- {megadetector-5.0.13.dist-info → megadetector-5.0.14.dist-info}/METADATA +1 -1
- {megadetector-5.0.13.dist-info → megadetector-5.0.14.dist-info}/RECORD +18 -18
- {megadetector-5.0.13.dist-info → megadetector-5.0.14.dist-info}/LICENSE +0 -0
- {megadetector-5.0.13.dist-info → megadetector-5.0.14.dist-info}/WHEEL +0 -0
- {megadetector-5.0.13.dist-info → megadetector-5.0.14.dist-info}/top_level.txt +0 -0
|
@@ -9,6 +9,7 @@ Utilities for splitting, rendering, and assembling videos.
|
|
|
9
9
|
#%% Constants, imports, environment
|
|
10
10
|
|
|
11
11
|
import os
|
|
12
|
+
import re
|
|
12
13
|
import cv2
|
|
13
14
|
import glob
|
|
14
15
|
import json
|
|
@@ -98,7 +99,7 @@ def find_videos(dirname,
|
|
|
98
99
|
return find_video_strings(files)
|
|
99
100
|
|
|
100
101
|
|
|
101
|
-
#%%
|
|
102
|
+
#%% Functions for rendering frames to video and vice-versa
|
|
102
103
|
|
|
103
104
|
# http://tsaith.github.io/combine-images-into-a-video-with-python-3-and-opencv-3.html
|
|
104
105
|
|
|
@@ -169,9 +170,55 @@ def _frame_number_to_filename(frame_number):
|
|
|
169
170
|
return 'frame{:06d}.jpg'.format(frame_number)
|
|
170
171
|
|
|
171
172
|
|
|
172
|
-
def
|
|
173
|
-
|
|
174
|
-
|
|
173
|
+
def _filename_to_frame_number(filename):
|
|
174
|
+
"""
|
|
175
|
+
Extract the frame number from a filename that was created using
|
|
176
|
+
_frame_number_to_filename.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
filename (str): a filename created with _frame_number_to_filename.
|
|
180
|
+
Returns:
|
|
181
|
+
int: the frame number extracted from [filename]
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
filename = os.path.basename(filename)
|
|
185
|
+
match = re.search(r'frame(\d+)\.jpg', filename)
|
|
186
|
+
if match is None:
|
|
187
|
+
raise ValueError('{} does not appear to be a frame file'.format(filename))
|
|
188
|
+
frame_number = match.group(1)
|
|
189
|
+
try:
|
|
190
|
+
frame_number = int(frame_number)
|
|
191
|
+
except:
|
|
192
|
+
raise ValueError('Filename {} does contain a valid frame number'.format(filename))
|
|
193
|
+
|
|
194
|
+
return frame_number
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _add_frame_numbers_to_results(results):
|
|
198
|
+
"""
|
|
199
|
+
Given the 'images' list from a set of MD results that was generated on video frames,
|
|
200
|
+
add a 'frame_number' field to each image.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
results (list): list of image dicts
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
# Add video-specific fields to the results
|
|
207
|
+
for im in results:
|
|
208
|
+
fn = im['file']
|
|
209
|
+
frame_number = _filename_to_frame_number(fn)
|
|
210
|
+
im['frame_number'] = frame_number
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def video_to_frames(input_video_file,
|
|
214
|
+
output_folder,
|
|
215
|
+
overwrite=True,
|
|
216
|
+
every_n_frames=None,
|
|
217
|
+
verbose=False,
|
|
218
|
+
quality=None,
|
|
219
|
+
max_width=None,
|
|
220
|
+
frames_to_extract=None,
|
|
221
|
+
allow_empty_videos=False):
|
|
175
222
|
"""
|
|
176
223
|
Renders frames from [input_video_file] to a .jpg in [output_folder].
|
|
177
224
|
|
|
@@ -184,11 +231,18 @@ def video_to_frames(input_video_file, output_folder, overwrite=True,
|
|
|
184
231
|
output_folder (str): folder to put frame images in
|
|
185
232
|
overwrite (bool, optional): whether to overwrite existing frame images
|
|
186
233
|
every_n_frames (int, optional): sample every Nth frame starting from the first frame;
|
|
187
|
-
if this is None or 1, every frame is extracted
|
|
234
|
+
if this is None or 1, every frame is extracted. Mutually exclusive with
|
|
235
|
+
frames_to_extract.
|
|
188
236
|
verbose (bool, optional): enable additional debug console output
|
|
189
237
|
quality (int, optional): JPEG quality for frame output, from 0-100. Defaults
|
|
190
238
|
to the opencv default (typically 95).
|
|
191
239
|
max_width (int, optional): resize frames to be no wider than [max_width]
|
|
240
|
+
frames_to_extract (list of int, optional): extract this specific set of frames;
|
|
241
|
+
mutually exclusive with every_n_frames. If all values are beyond the length
|
|
242
|
+
of the video, no frames are extracted. Can also be a single int, specifying
|
|
243
|
+
a single frame number.
|
|
244
|
+
allow_empty_videos (bool, optional): Just print a warning if a video appears to have no
|
|
245
|
+
frames (by default, this is an error).
|
|
192
246
|
|
|
193
247
|
Returns:
|
|
194
248
|
tuple: length-2 tuple containing (list of frame filenames,frame rate)
|
|
@@ -196,6 +250,14 @@ def video_to_frames(input_video_file, output_folder, overwrite=True,
|
|
|
196
250
|
|
|
197
251
|
assert os.path.isfile(input_video_file), 'File {} not found'.format(input_video_file)
|
|
198
252
|
|
|
253
|
+
if isinstance(frames_to_extract,int):
|
|
254
|
+
frames_to_extract = [frames_to_extract]
|
|
255
|
+
|
|
256
|
+
if (frames_to_extract is not None) and (every_n_frames is not None):
|
|
257
|
+
raise ValueError('frames_to_extract and every_n_frames are mutually exclusive')
|
|
258
|
+
|
|
259
|
+
os.makedirs(output_folder,exist_ok=True)
|
|
260
|
+
|
|
199
261
|
vidcap = cv2.VideoCapture(input_video_file)
|
|
200
262
|
n_frames = int(vidcap.get(cv2.CAP_PROP_FRAME_COUNT))
|
|
201
263
|
Fs = vidcap.get(cv2.CAP_PROP_FPS)
|
|
@@ -211,9 +273,17 @@ def video_to_frames(input_video_file, output_folder, overwrite=True,
|
|
|
211
273
|
for frame_number in range(0,n_frames):
|
|
212
274
|
|
|
213
275
|
if every_n_frames is not None:
|
|
276
|
+
assert frames_to_extract is None, \
|
|
277
|
+
'Internal error: frames_to_extract and every_n_frames are exclusive'
|
|
214
278
|
if (frame_number % every_n_frames) != 0:
|
|
215
279
|
continue
|
|
216
280
|
|
|
281
|
+
if frames_to_extract is not None:
|
|
282
|
+
assert every_n_frames is None, \
|
|
283
|
+
'Internal error: frames_to_extract and every_n_frames are exclusive'
|
|
284
|
+
if frame_number not in frames_to_extract:
|
|
285
|
+
continue
|
|
286
|
+
|
|
217
287
|
frame_filename = _frame_number_to_filename(frame_number)
|
|
218
288
|
frame_filename = os.path.join(output_folder,frame_filename)
|
|
219
289
|
frame_filenames.append(frame_filename)
|
|
@@ -240,15 +310,23 @@ def video_to_frames(input_video_file, output_folder, overwrite=True,
|
|
|
240
310
|
last_expected_frame_number = n_frames-1
|
|
241
311
|
if every_n_frames is not None:
|
|
242
312
|
last_expected_frame_number -= (every_n_frames*2)
|
|
313
|
+
|
|
314
|
+
# When specific frames are requested, if anything is missing, reprocess the video
|
|
315
|
+
if (frames_to_extract is not None) and (missing_frame_number is not None):
|
|
243
316
|
|
|
317
|
+
pass
|
|
318
|
+
|
|
244
319
|
# If no frames are missing, or only frames very close to the end of the video are "missing",
|
|
245
320
|
# skip this video
|
|
246
|
-
|
|
321
|
+
elif (missing_frame_number is None) or \
|
|
247
322
|
(allow_last_frame_missing and (missing_frame_number >= last_expected_frame_number)):
|
|
323
|
+
|
|
248
324
|
if verbose:
|
|
249
325
|
print('Skipping video {}, all output frames exist'.format(input_video_file))
|
|
250
326
|
return frame_filenames,Fs
|
|
327
|
+
|
|
251
328
|
else:
|
|
329
|
+
|
|
252
330
|
# If we found some frames, but not all, print a message
|
|
253
331
|
if verbose and found_existing_frame:
|
|
254
332
|
print("Rendering video {}, couldn't find frame {} ({}) of {}".format(
|
|
@@ -264,10 +342,14 @@ def video_to_frames(input_video_file, output_folder, overwrite=True,
|
|
|
264
342
|
|
|
265
343
|
frame_filenames = []
|
|
266
344
|
|
|
267
|
-
# YOLOv5 does some totally bananas monkey-patching of opencv,
|
|
268
|
-
#
|
|
269
|
-
#
|
|
270
|
-
#
|
|
345
|
+
# YOLOv5 does some totally bananas monkey-patching of opencv, which causes
|
|
346
|
+
# problems if we try to supply a third parameter to imwrite (to specify JPEG
|
|
347
|
+
# quality). Detect this case, and ignore the quality parameter if it looks
|
|
348
|
+
# like imwrite has been messed with.
|
|
349
|
+
#
|
|
350
|
+
# See:
|
|
351
|
+
#
|
|
352
|
+
# https://github.com/ultralytics/yolov5/issues/7285
|
|
271
353
|
imwrite_patched = False
|
|
272
354
|
n_imwrite_parameters = None
|
|
273
355
|
|
|
@@ -299,6 +381,12 @@ def video_to_frames(input_video_file, output_folder, overwrite=True,
|
|
|
299
381
|
if every_n_frames is not None:
|
|
300
382
|
if frame_number % every_n_frames != 0:
|
|
301
383
|
continue
|
|
384
|
+
|
|
385
|
+
if frames_to_extract is not None:
|
|
386
|
+
if frame_number > max(frames_to_extract):
|
|
387
|
+
break
|
|
388
|
+
if frame_number not in frames_to_extract:
|
|
389
|
+
continue
|
|
302
390
|
|
|
303
391
|
# Has resizing been requested?
|
|
304
392
|
if max_width is not None:
|
|
@@ -350,6 +438,10 @@ def video_to_frames(input_video_file, output_folder, overwrite=True,
|
|
|
350
438
|
except Exception as e:
|
|
351
439
|
print('Error on frame {} of {}: {}'.format(frame_number,n_frames,str(e)))
|
|
352
440
|
|
|
441
|
+
if len(frame_filenames) == 0:
|
|
442
|
+
raise Exception('Error: found no frames in file {}'.format(
|
|
443
|
+
input_video_file))
|
|
444
|
+
|
|
353
445
|
if verbose:
|
|
354
446
|
print('\nExtracted {} of {} frames for {}'.format(
|
|
355
447
|
len(frame_filenames),n_frames,input_video_file))
|
|
@@ -361,10 +453,12 @@ def video_to_frames(input_video_file, output_folder, overwrite=True,
|
|
|
361
453
|
|
|
362
454
|
|
|
363
455
|
def _video_to_frames_for_folder(relative_fn,input_folder,output_folder_base,
|
|
364
|
-
every_n_frames,overwrite,verbose,quality,max_width
|
|
456
|
+
every_n_frames,overwrite,verbose,quality,max_width,
|
|
457
|
+
frames_to_extract):
|
|
365
458
|
"""
|
|
366
|
-
Internal function to call video_to_frames in the context of
|
|
367
|
-
makes sure the right output folder exists, then calls
|
|
459
|
+
Internal function to call video_to_frames for a single video in the context of
|
|
460
|
+
video_folder_to_frames; makes sure the right output folder exists, then calls
|
|
461
|
+
video_to_frames.
|
|
368
462
|
"""
|
|
369
463
|
|
|
370
464
|
input_fn_absolute = os.path.join(input_folder,relative_fn)
|
|
@@ -379,7 +473,8 @@ def _video_to_frames_for_folder(relative_fn,input_folder,output_folder_base,
|
|
|
379
473
|
# input_video_file = input_fn_absolute; output_folder = output_folder_video
|
|
380
474
|
frame_filenames,fs = video_to_frames(input_fn_absolute,output_folder_video,
|
|
381
475
|
overwrite=overwrite,every_n_frames=every_n_frames,
|
|
382
|
-
verbose=verbose,quality=quality,max_width=max_width
|
|
476
|
+
verbose=verbose,quality=quality,max_width=max_width,
|
|
477
|
+
frames_to_extract=frames_to_extract)
|
|
383
478
|
|
|
384
479
|
return frame_filenames,fs
|
|
385
480
|
|
|
@@ -388,7 +483,8 @@ def video_folder_to_frames(input_folder, output_folder_base,
|
|
|
388
483
|
recursive=True, overwrite=True,
|
|
389
484
|
n_threads=1, every_n_frames=None,
|
|
390
485
|
verbose=False, parallelization_uses_threads=True,
|
|
391
|
-
quality=None, max_width=None
|
|
486
|
+
quality=None, max_width=None,
|
|
487
|
+
frames_to_extract=None):
|
|
392
488
|
"""
|
|
393
489
|
For every video file in input_folder, creates a folder within output_folder_base, and
|
|
394
490
|
renders frame of that video to images in that folder.
|
|
@@ -402,13 +498,18 @@ def video_folder_to_frames(input_folder, output_folder_base,
|
|
|
402
498
|
n_threads (int, optional): number of concurrent workers to use; set to <= 1 to disable
|
|
403
499
|
parallelism
|
|
404
500
|
every_n_frames (int, optional): sample every Nth frame starting from the first frame;
|
|
405
|
-
if this is None or 1, every frame is extracted
|
|
501
|
+
if this is None or 1, every frame is extracted. Mutually exclusive with
|
|
502
|
+
frames_to_extract.
|
|
406
503
|
verbose (bool, optional): enable additional debug console output
|
|
407
504
|
parallelization_uses_threads (bool, optional): whether to use threads (True) or
|
|
408
505
|
processes (False) for parallelization; ignored if n_threads <= 1
|
|
409
506
|
quality (int, optional): JPEG quality for frame output, from 0-100. Defaults
|
|
410
507
|
to the opencv default (typically 95).
|
|
411
508
|
max_width (int, optional): resize frames to be no wider than [max_width]
|
|
509
|
+
frames_to_extract (list of int, optional): extract this specific set of frames from
|
|
510
|
+
each video; mutually exclusive with every_n_frames. If all values are beyond
|
|
511
|
+
the length of a video, no frames are extracted. Can also be a single int,
|
|
512
|
+
specifying a single frame number.
|
|
412
513
|
|
|
413
514
|
Returns:
|
|
414
515
|
tuple: a length-3 tuple containing:
|
|
@@ -440,7 +541,8 @@ def video_folder_to_frames(input_folder, output_folder_base,
|
|
|
440
541
|
|
|
441
542
|
frame_filenames,fs = \
|
|
442
543
|
_video_to_frames_for_folder(input_fn_relative,input_folder,output_folder_base,
|
|
443
|
-
every_n_frames,overwrite,verbose,quality,max_width
|
|
544
|
+
every_n_frames,overwrite,verbose,quality,max_width,
|
|
545
|
+
frames_to_extract)
|
|
444
546
|
frame_filenames_by_video.append(frame_filenames)
|
|
445
547
|
fs_by_video.append(fs)
|
|
446
548
|
else:
|
|
@@ -457,7 +559,8 @@ def video_folder_to_frames(input_folder, output_folder_base,
|
|
|
457
559
|
overwrite=overwrite,
|
|
458
560
|
verbose=verbose,
|
|
459
561
|
quality=quality,
|
|
460
|
-
max_width=max_width
|
|
562
|
+
max_width=max_width,
|
|
563
|
+
frames_to_extract=frames_to_extract)
|
|
461
564
|
results = list(tqdm(pool.imap(
|
|
462
565
|
partial(process_video_with_options),input_files_relative_paths),
|
|
463
566
|
total=len(input_files_relative_paths)))
|
|
@@ -485,7 +588,7 @@ class FrameToVideoOptions:
|
|
|
485
588
|
#: video; can be 'error' or 'skip_with_warning'
|
|
486
589
|
self.non_video_behavior = 'error'
|
|
487
590
|
|
|
488
|
-
|
|
591
|
+
|
|
489
592
|
def frame_results_to_video_results(input_file,output_file,options=None):
|
|
490
593
|
"""
|
|
491
594
|
Given an MD results file produced at the *frame* level, corresponding to a directory
|
|
@@ -511,6 +614,7 @@ def frame_results_to_video_results(input_file,output_file,options=None):
|
|
|
511
614
|
images = input_data['images']
|
|
512
615
|
detection_categories = input_data['detection_categories']
|
|
513
616
|
|
|
617
|
+
|
|
514
618
|
## Break into videos
|
|
515
619
|
|
|
516
620
|
video_to_frame_info = defaultdict(list)
|
|
@@ -520,7 +624,9 @@ def frame_results_to_video_results(input_file,output_file,options=None):
|
|
|
520
624
|
|
|
521
625
|
fn = im['file']
|
|
522
626
|
video_name = os.path.dirname(fn)
|
|
627
|
+
|
|
523
628
|
if not is_video_file(video_name):
|
|
629
|
+
|
|
524
630
|
if options.non_video_behavior == 'error':
|
|
525
631
|
raise ValueError('{} is not a video file'.format(video_name))
|
|
526
632
|
elif options.non_video_behavior == 'skip_with_warning':
|
|
@@ -529,13 +635,25 @@ def frame_results_to_video_results(input_file,output_file,options=None):
|
|
|
529
635
|
else:
|
|
530
636
|
raise ValueError('Unrecognized non-video handling behavior: {}'.format(
|
|
531
637
|
options.non_video_behavior))
|
|
638
|
+
|
|
639
|
+
# Attach video-specific fields to the output, specifically attach the frame
|
|
640
|
+
# number to both the video and each detection. Only the frame number for the
|
|
641
|
+
# canonical detection will end up in the video-level output file.
|
|
642
|
+
frame_number = _filename_to_frame_number(fn)
|
|
643
|
+
im['frame_number'] = frame_number
|
|
644
|
+
for detection in im['detections']:
|
|
645
|
+
detection['frame_number'] = frame_number
|
|
646
|
+
|
|
532
647
|
video_to_frame_info[video_name].append(im)
|
|
533
648
|
|
|
649
|
+
# ...for each frame referred to in the results file
|
|
650
|
+
|
|
534
651
|
print('Found {} unique videos in {} frame-level results'.format(
|
|
535
652
|
len(video_to_frame_info),len(images)))
|
|
536
653
|
|
|
537
654
|
output_images = []
|
|
538
655
|
|
|
656
|
+
|
|
539
657
|
## For each video...
|
|
540
658
|
|
|
541
659
|
# video_name = list(video_to_frame_info.keys())[0]
|
|
@@ -594,37 +712,60 @@ def frame_results_to_video_results(input_file,output_file,options=None):
|
|
|
594
712
|
# ...def frame_results_to_video_results(...)
|
|
595
713
|
|
|
596
714
|
|
|
597
|
-
#%% Test
|
|
715
|
+
#%% Test drivers
|
|
598
716
|
|
|
599
717
|
if False:
|
|
600
718
|
|
|
719
|
+
pass
|
|
720
|
+
|
|
601
721
|
#%% Constants
|
|
602
722
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
input_folder
|
|
606
|
-
|
|
607
|
-
detected_frame_folder_base = r'e:\video_test\detected_frames'
|
|
608
|
-
rendered_videos_folder_base = r'e:\video_test\rendered_videos'
|
|
609
|
-
|
|
610
|
-
results_file = r'results.json'
|
|
611
|
-
os.makedirs(detected_frame_folder_base,exist_ok=True)
|
|
612
|
-
os.makedirs(rendered_videos_folder_base,exist_ok=True)
|
|
613
|
-
|
|
723
|
+
input_folder = r'G:\temp\usu-long\data'
|
|
724
|
+
frame_folder_base = r'g:\temp\usu-long-single-frames'
|
|
725
|
+
assert os.path.isdir(input_folder)
|
|
726
|
+
|
|
614
727
|
|
|
615
728
|
#%% Split videos into frames
|
|
616
729
|
|
|
617
730
|
frame_filenames_by_video,fs_by_video,video_filenames = \
|
|
618
|
-
video_folder_to_frames(input_folder,
|
|
731
|
+
video_folder_to_frames(input_folder,
|
|
732
|
+
frame_folder_base,
|
|
733
|
+
recursive=True,
|
|
734
|
+
overwrite=True,
|
|
735
|
+
n_threads=10,
|
|
736
|
+
every_n_frames=None,
|
|
737
|
+
verbose=True,
|
|
738
|
+
parallelization_uses_threads=True,
|
|
739
|
+
quality=None,
|
|
740
|
+
max_width=None,
|
|
741
|
+
frames_to_extract=150)
|
|
742
|
+
|
|
619
743
|
|
|
744
|
+
#%% Constants for detection tests
|
|
620
745
|
|
|
746
|
+
detected_frame_folder_base = r'e:\video_test\detected_frames'
|
|
747
|
+
rendered_videos_folder_base = r'e:\video_test\rendered_videos'
|
|
748
|
+
os.makedirs(detected_frame_folder_base,exist_ok=True)
|
|
749
|
+
os.makedirs(rendered_videos_folder_base,exist_ok=True)
|
|
750
|
+
results_file = r'results.json'
|
|
751
|
+
confidence_threshold = 0.75
|
|
752
|
+
|
|
753
|
+
#%% Load detector output
|
|
754
|
+
|
|
755
|
+
with open(results_file,'r') as f:
|
|
756
|
+
detection_results = json.load(f)
|
|
757
|
+
detections = detection_results['images']
|
|
758
|
+
detector_label_map = detection_results['detection_categories']
|
|
759
|
+
for d in detections:
|
|
760
|
+
d['file'] = d['file'].replace('\\','/').replace('video_frames/','')
|
|
761
|
+
|
|
762
|
+
|
|
621
763
|
#%% List image files, break into folders
|
|
622
764
|
|
|
623
765
|
frame_files = path_utils.find_images(frame_folder_base,True)
|
|
624
766
|
frame_files = [s.replace('\\','/') for s in frame_files]
|
|
625
767
|
print('Enumerated {} total frames'.format(len(frame_files)))
|
|
626
768
|
|
|
627
|
-
Fs = 30.01
|
|
628
769
|
# Find unique folders
|
|
629
770
|
folders = set()
|
|
630
771
|
# fn = frame_files[0]
|
|
@@ -634,16 +775,6 @@ if False:
|
|
|
634
775
|
print('Found {} folders for {} files'.format(len(folders),len(frame_files)))
|
|
635
776
|
|
|
636
777
|
|
|
637
|
-
#%% Load detector output
|
|
638
|
-
|
|
639
|
-
with open(results_file,'r') as f:
|
|
640
|
-
detection_results = json.load(f)
|
|
641
|
-
detections = detection_results['images']
|
|
642
|
-
detector_label_map = detection_results['detection_categories']
|
|
643
|
-
for d in detections:
|
|
644
|
-
d['file'] = d['file'].replace('\\','/').replace('video_frames/','')
|
|
645
|
-
|
|
646
|
-
|
|
647
778
|
#%% Render detector frames
|
|
648
779
|
|
|
649
780
|
# folder = list(folders)[0]
|
|
@@ -30,8 +30,11 @@ CONF_DIGITS = 3
|
|
|
30
30
|
|
|
31
31
|
#%% Conversion functions
|
|
32
32
|
|
|
33
|
-
def convert_json_to_csv(input_path,
|
|
34
|
-
|
|
33
|
+
def convert_json_to_csv(input_path,
|
|
34
|
+
output_path=None,
|
|
35
|
+
min_confidence=None,
|
|
36
|
+
omit_bounding_boxes=False,
|
|
37
|
+
output_encoding=None,
|
|
35
38
|
overwrite=True):
|
|
36
39
|
"""
|
|
37
40
|
Converts a MD results .json file to a totally non-standard .csv format.
|
|
@@ -76,9 +79,9 @@ def convert_json_to_csv(input_path,output_path=None,min_confidence=None,
|
|
|
76
79
|
# n_non_empty_detection_categories = len(annotation_constants.annotation_bbox_categories) - 1
|
|
77
80
|
n_non_empty_detection_categories = annotation_constants.NUM_DETECTOR_CATEGORIES
|
|
78
81
|
detection_category_column_names = []
|
|
79
|
-
assert annotation_constants.
|
|
82
|
+
assert annotation_constants.detector_bbox_category_id_to_name[0] == 'empty'
|
|
80
83
|
for cat_id in range(1,n_non_empty_detection_categories+1):
|
|
81
|
-
cat_name = annotation_constants.
|
|
84
|
+
cat_name = annotation_constants.detector_bbox_category_id_to_name[cat_id]
|
|
82
85
|
detection_category_column_names.append('max_conf_' + cat_name)
|
|
83
86
|
|
|
84
87
|
n_classification_categories = 0
|
|
@@ -370,6 +373,8 @@ def main():
|
|
|
370
373
|
parser.add_argument('--output_path',type=str,default=None,
|
|
371
374
|
help='Output filename ending in .json or .csv (defaults to ' + \
|
|
372
375
|
'input file, with .json/.csv replaced by .csv/.json)')
|
|
376
|
+
parser.add_argument('--omit_bounding_boxes',action='store_true',
|
|
377
|
+
help='Output bounding box text from .csv output (large and usually not useful)')
|
|
373
378
|
|
|
374
379
|
if len(sys.argv[1:]) == 0:
|
|
375
380
|
parser.print_help()
|
|
@@ -386,9 +391,11 @@ def main():
|
|
|
386
391
|
raise ValueError('Illegal input file extension')
|
|
387
392
|
|
|
388
393
|
if args.input_path.endswith('.csv') and args.output_path.endswith('.json'):
|
|
394
|
+
assert not args.omit_bounding_boxes, \
|
|
395
|
+
'--omit_bounding_boxes does not apply to csv --> json conversion'
|
|
389
396
|
convert_csv_to_json(args.input_path,args.output_path)
|
|
390
397
|
elif args.input_path.endswith('.json') and args.output_path.endswith('.csv'):
|
|
391
|
-
convert_json_to_csv(args.input_path,args.output_path)
|
|
398
|
+
convert_json_to_csv(args.input_path,args.output_path,omit_bounding_boxes=args.omit_bounding_boxes)
|
|
392
399
|
else:
|
|
393
400
|
raise ValueError('Illegal format combination')
|
|
394
401
|
|