| ''' |
| The code is modified from the Real-ESRGAN: |
| https://github.com/xinntao/Real-ESRGAN/blob/master/inference_realesrgan_video.py |
| |
| ''' |
| import cv2 |
| import sys |
| import numpy as np |
|
|
| try: |
| import ffmpeg |
| except ImportError: |
| import pip |
| pip.main(['install', '--user', 'ffmpeg-python']) |
| import ffmpeg |
|
|
| def get_video_meta_info(video_path): |
| ret = {} |
| probe = ffmpeg.probe(video_path) |
| video_streams = [stream for stream in probe['streams'] if stream['codec_type'] == 'video'] |
| has_audio = any(stream['codec_type'] == 'audio' for stream in probe['streams']) |
| ret['width'] = video_streams[0]['width'] |
| ret['height'] = video_streams[0]['height'] |
| ret['fps'] = eval(video_streams[0]['avg_frame_rate']) |
| ret['audio'] = ffmpeg.input(video_path).audio if has_audio else None |
| ret['nb_frames'] = int(video_streams[0]['nb_frames']) |
| return ret |
|
|
| class VideoReader: |
| def __init__(self, video_path): |
| self.paths = [] |
| self.audio = None |
| try: |
| self.stream_reader = ( |
| ffmpeg.input(video_path).output('pipe:', format='rawvideo', pix_fmt='bgr24', |
| loglevel='error').run_async( |
| pipe_stdin=True, pipe_stdout=True, cmd='ffmpeg')) |
| except FileNotFoundError: |
| print('Please install ffmpeg (not ffmpeg-python) by running\n', |
| '\t$ conda install -c conda-forge ffmpeg') |
| sys.exit(0) |
|
|
| meta = get_video_meta_info(video_path) |
| self.width = meta['width'] |
| self.height = meta['height'] |
| self.input_fps = meta['fps'] |
| self.audio = meta['audio'] |
| self.nb_frames = meta['nb_frames'] |
|
|
| self.idx = 0 |
|
|
| def get_resolution(self): |
| return self.height, self.width |
|
|
| def get_fps(self): |
| if self.input_fps is not None: |
| return self.input_fps |
| return 24 |
|
|
| def get_audio(self): |
| return self.audio |
|
|
| def __len__(self): |
| return self.nb_frames |
|
|
| def get_frame_from_stream(self): |
| img_bytes = self.stream_reader.stdout.read(self.width * self.height * 3) |
| if not img_bytes: |
| return None |
| img = np.frombuffer(img_bytes, np.uint8).reshape([self.height, self.width, 3]) |
| return img |
|
|
| def get_frame_from_list(self): |
| if self.idx >= self.nb_frames: |
| return None |
| img = cv2.imread(self.paths[self.idx]) |
| self.idx += 1 |
| return img |
|
|
| def get_frame(self): |
| return self.get_frame_from_stream() |
|
|
|
|
| def close(self): |
| self.stream_reader.stdin.close() |
| self.stream_reader.wait() |
|
|
|
|
| class VideoWriter: |
| def __init__(self, video_save_path, height, width, fps, audio): |
| if height > 2160: |
| print('You are generating video that is larger than 4K, which will be very slow due to IO speed.', |
| 'We highly recommend to decrease the outscale(aka, -s).') |
| if audio is not None: |
| self.stream_writer = ( |
| ffmpeg.input('pipe:', format='rawvideo', pix_fmt='bgr24', s=f'{width}x{height}', |
| framerate=fps).output( |
| audio, |
| video_save_path, |
| pix_fmt='yuv420p', |
| vcodec='libx264', |
| loglevel='error', |
| acodec='copy').overwrite_output().run_async( |
| pipe_stdin=True, pipe_stdout=True, cmd='ffmpeg')) |
| else: |
| self.stream_writer = ( |
| ffmpeg.input('pipe:', format='rawvideo', pix_fmt='bgr24', s=f'{width}x{height}', |
| framerate=fps).output( |
| video_save_path, pix_fmt='yuv420p', vcodec='libx264', |
| loglevel='error').overwrite_output().run_async( |
| pipe_stdin=True, pipe_stdout=True, cmd='ffmpeg')) |
|
|
| def write_frame(self, frame): |
| try: |
| frame = frame.astype(np.uint8).tobytes() |
| self.stream_writer.stdin.write(frame) |
| except BrokenPipeError: |
| print('Please re-install ffmpeg and libx264 by running\n', |
| '\t$ conda install -c conda-forge ffmpeg\n', |
| '\t$ conda install -c conda-forge x264') |
| sys.exit(0) |
|
|
| def close(self): |
| self.stream_writer.stdin.close() |
| self.stream_writer.wait() |