megadetector 5.0.13__py3-none-any.whl → 5.0.15__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 +230 -70
- megadetector/detection/pytorch_detector.py +16 -11
- megadetector/detection/run_detector.py +17 -5
- megadetector/detection/run_detector_batch.py +186 -67
- megadetector/detection/tf_detector.py +11 -3
- megadetector/detection/video_utils.py +177 -43
- megadetector/postprocessing/convert_output_format.py +12 -5
- megadetector/utils/md_tests.py +279 -108
- 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.15.dist-info}/METADATA +1 -1
- {megadetector-5.0.13.dist-info → megadetector-5.0.15.dist-info}/RECORD +18 -18
- {megadetector-5.0.13.dist-info → megadetector-5.0.15.dist-info}/LICENSE +0 -0
- {megadetector-5.0.13.dist-info → megadetector-5.0.15.dist-info}/WHEEL +0 -0
- {megadetector-5.0.13.dist-info → megadetector-5.0.15.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,17 @@ 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 quality is not None and quality < 0:
|
|
254
|
+
quality = None
|
|
255
|
+
|
|
256
|
+
if isinstance(frames_to_extract,int):
|
|
257
|
+
frames_to_extract = [frames_to_extract]
|
|
258
|
+
|
|
259
|
+
if (frames_to_extract is not None) and (every_n_frames is not None):
|
|
260
|
+
raise ValueError('frames_to_extract and every_n_frames are mutually exclusive')
|
|
261
|
+
|
|
262
|
+
os.makedirs(output_folder,exist_ok=True)
|
|
263
|
+
|
|
199
264
|
vidcap = cv2.VideoCapture(input_video_file)
|
|
200
265
|
n_frames = int(vidcap.get(cv2.CAP_PROP_FRAME_COUNT))
|
|
201
266
|
Fs = vidcap.get(cv2.CAP_PROP_FPS)
|
|
@@ -211,9 +276,17 @@ def video_to_frames(input_video_file, output_folder, overwrite=True,
|
|
|
211
276
|
for frame_number in range(0,n_frames):
|
|
212
277
|
|
|
213
278
|
if every_n_frames is not None:
|
|
279
|
+
assert frames_to_extract is None, \
|
|
280
|
+
'Internal error: frames_to_extract and every_n_frames are exclusive'
|
|
214
281
|
if (frame_number % every_n_frames) != 0:
|
|
215
282
|
continue
|
|
216
283
|
|
|
284
|
+
if frames_to_extract is not None:
|
|
285
|
+
assert every_n_frames is None, \
|
|
286
|
+
'Internal error: frames_to_extract and every_n_frames are exclusive'
|
|
287
|
+
if frame_number not in frames_to_extract:
|
|
288
|
+
continue
|
|
289
|
+
|
|
217
290
|
frame_filename = _frame_number_to_filename(frame_number)
|
|
218
291
|
frame_filename = os.path.join(output_folder,frame_filename)
|
|
219
292
|
frame_filenames.append(frame_filename)
|
|
@@ -240,15 +313,23 @@ def video_to_frames(input_video_file, output_folder, overwrite=True,
|
|
|
240
313
|
last_expected_frame_number = n_frames-1
|
|
241
314
|
if every_n_frames is not None:
|
|
242
315
|
last_expected_frame_number -= (every_n_frames*2)
|
|
316
|
+
|
|
317
|
+
# When specific frames are requested, if anything is missing, reprocess the video
|
|
318
|
+
if (frames_to_extract is not None) and (missing_frame_number is not None):
|
|
243
319
|
|
|
320
|
+
pass
|
|
321
|
+
|
|
244
322
|
# If no frames are missing, or only frames very close to the end of the video are "missing",
|
|
245
323
|
# skip this video
|
|
246
|
-
|
|
324
|
+
elif (missing_frame_number is None) or \
|
|
247
325
|
(allow_last_frame_missing and (missing_frame_number >= last_expected_frame_number)):
|
|
326
|
+
|
|
248
327
|
if verbose:
|
|
249
328
|
print('Skipping video {}, all output frames exist'.format(input_video_file))
|
|
250
329
|
return frame_filenames,Fs
|
|
330
|
+
|
|
251
331
|
else:
|
|
332
|
+
|
|
252
333
|
# If we found some frames, but not all, print a message
|
|
253
334
|
if verbose and found_existing_frame:
|
|
254
335
|
print("Rendering video {}, couldn't find frame {} ({}) of {}".format(
|
|
@@ -264,10 +345,14 @@ def video_to_frames(input_video_file, output_folder, overwrite=True,
|
|
|
264
345
|
|
|
265
346
|
frame_filenames = []
|
|
266
347
|
|
|
267
|
-
# YOLOv5 does some totally bananas monkey-patching of opencv,
|
|
268
|
-
#
|
|
269
|
-
#
|
|
270
|
-
#
|
|
348
|
+
# YOLOv5 does some totally bananas monkey-patching of opencv, which causes
|
|
349
|
+
# problems if we try to supply a third parameter to imwrite (to specify JPEG
|
|
350
|
+
# quality). Detect this case, and ignore the quality parameter if it looks
|
|
351
|
+
# like imwrite has been messed with.
|
|
352
|
+
#
|
|
353
|
+
# See:
|
|
354
|
+
#
|
|
355
|
+
# https://github.com/ultralytics/yolov5/issues/7285
|
|
271
356
|
imwrite_patched = False
|
|
272
357
|
n_imwrite_parameters = None
|
|
273
358
|
|
|
@@ -299,6 +384,12 @@ def video_to_frames(input_video_file, output_folder, overwrite=True,
|
|
|
299
384
|
if every_n_frames is not None:
|
|
300
385
|
if frame_number % every_n_frames != 0:
|
|
301
386
|
continue
|
|
387
|
+
|
|
388
|
+
if frames_to_extract is not None:
|
|
389
|
+
if frame_number > max(frames_to_extract):
|
|
390
|
+
break
|
|
391
|
+
if frame_number not in frames_to_extract:
|
|
392
|
+
continue
|
|
302
393
|
|
|
303
394
|
# Has resizing been requested?
|
|
304
395
|
if max_width is not None:
|
|
@@ -350,6 +441,10 @@ def video_to_frames(input_video_file, output_folder, overwrite=True,
|
|
|
350
441
|
except Exception as e:
|
|
351
442
|
print('Error on frame {} of {}: {}'.format(frame_number,n_frames,str(e)))
|
|
352
443
|
|
|
444
|
+
if len(frame_filenames) == 0:
|
|
445
|
+
raise Exception('Error: found no frames in file {}'.format(
|
|
446
|
+
input_video_file))
|
|
447
|
+
|
|
353
448
|
if verbose:
|
|
354
449
|
print('\nExtracted {} of {} frames for {}'.format(
|
|
355
450
|
len(frame_filenames),n_frames,input_video_file))
|
|
@@ -361,10 +456,12 @@ def video_to_frames(input_video_file, output_folder, overwrite=True,
|
|
|
361
456
|
|
|
362
457
|
|
|
363
458
|
def _video_to_frames_for_folder(relative_fn,input_folder,output_folder_base,
|
|
364
|
-
every_n_frames,overwrite,verbose,quality,max_width
|
|
459
|
+
every_n_frames,overwrite,verbose,quality,max_width,
|
|
460
|
+
frames_to_extract):
|
|
365
461
|
"""
|
|
366
|
-
Internal function to call video_to_frames in the context of
|
|
367
|
-
makes sure the right output folder exists, then calls
|
|
462
|
+
Internal function to call video_to_frames for a single video in the context of
|
|
463
|
+
video_folder_to_frames; makes sure the right output folder exists, then calls
|
|
464
|
+
video_to_frames.
|
|
368
465
|
"""
|
|
369
466
|
|
|
370
467
|
input_fn_absolute = os.path.join(input_folder,relative_fn)
|
|
@@ -379,7 +476,8 @@ def _video_to_frames_for_folder(relative_fn,input_folder,output_folder_base,
|
|
|
379
476
|
# input_video_file = input_fn_absolute; output_folder = output_folder_video
|
|
380
477
|
frame_filenames,fs = video_to_frames(input_fn_absolute,output_folder_video,
|
|
381
478
|
overwrite=overwrite,every_n_frames=every_n_frames,
|
|
382
|
-
verbose=verbose,quality=quality,max_width=max_width
|
|
479
|
+
verbose=verbose,quality=quality,max_width=max_width,
|
|
480
|
+
frames_to_extract=frames_to_extract)
|
|
383
481
|
|
|
384
482
|
return frame_filenames,fs
|
|
385
483
|
|
|
@@ -388,7 +486,8 @@ def video_folder_to_frames(input_folder, output_folder_base,
|
|
|
388
486
|
recursive=True, overwrite=True,
|
|
389
487
|
n_threads=1, every_n_frames=None,
|
|
390
488
|
verbose=False, parallelization_uses_threads=True,
|
|
391
|
-
quality=None, max_width=None
|
|
489
|
+
quality=None, max_width=None,
|
|
490
|
+
frames_to_extract=None):
|
|
392
491
|
"""
|
|
393
492
|
For every video file in input_folder, creates a folder within output_folder_base, and
|
|
394
493
|
renders frame of that video to images in that folder.
|
|
@@ -402,13 +501,18 @@ def video_folder_to_frames(input_folder, output_folder_base,
|
|
|
402
501
|
n_threads (int, optional): number of concurrent workers to use; set to <= 1 to disable
|
|
403
502
|
parallelism
|
|
404
503
|
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
|
|
504
|
+
if this is None or 1, every frame is extracted. Mutually exclusive with
|
|
505
|
+
frames_to_extract.
|
|
406
506
|
verbose (bool, optional): enable additional debug console output
|
|
407
507
|
parallelization_uses_threads (bool, optional): whether to use threads (True) or
|
|
408
508
|
processes (False) for parallelization; ignored if n_threads <= 1
|
|
409
509
|
quality (int, optional): JPEG quality for frame output, from 0-100. Defaults
|
|
410
510
|
to the opencv default (typically 95).
|
|
411
511
|
max_width (int, optional): resize frames to be no wider than [max_width]
|
|
512
|
+
frames_to_extract (list of int, optional): extract this specific set of frames from
|
|
513
|
+
each video; mutually exclusive with every_n_frames. If all values are beyond
|
|
514
|
+
the length of a video, no frames are extracted. Can also be a single int,
|
|
515
|
+
specifying a single frame number.
|
|
412
516
|
|
|
413
517
|
Returns:
|
|
414
518
|
tuple: a length-3 tuple containing:
|
|
@@ -440,7 +544,8 @@ def video_folder_to_frames(input_folder, output_folder_base,
|
|
|
440
544
|
|
|
441
545
|
frame_filenames,fs = \
|
|
442
546
|
_video_to_frames_for_folder(input_fn_relative,input_folder,output_folder_base,
|
|
443
|
-
every_n_frames,overwrite,verbose,quality,max_width
|
|
547
|
+
every_n_frames,overwrite,verbose,quality,max_width,
|
|
548
|
+
frames_to_extract)
|
|
444
549
|
frame_filenames_by_video.append(frame_filenames)
|
|
445
550
|
fs_by_video.append(fs)
|
|
446
551
|
else:
|
|
@@ -457,7 +562,8 @@ def video_folder_to_frames(input_folder, output_folder_base,
|
|
|
457
562
|
overwrite=overwrite,
|
|
458
563
|
verbose=verbose,
|
|
459
564
|
quality=quality,
|
|
460
|
-
max_width=max_width
|
|
565
|
+
max_width=max_width,
|
|
566
|
+
frames_to_extract=frames_to_extract)
|
|
461
567
|
results = list(tqdm(pool.imap(
|
|
462
568
|
partial(process_video_with_options),input_files_relative_paths),
|
|
463
569
|
total=len(input_files_relative_paths)))
|
|
@@ -485,7 +591,7 @@ class FrameToVideoOptions:
|
|
|
485
591
|
#: video; can be 'error' or 'skip_with_warning'
|
|
486
592
|
self.non_video_behavior = 'error'
|
|
487
593
|
|
|
488
|
-
|
|
594
|
+
|
|
489
595
|
def frame_results_to_video_results(input_file,output_file,options=None):
|
|
490
596
|
"""
|
|
491
597
|
Given an MD results file produced at the *frame* level, corresponding to a directory
|
|
@@ -511,6 +617,7 @@ def frame_results_to_video_results(input_file,output_file,options=None):
|
|
|
511
617
|
images = input_data['images']
|
|
512
618
|
detection_categories = input_data['detection_categories']
|
|
513
619
|
|
|
620
|
+
|
|
514
621
|
## Break into videos
|
|
515
622
|
|
|
516
623
|
video_to_frame_info = defaultdict(list)
|
|
@@ -520,7 +627,9 @@ def frame_results_to_video_results(input_file,output_file,options=None):
|
|
|
520
627
|
|
|
521
628
|
fn = im['file']
|
|
522
629
|
video_name = os.path.dirname(fn)
|
|
630
|
+
|
|
523
631
|
if not is_video_file(video_name):
|
|
632
|
+
|
|
524
633
|
if options.non_video_behavior == 'error':
|
|
525
634
|
raise ValueError('{} is not a video file'.format(video_name))
|
|
526
635
|
elif options.non_video_behavior == 'skip_with_warning':
|
|
@@ -529,13 +638,25 @@ def frame_results_to_video_results(input_file,output_file,options=None):
|
|
|
529
638
|
else:
|
|
530
639
|
raise ValueError('Unrecognized non-video handling behavior: {}'.format(
|
|
531
640
|
options.non_video_behavior))
|
|
641
|
+
|
|
642
|
+
# Attach video-specific fields to the output, specifically attach the frame
|
|
643
|
+
# number to both the video and each detection. Only the frame number for the
|
|
644
|
+
# canonical detection will end up in the video-level output file.
|
|
645
|
+
frame_number = _filename_to_frame_number(fn)
|
|
646
|
+
im['frame_number'] = frame_number
|
|
647
|
+
for detection in im['detections']:
|
|
648
|
+
detection['frame_number'] = frame_number
|
|
649
|
+
|
|
532
650
|
video_to_frame_info[video_name].append(im)
|
|
533
651
|
|
|
652
|
+
# ...for each frame referred to in the results file
|
|
653
|
+
|
|
534
654
|
print('Found {} unique videos in {} frame-level results'.format(
|
|
535
655
|
len(video_to_frame_info),len(images)))
|
|
536
656
|
|
|
537
657
|
output_images = []
|
|
538
658
|
|
|
659
|
+
|
|
539
660
|
## For each video...
|
|
540
661
|
|
|
541
662
|
# video_name = list(video_to_frame_info.keys())[0]
|
|
@@ -594,37 +715,60 @@ def frame_results_to_video_results(input_file,output_file,options=None):
|
|
|
594
715
|
# ...def frame_results_to_video_results(...)
|
|
595
716
|
|
|
596
717
|
|
|
597
|
-
#%% Test
|
|
718
|
+
#%% Test drivers
|
|
598
719
|
|
|
599
720
|
if False:
|
|
600
721
|
|
|
722
|
+
pass
|
|
723
|
+
|
|
601
724
|
#%% Constants
|
|
602
725
|
|
|
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
|
-
|
|
726
|
+
input_folder = r'G:\temp\usu-long\data'
|
|
727
|
+
frame_folder_base = r'g:\temp\usu-long-single-frames'
|
|
728
|
+
assert os.path.isdir(input_folder)
|
|
729
|
+
|
|
614
730
|
|
|
615
731
|
#%% Split videos into frames
|
|
616
732
|
|
|
617
733
|
frame_filenames_by_video,fs_by_video,video_filenames = \
|
|
618
|
-
video_folder_to_frames(input_folder,
|
|
734
|
+
video_folder_to_frames(input_folder,
|
|
735
|
+
frame_folder_base,
|
|
736
|
+
recursive=True,
|
|
737
|
+
overwrite=True,
|
|
738
|
+
n_threads=10,
|
|
739
|
+
every_n_frames=None,
|
|
740
|
+
verbose=True,
|
|
741
|
+
parallelization_uses_threads=True,
|
|
742
|
+
quality=None,
|
|
743
|
+
max_width=None,
|
|
744
|
+
frames_to_extract=150)
|
|
745
|
+
|
|
619
746
|
|
|
747
|
+
#%% Constants for detection tests
|
|
620
748
|
|
|
749
|
+
detected_frame_folder_base = r'e:\video_test\detected_frames'
|
|
750
|
+
rendered_videos_folder_base = r'e:\video_test\rendered_videos'
|
|
751
|
+
os.makedirs(detected_frame_folder_base,exist_ok=True)
|
|
752
|
+
os.makedirs(rendered_videos_folder_base,exist_ok=True)
|
|
753
|
+
results_file = r'results.json'
|
|
754
|
+
confidence_threshold = 0.75
|
|
755
|
+
|
|
756
|
+
#%% Load detector output
|
|
757
|
+
|
|
758
|
+
with open(results_file,'r') as f:
|
|
759
|
+
detection_results = json.load(f)
|
|
760
|
+
detections = detection_results['images']
|
|
761
|
+
detector_label_map = detection_results['detection_categories']
|
|
762
|
+
for d in detections:
|
|
763
|
+
d['file'] = d['file'].replace('\\','/').replace('video_frames/','')
|
|
764
|
+
|
|
765
|
+
|
|
621
766
|
#%% List image files, break into folders
|
|
622
767
|
|
|
623
768
|
frame_files = path_utils.find_images(frame_folder_base,True)
|
|
624
769
|
frame_files = [s.replace('\\','/') for s in frame_files]
|
|
625
770
|
print('Enumerated {} total frames'.format(len(frame_files)))
|
|
626
771
|
|
|
627
|
-
Fs = 30.01
|
|
628
772
|
# Find unique folders
|
|
629
773
|
folders = set()
|
|
630
774
|
# fn = frame_files[0]
|
|
@@ -634,16 +778,6 @@ if False:
|
|
|
634
778
|
print('Found {} folders for {} files'.format(len(folders),len(frame_files)))
|
|
635
779
|
|
|
636
780
|
|
|
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
781
|
#%% Render detector frames
|
|
648
782
|
|
|
649
783
|
# 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
|
|