From 1cede338d68ddb521f5308ffdf14ca37e4c2d52c Mon Sep 17 00:00:00 2001 From: vermavinay982 Date: Sat, 18 Sep 2021 20:40:29 +0530 Subject: [PATCH] V3.0 time stacking added --- CHANGELOG.md | 46 ++++++++++++ README.md | 9 ++- requirements.txt | 1 + setup.py | 2 +- vigenpy/video/stack_video.py | 131 ++++++++++++++++++++++++----------- 5 files changed, 146 insertions(+), 43 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..43d631e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,46 @@ +Changelog + +V0.3 +- Done + - Added time stack axis + - Display function updated + - Video limit using time duration + - Logs will tell the duration of video written, for human help +- Pending + - Getting the frame out + - Convert into class form + - Axis 3 - shuffle in the middle + +V0.2 +- Done + - If its q or Q it will work well to exit the code + - Multiple videos can be added to stack now + - Size fixed for single video via parameter, Giving the option to choose the size of video for user + - Limit_video index added - that video which will close the code None - infinite code run + - Video writer - when video is over - write the video in the end (if that doesn't work well - we will make it to write continuously + - If fps is fixed - thats fine who cares | if fps not fixed - calculate the fps for video + - Author name changed, gif added, gif created, image uploaded to git, + - added MIT licence so anyone can contribute and use the product https://choosealicense.com/licenses/mit/ +- Pending + - Fixing the size of output video by user - done + - Write one function to loop in the videos and generate frame - thats it - and keep other functions to get input as frame and perform the actions - so if someone wants specific thing - cool they can do that - if not then use the default function - it will use too much ram + - Time based - enter the time in seconds after that it will break the video writing easy but cool - done + - Next time + - Tqdm as requirement to be added + - Sending frame out to be used + - Converting it to class - to enable frame wise stacking - if you want to do directly do hstack vstack - why using this thing? + - Grid based output or people can repeat the activity twice + - Or I can do it for them, run 2 functions in 1 function - find the size - and use it + - Axis 3 = when one frame of one and another of other - thats easy - just append the frame directly into the main stack of frames - run the adding thing twice and both frames will be added at once. - done + +V0.1 +- Added + - Video is streaming so to watch the output + - Vstack and hstack is added as axis 0 and 1 + - q to stop the processing is added +- Future Addition + - It cant work with more than 2 videos - solved + - Getting the frame out, is not possible + - Using the frame as input to create a combined output + - Writing the videos if user passes the path - done + - Choose which video time to be taken as end of video - done diff --git a/README.md b/README.md index afd98e3..0009e5a 100644 --- a/README.md +++ b/README.md @@ -64,17 +64,20 @@ path2 = '../../../cars.mp4' videos = [path1, path2, path1, path2] video_size = (300,300) # w/h -limit_video = 0 # video index of video that will decide to close streaming +limit_video = 0 # video index, that will decide to close streaming video_path = 'test.mp4' +video_duration = 4 # in seconds default None output_video = stack_video( videos, - axis=0, + axis=2, size=video_size, limit_video=limit_video, write_path=video_path, writer_fps=None, - display=True) + display=True, + duration=video_duration) +print(output_video) ``` diff --git a/requirements.txt b/requirements.txt index 0e91821..ec4cba9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ numpy opencv_python +tqdm \ No newline at end of file diff --git a/setup.py b/setup.py index 086abd3..516e9f1 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ setup( name='vigenpy', - version='0.2.4', + version='0.3.0', description=short_desc, license='MIT', long_description=long_desc, diff --git a/vigenpy/video/stack_video.py b/vigenpy/video/stack_video.py index 561ec36..edb355b 100644 --- a/vigenpy/video/stack_video.py +++ b/vigenpy/video/stack_video.py @@ -2,7 +2,7 @@ __copyright__ = "Copyright 2021, Vinay Verma" __credits__ = ["Vinay Verma"] __license__ = "MIT" -__version__ = "0.1.0" +__version__ = "0.3.0" __maintainer__ = "Vinay Verma" __email__ = "vermavinay982@gmail.com" __module_name__ = "[Stack Video]" @@ -13,47 +13,33 @@ import numpy as np from tqdm import tqdm -def write_video(frame_list, video_name, output_shape, writer_fps): +def write_video(frame_list, video_name, output_shape, writer_fps, duration): fourcc = cv2.VideoWriter_fourcc(*"mp4v") height, width = output_shape + if duration is not None: + stop_frame = writer_fps * duration + out = cv2.VideoWriter(video_name, fourcc, int(writer_fps), (width, height)) + frame_counter = 0 for encoded_frame in tqdm(frame_list): frame_encoded = np.frombuffer(encoded_frame, np.uint8) frame = cv2.imdecode(frame_encoded, cv2.IMREAD_COLOR) out.write(frame) - out.release() - return video_name -def stack_video( - videos:list=[], - axis:int=0, - size=(300,400), - limit_video=None, - write_path=None, - writer_fps=None, - display=False -)->str: - """ - stack_video + frame_counter+=1 + + if duration is not None: + if frame_counter>stop_frame: + break # Video Duration Achieved - Write or display the video after stacking it - """ - if limit_video is None and write_path is not None: - print("Cant write an infinite video, Keep a limit video") - return None + out.release() - frame_list = list() # storing video frames to write - for video in videos: - if not os.path.exists(video): - print(f'Video {video} Not There') - return None - else: - print(f'Video {video} Found') + total_duration = frame_counter/writer_fps + return video_name, total_duration - caps = [cv2.VideoCapture(cam) for cam in videos] - fps = min([cap.get(cv2.CAP_PROP_FPS) for cap in caps]) - print("Min FPS of Video is :",fps) +def axis_stack(caps, videos, size, axis, display): + frame_list = list() # storing video frames to write past_frame=[] start = True video_over = False @@ -63,9 +49,12 @@ def stack_video( for i,cap in enumerate(caps): ret, frame = cap.read() if not ret: + # Video Still Not Over, Loop Again caps[i] = cv2.VideoCapture(videos[i]) print('Video Ended, RESTARTING',videos[i]) frame = past_frame[i] + + # Limiting Video Ended, Stop Now if i==limit_video: print(f'{videos[i]} Video Ended, Stopping Now',) video_over = True @@ -73,13 +62,13 @@ def stack_video( if start: # only initialize at start + # Save Past in case of frame drop start = False - # in case of frame drop past_frame = [frame for i in videos] past_frame[i]=frame frame = cv2.resize(frame, size) - frames.append(frame) + frames.append(frame) # combined frames in array if video_over: break @@ -90,22 +79,84 @@ def stack_video( resized_frame = np.vstack(frames) output_shape = resized_frame.shape[:2] - _, encoded_frame = cv2.imencode('.jpg', resized_frame) + flag, encoded_frame = cv2.imencode('.jpg', resized_frame) frame_list.append(encoded_frame) if display: cv2.imshow('test',resized_frame) wait_key = cv2.waitKey(1) - if ord('q')== wait_key or ord('Q')== wait_key: + if ord('q')==wait_key or ord('Q')==wait_key: break + return frame_list, output_shape + +def time_stack(caps, videos, size, display): + frame_list = list() # storing video frames to write + for i,cap in enumerate(caps): + while True: + ret, frame = cap.read() + if not ret: + print('Video Ended, Closed',videos[i]) + break # frame is NONE + + resized_frame = cv2.resize(frame, size) + output_shape = resized_frame.shape[:2] + flag, encoded_frame = cv2.imencode('.jpg', resized_frame) + frame_list.append(encoded_frame) + + if display: + cv2.imshow('test',resized_frame) + wait_key = cv2.waitKey(1) + + if ord('q')==wait_key or ord('Q')==wait_key: + break + return frame_list, output_shape + +def stack_video( + videos:list=[], + axis:int=0, + size=(300,400), + limit_video=None, + write_path=None, + writer_fps=None, + display=False, + duration=None + )->str: + """ + stack_video + + Write or display the video after stacking it + """ + if limit_video is None and write_path is not None: + print("Cant write an infinite video, Keep a limit video") + return None + + for video in videos: + if not os.path.exists(video): + print(f'Video {video} Not There') + return None + else: + print(f'Video {video} Found') + + caps = [cv2.VideoCapture(cam) for cam in videos] + fps = min([cap.get(cv2.CAP_PROP_FPS) for cap in caps]) + print(f"Min FPS of All Videos is :{fps:.2f}") + + if axis==2: + frame_list, output_shape = time_stack(caps, videos, size, display=display) + + if axis==1: + frame_list, output_shape = axis_stack(caps, videos, size, axis=axis, display=display) + + if axis==0: + frame_list, output_shape = axis_stack(caps, videos, size, axis=axis, display=display) if writer_fps is None: writer_fps = fps if write_path: - video_name = write_video(frame_list, write_path, output_shape, writer_fps=writer_fps) - print("Video written sucessfully at :",video_name) + video_name, vid_duration = write_video(frame_list, write_path, output_shape, writer_fps=writer_fps, duration=duration) + print(f"Video written sucessfully at :{video_name} || With Duration {vid_duration:.2f} seconds") return video_name if display: @@ -117,17 +168,19 @@ def stack_video( path1 = '../../../archery.mp4' path2 = '../../../cars.mp4' - videos = [path1, path2, path1, path2] + videos = [path1, path2] video_size = (300,300) # w/h limit_video = 0 # video index, that will decide to close streaming video_path = 'test.mp4' + video_duration = 4 # in seconds default None output_video = stack_video( videos, - axis=0, + axis=2, size=video_size, limit_video=limit_video, write_path=video_path, writer_fps=None, - display=True) + display=True, + duration=video_duration) print(output_video) \ No newline at end of file