From d5a76e6bd8d830aa57749c3f58c281c615fc77bb Mon Sep 17 00:00:00 2001 From: Tyler Date: Sun, 23 Oct 2022 14:05:20 -0700 Subject: [PATCH] Async optimizations (#269) * refractoring and cleaning up code for progressive frame extractor * made progressive frame extractor async writing * increased max frames ahead, added to config, decreased default bleed --- src/config_files/output_options.yaml | 3 +- src/dandere2x/dandere2x_service/__init__.py | 6 ++- .../dandere2x_service/core/min_disk_usage.py | 17 ++++----- .../dandere2x_service_context.py | 2 +- src/dandere2x/dandere2xlib/__init__.py | 0 .../dandere2xlib/wrappers/ffmpeg/__init__.py | 0 .../__init__.py} | 38 ++++++++----------- .../_ffmpeg_video_frame_extractor.py} | 12 +++--- 8 files changed, 37 insertions(+), 41 deletions(-) create mode 100644 src/dandere2x/dandere2xlib/__init__.py create mode 100644 src/dandere2x/dandere2xlib/wrappers/ffmpeg/__init__.py rename src/dandere2x/dandere2xlib/wrappers/ffmpeg/{progressive_frame_extractor_ffmpeg_rework.py => progressive_frame_extractor/__init__.py} (63%) rename src/dandere2x/dandere2xlib/wrappers/ffmpeg/{ffmpeg_progressive_frame_extractor.py => progressive_frame_extractor/_ffmpeg_video_frame_extractor.py} (95%) diff --git a/src/config_files/output_options.yaml b/src/config_files/output_options.yaml index 7aa798a5..a7bc6252 100644 --- a/src/config_files/output_options.yaml +++ b/src/config_files/output_options.yaml @@ -7,7 +7,8 @@ realsr_ncnn_vulkan: -verbose: null dandere2x: - bleed: 3 + bleed: 1 + max_frames_ahead = 500 dandere2x_cpp: block_matching_arg: "exhaustive" diff --git a/src/dandere2x/dandere2x_service/__init__.py b/src/dandere2x/dandere2x_service/__init__.py index 47d0c0e7..8135a70c 100644 --- a/src/dandere2x/dandere2x_service/__init__.py +++ b/src/dandere2x/dandere2x_service/__init__.py @@ -98,15 +98,17 @@ def run(self): self.progressive_noise_adder.start() - self.min_disk_demon.extract_initial_frames() + extract_initial_frames = threading.Thread(target=self.min_disk_demon.extract_initial_frames) + extract_initial_frames.start() self.__upscale_first_frame() + extract_initial_frames.join() - self.min_disk_demon.start() self.dandere2x_cpp_thread.start() self.merge_thread.start() self.residual_thread.start() self.waifu2x.start() self.status_thread.start() + self.min_disk_demon.start() while self.controller.get_current_frame() < self.context.frame_count - 1: time.sleep(1) diff --git a/src/dandere2x/dandere2x_service/core/min_disk_usage.py b/src/dandere2x/dandere2x_service/core/min_disk_usage.py index 62fe4f25..83c35ef1 100644 --- a/src/dandere2x/dandere2x_service/core/min_disk_usage.py +++ b/src/dandere2x/dandere2x_service/core/min_disk_usage.py @@ -30,8 +30,7 @@ from dandere2x.dandere2x_service.dandere2x_service_context import Dandere2xServiceContext from dandere2x.dandere2x_service.dandere2x_service_controller import Dandere2xController from dandere2x.dandere2xlib.utils.dandere2x_utils import get_lexicon_value -from dandere2x.dandere2xlib.wrappers.ffmpeg.progressive_frame_extractor_ffmpeg_rework import \ - ProgressiveFramesExtractorFFMPEG +from dandere2x.dandere2xlib.wrappers.ffmpeg.progressive_frame_extractor import ProgressiveFrameExtractor class MinDiskUsage(threading.Thread): @@ -53,12 +52,12 @@ def __init__(self, context: Dandere2xServiceContext, controller: Dandere2xContro self.controller = controller self.max_frames_ahead = self.context.max_frames_ahead self.frame_count = context.frame_count - self.progressive_frame_extractor = ProgressiveFramesExtractorFFMPEG(input_video=self.context.service_request.input_file, - extracted_frames_dir=self.context.input_frames_dir, - compressed_frames_dir=self.context.compressed_static_dir, - compressed_quality=self.context.service_request.quality_minimum, - block_size=self.context.service_request.block_size, - output_options_original=self.context.service_request.output_options) + self.progressive_frame_extractor = ProgressiveFrameExtractor(input_video=self.context.service_request.input_file, + extracted_frames_dir=self.context.input_frames_dir, + compressed_frames_dir=self.context.compressed_static_dir, + compressed_quality=self.context.service_request.quality_minimum, + block_size=self.context.service_request.block_size, + output_options_original=self.context.service_request.output_options) self.start_frame = 1 def join(self, timeout=None): @@ -84,14 +83,12 @@ def run(self): time.sleep(.00001) if not self.is_alive(): - self.progressive_frame_extractor.release_capture() return # when it does get ahead, extract the next frame self.progressive_frame_extractor.next_frame() self.__delete_used_files(x) - self.progressive_frame_extractor.release_capture() def extract_initial_frames(self): """ diff --git a/src/dandere2x/dandere2x_service/dandere2x_service_context.py b/src/dandere2x/dandere2x_service/dandere2x_service_context.py index 68d52a95..50ab0bfd 100644 --- a/src/dandere2x/dandere2x_service/dandere2x_service_context.py +++ b/src/dandere2x/dandere2x_service/dandere2x_service_context.py @@ -67,7 +67,7 @@ def __init__(self, service_request: Dandere2xServiceRequest): self.temp_image = self.temp_image_folder + "tempimage.jpg" self.debug = False self.step_size = 4 - self.max_frames_ahead = 100 + self.max_frames_ahead = self.service_request.output_options["dandere2x"]["max_frames_ahead"] # Dandere2xCPP self.dandere2x_cpp_block_matching_arg = self.service_request.output_options["dandere2x_cpp"]["block_matching_arg"] diff --git a/src/dandere2x/dandere2xlib/__init__.py b/src/dandere2x/dandere2xlib/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/dandere2x/dandere2xlib/wrappers/ffmpeg/__init__.py b/src/dandere2x/dandere2xlib/wrappers/ffmpeg/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/dandere2x/dandere2xlib/wrappers/ffmpeg/progressive_frame_extractor_ffmpeg_rework.py b/src/dandere2x/dandere2xlib/wrappers/ffmpeg/progressive_frame_extractor/__init__.py similarity index 63% rename from src/dandere2x/dandere2xlib/wrappers/ffmpeg/progressive_frame_extractor_ffmpeg_rework.py rename to src/dandere2x/dandere2xlib/wrappers/ffmpeg/progressive_frame_extractor/__init__.py index 9ca185a0..2b11a15d 100644 --- a/src/dandere2x/dandere2xlib/wrappers/ffmpeg/progressive_frame_extractor_ffmpeg_rework.py +++ b/src/dandere2x/dandere2xlib/wrappers/ffmpeg/progressive_frame_extractor/__init__.py @@ -1,12 +1,16 @@ +import threading +import time +import uuid from pathlib import Path from dandere2x.dandere2xlib.utils.dandere2x_utils import rename_file_wait from dandere2x.dandere2xlib.utils.yaml_utils import load_executable_paths_yaml -from dandere2x.dandere2xlib.wrappers.ffmpeg.ffmpeg import apply_noise_to_image -from dandere2x.dandere2xlib.wrappers.ffmpeg.ffmpeg_progressive_frame_extractor import VideoFrameExtractor +from dandere2x.dandere2xlib.wrappers.ffmpeg.progressive_frame_extractor._ffmpeg_video_frame_extractor import \ + FFMpegVideoFrameExtractor, D2xFrame from dandere2x.dandere2xlib.wrappers.ffmpeg.ffprobe import get_width_height -class ProgressiveFramesExtractorFFMPEG: + +class ProgressiveFrameExtractor: """ Temporally extract frames from a video each time next_frame is called. Saves into dandere2x's inputs DIR. @@ -19,7 +23,6 @@ def __init__(self, compressed_quality: int, block_size: int, output_options_original: dict): - ffprobe_path = load_executable_paths_yaml()['ffprobe'] ffmpeg_path = load_executable_paths_yaml()['ffmpeg'] @@ -30,29 +33,17 @@ def __init__(self, self.compressed_quality = compressed_quality width, height = get_width_height(ffprobe_dir=ffprobe_path, input_video=input_video) - self.cap = VideoFrameExtractor(Path(ffmpeg_path), Path(input_video), width, height, block_size, output_options_original) + self.cap = FFMpegVideoFrameExtractor(Path(ffmpeg_path), Path(input_video), width, height, block_size, + output_options_original) self.ffmpeg_path = load_executable_paths_yaml()['ffmpeg'] self.count = 1 def extract_frames_to(self, stop_frame: int): - for x in range(1, stop_frame): self.next_frame() - def release_capture(self): - pass - # #todo, investigate / remove this try catch block with an actual solution - # try: - # # Closes all the frames - # self.cap.release() - # cv2.destroyAllWindows() - # - # except cv2.error: - # print("cv2 error caught - this behaviour is unexpected by the developer, but testing to see if this is" - # " a potential duct-tape fix.") - # todo, need to find a fix for "stuck at 99%" error, or getting stuck prematurely. def next_frame(self): """ Call and save the next frame. """ @@ -60,11 +51,14 @@ def next_frame(self): success = False image = self.cap.get_frame() - temp_image = self.extracted_frames_dir + "frame_temp_%s.png" % self.count + temp_image = self.extracted_frames_dir + str(uuid.uuid4()) + "frame_temp_%s.png" % self.count final_image = self.extracted_frames_dir + "frame%s.png" % self.count - image.save(Path(temp_image)) - - rename_file_wait(temp_image, final_image) + threading.Thread(target=self.save_asyncable, args=(image, temp_image, final_image,)).start() self.count += 1 + + @staticmethod + def save_asyncable(image: D2xFrame, temp_image:str, final_image: str): + image.save(Path(temp_image)) + rename_file_wait(temp_image, final_image) \ No newline at end of file diff --git a/src/dandere2x/dandere2xlib/wrappers/ffmpeg/ffmpeg_progressive_frame_extractor.py b/src/dandere2x/dandere2xlib/wrappers/ffmpeg/progressive_frame_extractor/_ffmpeg_video_frame_extractor.py similarity index 95% rename from src/dandere2x/dandere2xlib/wrappers/ffmpeg/ffmpeg_progressive_frame_extractor.py rename to src/dandere2x/dandere2xlib/wrappers/ffmpeg/progressive_frame_extractor/_ffmpeg_video_frame_extractor.py index 1b17c033..7ae5e4c7 100644 --- a/src/dandere2x/dandere2xlib/wrappers/ffmpeg/ffmpeg_progressive_frame_extractor.py +++ b/src/dandere2x/dandere2xlib/wrappers/ffmpeg/progressive_frame_extractor/_ffmpeg_video_frame_extractor.py @@ -120,9 +120,11 @@ def valid_input_resolution(width: int, height: int, block_size: int): return new_output_options -class VideoFrameExtractor: - def __init__(self, ffmpeg_binary: Path, input_video: Path, width: int, height: int, block_size: int, output_options_original: dict): +class FFMpegVideoFrameExtractor: + + def __init__(self, ffmpeg_binary: Path, input_video: Path, width: int, height: int, block_size: int, + output_options_original: dict): self.__count: int = 0 self._width, self._height = get_a_valid_input_resolution(width, height, block_size) self._dtype = np.uint8 @@ -140,12 +142,12 @@ def __init__(self, ffmpeg_binary: Path, input_video: Path, width: int, height: print("pre output options") options = get_options_from_section(fixed_resolution["ffmpeg"]["pre_process_video"]['output_options'], - ffmpeg_command=True) + ffmpeg_command=True) for item in options: extraction_args.append(item) extraction_args.extend(["-c:v", "rawvideo", "-f", "rawvideo", - "-pix_fmt", "rgb24", "-an", "-"]) + "-pix_fmt", "rgb24", "-an", "-"]) pprint(extraction_args) self.ffmpeg = subprocess.Popen(extraction_args, stdout=subprocess.PIPE) @@ -172,4 +174,4 @@ def get_frame(self) -> D2xFrame: if __name__ == "__main__": - pass \ No newline at end of file + pass