Path: blob/master/merger/InteractiveMergerSubprocessor.py
628 views
import multiprocessing1import os2import pickle3import sys4import traceback5from pathlib import Path67import numpy as np89from core import imagelib, pathex10from core.cv2ex import *11from core.interact import interact as io12from core.joblib import Subprocessor13from merger import MergeFaceAvatar, MergeMasked, MergerConfig1415from .MergerScreen import Screen, ScreenManager1617MERGER_DEBUG = False18class InteractiveMergerSubprocessor(Subprocessor):1920class Frame(object):21def __init__(self, prev_temporal_frame_infos=None,22frame_info=None,23next_temporal_frame_infos=None):24self.prev_temporal_frame_infos = prev_temporal_frame_infos25self.frame_info = frame_info26self.next_temporal_frame_infos = next_temporal_frame_infos27self.output_filepath = None28self.output_mask_filepath = None2930self.idx = None31self.cfg = None32self.is_done = False33self.is_processing = False34self.is_shown = False35self.image = None3637class ProcessingFrame(object):38def __init__(self, idx=None,39cfg=None,40prev_temporal_frame_infos=None,41frame_info=None,42next_temporal_frame_infos=None,43output_filepath=None,44output_mask_filepath=None,45need_return_image = False):46self.idx = idx47self.cfg = cfg48self.prev_temporal_frame_infos = prev_temporal_frame_infos49self.frame_info = frame_info50self.next_temporal_frame_infos = next_temporal_frame_infos51self.output_filepath = output_filepath52self.output_mask_filepath = output_mask_filepath5354self.need_return_image = need_return_image55if self.need_return_image:56self.image = None5758class Cli(Subprocessor.Cli):5960#override61def on_initialize(self, client_dict):62self.log_info ('Running on %s.' % (client_dict['device_name']) )63self.device_idx = client_dict['device_idx']64self.device_name = client_dict['device_name']65self.predictor_func = client_dict['predictor_func']66self.predictor_input_shape = client_dict['predictor_input_shape']67self.face_enhancer_func = client_dict['face_enhancer_func']68self.xseg_256_extract_func = client_dict['xseg_256_extract_func']697071#transfer and set stdin in order to work code.interact in debug subprocess72stdin_fd = client_dict['stdin_fd']73if stdin_fd is not None:74sys.stdin = os.fdopen(stdin_fd)7576return None7778#override79def process_data(self, pf): #pf=ProcessingFrame80cfg = pf.cfg.copy()8182frame_info = pf.frame_info83filepath = frame_info.filepath8485if len(frame_info.landmarks_list) == 0:8687if cfg.mode == 'raw-predict':88h,w,c = self.predictor_input_shape89img_bgr = np.zeros( (h,w,3), dtype=np.uint8)90img_mask = np.zeros( (h,w,1), dtype=np.uint8)91else:92self.log_info (f'no faces found for {filepath.name}, copying without faces')93img_bgr = cv2_imread(filepath)94imagelib.normalize_channels(img_bgr, 3)95h,w,c = img_bgr.shape96img_mask = np.zeros( (h,w,1), dtype=img_bgr.dtype)9798cv2_imwrite (pf.output_filepath, img_bgr)99cv2_imwrite (pf.output_mask_filepath, img_mask)100101if pf.need_return_image:102pf.image = np.concatenate ([img_bgr, img_mask], axis=-1)103104else:105if cfg.type == MergerConfig.TYPE_MASKED:106try:107final_img = MergeMasked (self.predictor_func, self.predictor_input_shape,108face_enhancer_func=self.face_enhancer_func,109xseg_256_extract_func=self.xseg_256_extract_func,110cfg=cfg,111frame_info=frame_info)112except Exception as e:113e_str = traceback.format_exc()114if 'MemoryError' in e_str:115raise Subprocessor.SilenceException116else:117raise Exception( f'Error while merging file [{filepath}]: {e_str}' )118119elif cfg.type == MergerConfig.TYPE_FACE_AVATAR:120final_img = MergeFaceAvatar (self.predictor_func, self.predictor_input_shape,121cfg, pf.prev_temporal_frame_infos,122pf.frame_info,123pf.next_temporal_frame_infos )124125cv2_imwrite (pf.output_filepath, final_img[...,0:3] )126cv2_imwrite (pf.output_mask_filepath, final_img[...,3:4] )127128if pf.need_return_image:129pf.image = final_img130131return pf132133#overridable134def get_data_name (self, pf):135#return string identificator of your data136return pf.frame_info.filepath137138139140141#override142def __init__(self, is_interactive, merger_session_filepath, predictor_func, predictor_input_shape, face_enhancer_func, xseg_256_extract_func, merger_config, frames, frames_root_path, output_path, output_mask_path, model_iter, subprocess_count=4):143if len (frames) == 0:144raise ValueError ("len (frames) == 0")145146super().__init__('Merger', InteractiveMergerSubprocessor.Cli, io_loop_sleep_time=0.001)147148self.is_interactive = is_interactive149self.merger_session_filepath = Path(merger_session_filepath)150self.merger_config = merger_config151152self.predictor_func = predictor_func153self.predictor_input_shape = predictor_input_shape154155self.face_enhancer_func = face_enhancer_func156self.xseg_256_extract_func = xseg_256_extract_func157158self.frames_root_path = frames_root_path159self.output_path = output_path160self.output_mask_path = output_mask_path161self.model_iter = model_iter162163self.prefetch_frame_count = self.process_count = subprocess_count164165session_data = None166if self.is_interactive and self.merger_session_filepath.exists():167io.input_skip_pending()168if io.input_bool ("Use saved session?", True):169try:170with open( str(self.merger_session_filepath), "rb") as f:171session_data = pickle.loads(f.read())172173except Exception as e:174pass175176rewind_to_frame_idx = None177self.frames = frames178self.frames_idxs = [ *range(len(self.frames)) ]179self.frames_done_idxs = []180181if self.is_interactive and session_data is not None:182# Loaded session data, check it183s_frames = session_data.get('frames', None)184s_frames_idxs = session_data.get('frames_idxs', None)185s_frames_done_idxs = session_data.get('frames_done_idxs', None)186s_model_iter = session_data.get('model_iter', None)187188frames_equal = (s_frames is not None) and \189(s_frames_idxs is not None) and \190(s_frames_done_idxs is not None) and \191(s_model_iter is not None) and \192(len(frames) == len(s_frames)) # frames count must match193194if frames_equal:195for i in range(len(frames)):196frame = frames[i]197s_frame = s_frames[i]198# frames filenames must match199if frame.frame_info.filepath.name != s_frame.frame_info.filepath.name:200frames_equal = False201if not frames_equal:202break203204if frames_equal:205io.log_info ('Using saved session from ' + '/'.join (self.merger_session_filepath.parts[-2:]) )206207for frame in s_frames:208if frame.cfg is not None:209# recreate MergerConfig class using constructor with get_config() as dict params210# so if any new param will be added, old merger session will work properly211frame.cfg = frame.cfg.__class__( **frame.cfg.get_config() )212213self.frames = s_frames214self.frames_idxs = s_frames_idxs215self.frames_done_idxs = s_frames_done_idxs216217if self.model_iter != s_model_iter:218# model was more trained, recompute all frames219rewind_to_frame_idx = -1220for frame in self.frames:221frame.is_done = False222elif len(self.frames_idxs) == 0:223# all frames are done?224rewind_to_frame_idx = -1225226if len(self.frames_idxs) != 0:227cur_frame = self.frames[self.frames_idxs[0]]228cur_frame.is_shown = False229230if not frames_equal:231session_data = None232233if session_data is None:234for filename in pathex.get_image_paths(self.output_path): #remove all images in output_path235Path(filename).unlink()236237for filename in pathex.get_image_paths(self.output_mask_path): #remove all images in output_mask_path238Path(filename).unlink()239240241frames[0].cfg = self.merger_config.copy()242243for i in range( len(self.frames) ):244frame = self.frames[i]245frame.idx = i246frame.output_filepath = self.output_path / ( frame.frame_info.filepath.stem + '.png' )247frame.output_mask_filepath = self.output_mask_path / ( frame.frame_info.filepath.stem + '.png' )248249if not frame.output_filepath.exists() or \250not frame.output_mask_filepath.exists():251# if some frame does not exist, recompute and rewind252frame.is_done = False253frame.is_shown = False254255if rewind_to_frame_idx is None:256rewind_to_frame_idx = i-1257else:258rewind_to_frame_idx = min(rewind_to_frame_idx, i-1)259260if rewind_to_frame_idx is not None:261while len(self.frames_done_idxs) > 0:262if self.frames_done_idxs[-1] > rewind_to_frame_idx:263prev_frame = self.frames[self.frames_done_idxs.pop()]264self.frames_idxs.insert(0, prev_frame.idx)265else:266break267#override268def process_info_generator(self):269r = [0] if MERGER_DEBUG else range(self.process_count)270271for i in r:272yield 'CPU%d' % (i), {}, {'device_idx': i,273'device_name': 'CPU%d' % (i),274'predictor_func': self.predictor_func,275'predictor_input_shape' : self.predictor_input_shape,276'face_enhancer_func': self.face_enhancer_func,277'xseg_256_extract_func' : self.xseg_256_extract_func,278'stdin_fd': sys.stdin.fileno() if MERGER_DEBUG else None279}280281#overridable optional282def on_clients_initialized(self):283io.progress_bar ("Merging", len(self.frames_idxs)+len(self.frames_done_idxs), initial=len(self.frames_done_idxs) )284285self.process_remain_frames = not self.is_interactive286self.is_interactive_quitting = not self.is_interactive287288if self.is_interactive:289help_images = {290MergerConfig.TYPE_MASKED : cv2_imread ( str(Path(__file__).parent / 'gfx' / 'help_merger_masked.jpg') ),291MergerConfig.TYPE_FACE_AVATAR : cv2_imread ( str(Path(__file__).parent / 'gfx' / 'help_merger_face_avatar.jpg') ),292}293294self.main_screen = Screen(initial_scale_to_width=1368, image=None, waiting_icon=True)295self.help_screen = Screen(initial_scale_to_height=768, image=help_images[self.merger_config.type], waiting_icon=False)296self.screen_manager = ScreenManager( "Merger", [self.main_screen, self.help_screen], capture_keys=True )297self.screen_manager.set_current (self.help_screen)298self.screen_manager.show_current()299300self.masked_keys_funcs = {301'`' : lambda cfg,shift_pressed: cfg.set_mode(0),302'1' : lambda cfg,shift_pressed: cfg.set_mode(1),303'2' : lambda cfg,shift_pressed: cfg.set_mode(2),304'3' : lambda cfg,shift_pressed: cfg.set_mode(3),305'4' : lambda cfg,shift_pressed: cfg.set_mode(4),306'5' : lambda cfg,shift_pressed: cfg.set_mode(5),307'6' : lambda cfg,shift_pressed: cfg.set_mode(6),308'q' : lambda cfg,shift_pressed: cfg.add_hist_match_threshold(1 if not shift_pressed else 5),309'a' : lambda cfg,shift_pressed: cfg.add_hist_match_threshold(-1 if not shift_pressed else -5),310'w' : lambda cfg,shift_pressed: cfg.add_erode_mask_modifier(1 if not shift_pressed else 5),311's' : lambda cfg,shift_pressed: cfg.add_erode_mask_modifier(-1 if not shift_pressed else -5),312'e' : lambda cfg,shift_pressed: cfg.add_blur_mask_modifier(1 if not shift_pressed else 5),313'd' : lambda cfg,shift_pressed: cfg.add_blur_mask_modifier(-1 if not shift_pressed else -5),314'r' : lambda cfg,shift_pressed: cfg.add_motion_blur_power(1 if not shift_pressed else 5),315'f' : lambda cfg,shift_pressed: cfg.add_motion_blur_power(-1 if not shift_pressed else -5),316't' : lambda cfg,shift_pressed: cfg.add_super_resolution_power(1 if not shift_pressed else 5),317'g' : lambda cfg,shift_pressed: cfg.add_super_resolution_power(-1 if not shift_pressed else -5),318'y' : lambda cfg,shift_pressed: cfg.add_blursharpen_amount(1 if not shift_pressed else 5),319'h' : lambda cfg,shift_pressed: cfg.add_blursharpen_amount(-1 if not shift_pressed else -5),320'u' : lambda cfg,shift_pressed: cfg.add_output_face_scale(1 if not shift_pressed else 5),321'j' : lambda cfg,shift_pressed: cfg.add_output_face_scale(-1 if not shift_pressed else -5),322'i' : lambda cfg,shift_pressed: cfg.add_image_denoise_power(1 if not shift_pressed else 5),323'k' : lambda cfg,shift_pressed: cfg.add_image_denoise_power(-1 if not shift_pressed else -5),324'o' : lambda cfg,shift_pressed: cfg.add_bicubic_degrade_power(1 if not shift_pressed else 5),325'l' : lambda cfg,shift_pressed: cfg.add_bicubic_degrade_power(-1 if not shift_pressed else -5),326'p' : lambda cfg,shift_pressed: cfg.add_color_degrade_power(1 if not shift_pressed else 5),327';' : lambda cfg,shift_pressed: cfg.add_color_degrade_power(-1),328':' : lambda cfg,shift_pressed: cfg.add_color_degrade_power(-5),329'z' : lambda cfg,shift_pressed: cfg.toggle_masked_hist_match(),330'x' : lambda cfg,shift_pressed: cfg.toggle_mask_mode(),331'c' : lambda cfg,shift_pressed: cfg.toggle_color_transfer_mode(),332'n' : lambda cfg,shift_pressed: cfg.toggle_sharpen_mode(),333}334self.masked_keys = list(self.masked_keys_funcs.keys())335336#overridable optional337def on_clients_finalized(self):338io.progress_bar_close()339340if self.is_interactive:341self.screen_manager.finalize()342343for frame in self.frames:344frame.output_filepath = None345frame.output_mask_filepath = None346frame.image = None347348session_data = {349'frames': self.frames,350'frames_idxs': self.frames_idxs,351'frames_done_idxs': self.frames_done_idxs,352'model_iter' : self.model_iter,353}354self.merger_session_filepath.write_bytes( pickle.dumps(session_data) )355356io.log_info ("Session is saved to " + '/'.join (self.merger_session_filepath.parts[-2:]) )357358#override359def on_tick(self):360io.process_messages()361362go_prev_frame = False363go_first_frame = False364go_prev_frame_overriding_cfg = False365go_first_frame_overriding_cfg = False366367go_next_frame = self.process_remain_frames368go_next_frame_overriding_cfg = False369go_last_frame_overriding_cfg = False370371cur_frame = None372if len(self.frames_idxs) != 0:373cur_frame = self.frames[self.frames_idxs[0]]374375if self.is_interactive:376377screen_image = None if self.process_remain_frames else \378self.main_screen.get_image()379380self.main_screen.set_waiting_icon( self.process_remain_frames or \381self.is_interactive_quitting )382383if cur_frame is not None and not self.is_interactive_quitting:384385if not self.process_remain_frames:386if cur_frame.is_done:387if not cur_frame.is_shown:388if cur_frame.image is None:389image = cv2_imread (cur_frame.output_filepath, verbose=False)390image_mask = cv2_imread (cur_frame.output_mask_filepath, verbose=False)391if image is None or image_mask is None:392# unable to read? recompute then393cur_frame.is_done = False394else:395image = imagelib.normalize_channels(image, 3)396image_mask = imagelib.normalize_channels(image_mask, 1)397cur_frame.image = np.concatenate([image, image_mask], -1)398399if cur_frame.is_done:400io.log_info (cur_frame.cfg.to_string( cur_frame.frame_info.filepath.name) )401cur_frame.is_shown = True402screen_image = cur_frame.image403else:404self.main_screen.set_waiting_icon(True)405406self.main_screen.set_image(screen_image)407self.screen_manager.show_current()408409key_events = self.screen_manager.get_key_events()410key, chr_key, ctrl_pressed, alt_pressed, shift_pressed = key_events[-1] if len(key_events) > 0 else (0,0,False,False,False)411412if key == 9: #tab413self.screen_manager.switch_screens()414else:415if key == 27: #esc416self.is_interactive_quitting = True417elif self.screen_manager.get_current() is self.main_screen:418419if self.merger_config.type == MergerConfig.TYPE_MASKED and chr_key in self.masked_keys:420self.process_remain_frames = False421422if cur_frame is not None:423cfg = cur_frame.cfg424prev_cfg = cfg.copy()425426if cfg.type == MergerConfig.TYPE_MASKED:427self.masked_keys_funcs[chr_key](cfg, shift_pressed)428429if prev_cfg != cfg:430io.log_info ( cfg.to_string(cur_frame.frame_info.filepath.name) )431cur_frame.is_done = False432cur_frame.is_shown = False433else:434435if chr_key == ',' or chr_key == 'm':436self.process_remain_frames = False437go_prev_frame = True438439if chr_key == ',':440if shift_pressed:441go_first_frame = True442443elif chr_key == 'm':444if not shift_pressed:445go_prev_frame_overriding_cfg = True446else:447go_first_frame_overriding_cfg = True448449elif chr_key == '.' or chr_key == '/':450self.process_remain_frames = False451go_next_frame = True452453if chr_key == '.':454if shift_pressed:455self.process_remain_frames = not self.process_remain_frames456457elif chr_key == '/':458if not shift_pressed:459go_next_frame_overriding_cfg = True460else:461go_last_frame_overriding_cfg = True462463elif chr_key == '-':464self.screen_manager.get_current().diff_scale(-0.1)465elif chr_key == '=':466self.screen_manager.get_current().diff_scale(0.1)467elif chr_key == 'v':468self.screen_manager.get_current().toggle_show_checker_board()469470if go_prev_frame:471if cur_frame is None or cur_frame.is_done:472if cur_frame is not None:473cur_frame.image = None474475while True:476if len(self.frames_done_idxs) > 0:477prev_frame = self.frames[self.frames_done_idxs.pop()]478self.frames_idxs.insert(0, prev_frame.idx)479prev_frame.is_shown = False480io.progress_bar_inc(-1)481482if cur_frame is not None and (go_prev_frame_overriding_cfg or go_first_frame_overriding_cfg):483if prev_frame.cfg != cur_frame.cfg:484prev_frame.cfg = cur_frame.cfg.copy()485prev_frame.is_done = False486487cur_frame = prev_frame488489if go_first_frame_overriding_cfg or go_first_frame:490if len(self.frames_done_idxs) > 0:491continue492break493494elif go_next_frame:495if cur_frame is not None and cur_frame.is_done:496cur_frame.image = None497cur_frame.is_shown = True498self.frames_done_idxs.append(cur_frame.idx)499self.frames_idxs.pop(0)500io.progress_bar_inc(1)501502f = self.frames503504if len(self.frames_idxs) != 0:505next_frame = f[ self.frames_idxs[0] ]506next_frame.is_shown = False507508if go_next_frame_overriding_cfg or go_last_frame_overriding_cfg:509510if go_next_frame_overriding_cfg:511to_frames = next_frame.idx+1512else:513to_frames = len(f)514515for i in range( next_frame.idx, to_frames ):516f[i].cfg = None517518for i in range( min(len(self.frames_idxs), self.prefetch_frame_count) ):519frame = f[ self.frames_idxs[i] ]520if frame.cfg is None:521if i == 0:522frame.cfg = cur_frame.cfg.copy()523else:524frame.cfg = f[ self.frames_idxs[i-1] ].cfg.copy()525526frame.is_done = False #initiate solve again527frame.is_shown = False528529if len(self.frames_idxs) == 0:530self.process_remain_frames = False531532return (self.is_interactive and self.is_interactive_quitting) or \533(not self.is_interactive and self.process_remain_frames == False)534535536#override537def on_data_return (self, host_dict, pf):538frame = self.frames[pf.idx]539frame.is_done = False540frame.is_processing = False541542#override543def on_result (self, host_dict, pf_sent, pf_result):544frame = self.frames[pf_result.idx]545frame.is_processing = False546if frame.cfg == pf_result.cfg:547frame.is_done = True548frame.image = pf_result.image549550#override551def get_data(self, host_dict):552if self.is_interactive and self.is_interactive_quitting:553return None554555for i in range ( min(len(self.frames_idxs), self.prefetch_frame_count) ):556frame = self.frames[ self.frames_idxs[i] ]557558if not frame.is_done and not frame.is_processing and frame.cfg is not None:559frame.is_processing = True560return InteractiveMergerSubprocessor.ProcessingFrame(idx=frame.idx,561cfg=frame.cfg.copy(),562prev_temporal_frame_infos=frame.prev_temporal_frame_infos,563frame_info=frame.frame_info,564next_temporal_frame_infos=frame.next_temporal_frame_infos,565output_filepath=frame.output_filepath,566output_mask_filepath=frame.output_mask_filepath,567need_return_image=True )568569return None570571#override572def get_result(self):573return 0574575