Loading modules/textdetector/textblock.py +52 −91 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ import cv2 import re from utils.imgproc_utils import union_area, xywh2xyxypoly, rotate_polygons, color_difference from utils.structures import Tuple, Union, List, Dict, Config, field, nested_dataclass LANG_LIST = ['eng', 'ja', 'unknown'] LANGCLS2IDX = {'eng': 0, 'ja': 1, 'unknown': 2} Loading @@ -16,97 +17,57 @@ LANGCLS2IDX = {'eng': 0, 'ja': 1, 'unknown': 2} CJKPATTERN = re.compile(r'[\uac00-\ud7a3\u3040-\u30ff\u4e00-\u9FFF]') class TextBlock(object): def __init__(self, xyxy: List, lines: List = None, language: str = 'unknown', vertical: bool = False, font_size: float = -1, distance: List = None, angle: int = 0, vec: List = None, norm: float = -1, merged: bool = False, sort_weight: float = -1, text: List = None, translation: str = "", fg_r = 0, fg_g = 0, fg_b = 0, bg_r = 0, bg_g = 0, bg_b = 0, line_spacing = 1., letter_spacing = 1., font_family: str = "", bold: bool = False, underline: bool = False, italic: bool = False, _alignment: int = -1, rich_text: str = "", _bounding_rect: List = None, accumulate_color = True, default_stroke_width = 0.2, stroke_decide_by_colordiff: bool = True, font_weight = 50, _target_lang: str = "", opacity: float = 1., shadow_radius: float = 0., shadow_strength: float = 1., shadow_color: Tuple = (0, 0, 0), shadow_offset: List = [0, 0], **kwargs) -> None: self.xyxy = [int(num) for num in xyxy] # boundingbox of textblock self.lines = [] if lines is None else lines # polygons of textlines self.vertical = vertical # orientation of textlines self.language = language self.font_size = font_size # font pixel size self.distance = None if distance is None else np.array(distance, np.float64) # distance between textlines and "origin" self.angle = angle # rotation angle of textlines self.vec = None if vec is None else np.array(vec, np.float64) # primary vector of textblock self.norm = norm # primary norm of textblock self.merged = merged self.sort_weight = sort_weight self.text = text if text is not None else [] self.prob = 1 self.translation = translation # note they're accumulative rgb values of textlines self.fg_r = fg_r self.fg_g = fg_g self.fg_b = fg_b self.bg_r = bg_r self.bg_g = bg_g self.bg_b = bg_b # self.stroke_width = stroke_width self.font_family: str = font_family self.bold: bool = bold self.underline: bool = underline self.italic: bool = italic self.rich_text = rich_text self.line_spacing = line_spacing self.letter_spacing = letter_spacing self._alignment = _alignment self._target_lang = _target_lang self._bounding_rect = _bounding_rect self.default_stroke_width = default_stroke_width self.stroke_decide_by_colordiff = stroke_decide_by_colordiff self.font_weight = font_weight self.accumulate_color = accumulate_color self.opacity = opacity self.shadow_radius = shadow_radius self.shadow_strength = shadow_strength self.shadow_color = shadow_color self.shadow_offset = shadow_offset self.region_mask: np.ndarray = None self.region_inpaint_dict: dict = None @nested_dataclass class TextBlock: xyxy: List = field(default_factory = lambda: [0, 0, 0, 0]) lines: List = field(default_factory = lambda: []) language: str = 'unknown' vertical: bool = False font_size: float = -1. distance: np.ndarray = None angle: int = 0 vec: List = None norm: float = -1 merged: bool = False sort_weight: float = -1 text: List = field(default_factory = lambda : []) translation: str = "" fg_r: int = 0 fg_g: int = 0 fg_b: int = 0 bg_r: int = 0 bg_g: int = 0 bg_b: int = 0 line_spacing: float = 1. letter_spacing: float = 1. font_family: str = "" bold: bool = False underline: bool = False italic: bool = False _alignment: int = -1 rich_text: str = "" _bounding_rect: List = None accumulate_color: bool = True default_stroke_width: float = 0.2 stroke_decide_by_colordiff: bool = True font_weight: int = 50 _target_lang: str = "" opacity: float = 1. shadow_radius: float = 0. shadow_strength: float = 1. shadow_color: Tuple = (0, 0, 0) shadow_offset: List = field(default_factory = lambda : [0., 0.]) region_mask: np.ndarray = None region_inpaint_dict: Dict = None def __post_init__(self): if self.xyxy is not None: self.xyxy = [int(num) for num in self.xyxy] if self.distance is not None: self.distance = np.array(self.distance, np.float32) if self.vec is not None: self.vec = np.array(self.vec, np.float32) def adjust_bbox(self, with_bbox=False, x_range=None, y_range=None): lines = self.lines_array().astype(np.int32) Loading modules/translators/trans_google.py +1 −10 Original line number Diff line number Diff line Loading @@ -280,15 +280,6 @@ class TransGoogle(BaseTranslator): concate_text = True params: Dict = { 'delay': '0.0', 'url': { 'type': 'selector', 'options': [ # 'https://translate.google.cn/m', 'https://translate.google.com/m' ], 'select': 'https://translate.google.com/m' }, } def _setup_translator(self): Loading Loading @@ -320,7 +311,7 @@ class TransGoogle(BaseTranslator): self.googletrans._url_params['sl'] = self.lang_map[self.lang_source] self.googletrans._target = self.lang_map[self.lang_target] self.googletrans._url_params['tl'] = self.lang_map[self.lang_target] self.googletrans.__base_url = self.params['url']['select'] self.googletrans.__base_url = "https://translate.google.com/m" translations = [self.googletrans.translate(t) for t in src_list] return translations ui/fontformatpanel.py +8 −0 Original line number Diff line number Diff line Loading @@ -354,6 +354,14 @@ class SizeControlLabel(QLabel): self.cur_pos = new_pos return super().mouseMoveEvent(e) # dict(FontFormat) # local_fformat_change_to_func = { # 'frgb': None, # } # global_fformat_change_to_func = { # FontFormat. # } class FontFormatPanel(Widget): Loading ui/misc.py +93 −229 Original line number Diff line number Diff line Loading @@ -2,15 +2,13 @@ import cv2, re, json, os from pathlib import Path import numpy as np import os.path as osp from typing import Tuple, Union, List, Dict from qtpy.QtGui import QPixmap, QColor, QImage, QTextDocument, QTextCursor from . import constants as C from .constants import DEFAULT_FONT_FAMILY, STYLESHEET_PATH, THEME_PATH from utils.io_utils import find_all_imgs, NumpyEncoder, imread, imwrite from utils.io_utils import NumpyEncoder from utils.structures import Tuple, Union, List, Dict, Config, field, nested_dataclass from modules.textdetector.textblock import TextBlock # return bgr tuple def qrgb2bgr(color: Union[QColor, Tuple, List] = None) -> Tuple[int, int, int]: if color is not None: Loading Loading @@ -82,46 +80,27 @@ class InvalidModuleConfigException(Exception): class InvalidProgramConfigException(Exception): pass class FontFormat: def __init__(self, family: str = None, size: float = 24, stroke_width: float = 0, frgb=(0, 0, 0), srgb=(0, 0, 0), bold: bool = False, underline: bool = False, italic: bool = False, alignment: int = 0, vertical: bool = False, weight: int = 50, line_spacing: float = 1.2, letter_spacing: float = 1., opacity: float = 1., shadow_radius: float = 0., shadow_strength: float = 1., shadow_color: Tuple = (0, 0, 0), shadow_offset: List = [0, 0], **kwargs) -> None: self.family = family if family is not None else DEFAULT_FONT_FAMILY self.size = size self.stroke_width = stroke_width self.frgb = frgb # font color self.srgb = srgb # stroke color self.bold = bold self.underline = underline self.italic = italic self.weight: int = weight self.alignment: int = alignment self.vertical: bool = vertical self.line_spacing = line_spacing self.letter_spacing = letter_spacing self.opacity = opacity self.shadow_radius = shadow_radius self.shadow_strength = shadow_strength self.shadow_color = shadow_color self.shadow_offset = shadow_offset @nested_dataclass class FontFormat(Config): family: str = C.DEFAULT_FONT_FAMILY size: float = 24 stroke_width: float = 0 frgb: Tuple = (0, 0, 0) srgb: Tuple = (0, 0, 0) bold: bool = False underline: bool = False italic: bool = False alignment: int = 0 vertical: bool = False weight: int = 50 line_spacing: float = 1.2 letter_spacing: float = 1. opacity: float = 1. shadow_radius: float = 0. shadow_strength: float = 1. shadow_color: Tuple = (0, 0, 0) shadow_offset: List = field(default_factory=lambda: [0., 0.]) def from_textblock(self, text_block: TextBlock): self.family = text_block.font_family Loading @@ -148,191 +127,76 @@ class ProjHardSubExtract: self.type = 'hardsubextract' raise NotImplementedProjException('hardsubextract') class ModuleConfig: def __init__(self, textdetector: str = 'ctd', ocr = "mit48px_ctc", inpainter: str = 'lama_mpe', translator = "google", enable_ocr = True, enable_translate = True, enable_inpaint = True, textdetector_params = None, ocr_params = None, translator_params = None, inpainter_params = None, translate_source = '日本語', translate_target = '简体中文', check_need_inpaint = True, ) -> None: self.textdetector = textdetector self.ocr = ocr self.inpainter = inpainter self.translator = translator self.enable_ocr = enable_ocr self.enable_translate = enable_translate self.enable_inpaint = enable_inpaint if textdetector_params is None: self.textdetector_params = dict() else: self.textdetector_params = textdetector_params if ocr_params is None: self.ocr_params = dict() else: self.ocr_params = ocr_params if translator_params is None: self.translator_params = dict() else: self.translator_params = translator_params if 'google' in translator_params: if 'url' in translator_params['google'] and \ translator_params['google']['url']['select'] == 'https://translate.google.cn/m': translator_params['google']['url']['select'] = 'https://translate.google.com/m' if inpainter_params is None: self.inpainter_params = dict() else: self.inpainter_params = inpainter_params self.translate_source = translate_source self.translate_target = translate_target self.check_need_inpaint = check_need_inpaint def __getitem__(self, item: str): if item == 'textdetector': return self.textdetector elif item == 'ocr': return self.ocr elif item == 'translator': return self.translator elif item == 'inpainter': return self.inpainter else: raise KeyError(item) @nested_dataclass class ModuleConfig(Config): textdetector: str = 'ctd' ocr: str = "mit48px_ctc" inpainter: str = 'lama_mpe' translator: str = "google" enable_ocr: bool = True enable_translate: bool = True enable_inpaint: bool = True textdetector_params: Dict = field(default_factory=lambda: dict()) ocr_params: Dict = field(default_factory=lambda: dict()) translator_params: Dict = field(default_factory=lambda: dict()) inpainter_params: Dict = field(default_factory=lambda: dict()) translate_source: str = '日本語' translate_target: str = '简体中文' check_need_inpaint: bool = True def get_params(self, module_key: str) -> dict: if module_key == 'textdetector': return self.textdetector_params elif module_key == 'ocr': return self.ocr_params elif module_key == 'translator': return self.translator_params elif module_key == 'inpainter': return self.inpainter_params class DrawPanelConfig: def __init__(self, pentool_color: List = None, pentool_width: float = 30., pentool_shape: int = 0, inpainter_width: float = 30., inpainter_shape: int = 0, current_tool: int = 0, rectool_auto: bool = False, rectool_method: int = 0, recttool_dilate_ksize: int = 0, **kwargs) -> None: self.pentool_color = pentool_color if pentool_color is not None else [0, 0, 0] self.pentool_width = pentool_width self.pentool_shape = pentool_shape self.inpainter_width = inpainter_width self.inpainter_shape = inpainter_shape self.current_tool = current_tool self.rectool_auto = rectool_auto self.rectool_method = rectool_method self.recttool_dilate_ksize = recttool_dilate_ksize class ProgramConfig: def __init__( self, module: Union[Dict, ModuleConfig] = None, drawpanel: Union[Dict, DrawPanelConfig] = None, global_fontformat: Union[Dict, FontFormat] = None, recent_proj_list: List[str] = list(), imgtrans_paintmode: bool = False, imgtrans_textedit: bool = True, imgtrans_textblock: bool = True, mask_transparency: float = 0., original_transparency: float = 0., open_recent_on_startup: bool = True, let_fntsize_flag: int = 0, let_fntstroke_flag: int = 0, let_fntcolor_flag: int = 0, let_fnt_scolor_flag: int = 0, let_fnteffect_flag: int = 1, let_alignment_flag: int = 0, let_autolayout_flag: bool = True, let_uppercase_flag: bool = True, font_presets: dict = None, fsearch_case: bool = False, fsearch_whole_word: bool = False, fsearch_regex: bool = False, fsearch_range: int = 0, gsearch_case: bool = False, gsearch_whole_word: bool = False, gsearch_regex: bool = False, gsearch_range: int = 0, darkmode: bool = False, src_link_flag: str = '', textselect_mini_menu: bool = True, saladict_shortcut: str = "Alt+S", search_url: str = "https://www.google.com/search?q=", ocr_sublist: dict = None, mt_sublist: dict = None, display_lang: str = C.DEFAULT_DISPLAY_LANG, **kwargs) -> None: if isinstance(module, dict): self.module = ModuleConfig(**module) elif module is None: self.module = ModuleConfig() else: self.module = module if isinstance(drawpanel, dict): self.drawpanel = DrawPanelConfig(**drawpanel) elif drawpanel is None: self.drawpanel = DrawPanelConfig() else: self.drawpanel = drawpanel if isinstance(global_fontformat, dict): self.global_fontformat = FontFormat(**global_fontformat) elif global_fontformat is None: self.global_fontformat = FontFormat() else: self.global_fontformat = global_fontformat self.recent_proj_list = recent_proj_list self.imgtrans_paintmode = imgtrans_paintmode self.imgtrans_textedit = imgtrans_textedit self.imgtrans_textblock = imgtrans_textblock self.mask_transparency = mask_transparency self.original_transparency = original_transparency self.open_recent_on_startup = open_recent_on_startup self.let_fntsize_flag = let_fntsize_flag self.let_fntstroke_flag = let_fntstroke_flag self.let_fntcolor_flag = let_fntcolor_flag self.let_fnt_scolor_flag = let_fnt_scolor_flag self.let_fnteffect_flag = let_fnteffect_flag self.let_alignment_flag = let_alignment_flag self.let_autolayout_flag = let_autolayout_flag self.let_uppercase_flag = let_uppercase_flag self.font_presets = {} if font_presets is None else font_presets self.fsearch_case = fsearch_case self.fsearch_whole_word = fsearch_whole_word self.fsearch_regex = fsearch_regex self.fsearch_range = fsearch_range self.gsearch_case = gsearch_case self.gsearch_whole_word = gsearch_whole_word self.gsearch_regex = gsearch_regex self.gsearch_range = gsearch_range self.darkmode = darkmode self.src_link_flag = src_link_flag self.textselect_mini_menu = textselect_mini_menu self.saladict_shortcut = saladict_shortcut self.search_url = search_url self.ocr_sublist = [] if ocr_sublist is None else ocr_sublist self.mt_sublist = [] if mt_sublist is None else mt_sublist self.display_lang = display_lang return self[module_key + '_params'] @nested_dataclass class DrawPanelConfig(Config): pentool_color: List = field(default_factory=lambda: [0, 0, 0]) pentool_width: float = 30. pentool_shape: int = 0 inpainter_width: float = 30. inpainter_shape: int = 0 current_tool: int = 0 rectool_auto: bool = False rectool_method: int = 0 recttool_dilate_ksize: int = 0 @nested_dataclass class ProgramConfig(Config): module: ModuleConfig = field(default_factory=lambda: ModuleConfig()), drawpanel: DrawPanelConfig = field(default_factory=lambda: DrawPanelConfig()) global_fontformat: FontFormat = field(default_factory=lambda: FontFormat()) recent_proj_list: List = field(default_factory=lambda: []) imgtrans_paintmode: bool = False imgtrans_textedit: bool = True imgtrans_textblock: bool = True mask_transparency: float = 0. original_transparency: float = 0. open_recent_on_startup: bool = True let_fntsize_flag: int = 0 let_fntstroke_flag: int = 0 let_fntcolor_flag: int = 0 let_fnt_scolor_flag: int = 0 let_fnteffect_flag: int = 1 let_alignment_flag: int = 0 let_autolayout_flag: bool = True let_uppercase_flag: bool = True font_presets: dict = field(default_factory=lambda: dict()) fsearch_case: bool = False fsearch_whole_word: bool = False fsearch_regex: bool = False fsearch_range: int = 0 gsearch_case: bool = False gsearch_whole_word: bool = False gsearch_regex: bool = False gsearch_range: int = 0 darkmode: bool = False src_link_flag: str = '' textselect_mini_menu: bool = True saladict_shortcut: str = "Alt+S" search_url: str = "https://www.google.com/search?q=" ocr_sublist: dict = field(default_factory=lambda: []) mt_sublist: dict = field(default_factory=lambda: []) display_lang: str = C.DEFAULT_DISPLAY_LANG @staticmethod def load(cfg_path: str): Loading Loading @@ -459,9 +323,9 @@ def parse_stylesheet(theme: str = '', reverse_icon: bool = False) -> str: if reverse_icon: dark2light = True if theme == 'eva-light' else False reverse_icon_color(dark2light) with open(STYLESHEET_PATH, "r", encoding='utf-8') as f: with open(C.STYLESHEET_PATH, "r", encoding='utf-8') as f: stylesheet = f.read() with open(THEME_PATH, 'r', encoding='utf8') as f: with open(C.THEME_PATH, 'r', encoding='utf8') as f: theme_dict: Dict = json.loads(f.read()) if not theme or theme not in theme_dict: tgt_theme: Dict = theme_dict[list(theme_dict.keys())[0]] Loading utils/structures.py 0 → 100644 +55 −0 Original line number Diff line number Diff line from typing import Tuple, List, ClassVar, Union, Any, Dict from dataclasses import dataclass, field, is_dataclass # decorator to wrap original __init__ # https://www.geeksforgeeks.org/creating-nested-dataclass-objects-in-python/ def nested_dataclass(*args, **dataclass_kwargs): ''' nested dataclass support \n also ignore extra arguments ''' def wrapper(check_class): # passing class to investigate check_class = dataclass(check_class, **dataclass_kwargs) o_init = check_class.__init__ def __init__(self, *args, **kwargs): for name in list(kwargs.keys()): if name not in self.__annotations__: # print(f'warning: type object \'{self.__class__.__name__}\' has no attribute {name}, might be loading from an older config') kwargs.pop(name) continue value = kwargs[name] # getting field type ft = check_class.__annotations__.get(name, None) if is_dataclass(ft) and isinstance(value, dict): obj = ft(**value) kwargs[name]= obj o_init(self, *args, **kwargs) check_class.__init__=__init__ return check_class return wrapper(args[0]) if args else wrapper @dataclass class Config: def update(self, key: str, value): assert key in self.__annotations__, f'type object \'{self.__class__.__name__}\' has no attribute {key}' self.__setattr__(key, value) @classmethod def annotations_set(cls): return set(list(cls.__annotations__)) def __getitem__(self, key: str): assert key in self.__annotations__, f'type object \'{self.__class__.__name__}\' has no attribute {key}' return self.__getattribute__(key) def __setitem__(self, key: str, value): self.__setattr__(key, value) No newline at end of file Loading
modules/textdetector/textblock.py +52 −91 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ import cv2 import re from utils.imgproc_utils import union_area, xywh2xyxypoly, rotate_polygons, color_difference from utils.structures import Tuple, Union, List, Dict, Config, field, nested_dataclass LANG_LIST = ['eng', 'ja', 'unknown'] LANGCLS2IDX = {'eng': 0, 'ja': 1, 'unknown': 2} Loading @@ -16,97 +17,57 @@ LANGCLS2IDX = {'eng': 0, 'ja': 1, 'unknown': 2} CJKPATTERN = re.compile(r'[\uac00-\ud7a3\u3040-\u30ff\u4e00-\u9FFF]') class TextBlock(object): def __init__(self, xyxy: List, lines: List = None, language: str = 'unknown', vertical: bool = False, font_size: float = -1, distance: List = None, angle: int = 0, vec: List = None, norm: float = -1, merged: bool = False, sort_weight: float = -1, text: List = None, translation: str = "", fg_r = 0, fg_g = 0, fg_b = 0, bg_r = 0, bg_g = 0, bg_b = 0, line_spacing = 1., letter_spacing = 1., font_family: str = "", bold: bool = False, underline: bool = False, italic: bool = False, _alignment: int = -1, rich_text: str = "", _bounding_rect: List = None, accumulate_color = True, default_stroke_width = 0.2, stroke_decide_by_colordiff: bool = True, font_weight = 50, _target_lang: str = "", opacity: float = 1., shadow_radius: float = 0., shadow_strength: float = 1., shadow_color: Tuple = (0, 0, 0), shadow_offset: List = [0, 0], **kwargs) -> None: self.xyxy = [int(num) for num in xyxy] # boundingbox of textblock self.lines = [] if lines is None else lines # polygons of textlines self.vertical = vertical # orientation of textlines self.language = language self.font_size = font_size # font pixel size self.distance = None if distance is None else np.array(distance, np.float64) # distance between textlines and "origin" self.angle = angle # rotation angle of textlines self.vec = None if vec is None else np.array(vec, np.float64) # primary vector of textblock self.norm = norm # primary norm of textblock self.merged = merged self.sort_weight = sort_weight self.text = text if text is not None else [] self.prob = 1 self.translation = translation # note they're accumulative rgb values of textlines self.fg_r = fg_r self.fg_g = fg_g self.fg_b = fg_b self.bg_r = bg_r self.bg_g = bg_g self.bg_b = bg_b # self.stroke_width = stroke_width self.font_family: str = font_family self.bold: bool = bold self.underline: bool = underline self.italic: bool = italic self.rich_text = rich_text self.line_spacing = line_spacing self.letter_spacing = letter_spacing self._alignment = _alignment self._target_lang = _target_lang self._bounding_rect = _bounding_rect self.default_stroke_width = default_stroke_width self.stroke_decide_by_colordiff = stroke_decide_by_colordiff self.font_weight = font_weight self.accumulate_color = accumulate_color self.opacity = opacity self.shadow_radius = shadow_radius self.shadow_strength = shadow_strength self.shadow_color = shadow_color self.shadow_offset = shadow_offset self.region_mask: np.ndarray = None self.region_inpaint_dict: dict = None @nested_dataclass class TextBlock: xyxy: List = field(default_factory = lambda: [0, 0, 0, 0]) lines: List = field(default_factory = lambda: []) language: str = 'unknown' vertical: bool = False font_size: float = -1. distance: np.ndarray = None angle: int = 0 vec: List = None norm: float = -1 merged: bool = False sort_weight: float = -1 text: List = field(default_factory = lambda : []) translation: str = "" fg_r: int = 0 fg_g: int = 0 fg_b: int = 0 bg_r: int = 0 bg_g: int = 0 bg_b: int = 0 line_spacing: float = 1. letter_spacing: float = 1. font_family: str = "" bold: bool = False underline: bool = False italic: bool = False _alignment: int = -1 rich_text: str = "" _bounding_rect: List = None accumulate_color: bool = True default_stroke_width: float = 0.2 stroke_decide_by_colordiff: bool = True font_weight: int = 50 _target_lang: str = "" opacity: float = 1. shadow_radius: float = 0. shadow_strength: float = 1. shadow_color: Tuple = (0, 0, 0) shadow_offset: List = field(default_factory = lambda : [0., 0.]) region_mask: np.ndarray = None region_inpaint_dict: Dict = None def __post_init__(self): if self.xyxy is not None: self.xyxy = [int(num) for num in self.xyxy] if self.distance is not None: self.distance = np.array(self.distance, np.float32) if self.vec is not None: self.vec = np.array(self.vec, np.float32) def adjust_bbox(self, with_bbox=False, x_range=None, y_range=None): lines = self.lines_array().astype(np.int32) Loading
modules/translators/trans_google.py +1 −10 Original line number Diff line number Diff line Loading @@ -280,15 +280,6 @@ class TransGoogle(BaseTranslator): concate_text = True params: Dict = { 'delay': '0.0', 'url': { 'type': 'selector', 'options': [ # 'https://translate.google.cn/m', 'https://translate.google.com/m' ], 'select': 'https://translate.google.com/m' }, } def _setup_translator(self): Loading Loading @@ -320,7 +311,7 @@ class TransGoogle(BaseTranslator): self.googletrans._url_params['sl'] = self.lang_map[self.lang_source] self.googletrans._target = self.lang_map[self.lang_target] self.googletrans._url_params['tl'] = self.lang_map[self.lang_target] self.googletrans.__base_url = self.params['url']['select'] self.googletrans.__base_url = "https://translate.google.com/m" translations = [self.googletrans.translate(t) for t in src_list] return translations
ui/fontformatpanel.py +8 −0 Original line number Diff line number Diff line Loading @@ -354,6 +354,14 @@ class SizeControlLabel(QLabel): self.cur_pos = new_pos return super().mouseMoveEvent(e) # dict(FontFormat) # local_fformat_change_to_func = { # 'frgb': None, # } # global_fformat_change_to_func = { # FontFormat. # } class FontFormatPanel(Widget): Loading
ui/misc.py +93 −229 Original line number Diff line number Diff line Loading @@ -2,15 +2,13 @@ import cv2, re, json, os from pathlib import Path import numpy as np import os.path as osp from typing import Tuple, Union, List, Dict from qtpy.QtGui import QPixmap, QColor, QImage, QTextDocument, QTextCursor from . import constants as C from .constants import DEFAULT_FONT_FAMILY, STYLESHEET_PATH, THEME_PATH from utils.io_utils import find_all_imgs, NumpyEncoder, imread, imwrite from utils.io_utils import NumpyEncoder from utils.structures import Tuple, Union, List, Dict, Config, field, nested_dataclass from modules.textdetector.textblock import TextBlock # return bgr tuple def qrgb2bgr(color: Union[QColor, Tuple, List] = None) -> Tuple[int, int, int]: if color is not None: Loading Loading @@ -82,46 +80,27 @@ class InvalidModuleConfigException(Exception): class InvalidProgramConfigException(Exception): pass class FontFormat: def __init__(self, family: str = None, size: float = 24, stroke_width: float = 0, frgb=(0, 0, 0), srgb=(0, 0, 0), bold: bool = False, underline: bool = False, italic: bool = False, alignment: int = 0, vertical: bool = False, weight: int = 50, line_spacing: float = 1.2, letter_spacing: float = 1., opacity: float = 1., shadow_radius: float = 0., shadow_strength: float = 1., shadow_color: Tuple = (0, 0, 0), shadow_offset: List = [0, 0], **kwargs) -> None: self.family = family if family is not None else DEFAULT_FONT_FAMILY self.size = size self.stroke_width = stroke_width self.frgb = frgb # font color self.srgb = srgb # stroke color self.bold = bold self.underline = underline self.italic = italic self.weight: int = weight self.alignment: int = alignment self.vertical: bool = vertical self.line_spacing = line_spacing self.letter_spacing = letter_spacing self.opacity = opacity self.shadow_radius = shadow_radius self.shadow_strength = shadow_strength self.shadow_color = shadow_color self.shadow_offset = shadow_offset @nested_dataclass class FontFormat(Config): family: str = C.DEFAULT_FONT_FAMILY size: float = 24 stroke_width: float = 0 frgb: Tuple = (0, 0, 0) srgb: Tuple = (0, 0, 0) bold: bool = False underline: bool = False italic: bool = False alignment: int = 0 vertical: bool = False weight: int = 50 line_spacing: float = 1.2 letter_spacing: float = 1. opacity: float = 1. shadow_radius: float = 0. shadow_strength: float = 1. shadow_color: Tuple = (0, 0, 0) shadow_offset: List = field(default_factory=lambda: [0., 0.]) def from_textblock(self, text_block: TextBlock): self.family = text_block.font_family Loading @@ -148,191 +127,76 @@ class ProjHardSubExtract: self.type = 'hardsubextract' raise NotImplementedProjException('hardsubextract') class ModuleConfig: def __init__(self, textdetector: str = 'ctd', ocr = "mit48px_ctc", inpainter: str = 'lama_mpe', translator = "google", enable_ocr = True, enable_translate = True, enable_inpaint = True, textdetector_params = None, ocr_params = None, translator_params = None, inpainter_params = None, translate_source = '日本語', translate_target = '简体中文', check_need_inpaint = True, ) -> None: self.textdetector = textdetector self.ocr = ocr self.inpainter = inpainter self.translator = translator self.enable_ocr = enable_ocr self.enable_translate = enable_translate self.enable_inpaint = enable_inpaint if textdetector_params is None: self.textdetector_params = dict() else: self.textdetector_params = textdetector_params if ocr_params is None: self.ocr_params = dict() else: self.ocr_params = ocr_params if translator_params is None: self.translator_params = dict() else: self.translator_params = translator_params if 'google' in translator_params: if 'url' in translator_params['google'] and \ translator_params['google']['url']['select'] == 'https://translate.google.cn/m': translator_params['google']['url']['select'] = 'https://translate.google.com/m' if inpainter_params is None: self.inpainter_params = dict() else: self.inpainter_params = inpainter_params self.translate_source = translate_source self.translate_target = translate_target self.check_need_inpaint = check_need_inpaint def __getitem__(self, item: str): if item == 'textdetector': return self.textdetector elif item == 'ocr': return self.ocr elif item == 'translator': return self.translator elif item == 'inpainter': return self.inpainter else: raise KeyError(item) @nested_dataclass class ModuleConfig(Config): textdetector: str = 'ctd' ocr: str = "mit48px_ctc" inpainter: str = 'lama_mpe' translator: str = "google" enable_ocr: bool = True enable_translate: bool = True enable_inpaint: bool = True textdetector_params: Dict = field(default_factory=lambda: dict()) ocr_params: Dict = field(default_factory=lambda: dict()) translator_params: Dict = field(default_factory=lambda: dict()) inpainter_params: Dict = field(default_factory=lambda: dict()) translate_source: str = '日本語' translate_target: str = '简体中文' check_need_inpaint: bool = True def get_params(self, module_key: str) -> dict: if module_key == 'textdetector': return self.textdetector_params elif module_key == 'ocr': return self.ocr_params elif module_key == 'translator': return self.translator_params elif module_key == 'inpainter': return self.inpainter_params class DrawPanelConfig: def __init__(self, pentool_color: List = None, pentool_width: float = 30., pentool_shape: int = 0, inpainter_width: float = 30., inpainter_shape: int = 0, current_tool: int = 0, rectool_auto: bool = False, rectool_method: int = 0, recttool_dilate_ksize: int = 0, **kwargs) -> None: self.pentool_color = pentool_color if pentool_color is not None else [0, 0, 0] self.pentool_width = pentool_width self.pentool_shape = pentool_shape self.inpainter_width = inpainter_width self.inpainter_shape = inpainter_shape self.current_tool = current_tool self.rectool_auto = rectool_auto self.rectool_method = rectool_method self.recttool_dilate_ksize = recttool_dilate_ksize class ProgramConfig: def __init__( self, module: Union[Dict, ModuleConfig] = None, drawpanel: Union[Dict, DrawPanelConfig] = None, global_fontformat: Union[Dict, FontFormat] = None, recent_proj_list: List[str] = list(), imgtrans_paintmode: bool = False, imgtrans_textedit: bool = True, imgtrans_textblock: bool = True, mask_transparency: float = 0., original_transparency: float = 0., open_recent_on_startup: bool = True, let_fntsize_flag: int = 0, let_fntstroke_flag: int = 0, let_fntcolor_flag: int = 0, let_fnt_scolor_flag: int = 0, let_fnteffect_flag: int = 1, let_alignment_flag: int = 0, let_autolayout_flag: bool = True, let_uppercase_flag: bool = True, font_presets: dict = None, fsearch_case: bool = False, fsearch_whole_word: bool = False, fsearch_regex: bool = False, fsearch_range: int = 0, gsearch_case: bool = False, gsearch_whole_word: bool = False, gsearch_regex: bool = False, gsearch_range: int = 0, darkmode: bool = False, src_link_flag: str = '', textselect_mini_menu: bool = True, saladict_shortcut: str = "Alt+S", search_url: str = "https://www.google.com/search?q=", ocr_sublist: dict = None, mt_sublist: dict = None, display_lang: str = C.DEFAULT_DISPLAY_LANG, **kwargs) -> None: if isinstance(module, dict): self.module = ModuleConfig(**module) elif module is None: self.module = ModuleConfig() else: self.module = module if isinstance(drawpanel, dict): self.drawpanel = DrawPanelConfig(**drawpanel) elif drawpanel is None: self.drawpanel = DrawPanelConfig() else: self.drawpanel = drawpanel if isinstance(global_fontformat, dict): self.global_fontformat = FontFormat(**global_fontformat) elif global_fontformat is None: self.global_fontformat = FontFormat() else: self.global_fontformat = global_fontformat self.recent_proj_list = recent_proj_list self.imgtrans_paintmode = imgtrans_paintmode self.imgtrans_textedit = imgtrans_textedit self.imgtrans_textblock = imgtrans_textblock self.mask_transparency = mask_transparency self.original_transparency = original_transparency self.open_recent_on_startup = open_recent_on_startup self.let_fntsize_flag = let_fntsize_flag self.let_fntstroke_flag = let_fntstroke_flag self.let_fntcolor_flag = let_fntcolor_flag self.let_fnt_scolor_flag = let_fnt_scolor_flag self.let_fnteffect_flag = let_fnteffect_flag self.let_alignment_flag = let_alignment_flag self.let_autolayout_flag = let_autolayout_flag self.let_uppercase_flag = let_uppercase_flag self.font_presets = {} if font_presets is None else font_presets self.fsearch_case = fsearch_case self.fsearch_whole_word = fsearch_whole_word self.fsearch_regex = fsearch_regex self.fsearch_range = fsearch_range self.gsearch_case = gsearch_case self.gsearch_whole_word = gsearch_whole_word self.gsearch_regex = gsearch_regex self.gsearch_range = gsearch_range self.darkmode = darkmode self.src_link_flag = src_link_flag self.textselect_mini_menu = textselect_mini_menu self.saladict_shortcut = saladict_shortcut self.search_url = search_url self.ocr_sublist = [] if ocr_sublist is None else ocr_sublist self.mt_sublist = [] if mt_sublist is None else mt_sublist self.display_lang = display_lang return self[module_key + '_params'] @nested_dataclass class DrawPanelConfig(Config): pentool_color: List = field(default_factory=lambda: [0, 0, 0]) pentool_width: float = 30. pentool_shape: int = 0 inpainter_width: float = 30. inpainter_shape: int = 0 current_tool: int = 0 rectool_auto: bool = False rectool_method: int = 0 recttool_dilate_ksize: int = 0 @nested_dataclass class ProgramConfig(Config): module: ModuleConfig = field(default_factory=lambda: ModuleConfig()), drawpanel: DrawPanelConfig = field(default_factory=lambda: DrawPanelConfig()) global_fontformat: FontFormat = field(default_factory=lambda: FontFormat()) recent_proj_list: List = field(default_factory=lambda: []) imgtrans_paintmode: bool = False imgtrans_textedit: bool = True imgtrans_textblock: bool = True mask_transparency: float = 0. original_transparency: float = 0. open_recent_on_startup: bool = True let_fntsize_flag: int = 0 let_fntstroke_flag: int = 0 let_fntcolor_flag: int = 0 let_fnt_scolor_flag: int = 0 let_fnteffect_flag: int = 1 let_alignment_flag: int = 0 let_autolayout_flag: bool = True let_uppercase_flag: bool = True font_presets: dict = field(default_factory=lambda: dict()) fsearch_case: bool = False fsearch_whole_word: bool = False fsearch_regex: bool = False fsearch_range: int = 0 gsearch_case: bool = False gsearch_whole_word: bool = False gsearch_regex: bool = False gsearch_range: int = 0 darkmode: bool = False src_link_flag: str = '' textselect_mini_menu: bool = True saladict_shortcut: str = "Alt+S" search_url: str = "https://www.google.com/search?q=" ocr_sublist: dict = field(default_factory=lambda: []) mt_sublist: dict = field(default_factory=lambda: []) display_lang: str = C.DEFAULT_DISPLAY_LANG @staticmethod def load(cfg_path: str): Loading Loading @@ -459,9 +323,9 @@ def parse_stylesheet(theme: str = '', reverse_icon: bool = False) -> str: if reverse_icon: dark2light = True if theme == 'eva-light' else False reverse_icon_color(dark2light) with open(STYLESHEET_PATH, "r", encoding='utf-8') as f: with open(C.STYLESHEET_PATH, "r", encoding='utf-8') as f: stylesheet = f.read() with open(THEME_PATH, 'r', encoding='utf8') as f: with open(C.THEME_PATH, 'r', encoding='utf8') as f: theme_dict: Dict = json.loads(f.read()) if not theme or theme not in theme_dict: tgt_theme: Dict = theme_dict[list(theme_dict.keys())[0]] Loading
utils/structures.py 0 → 100644 +55 −0 Original line number Diff line number Diff line from typing import Tuple, List, ClassVar, Union, Any, Dict from dataclasses import dataclass, field, is_dataclass # decorator to wrap original __init__ # https://www.geeksforgeeks.org/creating-nested-dataclass-objects-in-python/ def nested_dataclass(*args, **dataclass_kwargs): ''' nested dataclass support \n also ignore extra arguments ''' def wrapper(check_class): # passing class to investigate check_class = dataclass(check_class, **dataclass_kwargs) o_init = check_class.__init__ def __init__(self, *args, **kwargs): for name in list(kwargs.keys()): if name not in self.__annotations__: # print(f'warning: type object \'{self.__class__.__name__}\' has no attribute {name}, might be loading from an older config') kwargs.pop(name) continue value = kwargs[name] # getting field type ft = check_class.__annotations__.get(name, None) if is_dataclass(ft) and isinstance(value, dict): obj = ft(**value) kwargs[name]= obj o_init(self, *args, **kwargs) check_class.__init__=__init__ return check_class return wrapper(args[0]) if args else wrapper @dataclass class Config: def update(self, key: str, value): assert key in self.__annotations__, f'type object \'{self.__class__.__name__}\' has no attribute {key}' self.__setattr__(key, value) @classmethod def annotations_set(cls): return set(list(cls.__annotations__)) def __getitem__(self, key: str): assert key in self.__annotations__, f'type object \'{self.__class__.__name__}\' has no attribute {key}' return self.__getattribute__(key) def __setitem__(self, key: str, value): self.__setattr__(key, value) No newline at end of file