Skip to content

Commit c39cb6d

Browse files
committed
Release v1.8.1: Add Spanish OCR, RealCUGAN upscaling, upscale-only mode and tile size config
1 parent 886e72b commit c39cb6d

20 files changed

Lines changed: 802 additions & 64 deletions

.gitignore

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,10 @@ venv/
77
.venv_cpu/
88
.venv_gpu/
99

10-
# Rust project directory (reference only)
11-
manga-image-translator-rust-master/
12-
1310
# Environment files (contain sensitive information)
1411
.env
1512
.env.*
1613

17-
# User configuration file (contains user-specific settings)
18-
examples/config.json
19-
2014
# Claude settings
2115
.claude/
2216

@@ -84,7 +78,7 @@ gemini_key_validator_ui.py30cadb668d2efc5c432fd2939130df0d_translations.json
8478
新建文件夹/
8579
manga-translator-ui-1.5.1/
8680
manga-translator-ui-1.7.6/
87-
BallonsTranslator-dev/
81+
BallonsTranslator-dev/*
8882
# Added by Gemini to ignore untracked files
8983
.cunzhi-memory/
9084
logs/
@@ -133,4 +127,9 @@ tmp/
133127
venv/
134128
.venv/
135129

136-
gen_scripts.py
130+
gen_scripts.py
131+
132+
# Ignore test/example directories
133+
ailab-main/
134+
manga-image-translator-rust-master/
135+
examples/config.json

desktop_qt_ui/app_logic.py

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ def update_single_config(self, full_key: str, value: Any):
309309
for key in keys[:-1]:
310310
parent_obj = getattr(parent_obj, key)
311311
setattr(parent_obj, keys[-1], value)
312+
312313
self.config_service.set_config(config_obj)
313314
self.config_service.save_config_file()
314315
self.logger.debug(f"配置已保存: '{full_key}' = '{value}'")
@@ -372,7 +373,7 @@ def get_display_mapping(self, key: str) -> Optional[Dict[str, str]]:
372373
"direction": "文本方向", "uppercase": "大写", "lowercase": "小写", "gimp_font": "GIMP字体",
373374
"font_path": "字体路径", "no_hyphenation": "禁用连字符", "font_color": "字体颜色",
374375
"auto_rotate_symbols": "竖排内横排", "rtl": "从右到左", "layout_mode": "排版模式",
375-
"upscaler": "超分模型", "revert_upscaling": "还原超分", "colorization_size": "上色大小",
376+
"upscaler": "超分模型", "upscale_ratio": "超分倍数", "realcugan_model": "Real-CUGAN模型", "tile_size": "分块大小(0=不分割)", "revert_upscaling": "还原超分", "colorization_size": "上色大小",
376377
"denoise_sigma": "降噪强度", "colorizer": "上色模型", "verbose": "详细日志",
377378
"attempts": "重试次数", "max_requests_per_minute": "每分钟最大请求数", "ignore_errors": "忽略错误", "use_gpu": "使用 GPU",
378379
"use_gpu_limited": "使用 GPU(受限)", "context_size": "上下文页数", "format": "输出格式",
@@ -403,6 +404,25 @@ def get_options_for_key(self, key: str) -> Optional[List[str]]:
403404
"alignment": [member.value for member in Alignment],
404405
"direction": [member.value for member in Direction],
405406
"upscaler": [member.value for member in Upscaler],
407+
"upscale_ratio": ["不使用", "2", "3", "4"],
408+
"realcugan_model": [
409+
"2x-conservative",
410+
"2x-conservative-pro",
411+
"2x-no-denoise",
412+
"2x-denoise1x",
413+
"2x-denoise2x",
414+
"2x-denoise3x",
415+
"2x-denoise3x-pro",
416+
"3x-conservative",
417+
"3x-conservative-pro",
418+
"3x-no-denoise",
419+
"3x-no-denoise-pro",
420+
"3x-denoise3x",
421+
"3x-denoise3x-pro",
422+
"4x-conservative",
423+
"4x-no-denoise",
424+
"4x-denoise3x",
425+
],
406426
"translator": [member.value for member in Translator],
407427
"detector": [member.value for member in Detector],
408428
"colorizer": [member.value for member in Colorizer],
@@ -1131,9 +1151,21 @@ async def _do_processing(self):
11311151
translator_config_data['attempts'] = cli_attempts
11321152
self.log_received.emit(f"--- Setting translator attempts to: {cli_attempts} (from UI config)")
11331153

1154+
# 转换超分倍数:'不使用' -> None, '2'/'4' -> int
1155+
upscale_config_data = self.config_dict.get('upscale', {}).copy()
1156+
if 'upscale_ratio' in upscale_config_data:
1157+
ratio_value = upscale_config_data['upscale_ratio']
1158+
if ratio_value == '不使用' or ratio_value is None:
1159+
upscale_config_data['upscale_ratio'] = None
1160+
else:
1161+
try:
1162+
upscale_config_data['upscale_ratio'] = int(ratio_value)
1163+
except (ValueError, TypeError):
1164+
upscale_config_data['upscale_ratio'] = None
1165+
11341166
config = Config(
11351167
render=RenderConfig(**render_config_data),
1136-
upscale=UpscaleConfig(**self.config_dict.get('upscale', {})),
1168+
upscale=UpscaleConfig(**upscale_config_data),
11371169
translator=TranslatorConfig(**translator_config_data),
11381170
detector=DetectorConfig(**self.config_dict.get('detector', {})),
11391171
colorizer=ColorizerConfig(**self.config_dict.get('colorizer', {})),
@@ -1170,7 +1202,10 @@ async def _do_processing(self):
11701202
workflow_mode = "正常翻译流程"
11711203
workflow_tip = ""
11721204
cli_config = self.config_dict.get('cli', {})
1173-
if cli_config.get('colorize_only', False):
1205+
if cli_config.get('upscale_only', False):
1206+
workflow_mode = "仅超分"
1207+
workflow_tip = "💡 提示:仅对图片进行超分处理,不进行检测、OCR、翻译和渲染"
1208+
elif cli_config.get('colorize_only', False):
11741209
workflow_mode = "仅上色"
11751210
workflow_tip = "💡 提示:仅对图片进行上色处理,不进行检测、OCR、翻译和渲染"
11761211
elif cli_config.get('generate_and_export', False):

desktop_qt_ui/core/config_models.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ class RenderSettings(BaseModel):
9191

9292
class UpscaleSettings(BaseModel):
9393
upscaler: str = "esrgan"
94+
upscale_ratio: Optional[int] = None
95+
realcugan_model: Optional[str] = None
96+
tile_size: Optional[int] = None
9497
revert_upscaling: bool = False
9598

9699
class ColorizerSettings(BaseModel):
@@ -118,6 +121,7 @@ class CliSettings(BaseModel):
118121
batch_concurrent: bool = False
119122
generate_and_export: bool = False
120123
colorize_only: bool = False
124+
upscale_only: bool = False # 仅超分模式
121125
high_quality_batch_size: int = 3
122126

123127
class AppSection(BaseModel):

desktop_qt_ui/main_view.py

Lines changed: 155 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,99 @@ def _on_setting_changed(self, value, full_key, display_map=None):
163163
reverse_map = {v: k for k, v in display_map.items()}
164164
final_value = reverse_map.get(value, value) # Fallback to value itself if not in map
165165

166+
# 特殊处理:当 upscaler 变化时,更新 upscale_ratio 动态下拉框
167+
if full_key == "upscale.upscaler":
168+
self._update_upscale_ratio_options(value)
169+
166170
self.setting_changed.emit(full_key, final_value)
167171

172+
def _on_upscale_ratio_changed(self, text, full_key):
173+
"""处理 upscale_ratio 动态下拉框的变化"""
174+
config = self.config_service.get_config()
175+
176+
if config.upscale.upscaler == "realcugan":
177+
# 当前是 realcugan
178+
if text == "不使用":
179+
# 禁用超分
180+
self.setting_changed.emit("upscale.upscale_ratio", None)
181+
self.setting_changed.emit("upscale.realcugan_model", None)
182+
else:
183+
# text 是模型名称,从中提取倍率
184+
scale_str = text.split('x')[0] if 'x' in text else None
185+
if scale_str and scale_str.isdigit():
186+
scale = int(scale_str)
187+
# 同时更新 realcugan_model 和 upscale_ratio
188+
self.setting_changed.emit("upscale.realcugan_model", text)
189+
self.setting_changed.emit("upscale.upscale_ratio", scale)
190+
else:
191+
# 无法解析倍率,只更新模型
192+
self.setting_changed.emit("upscale.realcugan_model", text)
193+
else:
194+
# 当前是其他超分模型,text 是倍率
195+
if text == "不使用":
196+
self.setting_changed.emit(full_key, None)
197+
else:
198+
try:
199+
ratio = int(text)
200+
self.setting_changed.emit(full_key, ratio)
201+
except ValueError:
202+
self.setting_changed.emit(full_key, None)
203+
204+
def _on_tile_size_input_changed(self, text, full_key):
205+
"""处理 tile_size 输入框的变化"""
206+
if not text or not text.strip():
207+
# 空值 = 使用默认值 (None)
208+
self.setting_changed.emit(full_key, None)
209+
else:
210+
try:
211+
tile_size = int(text)
212+
self.setting_changed.emit(full_key, tile_size)
213+
except ValueError:
214+
# 无效输入 = 使用默认值
215+
self.setting_changed.emit(full_key, None)
216+
217+
def _update_upscale_ratio_options(self, upscaler):
218+
"""当 upscaler 变化时,更新 upscale_ratio 下拉框的选项"""
219+
# 查找 upscale_ratio_dynamic widget
220+
upscale_ratio_widget = self.findChild(QComboBox, "upscale_ratio_dynamic")
221+
if not upscale_ratio_widget:
222+
return
223+
224+
# 阻止信号触发
225+
upscale_ratio_widget.blockSignals(True)
226+
227+
# 清空并重新填充
228+
upscale_ratio_widget.clear()
229+
230+
if upscaler == "realcugan":
231+
# 显示 Real-CUGAN 模型列表
232+
realcugan_models = self.controller.get_options_for_key("realcugan_model")
233+
if realcugan_models:
234+
# 添加"不使用"选项
235+
all_options = ["不使用"] + realcugan_models
236+
upscale_ratio_widget.addItems(all_options)
237+
# 设置默认值
238+
config = self.config_service.get_config()
239+
if config.upscale.realcugan_model:
240+
upscale_ratio_widget.setCurrentText(config.upscale.realcugan_model)
241+
elif config.upscale.upscale_ratio is None:
242+
upscale_ratio_widget.setCurrentText("不使用")
243+
elif realcugan_models:
244+
upscale_ratio_widget.setCurrentText(realcugan_models[0])
245+
else:
246+
# 显示普通倍率选项
247+
ratio_options = ["不使用", "2", "3", "4"]
248+
upscale_ratio_widget.addItems(ratio_options)
249+
# 设置默认值
250+
config = self.config_service.get_config()
251+
if config.upscale.upscale_ratio is None:
252+
upscale_ratio_widget.setCurrentText("不使用")
253+
else:
254+
upscale_ratio_widget.setCurrentText(str(config.upscale.upscale_ratio))
255+
256+
# 恢复信号
257+
upscale_ratio_widget.blockSignals(False)
258+
168259
def _create_param_widgets(self, data, parent_layout, prefix=""):
169260
if not isinstance(data, dict):
170261
return
@@ -173,7 +264,8 @@ def _create_param_widgets(self, data, parent_layout, prefix=""):
173264
full_key = f"{prefix}.{key}" if prefix else key
174265

175266
# 跳过这些选项,因为已经用下拉框替代或不需要在UI中显示
176-
if full_key in ["cli.load_text", "cli.template", "cli.generate_and_export", "cli.colorize_only"]:
267+
# realcugan_model 将通过 upscale_ratio 动态下拉框处理
268+
if full_key in ["cli.load_text", "cli.template", "cli.generate_and_export", "cli.colorize_only", "cli.upscale_only", "upscale.realcugan_model"]:
177269
continue
178270

179271
label_text = key
@@ -249,24 +341,71 @@ def showPopup(self):
249341
widget.setChecked(value)
250342
widget.stateChanged.connect(lambda state, k=full_key: self._on_setting_changed(bool(state), k, None))
251343

344+
# 特殊处理:upscale_ratio 动态下拉框(必须在 int/float 判断之前)
345+
elif full_key == "upscale.upscale_ratio":
346+
widget = QComboBox()
347+
widget.setObjectName("upscale_ratio_dynamic")
348+
349+
# 获取当前的 upscaler 值来决定显示什么选项
350+
config = self.config_service.get_config()
351+
current_upscaler = config.upscale.upscaler
352+
353+
if current_upscaler == "realcugan":
354+
# 显示 Real-CUGAN 模型列表
355+
realcugan_models = self.controller.get_options_for_key("realcugan_model")
356+
if realcugan_models:
357+
# 添加"不使用"选项
358+
all_options = ["不使用"] + realcugan_models
359+
widget.addItems(all_options)
360+
# 设置当前值(从 realcugan_model 获取)
361+
current_model = config.upscale.realcugan_model
362+
if current_model:
363+
widget.setCurrentText(current_model)
364+
elif value is None:
365+
widget.setCurrentText("不使用")
366+
elif realcugan_models:
367+
widget.setCurrentText(realcugan_models[0])
368+
else:
369+
# 显示普通倍率选项
370+
ratio_options = ["不使用", "2", "3", "4"]
371+
widget.addItems(ratio_options)
372+
# 设置当前值
373+
if value is None:
374+
widget.setCurrentText("不使用")
375+
else:
376+
widget.setCurrentText(str(value))
377+
378+
widget.currentTextChanged.connect(lambda text, k=full_key: self._on_upscale_ratio_changed(text, k))
379+
380+
# 特殊处理:tile_size 输入框(即使值为 None 也显示)
381+
elif full_key == "upscale.tile_size":
382+
widget = QLineEdit(str(value) if value is not None else "")
383+
widget.setPlaceholderText("默认: 400")
384+
widget.editingFinished.connect(lambda k=full_key, w=widget: self._on_tile_size_input_changed(w.text(), k))
385+
252386
elif isinstance(value, (int, float)):
253387
widget = QLineEdit(str(value))
254388
widget.editingFinished.connect(lambda k=full_key, w=widget: self._on_setting_changed(w.text(), k, None))
255389

256-
elif isinstance(value, str) and (options or display_map):
390+
elif (isinstance(value, str) or value is None) and (options or display_map):
257391
widget = QComboBox()
258392
if key == "translator":
259393
widget.setObjectName("translator.translator")
260394

261395
if display_map:
262396
widget.addItems(list(display_map.values()))
263-
current_display_name = display_map.get(value)
397+
current_display_name = display_map.get(value) if value is not None else None
264398
if current_display_name:
265399
widget.setCurrentText(current_display_name)
266400
widget.currentTextChanged.connect(lambda text, k=full_key, dm=display_map: self._on_setting_changed(text, k, dm))
267401
else:
268402
widget.addItems(options)
269-
widget.setCurrentText(value)
403+
if value is not None:
404+
widget.setCurrentText(value)
405+
else:
406+
# 对于 None 值,设置第一个选项为默认值(通常是 "不使用")
407+
if options:
408+
widget.setCurrentText(options[0])
270409
widget.currentTextChanged.connect(lambda text, k=full_key: self._on_setting_changed(text, k, None))
271410

272411
elif isinstance(value, str):
@@ -324,7 +463,8 @@ def _create_left_panel(self) -> QWidget:
324463
"导出翻译",
325464
"导出原文",
326465
"导入翻译并渲染",
327-
"仅上色"
466+
"仅上色",
467+
"仅超分"
328468
])
329469
self.workflow_mode_combo.currentIndexChanged.connect(self._on_workflow_mode_changed)
330470
left_layout.addWidget(self.workflow_mode_combo)
@@ -506,7 +646,9 @@ def _sync_workflow_mode_from_config(self):
506646
# 阻止信号触发,避免循环
507647
self.workflow_mode_combo.blockSignals(True)
508648

509-
if config.cli.colorize_only:
649+
if config.cli.upscale_only:
650+
self.workflow_mode_combo.setCurrentIndex(5) # 仅超分
651+
elif config.cli.colorize_only:
510652
self.workflow_mode_combo.setCurrentIndex(4) # 仅上色
511653
elif config.cli.load_text:
512654
self.workflow_mode_combo.setCurrentIndex(3) # 导入翻译并渲染
@@ -529,6 +671,7 @@ def _on_workflow_mode_changed(self, index: int):
529671
# 2: 导出原文
530672
# 3: 导入翻译并渲染
531673
# 4: 仅上色
674+
# 5: 仅超分
532675

533676
config = self.config_service.get_config()
534677

@@ -537,6 +680,7 @@ def _on_workflow_mode_changed(self, index: int):
537680
config.cli.template = False
538681
config.cli.generate_and_export = False
539682
config.cli.colorize_only = False
683+
config.cli.upscale_only = False
540684

541685
if index == 1: # 导出翻译
542686
config.cli.generate_and_export = True
@@ -546,6 +690,8 @@ def _on_workflow_mode_changed(self, index: int):
546690
config.cli.load_text = True
547691
elif index == 4: # 仅上色
548692
config.cli.colorize_only = True
693+
elif index == 5: # 仅超分
694+
config.cli.upscale_only = True
549695

550696
# ✅ 保存配置到内存和文件
551697
self.config_service.set_config(config)
@@ -561,7 +707,9 @@ def update_start_button_text(self):
561707

562708
try:
563709
config = self.config_service.get_config()
564-
if config.cli.colorize_only:
710+
if config.cli.upscale_only:
711+
self.start_button.setText("开始超分")
712+
elif config.cli.colorize_only:
565713
self.start_button.setText("开始上色")
566714
elif config.cli.load_text:
567715
self.start_button.setText("导入翻译并渲染")

0 commit comments

Comments
 (0)