Showing preview only (9,636K chars total). Download the full file or copy to clipboard to get everything.
Repository: zyddnys/manga-image-translator
Branch: main
Commit: 8e256098da2a
Files: 304
Total size: 63.2 MB
Directory structure:
gitextract_jo7y9pqx/
├── .dockerignore
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ └── feature_request.yml
│ ├── run.bat
│ └── workflows/
│ ├── ci.yml
│ ├── release.yml
│ └── stale.yml
├── .gitignore
├── CHANGELOG.md
├── CHANGELOG_CN.md
├── Dockerfile
├── LICENSE
├── Makefile
├── MangaStudioMain.py
├── MangaStudioMainRun.bat
├── MangaStudio_Data/
│ ├── README.md
│ ├── app/
│ │ ├── __init__.py
│ │ ├── core/
│ │ │ ├── __init__.py
│ │ │ ├── config_loader.py
│ │ │ ├── constants.py
│ │ │ └── pipeline.py
│ │ └── ui/
│ │ ├── __init__.py
│ │ └── main_window.py
│ ├── dicts/
│ │ ├── example_post_dict.txt
│ │ └── example_pre_dict.txt
│ ├── gpt_configs/
│ │ └── my_cool_prompt.yaml
│ ├── profiles/
│ │ └── Admin Preset (Creator's custom config - hardware dependent).json
│ ├── tasks.json
│ ├── temp/
│ │ └── .gitkeep
│ ├── themes/
│ │ ├── abyssal_depths.json
│ │ ├── classic_paper.json
│ │ ├── default_dark.json
│ │ ├── dracula.json
│ │ ├── emerald_sea.json
│ │ ├── golden_sands.json
│ │ ├── graphite.json
│ │ ├── lavender_dream.json
│ │ ├── matrix_code.json
│ │ ├── minty_fresh.json
│ │ ├── nordic_noir.json
│ │ ├── ocean_breeze.json
│ │ ├── ruby_glow.json
│ │ ├── sakura_sunset.json
│ │ └── solarized_light.json
│ └── ui_map.json
├── README.md
├── README_CN.md
├── demo/
│ └── doc/
│ ├── docker-compose-local-dev.yml
│ ├── docker-compose-web-with-cpu.yml
│ └── docker-compose-web-with-gpu.yml
├── devscripts/
│ ├── make_readme.py
│ └── utils.py
├── dict/
│ ├── galtransl_dict.txt
│ ├── mit_glossary.txt
│ ├── post_dict.txt
│ ├── pre_dict.txt
│ └── sakura_dict.txt
├── docker-compose.debug.yml
├── docker-compose.yml
├── docker_prepare.py
├── examples/
│ ├── Example.env
│ ├── config-example.json
│ ├── config-example.toml
│ ├── gpt_config-example.yaml
│ ├── response.cpp
│ ├── response.rs
│ └── translator_chain_example.json
├── fonts/
│ ├── NotoSansMonoCJK-VF.ttf.ttc
│ ├── msgothic.ttc
│ └── msyh.ttc
├── front/
│ ├── .dockerignore
│ ├── .gitignore
│ ├── Dockerfile
│ ├── README.md
│ ├── README_CN.md
│ ├── app/
│ │ ├── App.tsx
│ │ ├── app.css
│ │ ├── components/
│ │ │ ├── Header.tsx
│ │ │ ├── ImageHandlingArea.tsx
│ │ │ ├── ImageQueue.tsx
│ │ │ ├── LabeledInput.tsx
│ │ │ ├── LabeledSelect.tsx
│ │ │ ├── OptionsPanel.tsx
│ │ │ ├── PreviewImage.tsx
│ │ │ └── ResultGallery.tsx
│ │ ├── config.ts
│ │ ├── root.tsx
│ │ ├── routes/
│ │ │ └── home.tsx
│ │ ├── routes.ts
│ │ ├── types.ts
│ │ └── utils/
│ │ ├── fetchStatusText.ts
│ │ ├── getTranslatorName.ts
│ │ └── localStorage.ts
│ ├── package.json
│ ├── react-router.config.ts
│ ├── tsconfig.json
│ └── vite.config.ts
├── manga_translator/
│ ├── __init__.py
│ ├── __main__.py
│ ├── args.py
│ ├── colorization/
│ │ ├── __init__.py
│ │ ├── common.py
│ │ ├── manga_colorization_v2.py
│ │ └── manga_colorization_v2_utils/
│ │ ├── denoising/
│ │ │ ├── denoiser.py
│ │ │ ├── functions.py
│ │ │ ├── models.py
│ │ │ └── utils.py
│ │ ├── networks/
│ │ │ ├── extractor.py
│ │ │ └── models.py
│ │ └── utils/
│ │ └── utils.py
│ ├── config.py
│ ├── detection/
│ │ ├── __init__.py
│ │ ├── common.py
│ │ ├── common_rust.py
│ │ ├── craft.py
│ │ ├── craft_utils/
│ │ │ ├── refiner.py
│ │ │ └── vgg16_bn.py
│ │ ├── ctd.py
│ │ ├── ctd_utils/
│ │ │ ├── __init__.py
│ │ │ ├── basemodel.py
│ │ │ ├── textmask.py
│ │ │ ├── utils/
│ │ │ │ ├── db_utils.py
│ │ │ │ ├── imgproc_utils.py
│ │ │ │ ├── io_utils.py
│ │ │ │ ├── weight_init.py
│ │ │ │ └── yolov5_utils.py
│ │ │ └── yolov5/
│ │ │ ├── common.py
│ │ │ └── yolo.py
│ │ ├── dbnet_convnext.py
│ │ ├── default.py
│ │ ├── default_utils/
│ │ │ ├── CRAFT_resnet34.py
│ │ │ ├── DBHead.py
│ │ │ ├── DBNet_resnet101.py
│ │ │ ├── DBNet_resnet34.py
│ │ │ ├── craft_utils.py
│ │ │ ├── dbnet_utils.py
│ │ │ └── imgproc.py
│ │ ├── none.py
│ │ ├── paddle_rust.py
│ │ └── panel_finder.py
│ ├── inpainting/
│ │ ├── __init__.py
│ │ ├── booru_tagger.py
│ │ ├── common.py
│ │ ├── guided_ldm_inpaint4_v15.yaml
│ │ ├── guided_ldm_inpaint9_v15.yaml
│ │ ├── guided_ldm_inpainting.py
│ │ ├── inpainting_aot.py
│ │ ├── inpainting_attn.py
│ │ ├── inpainting_lama.py
│ │ ├── inpainting_lama_mpe.py
│ │ ├── inpainting_sd.py
│ │ ├── ldm/
│ │ │ ├── __init__.py
│ │ │ ├── data/
│ │ │ │ ├── __init__.py
│ │ │ │ └── util.py
│ │ │ ├── models/
│ │ │ │ ├── autoencoder.py
│ │ │ │ └── diffusion/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── ddim.py
│ │ │ │ ├── ddpm.py
│ │ │ │ ├── dpm_solver/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── dpm_solver.py
│ │ │ │ │ └── sampler.py
│ │ │ │ ├── plms.py
│ │ │ │ └── sampling_util.py
│ │ │ ├── modules/
│ │ │ │ ├── attention.py
│ │ │ │ ├── diffusionmodules/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── model.py
│ │ │ │ │ ├── openaimodel.py
│ │ │ │ │ ├── upscaling.py
│ │ │ │ │ └── util.py
│ │ │ │ ├── distributions/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── distributions.py
│ │ │ │ ├── ema.py
│ │ │ │ ├── encoders/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── modules.py
│ │ │ │ ├── image_degradation/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── bsrgan.py
│ │ │ │ │ ├── bsrgan_light.py
│ │ │ │ │ └── utils_image.py
│ │ │ │ └── midas/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── api.py
│ │ │ │ ├── midas/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── base_model.py
│ │ │ │ │ ├── blocks.py
│ │ │ │ │ ├── dpt_depth.py
│ │ │ │ │ ├── midas_net.py
│ │ │ │ │ ├── midas_net_custom.py
│ │ │ │ │ ├── transforms.py
│ │ │ │ │ └── vit.py
│ │ │ │ └── utils.py
│ │ │ └── util.py
│ │ ├── none.py
│ │ ├── original.py
│ │ └── sd_hack.py
│ ├── manga_translator.py
│ ├── mask_refinement/
│ │ ├── __init__.py
│ │ └── text_mask_utils.py
│ ├── mode/
│ │ ├── __init__.py
│ │ ├── local.py
│ │ ├── share.py
│ │ └── ws.py
│ ├── ocr/
│ │ ├── __init__.py
│ │ ├── common.py
│ │ ├── model_32px.py
│ │ ├── model_48px.py
│ │ ├── model_48px_ctc.py
│ │ ├── model_manga_ocr.py
│ │ ├── model_ocr_large.py
│ │ └── xpos_relative_position.py
│ ├── rendering/
│ │ ├── __init__.py
│ │ ├── ballon_extractor.py
│ │ ├── gimp_render.py
│ │ ├── text_render.py
│ │ ├── text_render_eng.py
│ │ └── text_render_pillow_eng.py
│ ├── save.py
│ ├── textline_merge/
│ │ └── __init__.py
│ ├── translators/
│ │ ├── __init__.py
│ │ ├── baidu.py
│ │ ├── caiyun.py
│ │ ├── chatgpt.py
│ │ ├── chatgpt_2stage.py
│ │ ├── common.py
│ │ ├── common_gpt.py
│ │ ├── config_gpt.py
│ │ ├── custom_openai.py
│ │ ├── deepl.py
│ │ ├── deepseek.py
│ │ ├── gemini.py
│ │ ├── gemini_2stage.py
│ │ ├── google.py
│ │ ├── google_gtoken.py
│ │ ├── groq.py
│ │ ├── keys.py
│ │ ├── m2m100.py
│ │ ├── mbart50.py
│ │ ├── nllb.py
│ │ ├── none.py
│ │ ├── original.py
│ │ ├── papago.py
│ │ ├── qwen2.py
│ │ ├── sakura.py
│ │ ├── selective.py
│ │ ├── sugoi.py
│ │ ├── tokenizers/
│ │ │ ├── deepseek/
│ │ │ │ ├── tokenizer.json
│ │ │ │ └── tokenizer_config.json
│ │ │ └── token_counters.py
│ │ └── youdao.py
│ ├── upscaling/
│ │ ├── __init__.py
│ │ ├── common.py
│ │ ├── esrgan.py
│ │ ├── esrgan_pytorch.py
│ │ └── waifu2x.py
│ └── utils/
│ ├── __init__.py
│ ├── bubble.py
│ ├── generic.py
│ ├── generic2.py
│ ├── inference.py
│ ├── log.py
│ ├── panel/
│ │ ├── __init__.py
│ │ └── kumikolib.py
│ ├── sort.py
│ ├── textblock.py
│ └── threading.py
├── pip-modules/
│ └── mit-renderer/
│ ├── .gitignore
│ ├── pyproject.toml
│ └── setup.py
├── pyproject.toml
├── pytest.ini
├── requirements-dev.txt
├── requirements.txt
├── run-as-kaggle.ipynb
├── run.bat
├── run.sh
├── run_as_colab.ipynb
├── sakura_dict.txt
├── server/
│ ├── args.py
│ ├── index.html
│ ├── instance.py
│ ├── main.py
│ ├── manual.html
│ ├── myqueue.py
│ ├── request_extraction.py
│ ├── sent_data_internal.py
│ ├── streaming.py
│ └── to_json.py
├── setup.cfg
├── test/
│ ├── README.md
│ ├── api_test.html
│ ├── conftest.py
│ ├── test_render.py
│ ├── test_textline_merge.py
│ ├── test_translation.py
│ └── test_translation_manual.py
└── training/
├── all-fonts.txt
├── inpainting/
│ └── README.md
└── ocr/
├── README.md
├── custom_ctc.cc
├── custom_ctc.py
├── custom_ctc_cuda_driver.cc
├── custom_ctc_gpu.py
├── custom_ctc_kernel.cu
├── setup.py
└── test_ctc.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
result
*.ckpt
*.pt
.vscode
*.onnx
__pycache__
ocrs
models/*
test/testdata/bboxes
/venv
.git
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
ko_fi: voilelabs
patreon: voilelabs
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: Bug Report
description: You think somethings is broken
title: "[Bug]: "
labels: ["bug"]
body:
- type: textarea
id: what-did
attributes:
label: Issue
description: You can describe the bug here as well as add your used images if you want.
validations:
required: true
- type: textarea
id: cmdargs
attributes:
label: Command Line Arguments
description: Please use --verbose when running
render: Shell
- type: textarea
id: logs
attributes:
label: Console logs
render: Shell
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: Feature request
description: Suggest an idea for this project
title: "[Feature Request]: "
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
*Please fill this form with as much information as possible, provide screenshots and/or illustrations of the feature if possible*
- type: textarea
id: feature
attributes:
label: What would your feature do?
description: Tell us about your feature in a very clear and simple way, and what problem it would solve
validations:
required: true
================================================
FILE: .github/run.bat
================================================
@echo off
pushd "%~dp0"
git pull --quiet
python -m manga_translator %*
popd
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
push:
branches: [main]
paths:
- '.github/workflows/ci.yml'
- 'manga_translator/**'
- 'test/**'
- 'pyproject.toml'
- 'requirements.txt'
- 'requirements-dev.txt'
pull_request:
branches: [main]
paths:
- '.github/workflows/ci.yml'
- 'manga_translator/**'
- 'test/**'
- 'pyproject.toml'
- 'requirements.txt'
- 'requirements-dev.txt'
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11"]
fail-fast: false
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python3 -mvenv venv
venv/bin/pip install -r requirements.txt -r requirements-dev.txt
- name: Test
run: |
venv/bin/pytest test
timeout-minutes: 5
- name: Lint *.py files
run: |
venv/bin/pylint $(git ls-files '*.py')
if: always()
continue-on-error: true # to not fail CI on lint errors
================================================
FILE: .github/workflows/release.yml
================================================
name: Publish Release docker image
on:
workflow_dispatch:
release:
types: [published]
jobs:
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Log in to Docker Hub
uses: docker/login-action@v2
if: github.repository == 'zyddnys/manga-image-translator'
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v2
with:
images: zyddnys/manga-image-translator
- name: Build and push Docker image
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Docker Hub Description
uses: peter-evans/dockerhub-description@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: zyddnys/manga-image-translator
================================================
FILE: .github/workflows/stale.yml
================================================
name: Mark stale issues
on:
schedule:
- cron: "0 0 * * *" # Runs once per day at midnight UTC
workflow_dispatch:
permissions:
issues: write
actions: write # allows writing/deleting cache state if needed
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
operations-per-run: 200
days-before-stale: 90
days-before-close: 7
stale-issue-message: >
👋 This issue has been automatically marked as stale
because it has not had any recent activity.
It will be closed in 7 days if no further activity occurs.
Thank you for your contributions!
close-issue-message: >
🚫 This issue has been automatically closed due to inactivity.
Please reopen if it’s still relevant.
stale-issue-label: "stale"
exempt-issue-labels: "enhancement,help wanted"
remove-stale-when-updated: true
================================================
FILE: .gitignore
================================================
result
*.ckpt
*.pt
.vscode
*.onnx
__pycache__
ocrs
Manga
Manga-translated
/models
.env
*.local
*.local.*
test/testdata
.idea
pyvenv.cfg
Scripts
Lib
include
share
.DS_Store
dev/
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
.history
/venv
.venv/
# Input and Output
/input/
/input-translated/
# SSL Used for running server locally
cert.pem
key.pem
================================================
FILE: CHANGELOG.md
================================================
# Changelogs
### 2023-11-11
1. Added new OCR model `48px`
### 2023-05-08
1. Added [4x-UltraSharp](https://mega.nz/folder/qZRBmaIY#nIG8KyWFcGNTuMX_XNbJ_g) upscaler
### 2023-04-30
1. Countless bug fixes and refactor
2. Add [CRAFT](https://github.com/clovaai/CRAFT-pytorch) detector, enable by `--detector craft`
### 2022-06-15
1. Added New inpainting model LaMa MPE by [dmMaze](https://github.com/dmMaze) and set as default
### 2022-04-23
Project version is now at beta-0.3
1. Added English text renderer by [dmMaze](https://github.com/dmMaze)
2. Added new CTC based OCR engine, significant speed improvement
3. The new OCR model now support Korean
### 2022-03-19
1. Use new font rendering method by [pokedexter](https://github.com/pokedexter)
2. Added manual translation UI by [rspreet92](https://github.com/rspreet92)
### 2022-01-24
1. Added text detection model by [dmMaze](https://github.com/dmMaze)
### 2021-08-21
1. New MST based text region merge algorithm, huge text region merge improvement
2. Add baidu translator in demo mode
3. Add google translator in demo mode
4. Various bugfixes
### 2021-07-29
1. Web demo adds translator, detection resolution and target language option
2. Slight text color extraction improvement
### 2021-07-26
Major upgrades for all components, now we are on beta! \
Note in this version all English texts are detected as capital letters, \
You need Python >= 3.8 for `cached_property` to work
1. Detection model upgrade
2. OCR model upgrade, better at text color extraction
3. Inpainting model upgrade
4. Major text rendering improvement, faster rendering and higher quality text with shadow
5. Slight mask generation improvement
6. Various bugfixes
7. Default detection resolution has been dialed back to 1536 from 2048
### 2021-07-09
1. Fix erroneous image rendering when inpainting is not used
### 2021-06-18
1. Support manual translation
2. Support detection and rendering of angled texts
### 2021-06-13
1. Text mask completion is now based on CRF, mask quality is drastically improved
### 2021-06-10
1. Improve text rendering
### 2021-06-09
1. New text region based text direction detection method
2. Support running demo as web service
### 2021-05-20
1. Text detection model is now based on DBNet with ResNet34 backbone
2. OCR model is now trained with more English sentences
3. Inpaint model is now based on [AOT](https://arxiv.org/abs/2104.01431) which requires far less memory
4. Default inpainting resolution is now increased to 2048, thanks to the new inpainting model
5. Support merging hyphenated English words
### 2021-05-11
1. Add youdao translate and set as default translator
### 2021-05-06
1. Text detection model is now based on DBNet with ResNet101 backbone
2. OCR model is now deeper
3. Default detection resolution has been increased to 2048 from 1536
Note this version is slightly better at handling English texts, other than that it is worse in every other ways
### 2021-03-04
1. Added inpainting model
### 2021-02-17
1. First version launched
================================================
FILE: CHANGELOG_CN.md
================================================
# 更新日志 (中文)
### 2023-11-11
1. 添加了新的OCR模型`48px`
### 2023-05-08
1. 添加了[4x-UltraSharp](https://mega.nz/folder/qZRBmaIY#nIG8KyWFcGNTuMX_XNbJ_g)超分辨率
### 2023-04-30
1. 无数bug修复和重构
2. 添加了[CRAFT](https://github.com/clovaai/CRAFT-pytorch)文本检测器,使用`--detector craft`启用
### 2022-06-15
1. 增加了来自[dmMaze](https://github.com/dmMaze)的LaMa MPE图像修补模型
### 2022-04-23
版本更新为beta-0.3
1. 增加了来自[dmMaze](https://github.com/dmMaze)的英语文本渲染器
2. 增加了基于CTC的OCR模型,识别速度大幅提升
3. 新OCR模型增加韩语识别支持
### 2022-03-19
1. 增加了来自[pokedexter](https://github.com/pokedexter)的新文本渲染器
2. 增加了来自[rspreet92](https://github.com/rspreet92)的人工翻译页面
### 2022-01-24
1. 增加了来自[dmMaze](https://github.com/dmMaze)的文本检测模型
### 2021-08-21
1. 文本区域合并算法更新,先已经实现几乎完美文本行合并
2. 增加演示模式百度翻译支持
3. 增加演示模式谷歌翻译支持
4. 各类 bug 修复
### 2021-07-29
1. 网页版增加翻译器、分辨率和目标语言选项
2. 文本颜色提取小腹提升
### 2021-07-26
程序所有组件都大幅升级,本程序现已进入 beta 版本! \
注意:该版本所有英文检测只会输出大写字母。\
你需要 Python>=3.8 版本才能运行
1. 检测模型升级
2. OCR 模型升级,文本颜色抽取质量大幅提升
3. 图像修补模型升级
4. 文本渲染升级,渲染更快,并支持更高质量的文本和文本阴影渲染
5. 文字掩膜补全算法小幅提升
6. 各类 BUG 修复
7. 默认检测分辨率为 1536
### 2021-07-09
1. 修复不使用 inpainting 时图片错误
### 2021-06-18
1. 增加手动翻译选项
2. 支持倾斜文本的识别和渲染
### 2021-06-13
1. 文字掩膜补全算法更新为基于 CRF 算法,补全质量大幅提升
### 2021-06-10
1. 完善文本渲染
### 2021-06-09
1. 使用基于区域的文本方向检测,文本方向检测效果大幅提升
2. 增加 web 服务功能
### 2021-05-20
1. 检测模型更新为基于 ResNet34 的 DBNet
2. OCR 模型更新增加更多英语预料训练
3. 图像修补模型升级到基于[AOT](https://arxiv.org/abs/2104.01431)的模型,占用更少显存
4. 图像修补默认分辨率增加到 2048
5. 支持多行英语单词合并
### 2021-05-11
1. 增加并默认使用有道翻译
### 2021-05-06
1. 检测模型更新为基于 ResNet101 的 DBNet
2. OCR 模型更新更深
3. 默认检测分辨率增加到 2048
注意这个版本除了英文检测稍微好一些,其他方面都不如之前版本
### 2021-03-04
1. 添加图片修补模型
### 2021-02-17
1. 初步版本发布
================================================
FILE: Dockerfile
================================================
FROM pytorch/pytorch:2.5.1-cuda11.8-cudnn9-runtime
# not apt update: most effective code in pytorch base image is in /opt/conda
WORKDIR /app
# Install pip dependencies
COPY requirements.txt /app/requirements.txt
RUN export TZ=Etc/UTC \
&& apt update --yes \
&& apt install g++ wget ffmpeg libsm6 libxext6 gimp libvulkan1 --yes \
&& wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb \
&& dpkg -i cuda-keyring_1.1-1_all.deb \
&& rm -f cuda-keyring_1.1-1_all.deb \
&& apt update --yes \
&& apt install -y libcudnn8=8*-1+cuda11.8 libcudnn8-dev=8*-1+cuda11.8 \
&& pip install -r /app/requirements.txt \
&& apt remove g++ wget --yes \
&& apt autoremove --yes \
&& rm -rf /var/cache/apt
COPY . /app
# Prepare models
RUN --mount=type=cache,target=/.cache/models \
cp -rn /.cache/models/. /app/models/ || true && \
python -u docker_prepare.py --continue-on-error && \
cp -rn /app/models/. /.cache/models/ || true
RUN rm -rf /tmp && mkdir /tmp && chmod 1777 /tmp
# Add /app to Python module path
ENV PYTHONPATH="/app"
WORKDIR /app
ENTRYPOINT ["python", "-m", "manga_translator"]
================================================
FILE: LICENSE
================================================
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
================================================
FILE: Makefile
================================================
build-image:
docker rmi manga-image-translator || true
docker build . --tag=manga-image-translator
run-web-server:
docker run --gpus all -p 5003:5003 --ipc=host --rm manga-image-translator \
--verbose \
--use-gpu \
--host=0.0.0.0 \
--port=5003 \
--entrypoint python \
-v /demo/doc/../../result:/app/result \
-v /demo/doc/../../server/main.py:/app/server/main.py \
-v /demo/doc/../../server/instance.py:/app/server/instance.py \
zyddnys/manga-image-translator:main \
server/main.py --verbose --start-instance --host=0.0.0.0 --port=5003 --use-gpu
================================================
FILE: MangaStudioMain.py
================================================
# ===============================================================
# Manga Translation Studio - Main Entry Point
#
# Author: User & Gemini Collaboration
#
# Description: This file is the main entry point for the
# application. It configures the system path
# and launches the main UI window.
# ===============================================================
import os
import sys
from PySide6.QtWidgets import QApplication, QMessageBox
# --- Path Configuration ---
# This is crucial for the modular structure to work correctly.
# It ensures that Python can find the 'app' module inside the 'MangaStudio_Data' directory.
# Get the absolute path of the directory where this script is located (the project root)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# Define the path to the directory containing our application source code
APP_SOURCE_DIR = os.path.join(BASE_DIR, "MangaStudio_Data")
# Add the source directory to the Python system path.
# This allows us to use `from app.ui.main_window import ...`
sys.path.insert(0, APP_SOURCE_DIR)
# --- Application Launch ---
try:
# Now that the path is configured, we can import the main application class.
# We will modify main_window.py to contain a PySide class with the same name.
from app.ui.main_window import TranslatorStudioApp
except ImportError as e:
# A QApplication instance is needed to show a QMessageBox.
# We create a dummy app here just for the error message.
error_app = QApplication(sys.argv)
QMessageBox.critical(
None,
"Fatal Import Error",
"Could not import the main application class. "
"Please check that the following structure is correct:\n\n"
"MangaStudio_Data -> app -> ui -> main_window.py\n\n"
f"Error: {e}"
)
sys.exit(1)
if __name__ == "__main__":
try:
# 1. Create the PySide Application instance
app = QApplication(sys.argv)
# 2. Create an instance of our main window
# (The TranslatorStudioApp class will be a PySide window now)
main_window = TranslatorStudioApp()
# 3. Show the window
main_window.show()
# 4. Start the application's event loop
sys.exit(app.exec())
except Exception as e:
import traceback
error_title = "Critical Application Error"
error_message = (
"The application encountered a critical error and had to shut down.\n\n"
f"Error Type: {type(e).__name__}\n"
f"Error Details: {e}\n\n"
"Please check the console output for the full traceback."
)
print(f"---! {error_title.upper()} !---")
traceback.print_exc()
print("---------------------------------")
# We still need a QApplication to show the error message.
# Create one if it doesn't exist yet.
error_app = QApplication.instance() or QApplication(sys.argv)
QMessageBox.critical(None, error_title, error_message)
sys.exit(1)
================================================
FILE: MangaStudioMainRun.bat
================================================
@echo off
REM =================================================================
REM == ==
REM == MANGA TRANSLATION STUDIO - LAUNCHER ==
REM == ==
REM =================================================================
REM Set the working directory to this script's location.
cd /d "%~dp0"
:MENU
cls
echo.
echo =========================================
echo MANGA TRANSLATION STUDIO LAUNCHER
echo =========================================
echo.
echo Please choose a launch mode:
echo.
echo [1] Normal Mode (No Console Window)
echo.
echo [2] Debug Mode (With Console Window)
echo.
echo [Q] Quit
echo.
echo =========================================
echo.
CHOICE /C 12Q /M "Enter your choice: "
IF ERRORLEVEL 3 GOTO END
IF ERRORLEVEL 2 GOTO DEBUG_MODE
IF ERRORLEVEL 1 GOTO NORMAL_MODE
:NORMAL_MODE
echo [INFO] Launching in Normal (Silent) Mode...
start "" ".\venv\Scripts\pythonw.exe" MangaStudioMain.py
GOTO END
:DEBUG_MODE
echo [INFO] Launching in Debug (Console) Mode...
echo -----------------------------------------------------------------
.\venv\Scripts\python.exe MangaStudioMain.py
echo -----------------------------------------------------------------
echo [DEBUG] Script has finished or was stopped.
pause
GOTO END
:END
exit
================================================
FILE: MangaStudio_Data/README.md
================================================
# Manga Translation Studio
This document describes the **Manga Translation Studio**, a comprehensive graphical user interface (GUI) built with PySide6 for the powerful `zyddnys/manga-image-translator` backend.
This GUI provides a user-friendly studio environment to manage, configure, and run manga/comic translation tasks without needing to use the command line.
---
### **Table of Contents**
1. [Core Features](#-core-features)
2. [Requirements](#-requirements)
3. [How to Install](#-how-to-install)
4. [Project Structure](#-project-structure)
5. [Quick Start Guide](#-quick-start-guide)
6. [Key Concepts Explained](#-key-concepts-explained)
* [The Checkpoint System](#the-checkpoint-system)
* [Tasks vs. Configuration](#tasks-vs-configuration)
* [Advanced Dictionaries and GPT Configs](#advanced-dictionaries-and-gpt-configs)
---
## ✨ Core Features
* **Intuitive Job Management:** Add translation jobs via a file dialog or simple drag-and-drop. Reorder, duplicate, or remove jobs with ease.
* **Checkpoint System:** Save specific settings directly to a job, making it independent of the global panel settings. Queue up multiple jobs with different configurations.
* **Dedicated Task System:** Go beyond translation with pre-configured tasks for "RAW Output" (text removal), "Image Upscaling", or "Image Colorization".
* **Smart VRAM Management:** Choose between **High VRAM** (fast), **Low VRAM** (safe for less powerful GPUs), or **Automatic** modes to prevent CUDA "Out of Memory" errors.
* **Safe & Smart Processing:**
* **Resume on Relaunch:** Automatically skips already processed files in the output folder.
* **Conflict-Free Outputs:** By default, creates new numbered folders (e.g., `Manga-ENG (1)`) to prevent overwriting previous results.
* **Full Backend Control:** Access and configure nearly every feature of the backend, from advanced detector settings to text rendering options.
* **Live Log & History:** Monitor progress in real-time and review completed jobs in the History panel. Re-queue past jobs with a single click.
* **Secure API & File Management:** Includes helpers for managing API keys (`.env`), translation dictionaries (`--pre/--post-dict`), and custom GPT configurations.
## 📋 Requirements
1. **Backend Project:** This GUI requires the original `manga-image-translator` project. Please follow the setup instructions from the [official repository](https://github.com/zyddnys/manga-image-translator) first. This will install the core dependencies like `torch`.
2. **Python Libraries for the GUI:** This user interface requires two additional libraries. Ensure they are installed in your activated virtual environment:
```bash
pip install PySide6 Pillow
```
* **PySide6:** The core framework for the user interface.
* **Pillow:** Used for image processing tasks, such as reading image dimensions for the smart colorization feature.
## 🚀 How to Install
1. Place the `MangaStudioMain.py` file and the entire `MangaStudio_Data` folder into the **root directory** of your cloned `manga-image-translator` project.
2. Make sure your Python virtual environment (`venv`) is activated.
3. Run the application from the root directory:
```bash
python MangaStudioMain.py
```
## 📁 Project Structure
The GUI's core files are self-contained within the `MangaStudio_Data` directory to keep the main project folder clean.
```
manga-image-translator/ (Project Root)
|
|-- MangaStudioMain.py # <-- Main executable to run the UI
|-- MangaStudioMainRun.py # <-- Run
|
|-- MangaStudio_Data/
| |-- app/ # Core application logic and UI window
| |-- dicts/ # Folder for pre/post-translation dictionaries
| |-- fonts/ # Place custom .ttf/.otf fonts here
| |-- gpt_configs/ # Folder for custom GPT/AI configurations
| |-- profiles/ # Saved user setting presets
| |-- temp/ # For temporary files (ignored by Git)
| |-- themes/ # UI theme files
| |-- tasks.json # Configuration for the "Tasks" tab
| |-- ui_map.json # Maps backend settings to UI widgets
| |-- README.md # This file
|
|-- ... (all other original backend folders and files)
```
## 📖 Quick Start Guide
1. **Add a Job:**
* Click `➕ Add Job` to select a folder containing your images.
* Or, simply **drag and drop** the folder onto the "Queue" panel.
2. **Configure Your Job:**
* Select the job in the "Queue".
* Go to the **`Configuration ⚙️`** tab on the right.
* Set your desired `Translator`, `Target Language`, `Output Format`, and other settings.
3. **Create a Checkpoint:**
* This is the most important step. **Right-click** the configured job in the queue.
* Select **`✅ Save Settings to Job (Checkpoint)`**.
* The job's icon will turn green (🟢), indicating it's ready and its settings are locked in.
4. **Start Processing:**
* Click the **`▶️ START PIPELINE`** button.
* You can monitor the detailed progress in the **`Live Log 📊`** tab.
## 💡 Key Concepts Explained
#### The Checkpoint System
A "Checkpoint" is created when you use `Save Settings to Job`. This action "locks" all the settings from the right-hand panel onto that specific job. This is powerful because you can:
* Configure a job for Japanese-to-English translation.
* Configure a second job for German-to-Spanish translation with different quality settings.
* Run them back-to-back in the same pipeline without the settings interfering with each other.
A job **must** have a checkpoint (🟢) to be processed.
#### Tasks vs. Configuration
* The **`Configuration`** tab is for the main goal: translating manga. It gives you full control over every detail.
* The **`Tasks 🛠️`** tab is for specific, one-off jobs where you don't need a full translation.
* **RAW Output:** Just removes the text from the bubbles.
* **Image Upscaling:** Just increases the resolution of the images.
* **Image Colorization:** Just colorizes black-and-white images.
To use a task, select a job, go to the `Tasks` tab, configure the few settings available, and click the **`Assign...`** button. This will automatically create a checkpoint for that task.
#### Advanced Dictionaries and GPT Configs
Under `Configuration ⚙️` > `General & Translator (Advanced)`, you can find powerful customization options:
* **Pre/Post-Translation Dictionaries:** These allow you to provide your own `.txt` files to automatically fix common OCR errors or standardize translation terms for consistency. Example files are provided in the `MangaStudio_Data/dicts` folder.
* **GPT Config File:** This allows you to provide a `.yaml` file to deeply customize the behavior of AI translators (like GPT-4o or Gemini). You can change their "persona," style, and more. An example file is provided in `MangaStudio_Data/gpt_configs`.
================================================
FILE: MangaStudio_Data/app/__init__.py
================================================
================================================
FILE: MangaStudio_Data/app/core/__init__.py
================================================
================================================
FILE: MangaStudio_Data/app/core/config_loader.py
================================================
import os
import json
import subprocess
import sys
import re
class ConfigLoader:
def __init__(self, project_base_dir):
self.project_base_dir = project_base_dir
self.python_executable = self._find_python_executable()
self.cache_path = os.path.join(self.project_base_dir, "MangaStudio_Data", "temp", "schema_cache.json")
self.backend_schema = self._load_backend_schema()
self.ui_map = self._load_ui_map()
self.tasks_config = self._load_tasks_config() # This line is correct
if not self.backend_schema:
raise RuntimeError("Failed to load backend configuration schema.")
# The data is built and stored directly as attributes, not through getter methods
self.factory_defaults = self._parse_factory_defaults()
self.full_config_data = self._build_full_config_data()
def _find_python_executable(self):
venv_path_win = os.path.join(self.project_base_dir, 'venv', 'Scripts', 'python.exe')
venv_path_unix = os.path.join(self.project_base_dir, 'venv', 'bin', 'python')
if os.path.exists(venv_path_win):
return venv_path_win
elif os.path.exists(venv_path_unix):
return venv_path_unix
return sys.executable
def _load_backend_schema(self):
if os.path.exists(self.cache_path):
try:
with open(self.cache_path, 'r', encoding='utf-8') as f:
print("[ConfigLoader] Loading schema from cache...")
return json.load(f)
except Exception:
pass
print("[ConfigLoader] Fetching fresh configuration schema...")
try:
command = [self.python_executable, "-m", "manga_translator", "config-help"]
result = subprocess.run(command, capture_output=True, text=True, encoding='utf-8', check=True)
schema_data = self._parse_schema_output(result.stdout)
if schema_data is None:
raise ValueError("Schema command did not return valid JSON.")
os.makedirs(os.path.dirname(self.cache_path), exist_ok=True)
with open(self.cache_path, 'w', encoding='utf-8') as f:
json.dump(schema_data, f, indent=4)
return schema_data
except Exception as e:
print(f"[ERROR] Could not fetch schema: {e}")
return None
def _parse_schema_output(self, stdout):
"""Extracts the JSON portion of the schema output."""
try:
return json.loads(stdout)
except json.JSONDecodeError:
cleaned_stdout = self._strip_ansi(stdout)
json_start = cleaned_stdout.find('{')
json_end = cleaned_stdout.rfind('}')
if json_start == -1 or json_end == -1 or json_end < json_start:
return None
try:
return json.loads(cleaned_stdout[json_start:json_end + 1])
except json.JSONDecodeError:
return None
def _strip_ansi(self, text):
ansi_escape = re.compile(r'\x1B\[[0-9;]*[A-Za-z]')
return ansi_escape.sub('', text)
def _load_ui_map(self):
map_path = os.path.join(self.project_base_dir, 'MangaStudio_Data', 'ui_map.json')
try:
with open(map_path, 'r', encoding='utf-8') as f:
return json.load(f)
except Exception as e:
print(f"[ERROR] UI map loading failed: {e}")
return {}
def _load_tasks_config(self):
"""Loads the special tasks configuration from tasks.json."""
tasks_path = os.path.join(self.project_base_dir, 'MangaStudio_Data', 'tasks.json')
try:
with open(tasks_path, 'r', encoding='utf-8') as f:
print("[ConfigLoader] Loading tasks configuration...")
return json.load(f)
except FileNotFoundError:
print(f"[ERROR] tasks.json not found at: {tasks_path}")
return {}
except Exception as e:
print(f"[ERROR] Tasks config loading failed: {e}")
return {}
def _get_definition_from_ref(self, ref_path):
try:
parts = ref_path.split('/')[1:]
node = self.backend_schema
for part in parts:
node = node[part]
return node
except Exception:
return None
def _parse_factory_defaults(self):
"""Deep-parses the schema to get ALL default values, including nested ones."""
if not self.backend_schema:
return {}
defaults = {}
properties = self.backend_schema.get("properties", {})
for prop_key, prop_value in properties.items():
# This handles nested defaults (e.g., from 'detector', 'render')
if "default" in prop_value and isinstance(prop_value.get("default"), dict):
defaults.update(prop_value["default"])
# This handles simple root-level properties (e.g., 'kernel_size')
elif "default" in prop_value:
defaults[prop_key] = prop_value["default"]
return defaults
def _build_full_config_data(self):
"""Builds the final, merged config data for the UI, reading ALL properties."""
if not self.ui_map:
return {}
full_data = {}
all_properties = {}
# 1. Gather all root-level properties
root_props = self.backend_schema.get("properties", {})
all_properties.update(root_props)
# 2. Gather all nested properties from complex types (e.g., DetectorConfig)
for prop in root_props.values():
ref_path = prop.get("allOf", [{}])[0].get('$ref')
if ref_path:
config_def = self._get_definition_from_ref(ref_path)
if config_def and "properties" in config_def:
all_properties.update(config_def["properties"])
# 3. Build the final data structure using the UI map as the guide
for key, ui_info in self.ui_map.items():
if key.startswith("__"):
continue
merged_info = ui_info.copy()
merged_info['key'] = key
# Use the already parsed factory default
merged_info['default'] = self.factory_defaults.get(key)
# Add enum values (for dropdowns) if they exist
prop_def = all_properties.get(key)
if prop_def and isinstance(prop_def, dict):
ref_path = prop_def.get("allOf", [{}])[0].get('$ref')
if ref_path:
enum_def = self._get_definition_from_ref(ref_path)
if enum_def and "enum" in enum_def:
merged_info['values'] = enum_def["enum"]
full_data[key] = merged_info
return full_data
def get_tasks_config(self):
"""Returns the loaded tasks configuration."""
return self.tasks_config
def get_factory_defaults(self):
return self.factory_defaults
def get_tab_order(self):
return self.ui_map.get("__tab_order__", [])
================================================
FILE: MangaStudio_Data/app/core/constants.py
================================================
# ===============================================================
# Application Constants
#
# Description: This file holds all semi-static data for the
# application, such as language lists, model groups,
# and other constants. This makes it easy to update
# this data without searching through all the code.
# ===============================================================
# A dictionary mapping user-friendly language names to their API codes.
LANGUAGES = {
"Auto-Detect": "auto",
"English": "ENG",
"Turkish": "TRK",
"Japanese": "JPN",
"Korean": "KOR",
"Simplified Chinese": "CHS",
"Traditional Chinese": "CHT",
"Spanish": "ESP",
"French": "FRA",
"German": "DEU",
"Russian": "RUS",
"Portuguese (Brazilian)": "PTB",
"Italian": "ITA",
"Polish": "POL",
"Dutch": "NLD",
"Czech": "CSY",
"Hungarian": "HUN",
"Romanian": "ROM",
"Ukrainian": "UKR",
"Vietnamese": "VIN",
"Arabic": "ARA",
"Serbian": "SRP",
"Croatian": "HRV",
"Thai": "THA",
"Indonesian": "IND",
"Filipino (Tagalog)": "FIL"
}
# A dictionary to group translators in the dropdown menu for better readability.
TRANSLATOR_GROUPS = {
"--- OFFLINE MODELS (No API Key) ---": [
"sugoi", "m2m100", "m2m100_big", "nllb", "nllb_big", "mbart50",
"jparacrawl", "jparacrawl_big", "qwen2", "qwen2_big", "offline"
],
"--- API-BASED (Requires Setup) ---": [
"deepl", "gemini", "deepseek", "groq", "youdao", "baidu",
"caiyun", "sakura", "papago", "openai", "custom_openai"
],
"--- OTHER ACTIONS ---": [
"original",
"none"
]
}
# A dictionary that maps translators to their supported language pairs (source, target).
# This provides the data for both filtering the target language dropdown and
# displaying informative tooltips to the user.
#
# Format:
# 'translator_name': {
# 'source_language_code': ['list_of', 'supported', 'target_codes'],
# '__any__': '__all__' // A special key indicating that this model can translate
# // from any supported language to any other.
# }
TRANSLATOR_CAPABILITIES = {
# --- API-BASED (Generally versatile) ---
# For major APIs, assuming they can handle any pair we have in our language list.
"deepl": {'__any__': '__all__'},
"gemini": {'__any__': '__all__'},
"deepseek": {'__any__': '__all__'},
"groq": {'__any__': '__all__'},
"youdao": {'__any__': '__all__'},
"baidu": {'__any__': '__all__'},
"caiyun": {'__any__': '__all__'},
"openai": {'__any__': '__all__'},
"custom_openai": {'__any__': '__all__'},
# --- SPECIALIZED APIs (Limited pairs) ---
"papago": { # Based on Naver Papago's known strengths
'KOR': ['ENG', 'JPN', 'CHS', 'CHT', 'FRA', 'DEU', 'RUS', 'ESP', 'ITA', 'VIE', 'THA', 'IND'],
'JPN': ['ENG', 'KOR', 'CHS', 'CHT'],
'CHS': ['ENG', 'KOR', 'JPN'],
'CHT': ['ENG', 'KOR', 'JPN'],
'ENG': ['KOR', 'JPN', 'CHS', 'CHT', 'FRA', 'DEU', 'ESP', 'ITA'],
'FRA': ['ENG', 'KOR'],
'ESP': ['ENG', 'KOR'],
'ITA': ['ENG', 'KOR'],
'DEU': ['ENG', 'KOR']
},
"sakura": { # Specialized for CJK languages as per docs
'JPN': ['CHS', 'CHT'],
'CHS': ['JPN'],
'CHT': ['JPN']
},
# --- SPECIALIZED OFFLINE MODELS (Often one-way) ---
"sugoi": { # Primarily Japanese to English
'JPN': ['ENG']
},
"jparacrawl": { # Japanese to English
'JPN': ['ENG']
},
"jparacrawl_big": { # Japanese to English
'JPN': ['ENG']
},
# --- MULTILINGUAL OFFLINE MODELS (Assumed to be versatile) ---
"nllb": {'__any__': '__all__'},
"nllb_big": {'__any__': '__all__'},
"m2m100": {'__any__': '__all__'},
"m2m100_big": {'__any__': '__all__'},
"mbart50": {'__any__': '__all__'},
"qwen2": {'__any__': '__all__'},
"qwen2_big": {'__any__': '__all__'},
"offline": {'__any__': '__all__'},
# --- OTHER ACTIONS (No translation capabilities) ---
"original": {},
"none": {}
}
LOG_COLORS = {
"ERROR": "#E74C3C",
"SUCCESS": "#2ECC71",
"PIPELINE": "#5DADE2",
"WARNING": "#F39C12",
"INFO": "white",
"DEBUG": "gray",
"RAW": "gray"
}
================================================
FILE: MangaStudio_Data/app/core/pipeline.py
================================================
import os
import json
import subprocess
import gc
import shutil
try:
import torch
except ImportError:
torch = None
class Pipeline:
"""Handles the execution of the backend translation process."""
def __init__(self, app, python_executable, temp_dir):
"""Handles the execution of the backend translation process."""
self.app = app
self.python_executable = python_executable
self.temp_dir = temp_dir
os.makedirs(self.temp_dir, exist_ok=True)
self.process = None
self._stopped_by_user = False
def run(self, job, output_path, config_dict, log_callback, is_verbose=False, output_format='png'):
"""
Runs a job using a provided configuration dictionary.
Now supports passing all command line arguments.
"""
log_callback("PIPELINE", f"Starting job '{os.path.basename(job['source_path'])}'.")
self._stopped_by_user = False
config_path = ""
try:
# --- Extract all potential CLI arguments from the config dict ---
font_path = config_dict.pop('font_path', None)
pre_dict_path = config_dict.pop('pre_dict_path', None)
post_dict_path = config_dict.pop('post_dict_path', None)
config_path = os.path.join(self.temp_dir, f"temp_config_job_{job['id']}.json")
with open(config_path, 'w', encoding='utf-8') as f:
json.dump(config_dict, f, indent=4)
command = [self.python_executable, "-m", "manga_translator", "local", "-i", job['source_path'], "-o", output_path, "--config-file", config_path]
# --- Add all optional arguments if they exist ---
if is_verbose:
command.append("-v")
if output_format:
command.extend(["--format", output_format])
if font_path:
# The path should be absolute for the backend
command.extend(["--font-path", os.path.join(self.app.project_base_dir, font_path)])
if pre_dict_path:
command.extend(["--pre-dict", os.path.join(self.app.project_base_dir, pre_dict_path)])
if post_dict_path:
command.extend(["--post-dict", os.path.join(self.app.project_base_dir, post_dict_path)])
if job['settings'].get('processing_device') == 'NVIDIA GPU':
command.append("--use-gpu")
return self._execute_subprocess(log_callback, command)
except Exception as e:
log_callback("ERROR", f"Critical error preparing job: {e}")
return False
finally:
if config_path and os.path.exists(config_path):
try: os.remove(config_path)
except: pass
self._cleanup_memory(log_callback)
def run_single_image_test(self, test_image_path, output_path, config_dict, log_callback, is_verbose=False):
"""Runs the pipeline for a single test image using a provided config."""
log_callback("PIPELINE", f"Starting visual test for: {os.path.basename(test_image_path)}")
self._stopped_by_user = False
temp_input_dir = os.path.join(self.temp_dir, "visual_test_input")
if os.path.exists(temp_input_dir):
shutil.rmtree(temp_input_dir)
os.makedirs(temp_input_dir)
config_path = ""
try:
shutil.copy(test_image_path, temp_input_dir)
output_format = config_dict.pop('output_format_cli', None)
config_path = os.path.join(self.temp_dir, "temp_config_visual_test.json")
with open(config_path, 'w', encoding='utf-8') as f:
json.dump(config_dict, f, indent=4)
command = [self.python_executable, "-m", "manga_translator", "local", "-i", temp_input_dir, "-o", output_path, "--config-file", config_path]
if is_verbose:
command.append("-v")
if output_format:
command.extend(["-f", output_format])
if config_dict.get('processing_device') == 'NVIDIA GPU':
command.append("--use-gpu")
return self._execute_subprocess(log_callback, command)
except Exception as e:
log_callback("ERROR", f"Visual test preparation failed: {e}")
return False
finally:
if os.path.exists(temp_input_dir):
shutil.rmtree(temp_input_dir)
if config_path and os.path.exists(config_path):
try:
os.remove(config_path)
except:
pass
self._cleanup_memory(log_callback)
def _execute_subprocess(self, log_callback, command):
"""
Executes the given command, now also checks the output stream for error keywords
to determine the true success of the operation.
"""
log_callback("DEBUG", f"Executing: {' '.join(command)}")
my_env = os.environ.copy()
my_env["PYTHONUTF8"] = "1"
has_failed = False # Flag to track if we've seen an error message
try:
self.process = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
encoding='utf-8',
errors='replace',
env=my_env,
creationflags=subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0
)
for line in iter(self.process.stdout.readline, ''):
if self._stopped_by_user:
log_callback("WARNING", "User interrupt received, stopping process.")
break
stripped_line = line.strip()
if stripped_line:
log_callback("RAW", stripped_line)
# --- NEW ERROR CHECKING ---
# Check for keywords that indicate a failure, even if the process exits cleanly.
if stripped_line.startswith("ERROR:") or "Traceback (most recent call last)" in stripped_line:
has_failed = True
return_code = self.process.wait()
if self._stopped_by_user:
return False
# The job is only successful if the return code is 0 AND we haven't seen any error flags.
return return_code == 0 and not has_failed
except Exception as e:
if not self._stopped_by_user:
log_callback("ERROR", f"Subprocess execution failed: {e}")
return False
finally:
self.process = None
def stop(self, log_callback):
"""Stops the currently running subprocess."""
if self.process and self.process.poll() is None:
log_callback("PIPELINE", "Attempting to terminate running process...")
self._stopped_by_user = True
try:
self.process.kill()
log_callback("SUCCESS", "Process terminated by user.")
return True
except Exception as e:
log_callback("ERROR", f"Failed to kill process: {e}")
return False
return False
def _cleanup_memory(self, log_callback):
log_callback("DEBUG", "Performing memory cleanup...")
gc.collect()
if torch and torch.cuda.is_available():
try:
torch.cuda.empty_cache()
except Exception:
pass
================================================
FILE: MangaStudio_Data/app/ui/__init__.py
================================================
================================================
FILE: MangaStudio_Data/app/ui/main_window.py
================================================
# ===============================================================
# Main Application Window (PySide6 Version)
#
# Author: User & Gemini Collaboration
#
# Description: This module contains the main application class,
# which is being rebuilt using PySide6 to create the UI.
# ===============================================================
import os
import sys
import shutil
import threading
import copy
import time
import json
import subprocess
# PySide6 imports for the main window structure and new widgets
from PySide6.QtWidgets import (
QMainWindow, QLabel, QWidget, QVBoxLayout, QHBoxLayout,
QFrame, QPushButton, QProgressBar, QTabWidget, QScrollArea,
QComboBox, QCheckBox, QButtonGroup, QSlider, QLineEdit, QGridLayout,
QColorDialog, QMessageBox, QListWidget, QListWidgetItem, QFileDialog,
QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, QTextEdit,
QApplication, QMenu, QSizePolicy
)
from PySide6.QtCore import Qt, QSize, QTimer, Signal, QByteArray, QEvent
from PySide6.QtGui import QFont, QCursor, QStandardItemModel, QFontDatabase, QPixmap, QPainter
from PIL import Image
# Core non-UI imports remain the same
from app.core.pipeline import Pipeline
from app.core.config_loader import ConfigLoader
from app.core.constants import (
LANGUAGES, TRANSLATOR_GROUPS, TRANSLATOR_CAPABILITIES, LOG_COLORS
)
class DynamicHeightListWidget(QListWidget):
"""
A custom QListWidget that:
1. Overrides its size hints to always match its content's height.
2. Disables its own vertical scrollbar, forcing the parent to scroll.
3. Ignores mouse wheel events to prevent accidental scrolling inside parent.
"""
def __init__(self, parent=None):
super().__init__(parent)
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
def minimumSizeHint(self) -> QSize:
"""Override to report the content's height as the minimum possible height."""
height = self._get_content_height()
return QSize(super().minimumSizeHint().width(), height)
def sizeHint(self) -> QSize:
"""Override to report the content's height as the preferred height."""
height = self._get_content_height()
return QSize(super().sizeHint().width(), height)
def wheelEvent(self, event: QEvent):
"""Pass the wheel event to the parent to allow scrolling the main area."""
event.ignore()
def _get_content_height(self) -> int:
"""Calculates the total height required to display all items without scrolling."""
if self.count() == 0:
return 35 # Return a default small height when empty
# Sum of the height of each item in the list
content_height = 0
for i in range(self.count()):
content_height += self.sizeHintForRow(i)
# Add the height of the frame around the content
content_height += self.frameWidth() * 2
return content_height
class NoScrollComboBox(QComboBox):
"""A custom QComboBox that ignores wheel events to prevent accidental scrolling."""
def wheelEvent(self, event: QEvent):
# Ignore the event completely, passing it to the parent widget (the scroll area)
event.ignore()
class TranslatorStudioApp(QMainWindow):
log_signal = Signal(str, str)
pipeline_finished_signal = Signal()
def __init__(self):
super().__init__()
self.project_base_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
self.config_loader = ConfigLoader(self.project_base_dir)
self._load_app_state()
self._build_font_map()
self.setting_widgets = {}
self.task_widgets = {}
self.task_settings = {}
self.widget_references = {}
self.current_settings = self.config_loader.get_factory_defaults()
self.job_queue = []
self.history_queue = []
self.selected_job_id = None
self.is_running_pipeline = False
self._stopped_by_user = False
self.pipeline_process = None
self.available_themes = {}
# --- Variables for the Visual Compare Tab ---
self.test_image_path = None
self.original_pixmap_item = None
self.translated_pixmap_item = None
self.is_panning = False
self.last_pan_pos = None
self.temp_dir = os.path.join(self.project_base_dir, "MangaStudio_Data", "temp")
self.detected_vram_gb = 0
try:
import torch
if torch.cuda.is_available():
# Get total memory in bytes and convert to gigabytes
mem_bytes = torch.cuda.get_device_properties(0).total_memory
self.detected_vram_gb = mem_bytes / (1024**3)
print(f"[INFO] Detected {self.detected_vram_gb:.2f} GB of VRAM.")
except Exception as e:
print(f"[WARNING] Could not detect VRAM. Automatic mode will default to Safe. Error: {e}")
# --- Pipeline for backend processing (CORRECTED LINE) ---
temp_dir = os.path.join(self.project_base_dir, "MangaStudio_Data", "temp")
self.pipeline = Pipeline(self, self.config_loader.python_executable, temp_dir)
self._initialize_app()
# Connect custom signals to their slots
self.log_signal.connect(self._insert_log_text)
self.pipeline_finished_signal.connect(self._on_pipeline_finished)
def _initialize_app(self):
"""
Sets up the main window, its properties, and creates the main layout.
"""
print("[UI] Initializing PySide6 application window...")
self.setWindowTitle("🎌 Manga Translation Studio - PySide")
self.resize(1280, 720)
self.setMinimumSize(QSize(960, 540))
self._create_main_layout()
print("[UI] Main layout and dynamic widgets created successfully.")
def _create_main_layout(self):
"""Creates the main QWidget and layouts to structure the window."""
central_widget = QWidget(self)
self.setCentralWidget(central_widget)
main_layout = QVBoxLayout(central_widget)
main_layout.setContentsMargins(10, 10, 10, 10)
main_layout.setSpacing(10)
top_area_widget = QWidget()
top_layout = QHBoxLayout(top_area_widget)
top_layout.setContentsMargins(0, 0, 0, 0)
top_layout.setSpacing(10)
left_panel = self._create_left_panel()
right_panel = self._create_right_panel()
top_layout.addWidget(left_panel, stretch=1)
top_layout.addWidget(right_panel, stretch=3) # Give right panel more space
bottom_panel = self._create_bottom_panel()
main_layout.addWidget(top_area_widget)
main_layout.addWidget(bottom_panel)
def _create_left_panel(self) -> QWidget:
"""Creates the main left panel, divided into a 'Queue' and 'History' section."""
# Main container for the entire left side
left_panel_container = QFrame()
left_panel_container.setObjectName("LeftPanel")
left_panel_layout = QVBoxLayout(left_panel_container)
# --- 1. Top Section: Queue ---
queue_frame = QWidget()
queue_layout = QVBoxLayout(queue_frame)
queue_layout.setContentsMargins(0, 0, 0, 0)
queue_title = QLabel("Queue (Next Up)")
font = queue_title.font()
font.setPointSize(12)
font.setBold(True)
queue_title.setFont(font)
queue_layout.addWidget(queue_title)
self.queue_list_widget = QListWidget()
self.queue_list_widget.setToolTip("Add folders by clicking 'Add Job' or by dragging and dropping them here.")
self.queue_list_widget.setDragDropMode(QListWidget.DragDropMode.InternalMove) # Enable drag-drop reordering
self.queue_list_widget.setSelectionMode(QListWidget.SelectionMode.ExtendedSelection) # Allow multi-select with Ctrl/Shift
self.queue_list_widget.itemSelectionChanged.connect(self._on_job_selection_changed)
self.queue_list_widget.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.queue_list_widget.customContextMenuRequested.connect(self._show_queue_context_menu)
queue_layout.addWidget(self.queue_list_widget)
# --- Job Control Buttons for the Queue ---
job_controls_container = QWidget()
job_controls_layout = QHBoxLayout(job_controls_container)
job_controls_layout.setContentsMargins(0, 0, 0, 0)
# We will replace this with a dropdown button later
add_btn = QPushButton("➕ Add Job")
add_btn.clicked.connect(self._add_job)
remove_btn = QPushButton("🗑️ Remove Selected")
# Connect this button to the working function that removes jobs
remove_btn.clicked.connect(self._remove_selected_jobs_from_queue)
clear_btn = QPushButton("🧹 Clear Queue")
clear_btn.clicked.connect(self._clear_queue)
# Add all buttons to the horizontal layout
job_controls_layout.addWidget(add_btn)
job_controls_layout.addWidget(remove_btn)
job_controls_layout.addWidget(clear_btn)
# Add the button container to the main vertical layout for the queue
queue_layout.addWidget(job_controls_container)
# --- 2. Bottom Section: History ---
history_frame = QWidget()
history_layout = QVBoxLayout(history_frame)
history_layout.setContentsMargins(0, 0, 0, 0)
history_title = QLabel("History (Completed Jobs)")
history_title.setFont(font) # Reuse the same font
history_layout.addWidget(history_title)
self.history_list_widget = QListWidget()
self.history_list_widget.setSelectionMode(QListWidget.SelectionMode.ExtendedSelection)
self.history_list_widget.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.history_list_widget.customContextMenuRequested.connect(self._show_history_context_menu)
history_layout.addWidget(self.history_list_widget)
# --- History Control Buttons ---
history_controls_container = QWidget()
history_controls_layout = QHBoxLayout(history_controls_container)
history_controls_layout.setContentsMargins(0, 0, 0, 0)
history_controls_layout.addStretch() # Push button to the right
clear_history_btn = QPushButton("Clear History")
clear_history_btn.clicked.connect(self._clear_history)
history_controls_layout.addWidget(clear_history_btn)
history_layout.addWidget(history_controls_container)
# --- Add both sections to the main panel layout ---
left_panel_layout.addWidget(queue_frame, stretch=3) # Give more space to the queue
left_panel_layout.addWidget(history_frame, stretch=2) # Give less space to history
self.left_panel_widget = left_panel_container
return left_panel_container
def _create_right_panel(self) -> QWidget:
"""Creates the right panel widget containing the main tabs."""
self.main_tabs = QTabWidget()
# --- CHANGED: The configuration tab is now built dynamically ---
tab_config = self._create_settings_tab_container()
tab_compare = self._create_visual_compare_tab()
tab_log = self._create_log_tab()
# Add a simple label to the other tabs for now
compare_layout = QVBoxLayout(tab_compare)
compare_layout.addWidget(QLabel("Visual comparison tools will be built here."))
log_layout = QVBoxLayout(tab_log)
log_layout.addWidget(QLabel("The live log will be displayed here."))
self.main_tabs.addTab(tab_config, "Configuration ⚙️")
# --- DISABLED FEATURE: The Visual Compare tab is hidden until its core bug is fixed. ---
# TODO: The 'translated_view' panel does not correctly display the output image after
# the single-image pipeline finishes. The underlying logic in _run_visual_test
# and _display_test_result needs to be debugged.
# self.main_tabs.addTab(tab_compare, "Visual Compare 👁️")
self.main_tabs.addTab(tab_log, "Live Log 📊")
return self.main_tabs
def _create_settings_tab_container(self) -> QWidget:
"""
Creates the content for the 'Configuration' tab, which itself is another
set of tabs read dynamically from the config loader.
"""
container_widget = QWidget()
container_layout = QVBoxLayout(container_widget)
container_layout.setContentsMargins(5, 5, 5, 5)
container_layout.setSpacing(10)
self.settings_tab_view = QTabWidget()
container_layout.addWidget(self.settings_tab_view)
# --- Build the regular settings tabs from ui_map.json ---
config_data = self.config_loader.full_config_data
tab_order = self.config_loader.get_tab_order()
grouped_settings = {tab_name: [] for tab_name in tab_order}
for key, info in config_data.items():
group = info.get("group", "Other")
if group in grouped_settings:
grouped_settings[group].append(info)
for tab_name in tab_order:
settings_list = sorted(grouped_settings.get(tab_name, []), key=lambda x: x.get('order', 999))
tab_content_widget = self._build_dynamic_tab_content(tab_name, settings_list)
self.settings_tab_view.addTab(tab_content_widget, tab_name)
# --- NEW: Build the 'Tasks' tab ---
tasks_tab_content = self._build_tasks_tab_content()
self.settings_tab_view.addTab(tasks_tab_content, "Tasks 🛠️")
return container_widget
def _create_visual_compare_tab(self) -> QWidget:
"""Creates the entire UI for the Visual Compare tab and connects its signals."""
container = QWidget()
layout = QVBoxLayout(container)
# 1. Top Controls Panel
controls_frame = QFrame()
controls_layout = QHBoxLayout(controls_frame)
load_button = QPushButton("Load Test Image...")
load_button.clicked.connect(self._load_test_image) # Connect signal
self.fast_preview_check = QCheckBox("Fast Preview")
self.fast_preview_check.setChecked(True)
self.run_test_button = QPushButton("Run Test")
self.run_test_button.setEnabled(False)
self.run_test_button.clicked.connect(self._run_visual_test_thread)
reset_button = QPushButton("Reset View")
reset_button.clicked.connect(self._fit_image_to_view) # Connect signal
self.zoom_label = QLabel("Zoom: 100%")
self.limit_zoom_check = QCheckBox("Limit Zoom")
self.limit_zoom_check.setChecked(True) # Enabled by default
self.limit_zoom_check.setToolTip("When checked, zoom is limited between 5% and 800%.")
controls_layout.addWidget(load_button)
controls_layout.addWidget(self.fast_preview_check)
controls_layout.addStretch()
controls_layout.addWidget(self.zoom_label)
controls_layout.addWidget(reset_button)
controls_layout.addWidget(self.run_test_button)
# 2. Image Display Area
image_area_frame = QFrame()
image_area_frame.setFrameShape(QFrame.Shape.StyledPanel)
self.image_area_layout = QHBoxLayout(image_area_frame)
self.original_view = QGraphicsView()
self.original_scene = QGraphicsScene()
self.original_view.setScene(self.original_scene)
self.original_view.setRenderHint(QPainter.RenderHint.Antialiasing)
self.original_view.setDragMode(QGraphicsView.DragMode.ScrollHandDrag) # Enable panning!
self.translated_view = QGraphicsView()
self.translated_scene = QGraphicsScene()
self.translated_view.setScene(self.translated_scene)
self.translated_view.setRenderHint(QPainter.RenderHint.Antialiasing)
self.translated_view.setDragMode(QGraphicsView.DragMode.ScrollHandDrag)
# --- Connect events for zooming and synchronized panning ---
self.original_view.wheelEvent = self._wheel_event_zoom
self.translated_view.wheelEvent = self._wheel_event_zoom
self.original_view.horizontalScrollBar().valueChanged.connect(self.translated_view.horizontalScrollBar().setValue)
self.original_view.verticalScrollBar().valueChanged.connect(self.translated_view.verticalScrollBar().setValue)
self.translated_view.horizontalScrollBar().valueChanged.connect(self.original_view.horizontalScrollBar().setValue)
self.translated_view.verticalScrollBar().valueChanged.connect(self.original_view.verticalScrollBar().setValue)
original_container = QWidget()
original_layout = QVBoxLayout(original_container)
original_layout.addWidget(QLabel("Original (Ctrl+Scroll=Zoom, Drag=Pan)"))
original_layout.addWidget(self.original_view)
translated_container = QWidget()
translated_layout = QVBoxLayout(translated_container)
translated_layout.addWidget(QLabel("Output"))
translated_layout.addWidget(self.translated_view)
self.image_area_layout.addWidget(original_container)
self.image_area_layout.addWidget(translated_container)
layout.addWidget(controls_frame)
layout.addWidget(image_area_frame, stretch=1)
return container
def _build_dynamic_tab_content(self, tab_name: str, settings_list: list) -> QWidget:
"""
Creates a scrollable area and populates it with settings widgets,
now with a dedicated, collapsible section for advanced settings.
"""
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True)
scroll_area.setFrameShape(QFrame.Shape.NoFrame)
content_widget = QWidget()
layout = QVBoxLayout(content_widget)
layout.setContentsMargins(10, 10, 10, 10)
layout.setSpacing(8)
# --- NEW LOGIC: Split settings into standard and advanced groups first ---
standard_settings = []
advanced_settings = []
for info in settings_list:
if info.get("section") == "advanced":
advanced_settings.append(info)
else:
standard_settings.append(info)
# --- 1. Render all standard settings ---
for info in standard_settings:
widget_row = self._create_setting_row(info)
layout.addWidget(widget_row)
# --- 2. Render the 'Advanced Settings' separator and section ---
# Only add the separator if there are actually advanced settings for this tab
if advanced_settings:
# Add some vertical space before the separator
layout.addSpacing(15)
# Create the separator widget (Label + Line)
separator_container = QWidget()
separator_layout = QVBoxLayout(separator_container)
separator_layout.setContentsMargins(0, 5, 0, 5)
separator_layout.setSpacing(5)
label = QLabel("<b>ADVANCED SETTINGS</b>")
line = QFrame()
line.setFrameShape(QFrame.Shape.HLine)
line.setFrameShadow(QFrame.Shadow.Sunken)
separator_layout.addWidget(label)
separator_layout.addWidget(line)
layout.addWidget(separator_container)
# --- 3. Render all advanced settings ---
for info in advanced_settings:
widget_row = self._create_setting_row(info)
layout.addWidget(widget_row)
# Special handling for Extra Settings tab (Theme manager, etc.)
if tab_name == "Extra Settings":
vram_info_label = QLabel()
vram_info_label.setWordWrap(True)
vram_info_label.setStyleSheet("font-size: 9pt; color: #999;")
if self.detected_vram_gb > 0:
vram_text = f"Detected {self.detected_vram_gb:.2f} GB of VRAM. "
if self.detected_vram_gb <= 6:
vram_text += "<b>Recommendation: 'Low VRAM' mode.</b>"
else:
vram_text += "<b>Recommendation: 'High VRAM' mode.</b>"
else:
vram_text = "Could not detect GPU VRAM. 'Low VRAM' mode is recommended for safety."
vram_info_label.setText(vram_text)
layout.addWidget(vram_info_label)
separator = QFrame()
separator.setFrameShape(QFrame.Shape.HLine)
separator.setFrameShadow(QFrame.Shadow.Sunken)
layout.addWidget(separator)
font_scale_widget = self._create_font_scale_widget()
theme_manager_widget = self._create_theme_manager_widget()
layout.addWidget(font_scale_widget)
layout.addWidget(theme_manager_widget)
layout.addStretch() # Pushes all widgets to the top
scroll_area.setWidget(content_widget)
return scroll_area
def _build_tasks_tab_content(self) -> QWidget:
"""Creates the content for the 'Tasks' tab with improved layout."""
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True)
scroll_area.setFrameShape(QFrame.Shape.NoFrame)
content_widget = QWidget()
layout = QVBoxLayout(content_widget)
layout.setContentsMargins(10, 10, 10, 10)
layout.setSpacing(15)
# This widget is created manually and not from ui_map.json
device_widget_container = QWidget()
device_layout = QHBoxLayout(device_widget_container)
device_layout.setContentsMargins(0, 0, 0, 0)
device_layout.addWidget(QLabel("Task Processing Device:"))
# We reuse the segmented button logic for consistency
seg_button_container = QWidget()
seg_button_layout = QHBoxLayout(seg_button_container)
seg_button_layout.setContentsMargins(0, 0, 0, 0)
seg_button_layout.setSpacing(0)
button_group = QButtonGroup(seg_button_container)
button_group.setExclusive(True)
for val in ["CPU", "NVIDIA GPU"]:
button = QPushButton(val)
button.setCheckable(True)
seg_button_layout.addWidget(button)
button_group.addButton(button)
if val == "CPU": # Default to CPU
button.setChecked(True)
seg_button_container.setLayout(seg_button_layout)
device_layout.addWidget(seg_button_container, stretch=1)
# Store a reference to this new widget so we can read its value later
self.tasks_processing_device_widget = seg_button_container
layout.addWidget(device_widget_container)
# Add a separator line
separator = QFrame()
separator.setFrameShape(QFrame.Shape.HLine)
separator.setFrameShadow(QFrame.Shadow.Sunken)
layout.addWidget(separator)
tasks_config = self.config_loader.tasks_config
if not tasks_config:
layout.addWidget(QLabel("Could not load tasks.json or it is empty."))
content_widget.setLayout(layout)
scroll_area.setWidget(content_widget)
return scroll_area
if not hasattr(self, 'task_settings'):
self.task_settings = {}
self.task_widgets = {}
for task_key, task_info in tasks_config.items():
task_frame = QFrame()
task_frame.setObjectName("StyledPanel")
task_frame.setFrameShape(QFrame.Shape.StyledPanel)
task_layout = QVBoxLayout(task_frame)
# --- Top Section: Title and Description ---
title_label = QLabel(task_info.get("label", "Unnamed Task"))
font = title_label.font()
font.setPointSize(12)
font.setBold(True)
title_label.setFont(font)
task_layout.addWidget(title_label)
description_label = QLabel(task_info.get("description", ""))
description_label.setWordWrap(True)
task_layout.addWidget(description_label)
separator = QFrame()
separator.setFrameShape(QFrame.Shape.HLine)
separator.setFrameShadow(QFrame.Shadow.Sunken)
task_layout.addWidget(separator)
# --- Middle Section: Dynamically created settings ---
self.task_settings.setdefault(task_key, task_info.get("defaults", {}).copy())
self.task_widgets.setdefault(task_key, {})
settings_keys = task_info.get("settings_keys", [])
for setting_key in settings_keys:
widget_info = self.config_loader.full_config_data.get(setting_key)
if widget_info:
widget_row = self._create_setting_row(widget_info, task_key)
task_layout.addWidget(widget_row)
else:
task_layout.addWidget(QLabel(f"Warning: Definition for '{setting_key}' not found."))
task_layout.addStretch(1) # Add stretch to push buttons to the bottom
# --- Bottom Section: Action Buttons ---
button_container = QWidget()
button_layout = QHBoxLayout(button_container)
button_layout.setContentsMargins(0, 0, 0, 0)
reset_button = QPushButton("Reset to Defaults")
reset_button.clicked.connect(lambda checked, tk=task_key: self._reset_task_settings(tk))
# CORRECT DEFINITION ORDER
# Define the button first, then set its text and connect the signal.
run_button = QPushButton()
run_button.setText(f"Assign {task_info.get('label', 'Task')}")
run_button.clicked.connect(lambda checked, tk=task_key: self._assign_task_to_selection(tk))
button_layout.addWidget(reset_button, alignment=Qt.AlignmentFlag.AlignLeft)
button_layout.addStretch() # Pushes the two buttons apart
button_layout.addWidget(run_button, alignment=Qt.AlignmentFlag.AlignRight)
task_layout.addWidget(button_container)
layout.addWidget(task_frame)
layout.addStretch(1) # Pushes all task frames to the top
content_widget.setLayout(layout)
scroll_area.setWidget(content_widget)
return scroll_area
def _create_setting_row(self, info: dict, context_key: str = None) -> QWidget:
"""
Creates a single row (Label + Tooltip Icon + Widget) for a setting.
This function now handles the special 'translator_chain_builder' case separately
to prevent duplicate labels.
"""
row_widget = QWidget()
row_layout = QHBoxLayout(row_widget)
row_layout.setContentsMargins(0, 0, 0, 0)
row_layout.setSpacing(5)
widget_type = info.get("widget")
# --- SPECIAL CASE: Non-interactive Label ---
# This widget type is for display only and doesn't follow the standard flow.
if widget_type == "label":
widget = QLabel(info.get("label", ""))
if "style" in info:
widget.setStyleSheet(info["style"])
row_layout.addWidget(widget)
# Store a reference so we can update it later
if not context_key:
self.setting_widgets[info['key']] = widget
return row_widget # Return immediately, do not process further.
# --- PATH 1: For the special self-contained widget ---
if widget_type == "translator_chain_builder":
widget = self._create_translator_chain_builder(info)
row_layout.addWidget(widget)
if context_key:
self.task_widgets[context_key][info['key']] = widget
else:
self.setting_widgets[info['key']] = widget
if not hasattr(self, 'widget_references'): self.widget_references = {}
self.widget_references[info['key']] = widget
self._connect_widget_signal(info['key'], widget, context_key)
# --- PATH 2: For all other standard widgets ---
else:
label_container = QWidget()
label_layout = QHBoxLayout(label_container)
label_layout.setContentsMargins(0, 0, 0, 0)
label_layout.setSpacing(5)
label_text = info.get("label", info.get("key", "N/A"))
main_label = QLabel(label_text)
label_layout.addWidget(main_label)
tooltip_text = info.get("tooltip")
if tooltip_text:
tooltip_icon = QLabel("(?)")
tooltip_icon.setStyleSheet("color: #40E0D0;")
tooltip_icon.setCursor(Qt.CursorShape.PointingHandCursor)
default_val = info.get('default', 'N/A')
full_tooltip = f"<b>{label_text}</b><hr>{tooltip_text}<br><i>(Default: {default_val})</i>"
tooltip_icon.setToolTip(full_tooltip)
label_layout.addWidget(tooltip_icon)
label_layout.addStretch()
row_layout.addWidget(label_container, stretch=1)
if widget_type == "segmented_button":
widget = self._create_segmented_button(info)
elif widget_type in ["optionmenu", "optionmenu_languages", "optionmenu_separators"]:
widget = self._create_combobox(info)
elif widget_type == "checkbox":
widget = self._create_checkbox(info)
elif widget_type == "slider":
widget = self._create_slider(info)
elif widget_type == "entry":
widget = self._create_entry(info)
elif widget_type == "language_checkbox_grid":
widget = self._create_language_checkbox_grid(info)
elif widget_type == "combobox_fonts":
widget = self._create_font_combobox(info)
elif widget_type == "entry_with_button":
widget = self._create_entry_with_button(info)
elif widget_type == "api_key_manager":
widget = self._create_api_manager_widget(info)
elif widget_type == "grid_segmented_button":
widget = self._create_grid_segmented_button(info)
elif widget_type == "preset_manager":
widget = self._create_preset_manager(info)
else:
widget = QLabel(f"TODO: '{widget_type}'")
widget.setStyleSheet("color: yellow;")
row_layout.addWidget(widget, stretch=2)
if context_key:
self.task_widgets[context_key][info['key']] = widget
initial_value = self.task_settings[context_key].get(info['key'])
else:
self.setting_widgets[info['key']] = widget
initial_value = self.current_settings.get(info['key'])
self._set_widget_value(info['key'], initial_value, widget)
self._connect_widget_signal(info['key'], widget, context_key)
return row_widget
def _create_segmented_button(self, info: dict) -> QWidget:
"""Creates a group of toggleable buttons."""
container = QWidget()
layout = QHBoxLayout(container)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
button_group = QButtonGroup(container)
button_group.setExclusive(True) # Only one can be checked at a time
values = info.get("values", [])
for val in values:
button = QPushButton(val)
button.setCheckable(True)
if val == info.get("default"):
button.setChecked(True)
layout.addWidget(button)
button_group.addButton(button)
return container
def _create_combobox(self, info: dict) -> QComboBox:
"""Creates a dropdown (ComboBox) widget."""
combo_box = QComboBox()
values = info.get("values", [])
if info.get("widget") == "optionmenu_separators":
for group_name, translators in TRANSLATOR_GROUPS.items():
# Add the group name as a non-selectable header
item_index = combo_box.count()
combo_box.addItem(group_name)
combo_box.model().item(item_index).setEnabled(False)
# Add the translators under that group
combo_box.addItems(translators)
# Set default value
combo_box.setCurrentText(str(info.get("default")))
else:
combo_box.addItems(values)
combo_box.setCurrentText(str(info.get("default")))
return combo_box
def _create_checkbox(self, info: dict) -> QCheckBox:
"""Creates a checkbox widget."""
# In PySide, the label is part of the checkbox, so we pass an empty string
check_box = QCheckBox("")
if info.get("default") is True:
check_box.setChecked(True)
return check_box
def _create_slider(self, info: dict) -> QWidget:
"""Creates a container with a QSlider and a QLabel to display its value."""
container = QWidget()
layout = QHBoxLayout(container)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(10)
slider = QSlider(Qt.Orientation.Horizontal)
# --- FIX: Prevent focus and wheel events ---
# This makes the slider only controllable by dragging the handle.
slider.setFocusPolicy(Qt.FocusPolicy.NoFocus)
value_label = QLabel()
value_label.setMinimumWidth(45)
value_label.setAlignment(Qt.AlignmentFlag.AlignRight)
options = info.get("options", {})
from_val = options.get("from_", 0)
to_val = options.get("to", 100)
# --- FIX: Ensure integer steps for integer-based sliders ---
# The 'number_of_steps' key in ui_map.json can be used to control this,
# but for now, we'll ensure the final value is rounded correctly.
multiplier = info.get("value_multiplier", 1)
# Use a high precision for smooth dragging
internal_precision = 100
slider.setMinimum(int(from_val * internal_precision))
slider.setMaximum(int(to_val * internal_precision))
def update_label(value):
actual_value = (value / internal_precision) * multiplier
value_format = info.get("value_format", "{:.0f}")
if 'f' in value_format:
display_value = float(actual_value)
else:
display_value = int(round(actual_value))
if value_label:
value_label.setText(value_format.format(display_value))
slider.update_label_func = update_label
slider.valueChanged.connect(update_label)
default_value = info.get("default")
initial_slider_value = 0
if default_value is not None:
try:
initial_slider_value = int((float(default_value) / multiplier) * internal_precision)
except (ValueError, TypeError):
initial_slider_value = 0
slider.setValue(initial_slider_value)
update_label(initial_slider_value)
layout.addWidget(slider)
layout.addWidget(value_label)
return container
def _create_entry(self, info: dict) -> QLineEdit:
"""Creates a text input (QLineEdit) widget."""
entry = QLineEdit()
default_text = info.get("default", "")
if default_text is not None:
entry.setText(str(default_text))
placeholder = info.get("placeholder", "")
if placeholder:
entry.setPlaceholderText(placeholder)
return entry
def _create_language_checkbox_grid(self, info: dict) -> QWidget:
"""Creates a scrollable grid of checkboxes for language selection."""
# This widget is more complex, so it gets its own container and layout
container = QFrame()
container.setFrameShape(QFrame.Shape.StyledPanel)
container_layout = QVBoxLayout(container)
# We don't need a scroll area if the container itself can be a fixed size
# for simplicity for now. A QScrollArea can be added later if needed.
grid_widget = QWidget()
grid_layout = QGridLayout(grid_widget)
grid_layout.setSpacing(5)
checkboxes = {}
lang_items = [lang for lang in LANGUAGES.items() if lang[1] != 'auto']
# Create a 2-column grid of checkboxes
for i, (name, code) in enumerate(lang_items):
row, col = divmod(i, 2)
check_box = QCheckBox(name)
grid_layout.addWidget(check_box, row, col)
checkboxes[code] = check_box
container_layout.addWidget(grid_widget)
# We need to store these checkboxes somewhere to get their values later.
# Let's create a storage for them if it doesn't exist.
if not hasattr(self, 'widget_references'):
self.widget_references = {}
self.widget_references[info['key']] = checkboxes
return container
def _create_font_combobox(self, info: dict) -> QComboBox:
"""Creates a combobox populated with system and project fonts."""
combo_box = QComboBox()
font_list = self._get_font_list()
combo_box.addItems(font_list)
# Make the header items non-selectable for better UX
for i, item_text in enumerate(font_list):
if item_text.startswith("---"):
combo_box.model().item(i).setEnabled(False)
default_font = info.get("default", "Sans-serif")
combo_box.setCurrentText(default_font)
return combo_box
def _create_entry_with_button(self, info: dict) -> QWidget:
"""Creates a QLineEdit with a QPushButton next to it."""
container = QWidget()
layout = QHBoxLayout(container)
layout.setContentsMargins(0, 0, 0, 0)
entry = QLineEdit()
entry.setText(str(info.get("default", "")))
layout.addWidget(entry)
button = QPushButton(info.get("button_text", "..."))
button.setFixedWidth(40)
# Pass both the widget key and the associated entry field to the handler
button.clicked.connect(lambda: self._handle_widget_button_click(info['key'], entry))
layout.addWidget(button)
return container
def _create_translator_chain_builder(self, info: dict) -> QWidget:
"""
Creates a self-contained component for the translator chain,
including its own header with a label and control buttons.
"""
# Main container for the whole builder
container = QFrame()
container.setObjectName("ChainBuilderFrame")
container_layout = QVBoxLayout(container)
container_layout.setContentsMargins(0, 0, 0, 0)
container_layout.setSpacing(5)
# --- Header Row ---
header_widget = QWidget()
header_layout = QHBoxLayout(header_widget)
header_layout.setContentsMargins(0, 0, 0, 0)
# The label is now created INSIDE the component
label = QLabel(info.get("label", "Translation Steps:"))
header_layout.addWidget(label)
header_layout.addStretch() # This pushes the buttons to the right
# Control buttons for the list
add_btn = QPushButton("➕ Add Step")
remove_btn = QPushButton("➖ Remove Selected")
header_layout.addWidget(add_btn)
header_layout.addWidget(remove_btn)
# Add the completed header to the main vertical layout
container_layout.addWidget(header_widget)
# --- List Widget ---
self.chain_list_widget = DynamicHeightListWidget()
self.chain_list_widget.setDragDropMode(QListWidget.DragDropMode.InternalMove)
container_layout.addWidget(self.chain_list_widget)
# --- Connect signals ---
add_btn.clicked.connect(self._add_chain_step)
remove_btn.clicked.connect(self._remove_chain_step)
# Store a reference for enabling/disabling the whole container
self.widget_references[info['key']] = container
# Initialize UI state
QTimer.singleShot(0, self._update_chain_ui_state)
return container
def _create_chain_step_widget(self) -> QWidget:
"""Creates the widget for a single row/step in the translator chain."""
step_widget = QWidget()
layout = QHBoxLayout(step_widget)
layout.setContentsMargins(5, 5, 5, 5)
translator_combo = NoScrollComboBox()
# Populate with translators from TRANSLATOR_GROUPS constant
for group_name, translators in TRANSLATOR_GROUPS.items():
item_index = translator_combo.count()
translator_combo.addItem(group_name)
translator_combo.model().item(item_index).setEnabled(False)
translator_combo.addItems(translators)
lang_combo = NoScrollComboBox()
# Populate with languages from LANGUAGES constant
lang_combo.addItems(list(LANGUAGES.keys()))
layout.addWidget(QLabel("Translate with:"))
layout.addWidget(translator_combo)
layout.addWidget(QLabel("to"))
layout.addWidget(lang_combo)
# Store combo boxes in the widget's properties for later access
step_widget.translator_combo = translator_combo
step_widget.lang_combo = lang_combo
translator_combo.currentTextChanged.connect(
lambda text, lc=lang_combo: self._filter_language_dropdown(text, lc)
)
# Trigger it once to set the initial state
self._filter_language_dropdown(translator_combo.currentText(), lang_combo)
handler = lambda: self._on_setting_changed('translator_chain')
translator_combo.currentTextChanged.connect(handler)
lang_combo.currentTextChanged.connect(handler)
return step_widget
def _add_chain_step(self):
"""Adds a new, empty step to the translator chain list."""
step_widget = self._create_chain_step_widget()
list_item = QListWidgetItem(self.chain_list_widget)
list_item.setSizeHint(step_widget.sizeHint())
self.chain_list_widget.addItem(list_item)
self.chain_list_widget.setItemWidget(list_item, step_widget)
self._on_setting_changed('translator_chain') # Notify that settings have changed
self.chain_list_widget.updateGeometry()
def _remove_chain_step(self):
"""Removes the currently selected step from the chain list."""
selected_items = self.chain_list_widget.selectedItems()
if not selected_items:
return
for item in selected_items:
row = self.chain_list_widget.row(item)
self.chain_list_widget.takeItem(row)
self._on_setting_changed('translator_chain')
self.chain_list_widget.updateGeometry()
def _get_translator_chain_string(self) -> str:
"""
Reads all steps from the chain_list_widget and builds the
backend-compatible string (e.g., 'sugoi:ENG;deepl:TRK').
"""
if not hasattr(self, 'chain_list_widget'):
return ""
steps = []
for i in range(self.chain_list_widget.count()):
item = self.chain_list_widget.item(i)
widget = self.chain_list_widget.itemWidget(item)
if widget and hasattr(widget, 'translator_combo') and hasattr(widget, 'lang_combo'):
translator_name = widget.translator_combo.currentText()
lang_name = widget.lang_combo.currentText()
# Make sure the selected item is not a separator/header
if translator_name not in TRANSLATOR_GROUPS:
lang_code = LANGUAGES.get(lang_name, '')
if lang_code:
steps.append(f"{translator_name}:{lang_code}")
return ";".join(steps)
def _rebuild_chain_from_string(self, chain_string: str):
"""Clears and rebuilds the translator chain UI from a saved string."""
self.chain_list_widget.clear()
if not chain_string:
return
steps = chain_string.split(';')
code_to_lang_name = {v: k for k, v in LANGUAGES.items()}
for step in steps:
parts = step.split(':')
if len(parts) == 2:
translator_name, lang_code = parts
# Add a new visual step to the list
step_widget = self._create_chain_step_widget()
list_item = QListWidgetItem(self.chain_list_widget)
list_item.setSizeHint(step_widget.sizeHint())
self.chain_list_widget.addItem(list_item)
self.chain_list_widget.setItemWidget(list_item, step_widget)
# Set the combobox values based on the loaded data
step_widget.translator_combo.setCurrentText(translator_name)
lang_name = code_to_lang_name.get(lang_code, "")
if lang_name:
step_widget.lang_combo.setCurrentText(lang_name)
self.chain_list_widget.updateGeometry()
def _create_grid_segmented_button(self, info: dict) -> QWidget:
"""Creates a grid of toggleable buttons that can wrap to multiple lines."""
container = QWidget()
layout = QGridLayout(container)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(5)
button_group = QButtonGroup(container)
button_group.setExclusive(True)
values = info.get("values", [])
columns = info.get("options", {}).get("columns", 4) # Default to 4 columns
row, col = 0, 0
for val in values:
button = QPushButton(val)
button.setCheckable(True)
if val == info.get("default"):
button.setChecked(True)
layout.addWidget(button, row, col)
button_group.addButton(button)
col += 1
if col >= columns:
col = 0
row += 1
return container
def _create_preset_manager(self, info: dict) -> QWidget:
"""Creates the preset management compound widget."""
preset_frame = QFrame()
preset_frame.setObjectName("StyledPanel")
preset_frame.setFrameShape(QFrame.Shape.StyledPanel)
layout = QVBoxLayout(preset_frame)
self.profile_combobox = QComboBox()
layout.addWidget(self.profile_combobox)
self.profile_name_entry = QLineEdit()
self.profile_name_entry.setPlaceholderText("Enter new preset name")
layout.addWidget(self.profile_name_entry)
# When a profile is selected from the dropdown, copy its name to the entry field
self.profile_combobox.currentTextChanged.connect(self.profile_name_entry.setText)
button_container = QWidget()
button_layout = QHBoxLayout(button_container)
button_layout.setContentsMargins(0, 0, 0, 0)
save_btn = QPushButton("Save")
load_btn = QPushButton("Load")
delete_btn = QPushButton("Delete")
button_layout.addWidget(save_btn)
button_layout.addWidget(load_btn)
button_layout.addWidget(delete_btn)
layout.addWidget(button_container)
# Connect buttons to their handler methods
save_btn.clicked.connect(self._save_profile)
load_btn.clicked.connect(self._load_profile)
delete_btn.clicked.connect(self._delete_profile)
self._refresh_profile_list() # Initial population of the combobox
return preset_frame
def _get_font_list(self) -> list:
"""Gets a combined list of project and system fonts."""
project_fonts = []
fonts_dir = os.path.join(self.project_base_dir, "fonts")
if os.path.isdir(fonts_dir):
project_fonts = sorted([f for f in os.listdir(fonts_dir) if f.lower().endswith(('.ttf', '.otf'))])
# Use QFontDatabase for a reliable way to get system fonts
system_fonts = sorted(QFontDatabase().families())
final_list = []
if project_fonts:
final_list.extend(project_fonts)
final_list.extend(system_fonts)
return final_list
def _update_chain_ui_state(self):
"""
Enables or disables the translator chain builder and the main translator dropdowns
based on the 'Enable Translator Chain' checkbox.
"""
# Find the necessary widgets
enable_checkbox = self.setting_widgets.get('enable_translator_chain')
chain_container = self.widget_references.get('translator_chain')
main_translator_combo = self.setting_widgets.get('translator')
main_language_combo = self.setting_widgets.get('target_lang')
if not all([enable_checkbox, chain_container, main_translator_combo, main_language_combo]):
return
is_chain_enabled = enable_checkbox.isChecked()
# Enable/disable the builder and its contents
chain_container.setEnabled(is_chain_enabled)
# Enable/disable the main dropdowns (opposite logic)
main_translator_combo.setEnabled(not is_chain_enabled)
main_language_combo.setEnabled(not is_chain_enabled)
# If the chain is disabled, clear its contents
if not is_chain_enabled:
self.chain_list_widget.clear()
self.chain_list_widget.updateGeometry()
self._on_setting_changed('translator_chain')
def _update_chain_list_height(self):
"""Calculates and sets the minimum height of the chain list widget to fit all its items."""
if not hasattr(self, 'chain_list_widget'):
return
# Calculate the total height of all item widgets
content_height = 0
for i in range(self.chain_list_widget.count()):
# Using sizeHintForRow is more accurate than getting the widget's hint directly
content_height += self.chain_list_widget.sizeHintForRow(i)
# Add spacing between items to the calculation
if self.chain_list_widget.count() > 1:
content_height += self.chain_list_widget.spacing() * (self.chain_list_widget.count() - 1)
# Ensure the widget doesn't collapse completely when empty
if content_height == 0:
content_height = 40 # A sensible default height for an empty list
# Force the layout to give the list at least this much vertical space
self.chain_list_widget.setMinimumHeight(content_height)
def _update_translator_tooltip(self, translator_name: str):
"""Updates the tooltip of the translator combobox to show its capabilities."""
translator_combo = self.setting_widgets.get('translator')
if not translator_combo:
return
capabilities = TRANSLATOR_CAPABILITIES.get(translator_name, {})
# Reverse the LANGUAGES dict for easy code-to-name lookup
code_to_name = {v: k for k, v in LANGUAGES.items()}
tooltip_html = f"<b>{translator_name} Capabilities:</b><hr>"
if not capabilities:
tooltip_html += "No translation is performed."
elif capabilities.get('__any__') == '__all__':
tooltip_html += "Supports translation between most languages."
else:
lines = []
for source_code, target_codes in capabilities.items():
source_name = code_to_name.get(source_code, source_code)
target_names = [code_to_name.get(tc, tc) for tc in target_codes]
lines.append(f"<b>From {source_name}:</b><br> → {', '.join(target_names)}")
tooltip_html += "<br>".join(lines)
translator_combo.setToolTip(tooltip_html)
def _handle_widget_button_click(self, key: str, associated_widget: QWidget):
"""Handles clicks for buttons that are part of a widget row."""
if key == "font_color":
current_color = associated_widget.text()
if not current_color: current_color = "000000"
color = QColorDialog.getColor(initial=f"#{current_color}", title="Choose Font Color")
if color.isValid():
new_color_hex = color.name()[1:]
associated_widget.setText(new_color_hex)
self._on_setting_changed(key)
elif key == "gpt_config":
configs_dir = os.path.join(self.project_base_dir, "MangaStudio_Data", "gpt_configs")
os.makedirs(configs_dir, exist_ok=True)
config_path, _ = QFileDialog.getOpenFileName(self, "Select GPT Config File", configs_dir, "YAML Files (*.yaml *.yml);;All Files (*)")
if config_path:
file_name = os.path.basename(config_path)
associated_widget.setText(file_name)
self._on_setting_changed(key)
elif key in ["pre_dict_path", "post_dict_path"]:
# --- NEW DICTIONARY LOGIC ---
dicts_dir = os.path.join(self.project_base_dir, "MangaStudio_Data", "dicts")
os.makedirs(dicts_dir, exist_ok=True)
file_path, _ = QFileDialog.getOpenFileName(
self,
"Select Dictionary File",
dicts_dir,
"Text Files (*.txt);;All Files (*)"
)
if file_path:
# We only want the relative path from the project base directory
relative_path = os.path.relpath(file_path, self.project_base_dir)
associated_widget.setText(relative_path.replace("\\", "/")) # Use forward slashes for consistency
self._on_setting_changed(key)
def _refresh_profile_list(self):
"""Reloads the list of profiles from the directory and updates the combobox."""
profiles_dir = os.path.join(self.project_base_dir, "MangaStudio_Data", "profiles")
os.makedirs(profiles_dir, exist_ok=True)
try:
profiles = sorted([f.replace(".json", "") for f in os.listdir(profiles_dir) if f.endswith(".json")])
self.profile_combobox.clear()
if profiles:
self.profile_combobox.addItems(profiles)
else:
self.profile_combobox.addItem("No profiles found")
except Exception as e:
print(f"[ERROR] Failed to refresh profiles: {e}")
def _save_profile(self):
"""Saves the current settings dictionary as a profile."""
name = self.profile_name_entry.text().strip()
if not name:
QMessageBox.warning(self, "Warning", "Please enter a profile name.")
return
profiles_dir = os.path.join(self.project_base_dir, "MangaStudio_Data", "profiles")
path = os.path.join(profiles_dir, f"{name}.json")
if os.path.exists(path):
reply = QMessageBox.question(self, "Confirm Overwrite", f"Profile '{name}' already exists. Overwrite it?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No)
if reply == QMessageBox.StandardButton.No:
return
try:
with open(path, 'w', encoding='utf-8') as f:
# Save the current_settings dictionary to the JSON file
import json
json.dump(self.current_settings, f, indent=4)
self._refresh_profile_list()
self.profile_combobox.setCurrentText(name)
print(f"Profile '{name}' saved successfully.")
except Exception as e:
QMessageBox.critical(self, "Error", f"Failed to save profile: {e}")
def _load_profile(self):
"""Loads a profile and applies its settings, ensuring the UI remains enabled."""
name = self.profile_combobox.currentText()
if not name or name == "No profiles found":
return
path = os.path.join(self.project_base_dir, "MangaStudio_Data", "profiles", f"{name}.json")
if not os.path.exists(path):
QMessageBox.critical(self, "Error", f"Profile file not found: {name}.json")
self._refresh_profile_list()
return
try:
with open(path, 'r', encoding='utf-8') as f:
import json
loaded_settings = json.load(f)
# Update the settings for the currently selected job (if any)
job_index = self._get_selected_job_index()
if job_index is not None:
self.job_queue[job_index]['settings'].update(loaded_settings)
else:
# If no job is selected, update the global defaults instead
self.current_settings.update(loaded_settings)
# Repopulate the panel with the new settings
self._populate_settings_panel()
if 'translator_chain' in loaded_settings:
self._rebuild_chain_from_string(loaded_settings['translator_chain'])
# Manually trigger the UI state update for the chain builder
self._update_chain_ui_state()
# --- CRITICAL FIX ---
# After populating, explicitly ensure the settings panel is enabled,
# as long as a job is selected. This prevents it from getting stuck in a disabled state.
self._set_settings_panel_enabled(job_index is not None)
self.log("SUCCESS", f"Profile '{name}' loaded and applied.")
print(f"Profile '{name}' loaded successfully.")
except Exception as e:
error_message = f"An unexpected error occurred while loading profile '{name}'.\n\nDetails: {e}"
print(f"[ERROR] {error_message}")
QMessageBox.critical(self, "Profile Load Error", error_message)
self._set_settings_panel_enabled(True)
def _delete_profile(self):
"""Deletes the selected profile."""
name = self.profile_combobox.currentText()
if not name or name == "No profiles found":
return
reply = QMessageBox.question(self, "Confirm Delete", f"Are you sure you want to delete profile '{name}'?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No)
if reply == QMessageBox.StandardButton.No:
return
profiles_dir = os.path.join(self.project_base_dir, "MangaStudio_Data", "profiles")
path = os.path.join(profiles_dir, f"{name}.json")
try:
if os.path.exists(path):
os.remove(path)
print(f"Profile '{name}' deleted.")
self._refresh_profile_list()
except Exception as e:
QMessageBox.critical(self, "Error", f"Failed to delete profile: {e}")
print(f"[ERROR] Failed to delete profile '{name}': {e}")
def _create_bottom_panel(self) -> QWidget:
"""Creates the bottom panel with progress bar and control buttons."""
bottom_frame = QFrame()
bottom_frame.setFrameShape(QFrame.Shape.NoFrame)
layout = QHBoxLayout(bottom_frame)
layout.setContentsMargins(0, 0, 0, 0)
progress_widget = QWidget()
progress_layout = QVBoxLayout(progress_widget)
progress_layout.setSpacing(2)
progress_layout.setContentsMargins(0, 0, 0, 0)
self.progress_label = QLabel("Ready")
self.progress_bar = QProgressBar()
self.progress_bar.setValue(0)
self.progress_bar.setTextVisible(False)
progress_layout.addWidget(self.progress_label)
progress_layout.addWidget(self.progress_bar)
self.start_button = QPushButton("▶️ START PIPELINE")
self.start_button.clicked.connect(self._start_pipeline_thread)
self.start_button.setFixedHeight(40)
font = self.start_button.font()
font.setBold(True)
self.start_button.setFont(font)
self.stop_button = QPushButton("⏹️ STOP")
self.stop_button.clicked.connect(self._stop_pipeline)
self.stop_button.setEnabled(False)
self.stop_button.setFixedHeight(40)
layout.addWidget(progress_widget, stretch=1)
layout.addWidget(self.start_button)
layout.addWidget(self.stop_button)
return bottom_frame
def _create_font_scale_widget(self) -> QWidget:
"""Creates a special row for the UI font scaling option."""
row_widget = QWidget()
row_layout = QHBoxLayout(row_widget)
row_layout.setContentsMargins(0, 0, 0, 0)
row_layout.setSpacing(5)
label = QLabel("UI Font Scale:")
label.setToolTip("Changes the font size for the entire application UI.")
row_layout.addWidget(label, stretch=1)
self.font_scale_combobox = QComboBox()
self.font_scale_combobox.addItems(["75%", "85%", "100% (Default)", "115%", "125%", "150%"])
self.font_scale_combobox.setCurrentText("100% (Default)")
# Connect the combobox signal to the handler function
self.font_scale_combobox.currentTextChanged.connect(self._on_font_scale_changed)
row_layout.addWidget(self.font_scale_combobox, stretch=2)
return row_widget
def _on_font_scale_changed(self, text: str):
"""
Applies a global font size by RE-APPLYING the currently selected theme,
which will automatically use the new font scale.
"""
# Get the name of the currently selected theme from the combobox
current_theme_name = self.theme_combobox.currentText()
# Re-apply the theme. This function is smart enough to read the new font
# scale from the combobox and include it in the full stylesheet.
self._apply_theme(current_theme_name)
def _connect_widget_signal(self, key: str, widget: QWidget, context_key: str = None):
"""
Connects the appropriate signal of a widget to the setting change handler.
This version correctly passes the context_key to the handler.
"""
info = self.config_loader.full_config_data.get(key, {})
widget_type = info.get("widget")
# Create a handler function that "captures" the current key and context_key.
# The lambda function is perfect for this.
handler = lambda *args, k=key, ctx=context_key: self._on_setting_changed(k, ctx)
if isinstance(widget, QComboBox):
# For QComboBox, currentIndexChanged sends an integer index, which we can ignore with *args
widget.currentIndexChanged.connect(handler)
# If this is the main translator dropdown, connect our special handlers
if key == 'translator':
# currentTextChanged sends the string name, which is what we need
widget.currentTextChanged.connect(self._on_translator_changed)
widget.currentTextChanged.connect(self._update_translator_tooltip)
# Call it once at the beginning to set the initial tooltip
self._update_translator_tooltip(widget.currentText())
elif isinstance(widget, QCheckBox):
# For QCheckBox, stateChanged sends the state, which we can ignore with *args
widget.stateChanged.connect(handler)
if key == 'enable_translator_chain':
widget.stateChanged.connect(self._update_chain_ui_state)
if key == 'restore_size_after_colorize':
widget.stateChanged.connect(self._update_colorize_restore_ui_state)
elif isinstance(widget, QLineEdit):
# editingFinished has no arguments, so it works perfectly.
widget.editingFinished.connect(handler)
elif widget_type in ["segmented_button", "grid_segmented_button"]:
button_group = widget.findChild(QButtonGroup)
if button_group:
button_group.buttonClicked.connect(handler)
elif widget_type == "language_checkbox_grid":
checkbox_dict = self.widget_references.get(key, {})
if checkbox_dict:
for checkbox in checkbox_dict.values():
# stateChanged sends the state, which we can ignore with *args
checkbox.stateChanged.connect(handler)
elif widget_type == "slider":
slider = widget.findChild(QSlider)
if slider:
# valueChanged sends the new value, which we can ignore with *args
slider.valueChanged.connect(handler)
elif widget_type == "entry_with_button":
entry = widget.findChild(QLineEdit)
if entry:
# editingFinished has no arguments.
entry.editingFinished.connect(handler)
def _on_setting_changed(self, key: str, context_key: str = None):
"""
A generic handler called whenever a setting widget's value changes.
"""
if context_key: # It's a setting for a special task
widget = self.task_widgets[context_key].get(key)
new_value = self._get_value_from_widget(key, widget) # Pass widget directly
self.task_settings[context_key][key] = new_value
print(f"[Task Settings] Updated '{context_key}.{key}' to: {new_value}")
else: # It's a global setting for the main pipeline
widget = self.setting_widgets.get(key)
if key == 'translator_chain':
new_value = self._get_translator_chain_string()
else:
new_value = self._get_value_from_widget(key, widget)
self.current_settings[key] = new_value
print(f"[Settings] Updated '{key}' to: {new_value}")
def _on_translator_changed(self, translator_name: str):
"""Handles changes in the main translator selection."""
# Filter the main target language dropdown
lang_combo = self.setting_widgets.get('target_lang')
self._filter_language_dropdown(translator_name, lang_combo)
# Update the tooltip
self._update_translator_tooltip(translator_name)
def _filter_language_dropdown(self, translator_name: str, lang_combo: QComboBox):
"""
A centralized function to filter a given language QComboBox based on
the capabilities of the selected translator.
"""
if not lang_combo:
return
capabilities = TRANSLATOR_CAPABILITIES.get(translator_name, {})
supported_codes = set()
if capabilities.get('__any__') == '__all__':
all_langs = list(LANGUAGES.values())
if "auto" in all_langs:
all_langs.remove("auto")
supported_codes = set(all_langs)
else:
for source_lang, target_langs in capabilities.items():
supported_codes.update(target_langs)
supported_display_names = [name for name, code in LANGUAGES.items() if code in supported_codes]
current_selection = lang_combo.currentText()
lang_combo.blockSignals(True)
lang_combo.clear()
if not supported_display_names:
lang_combo.addItem("No Supported Targets")
lang_combo.setEnabled(False)
else:
lang_combo.addItems(sorted(supported_display_names))
lang_combo.setEnabled(True)
lang_combo.blockSignals(False)
if current_selection in supported_display_names:
lang_combo.setCurrentText(current_selection)
elif "English" in supported_display_names:
lang_combo.setCurrentText("English")
def _get_value_from_widget(self, key: str, widget: QWidget) -> any:
"""Retrieves the current value from a given widget by its key."""
# The widget is now passed directly, no need for lookup
if not widget:
return None
info = self.config_loader.full_config_data.get(key, {})
widget_type = info.get("widget")
if isinstance(widget, QComboBox):
if widget_type == "optionmenu_languages":
return LANGUAGES.get(widget.currentText(), "auto")
return widget.currentText()
elif isinstance(widget, QCheckBox):
return widget.isChecked()
elif isinstance(widget, QLineEdit):
return widget.text()
elif widget_type in ["segmented_button", "grid_segmented_button"]:
button_group = widget.findChild(QButtonGroup)
if button_group and button_group.checkedButton():
value = button_group.checkedButton().text()
if key == "upscale_ratio":
if value == "Disabled":
return None # Return None instead of the string "Disabled"
else:
return int(value.replace("x", ""))
return value
return None # Return None if no button is checked
elif key == "language_checkbox_grid":
checkbox_dict = self.widget_references.get(key, {})
selected = [code for code, cb in checkbox_dict.items() if cb.isChecked()]
return ",".join(sorted(selected))
elif widget_type == "slider":
slider = widget.findChild(QSlider)
if slider:
precision = 100
multiplier = info.get("value_multiplier", 1)
actual_value = (slider.value() / precision) * multiplier
# Get the format string to decide if we need an int or a float
value_format = info.get("value_format", "{:.0f}")
# If the format string specifies an integer (like "{:.0f}")
if value_format.endswith("0f}"):
return int(round(actual_value))
else:
# Otherwise, it's a float. Return it rounded for cleanliness.
return round(actual_value, 4)
return None
elif widget_type == "entry_with_button":
entry = widget.findChild(QLineEdit)
if entry:
return entry.text()
return None
return None
def _set_widget_value(self, key: str, value: any, widget: QWidget):
"""Sets the value of a given widget by its key."""
if not widget or value is None:
return
info = self.config_loader.full_config_data.get(key, {})
widget_type = info.get("widget")
if isinstance(widget, QComboBox):
if widget_type == "optionmenu_languages":
display_name = next((k for k, v in LANGUAGES.items() if v == value), None)
if display_name:
widget.setCurrentText(display_name)
else:
widget.setCurrentText(str(value))
elif isinstance(widget, QCheckBox):
widget.setChecked(bool(value))
elif isinstance(widget, QLineEdit):
widget.setText(str(value))
elif widget_type == "segmented_button":
button_group = widget.findChild(QButtonGroup)
if button_group:
value_to_check = str(value)
if key == "upscale_ratio":
if value is None:
value_to_check = "Disabled"
else:
value_to_check = f"{value}x"
for button in button_group.buttons():
if button.text() == value_to_check:
button.setChecked(True)
break
elif widget_type == "grid_segmented_button":
button_group = widget.findChild(QButtonGroup)
if button_group:
value_to_check = str(value)
for button in button_group.buttons():
if button.text() == value_to_check:
button.setChecked(True)
break
elif key == "language_checkbox_grid":
checkbox_dict = self.widget_references.get(key, {})
selected_langs = set(str(value).split(','))
for code, cb in checkbox_dict.items():
cb.setChecked(code in selected_langs)
elif widget_type == "slider":
slider = widget.findChild(QSlider)
if slider and value is not None:
precision = 100
multiplier = info.get("value_multiplier", 1)
slider_value = int((float(value) / multiplier) * precision) if multiplier != 0 else 0
slider.setValue(slider_value)
update_func = getattr(slider, 'update_label_func', None)
if update_func:
update_func(slider_value)
elif widget_type == "entry_with_button":
entry = widget.findChild(QLineEdit)
if entry:
entry.setText(str(value))
def _add_job(self):
"""Opens a dialog to select a folder and adds it as a job."""
# Use the last selected directory, or the project base directory as a fallback.
initial_dir = getattr(self, 'last_selected_directory', self.project_base_dir)
folder_path = QFileDialog.getExistingDirectory(self, "Select Manga/Image Folder", initial_dir)
if folder_path:
# Store the newly selected directory to be saved on exit.
self.last_selected_directory = folder_path
self._add_job_from_path(folder_path)
def _add_job_from_path(self, path):
"""
Adds a job with a default 'Awaiting Config' status to the queue
and selects it in the UI.
"""
import time
job_id = f"job_{int(time.time() * 1000)}_{len(self.job_queue)}"
job_data = {
"id": job_id,
"source_path": path,
"name": os.path.basename(path),
# A new job starts with a fresh copy of factory defaults
"settings": self.config_loader.get_factory_defaults().copy(),
# A new job is awaiting configuration by the user
"status": "Awaiting Config", # Status: ⚪
# A new job has no assigned type until a configuration is applied
"job_type": None
}
self.job_queue.append(job_data)
# Refresh the entire queue UI to show the new job
self._update_job_list_ui()
# --- CRITICAL FIX: Select the newly added item in the correct list widget ---
# Find the item we just added by its unique job_id and set it as the current row.
for i in range(self.queue_list_widget.count()):
item = self.queue_list_widget.item(i)
if item.data(Qt.ItemDataRole.UserRole) == job_id:
# Setting the current row will automatically trigger the
# _on_job_selection_changed signal, which is what we want.
self.queue_list_widget.setCurrentRow(i)
break
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.acceptProposedAction()
else:
event.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasUrls():
event.acceptProposedAction()
else:
event.ignore()
def dropEvent(self, event):
if event.mimeData().hasUrls():
for url in event.mimeData().urls():
path = url.toLocalFile()
if os.path.isdir(path):
self._add_job_from_path(path)
else:
self.log("WARNING", f"Dropped item is not a directory: {path}")
event.acceptProposedAction()
else:
event.ignore()
def _remove_selected_jobs_from_queue(self):
"""Placeholder for removing selected jobs. To be implemented with context menu."""
print("Action: Remove Selected Jobs (Not yet implemented)")
# Future logic will go here
# selected_items = self.queue_list_widget.selectedItems()
# ... loop and remove from self.job_queue ...
# self._update_job_list_ui()
def _duplicate_selected_jobs(self):
"""
Creates a new, clean job using the same source path as the selected job(s).
The new job will have factory default settings and no assigned job type.
"""
selected_items = self.queue_list_widget.selectedItems()
if not selected_items:
return
jobs_to_add = []
for item in selected_items:
original_job_id = item.data(Qt.ItemDataRole.UserRole)
original_job = next((job for job in self.job_queue if job['id'] == original_job_id), None)
if original_job:
# Create a completely new job dictionary, only reusing the source path and name.
# This is similar to adding a brand new job.
new_job = {
"id": f"job_{int(time.time() * 1000)}_{len(self.job_queue) + len(jobs_to_add)}",
"source_path": original_job['source_path'],
"name": original_job['name'],
# The new job gets fresh factory defaults, not copied ones.
"settings": self.config_loader.get_factory_defaults().copy(),
# The new job starts as a blank slate, awaiting configuration.
"status": "Awaiting Config",
"job_type": None
}
jobs_to_add.append(new_job)
self.job_queue.extend(jobs_to_add)
self._update_job_list_ui()
self.log("INFO", f"Duplicated {len(jobs_to_add)} job(s) as new, unconfigured tasks.")
def _clear_queue(self):
"""Removes all jobs from the queue after confirmation."""
if not self.job_queue:
return
reply = QMessageBox.question(self, "Confirm Clear Queue",
"Are you sure you want to remove ALL jobs from the queue?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No)
if reply == QMessageBox.StandardButton.Yes:
self.job_queue.clear()
self.log("INFO", "All jobs have been cleared from the queue.")
self._update_job_list_ui()
def _clear_history(self):
"""Removes all jobs from the history list after confirmation."""
if not self.history_queue:
return
reply = QMessageBox.question(self, "Confirm Clear History",
"Are you sure you want to remove ALL jobs from the history?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No)
if reply == QMessageBox.StandardButton.Yes:
self.history_queue.clear()
self.log("INFO", "History has been cleared.")
self._update_history_list_ui()
def _move_job(self, direction: str):
"""Moves the selected job up or down in the queue."""
# This function is now superseded by drag-and-drop, but we keep it for now.
if not self.selected_job_id or len(self.job_queue) < 2:
return
index = self._get_selected_job_index()
if index is None:
return
if direction == "up" and index > 0:
new_index = index - 1
elif direction == "down" and index < len(self.job_queue) - 1:
new_index = index + 1
else:
return
self.job_queue.insert(new_index, self.job_queue.pop(index))
self._update_job_list_ui()
# Keep the moved item selected in the new list widget
self.queue_list_widget.setCurrentRow(new_index)
def _update_job_list_ui(self):
"""
Refreshes both the queue and history list widgets based on the current state
of self.job_queue and self.history_queue.
"""
# Block signals to prevent selection changes from firing events during redraw
self.queue_list_widget.blockSignals(True)
self.queue_list_widget.clear()
# Populate the queue list
for i, job in enumerate(self.job_queue, 1): # Use enumerate to get numbers starting from 1
status_icon = "⚪"
if job.get('status') == "Ready":
status_icon = "🟢"
elif job.get('status') == "Processing":
status_icon = "🟡"
job_type = job.get('job_type')
job_type_tag = f"[{job_type}]" if job_type else ""
# Prepend the number to the display text
display_text = f"{i}. {job_type_tag} {status_icon} {job['name']}"
item = QListWidgetItem(display_text)
item.setData(Qt.ItemDataRole.UserRole, job['id']) # Store ID for reference
self.queue_list_widget.addItem(item)
self.queue_list_widget.blockSignals(False)
def _update_history_list_ui(self):
"""Refreshes the history list widget based on the self.history_queue."""
self.history_list_widget.clear()
# Populate the history list from the end (most recent first)
for i, job in enumerate(reversed(self.history_queue), 1): # Use enumerate here as well
status = job.get('status', 'Unknown')
if status == "Completed":
status_icon = "✅"
elif status == "Failed":
status_icon = "❌"
elif status == "Stopped":
status_icon = "⏹️"
else:
status_icon = "❔"
job_type = job.get('job_type')
job_type_tag = f"[{job_type}]" if job_type else ""
# Prepend the number
display_text = f"{i}. {job_type_tag} {status_icon} {job['name']}"
item = QListWidgetItem(display_text)
item.setData(Qt.ItemDataRole.UserRole, job['id'])
# Color the item based on status
if status == "Failed" or status == "Stopped":
item.setForeground(Qt.GlobalColor.red)
elif status == "Completed":
item.setForeground(Qt.GlobalColor.green)
self.history_list_widget.addItem(item)
def _on_job_selection_changed(self):
"""
Handles the logic when a different job is selected in the queue list.
This now ONLY updates the internal reference to the selected job ID
and no longer automatically loads its settings into the panel.
"""
selected_items = self.queue_list_widget.selectedItems()
if not selected_items:
self.selected_job_id = None
else:
# We still need to know which job is selected for context menu actions.
self.selected_job_id = selected_items[0].data(Qt.ItemDataRole.UserRole)
print(f"[Jobs] Selection changed to job ID: {self.selected_job_id}. Panel state is not affected.")
def _populate_settings_panel(self):
"""
Updates all setting widgets to reflect the settings of the currently selected job
OR the application's default settings if no job is selected.
This function is now smart enough to handle special compound widgets.
"""
# Determine the source of settings
job_index = self._get_selected_job_index()
if job_index is not None:
settings_source = self.job_queue[job_index]['settings']
else:
# If no job is selected, show factory defaults
settings_source = self.config_loader.get_factory_defaults()
# Update the main settings dictionary to reflect what's being shown
self.current_settings = copy.deepcopy(settings_source)
# Block signals on all widgets to prevent infinite loops during programmatic changes
for widget in self.setting_widgets.values():
if widget:
widget.blockSignals(True)
if isinstance(widget, QWidget) and widget.findChild(QSlider):
widget.findChild(QSlider).blockSignals(True)
# Update all widgets with the new values from the determined source
for key, value in self.current_settings.items():
widget = self.setting_widgets.get(key)
if widget:
# SPECIAL HANDLING for the translator chain
if key == 'translator_chain':
# The value is a string like 'sugoi:ENG'. Rebuild the UI from it.
if hasattr(self, '_rebuild_chain_from_string'):
self._rebuild_chain_from_string(value or "")
# Also update the 'enable' checkbox state
enable_checkbox = self.setting_widgets.get('enable_translator_chain')
if enable_checkbox:
# If the chain string is not empty, the checkbox should be checked.
is_chain_enabled = bool(value)
enable_checkbox.setChecked(is_chain_enabled)
# Trigger the UI state update to enable/disable the correct panels
self._update_chain_ui_state()
else:
# Standard handling for all other widgets
self._set_widget_value(key, value, widget)
# Unblock signals to restore normal user interaction
for widget in self.setting_widgets.values():
if widget:
widget.blockSignals(False)
if isinstance(widget, QWidget) and widget.findChild(QSlider):
widget.findChild(QSlider).blockSignals(False)
def _get_selected_job_index(self) -> int | None:
"""Finds the index in job_queue for the currently selected job_id."""
if not self.selected_job_id:
return None
for i, job in enumerate(self.job_queue):
if job['id'] == self.selected_job_id:
return i
return None
def _load_test_image(self):
"""Opens a file dialog to load a test image and displays it."""
file_path, _ = QFileDialog.getOpenFileName(self, "Select a Test Image", "", "Image Files (*.png *.jpg *.jpeg *.webp *.bmp)")
if not file_path:
return
self.test_image_path = file_path
print(f"[Visual Test] Loaded test image: {os.path.basename(file_path)}")
try:
pixmap = QPixmap(file_path)
if pixmap.isNull():
raise ValueError("Pixmap is null. The image file may be corrupt or in an unsupported format.")
# Clear previous images
self.original_scene.clear()
self.translated_scene.clear()
# Display the new image in the 'Original' view
self.original_pixmap_item = self.original_scene.addPixmap(pixmap)
# Also create a placeholder in the 'Translated' view to maintain sync
self.translated_pixmap_item = self.translated_scene.addPixmap(QPixmap()) # Empty pixmap
# Fit the image to the view and enable the run button
self.run_test_button.setEnabled(True)
QTimer.singleShot(50, self._fit_image_to_view)
except Exception as e:
print(f"[ERROR] Failed to load image file: {e}")
QMessageBox.critical(self, "Error", f"Could not load the image:\n{e}")
def _fit_image_to_view(self):
"""Resets the view to fit the entire image within the visible area."""
if not self.original_pixmap_item or self.original_pixmap_item.pixmap().isNull():
return
# Use the bounding rectangle of the pixmap item to fit it perfectly
self.original_view.fitInView(self.original_pixmap_item, Qt.AspectRatioMode.KeepAspectRatio)
self.translated_view.fitInView(self.original_pixmap_item, Qt.AspectRatioMode.KeepAspectRatio) # Use original's rect for sync
self._update_zoom_label()
def _wheel_event_zoom(self, event):
"""Handles zooming with Ctrl+MouseWheel, respecting the zoom limit checkbox."""
if not self.original_pixmap_item or event.modifiers() != Qt.KeyboardModifier.ControlModifier:
QGraphicsView.wheelEvent(self.original_view, event)
QGraphicsView.wheelEvent(self.translated_view, event)
return
# Define zoom factors and limits
zoom_in_factor = 1.15
zoom_out_factor = 1 / zoom_in_factor
# Get the current zoom level before making changes
current_zoom = self.original_view.transform().m11()
# Determine the zoom direction
if event.angleDelta().y() > 0:
zoom_factor = zoom_in_factor
else:
zoom_factor = zoom_out_factor
# Check if zoom limiting is enabled
if self.limit_zoom_check.isChecked():
# Define standard limits (e.g., 5% to 800%)
min_zoom, max_zoom = 0.05, 8.0
# Only apply the zoom if the new level will be within the allowed range
if (current_zoom * zoom_factor > min_zoom
and current_zoom * zoom_factor < max_zoom):
self.original_view.scale(zoom_factor, zoom_factor)
self.translated_view.scale(zoom_factor, zoom_factor)
else:
# If limiting is off, apply more generous limits to prevent freezing
min_zoom, max_zoom = 0.01, 100.0
if (current_zoom * zoom_factor > min_zoom
and current_zoom * zoom_factor < max_zoom):
self.original_view.scale(zoom_factor, zoom_factor)
self.translated_view.scale(zoom_factor, zoom_factor)
self._update_zoom_label()
def _update_zoom_label(self):
"""Updates the zoom level display label."""
# The zoom level is the square root of the determinant of the view's matrix
zoom = self.original_view.transform().m11()
self.zoom_label.setText(f"Zoom: {zoom * 100:.0f}%")
def _run_visual_test_thread(self):
"""Starts the visual test pipeline in a separate thread to avoid freezing the UI."""
if not self.test_image_path:
QMessageBox.warning(self, "No Image", "Please load a test image first.")
return
# Disable the button to prevent multiple clicks
self.run_test_button.setEnabled(False)
self.run_test_button.setText("Testing...")
# Run the _run_visual_test method in a new thread
thread = threading.Thread(target=self._run_visual_test, daemon=True)
thread.start()
def _run_visual_test(self):
"""Prepares and runs the pipeline on the single loaded test image."""
self.log("PIPELINE", "Starting visual test pipeline...")
# Create a temporary 'job' dictionary to hold the settings for the test.
# This job is a 'Translate' type job for the purpose of config building.
test_job = {
"id": "visual_test_job",
"job_type": "T", # Treat it as a standard translate job
"settings": copy.deepcopy(self.current_settings)
}
if self.fast_preview_check.isChecked():
self.log("INFO", "Fast Preview enabled. Overriding settings for speed.")
test_job['settings'].update({'detection_size': 1024, 'inpainting_size': 1024})
if test_job['settings'].get('processing_device') == 'NVIDIA GPU':
test_job['settings']['inpainting_precision'] = 'bf16'
# Build the final configuration using our new centralized function
final_config = self._build_final_config_for_job(test_job)
# Define temporary and final paths for the output
source_dir = os.path.dirname(self.test_image_path)
source_name = os.path.splitext(os.path.basename(self.test_image_path))[0]
# Use a more descriptive name for the final output
final_output_dir = os.path.join(source_dir, f"{source_name}_translated_test")
# Clean up old results before starting
if os.path.exists(final_output_dir):
shutil.rmtree(final_output_dir)
# The pipeline now directly creates the final folder, so we don't need a temp output dir
is_verbose = test_job['settings'].get("enable_verbose_output", False)
# Call the updated pipeline function with the ready-made config
success = self.pipeline.run_single_image_test(
self.test_image_path,
final_output_dir,
final_config,
self.log,
is_verbose
)
if success:
self.log("SUCCESS", "Visual test backend process completed.")
result_files = os.listdir(final_output_dir)
if result_files:
# Find the resulting image (it should have the same name as the original)
original_filename = os.path.basename(self.test_image_path)
result_path = os.path.join(final_output_dir, original_filename)
if os.path.exists(result_path):
# Use QTimer to ensure UI updates happen on the main thread
QTimer.singleShot(0, lambda: self._display_test_result(result_path))
else:
self.log("ERROR", "Could not find the translated image in the output folder.")
else:
self.log("ERROR", "Visual test failed or was stopped.")
# Clean up the potentially empty output folder on failure
if os.path.exists(final_output_dir) and not os.listdir(final_output_dir):
shutil.rmtree(final_output_dir)
# Use QTimer to ensure the button is re-enabled on the main thread
QTimer.singleShot(0, self._on_visual_test_finished)
def _display_test_result(self, image_path: str):
"""Loads the result image and displays it in the 'Output' view."""
print(f"[Visual Test] Displaying result from: {image_path}")
try:
pixmap = QPixmap(image_path)
if pixmap.isNull():
raise ValueError("Result pixmap is null.")
# Clear the old placeholder and display the new result
self.translated_scene.clear()
self.translated_pixmap_item = self.translated_scene.addPixmap(pixmap)
# Ensure the view is still synchronized
self._fit_image_to_view()
except Exception as e:
print(f"[ERROR] Failed to load result image: {e}")
QMessageBox.critical(self, "Error", f"Could not load the result image:\n{e}")
def _on_visual_test_finished(self):
"""Resets the 'Run Test' button to its normal state."""
self.run_test_button.setEnabled(True)
self.run_test_button.setText("Run Test")
def _create_log_tab(self) -> QWidget:
"""Creates the content for the 'Live Log' tab."""
container = QWidget()
layout = QVBoxLayout(container)
header_frame = QFrame()
header_layout = QHBoxLayout(header_frame)
header_layout.setContentsMargins(0, 0, 0, 0)
header_layout.addStretch() # Push button to the right
clear_button = QPushButton("Clear Log")
clear_button.clicked.connect(self._clear_log)
header_layout.addWidget(clear_button)
# The main text widget for logging, set to read-only
self.log_textbox = QTextEdit()
self.log_textbox.setReadOnly(True)
self.log_textbox.setFont(QFont("Consolas", 10)) # Use a monospaced font
layout.addWidget(header_frame)
layout.addWidget(self.log_textbox, stretch=1)
return container
def log(self, level: str, message: str):
"""
Thread-safe method to log messages, with intelligent parsing for RAW backend output.
It emits a signal that the main UI thread will catch.
"""
# This logic mimics the original application's behavior for cleaner logs.
if level.upper() == "RAW":
# For RAW messages from the backend, we don't add our own prefix.
# We pass the message through directly.
raw_message = message.strip()
msg_lower = raw_message.lower()
# We can still re-classify the message type based on content for coloring.
log_level_for_color = "INFO" # Default for raw messages
if msg_lower.startswith(('error:', 'validationerror:', 'exception:', 'traceback')):
log_level_for_color = "ERROR"
elif "out of memory" in msg_lower or "allocation failed" in msg_lower:
log_level_for_color = "ERROR"
color = LOG_COLORS.get(log_level_for_color, "white")
# We emit the RAW message without any extra prefixes.
self.log_signal.emit(color, raw_message)
else:
# For our own UI-generated logs (PIPELINE, SUCCESS, etc.), we add a prefix.
color = LOG_COLORS.get(level.upper(), "white")
self.log_signal.emit(color, f"[{level.upper()}] {message.strip()}")
def _insert_log_text(self, color: str, message: str):
"""
This is the slot that receives the log signal. It safely updates the
QTextEdit widget from the main UI thread.
"""
# Use simple HTML to color the text
self.log_textbox.append(f'<span style="color:{color};">{message}</span>')
def _clear_log(self):
"""Clears all text from the log box."""
self.log_textbox.clear()
def _start_pipeline_thread(self):
"""Starts the main job processing pipeline in a separate thread."""
if self.is_running_pipeline:
return
if not self.job_queue:
QMessageBox.information(self, "Information", "Please add one or more jobs to the queue first.")
return
self._stopped_by_user = False
self._toggle_ui_state(True)
thread = threading.Thread(target=self._run_pipeline, daemon=True)
thread.start()
def _update_colorize_restore_ui_state(self):
"""Enables or disables the upscale factor widget based on the checkbox."""
restore_checkbox = self.setting_widgets.get('restore_size_after_colorize')
factor_widget = self.setting_widgets.get('colorize_upscale_factor')
if not all([restore_checkbox, factor_widget]):
return
is_enabled = restore_checkbox.isChecked()
factor_widget.setEnabled(is_enabled)
def _build_final_config_for_job(self, job: dict) -> dict:
"""
Builds the correct, nested config dictionary for a specific job
by ONLY using the settings stored within that job object.
"""
job_type = job.get('job_type')
settings = job.get('settings', {})
final_config = {}
all_props = self.config_loader.full_config_data
for key, prop_info in all_props.items():
if key not in settings: continue
value = settings.get(key)
if value == "" or value is None: continue
# Skip font_family, it's handled manually below
if key == 'font_family':
continue
group = prop_info.get("group", "")
if "General & Translator" in group:
target_dict = final_config.setdefault("translator", {})
elif "Detector & OCR" in group:
if key in ["ocr", "use_mocr_merge", "min_text_length", "ignore_bubble", "prob"]:
target_dict = final_config.setdefault("ocr", {})
else:
target_dict = final_config.setdefault("detector", {})
elif "Image & Inpainter" in group:
if key in ["upscaler", "revert_upscaling", "upscale_ratio"]:
target_dict = final_config.setdefault("upscale", {})
elif key in ["colorizer", "colorization_size", "denoise_sigma"]:
target_dict = final_config.setdefault("colorizer", {})
else:
target_dict = final_config.setdefault("inpainter", {})
elif "Render & Output" in group:
target_dict = final_config.setdefault("render", {})
else:
final_config[key] = value
continue
target_dict[key] = value
# --- NEW SIMPLIFIED FONT LOGIC ---
selected_font_name = settings.get('font_family')
if selected_font_name and selected_font_name in self.font_map:
# Always get the path from our map and add it for the CLI argument
final_config['font_path'] = self.font_map[selected_font_name]
# Also add it to the render config for GIMP, just in case
final_config.setdefault('render', {})['gimp_font'] = selected_font_name
# --- END OF FONT LOGIC ---
if settings.get('translator_chain'):
final_config.get("translator", {}).pop('translator', None)
final_config['processing_device'] = settings.get('processing_device', 'CPU')
if job_type in ['R', 'U', 'C']:
task_key_map = {'R': 'raw_output', 'U': 'upscale', 'C': 'colorize'}
task_info = self.config_loader.tasks_config.get(task_key_map.get(job_type), {})
backend_overrides = task_info.get("backend_config", {})
for category, overrides in backend_overrides.items():
final_config.setdefault(category, {}).update(overrides)
return final_config
def _run_pipeline(self):
"""
Processes all 'Ready' jobs in the queue sequentially.
This version includes "resume" functionality and smart folder naming
to avoid conflicts, based on user settings.
"""
try:
while self.is_running_pipeline:
job_to_process = next((job for job in self.job_queue if job.get('status') == 'Ready'), None)
if not job_to_process:
self.log("PIPELINE", "No more 'Ready' jobs in the queue. Finishing run.")
break
job = job_to_process
self.currently_processing_job_id = job['id']
job['status'] = 'Processing'
self._update_job_list_ui()
self._toggle_ui_state(True, job['id'])
settings = job.get('settings', {})
selected_mode = settings.get('processing_mode', 'Automatic')
output_format = settings.get('output_format', 'png')
mode_to_use = 'High VRAM'
if selected_mode == 'Low VRAM' or (selected_mode == 'Automatic' and self.detected_vram_gb > 0 and self.detected_vram_gb <= 6):
mode_to_use = 'Low VRAM'
# --- NEW FOLDER NAMING LOGIC ---
source_path = job['source_path']
job_type_tag = f"TASK-{job.get('job_type')}" if job.get('job_type') != 'T' else settings.get('target_lang', 'ENG')
base_output_folder_name = f"{os.path.basename(source_path)}-{job_type_tag}"
output_dir = os.path.dirname(source_path)
final_output_folder_name = base_output_folder_name
# Check the user's preference for avoiding conflicts
if settings.get('avoid_conflicts', True):
counter = 1
# Append (1), (2), etc., until a unique name is found
while os.path.exists(os.path.join(output_dir, final_output_folder_name)):
final_output_folder_name = f"{base_output_folder_name} ({counter})"
counter += 1
final_output_path = os.path.join(output_dir, final_output_folder_name)
# --- END OF FOLDER NAMING LOGIC ---
os.makedirs(final_output_path, exist_ok=True)
all_source_files = sorted([f for f in os.listdir(source_path) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.webp', '.bmp'))])
try:
processed_files = {os.path.splitext(f)[0] for f in os.listdir(final_output_path) if f.lower().endswith(f".{output_format}")}
files_to_process = [f for f in all_source_files if os.path.splitext(f)[0] not in processed_files]
except FileNotFoundError:
files_to_process = all_source_files
if not files_to_process:
self.log("INFO", f"All files for job '{job['name']}' seem to be processed already. Skipping to avoid errors.")
job['status'] = "Completed"
self.job_queue.remove(job)
self.history_queue.append(job)
self._update_job_list_ui()
self._update_history_list_ui()
# A small delay to let the UI update before the pipeline finishes
QApplication.processEvents()
continue # Move to the next job in the queue
# (The rest of the function remains exactly the same as before)
self.log("INFO", f"Found {len(files_to_process)} unprocessed image(s) for job '{job['name']}'.")
success = True
if mode_to_use == 'Low VRAM':
try:
batch_size = int(settings.get('batch_size', 5))
if batch_size <= 0: batch_size = 1
except ValueError:
batch_size = 5
self.log("PIPELINE", f"Resuming job '{job['name']}' in Low VRAM Mode (Batch Size: {batch_size}).")
num_batches = (len(files_to_process) + batch_size - 1) // batch_size
for i in range(num_batches):
if not self.is_running_pipeline:
success = False
break
batch_files = files_to_process[i * batch_size: (i + 1) * batch_size]
self.log("INFO", f"Processing batch {i + 1}/{num_batches} ({len(batch_files)} images)...")
temp_batch_dir = os.path.join(self.temp_dir, f"batch_{job['id']}")
if os.path.exists(temp_batch_dir): shutil.rmtree(temp_batch_dir)
os.makedirs(temp_batch_dir)
for f in batch_files:
shutil.copy(os.path.join(source_path, f), temp_batch_dir)
job_for_batch = copy.deepcopy(job)
job_for_batch['source_path'] = temp_batch_dir
final_config = self._build_final_config_for_job(job_for_batch)
is_verbose = settings.get("enable_verbose_output", False)
batch_success = self.pipeline.run(job_for_batch, final_output_path, final_config, self.log, is_verbose, output_format)
shutil.rmtree(temp_batch_dir)
if not batch_success:
success = False
break
else: # High VRAM Mode
self.log("PIPELINE", f"Resuming job '{job['name']}' in High VRAM Mode.")
temp_source_dir = os.path.join(self.temp_dir, "high_vram_processing")
if os.path.exists(temp_source_dir): shutil.rmtree(temp_source_dir)
os.makedirs(temp_source_dir)
for f in files_to_process:
shutil.copy(os.path.join(source_path, f), temp_source_dir)
job_for_run = copy.deepcopy(job)
job_for_run['source_path'] = temp_source_dir
final_config = self._build_final_config_for_job(job_for_run)
is_verbose = settings.get("enable_verbose_output", False)
success = self.pipeline.run(job_for_run, final_output_path, final_config, self.log, is_verbose, output_format)
shutil.rmtree(temp_source_dir)
job['status'] = "Completed" if success else ("Stopped" if self._stopped_by_user else "Failed")
if not success and not self._stopped_by_user:
QTimer.singleShot(0, lambda j=job: QMessageBox.critical(self, "Job Failed", f"The job '{j['name']}' failed due to a critical error.\n\nCheck the Live Log for details."))
self.job_queue.remove(job)
self.history_queue.append(job)
self.currently_processing_job_id = None
self._update_job_list_ui()
self._update_history_list_ui()
if self._stopped_by_user:
self.log("PIPELINE", "Pipeline stopped by user command.")
break
finally:
self.pipeline_finished_signal.emit()
def _stop_pipeline(self):
"""Stops the running pipeline process immediately and updates the UI."""
if not self.is_running_pipeline:
return
self.log("PIPELINE", "Stop command received. Terminating backend process...")
# --- GÜNCELLEME: Set the flag BEFORE stopping the process ---
self._stopped_by_user = True
# The pipeline object will handle the actual process killing.
self.pipeline.stop(self.log)
def _toggle_ui_state(self, is_running: bool, running_job_id: str = None):
"""
Locks ONLY the essential UI elements during processing.
- Toggles Start/Stop buttons.
- Disables the specific list item being processed.
- The rest of the UI remains interactive.
"""
self.is_running_pipeline = is_running
# 1. Toggle Start/Stop buttons
self.start_button.setEnabled(not is_running)
self.stop_button.setEnabled(is_running)
# 2. Find and visually lock/unlock the specific job item in the queue
for i in range(self.queue_list_widget.count()):
item = self.queue_list_widget.item(i)
# If a job is running and its ID matches this item's ID
if is_running and item.data(Qt.ItemDataRole.UserRole) == running_job_id:
# Disable interaction (can't be selected, moved, or right-clicked)
item.setFlags(item.flags() & ~Qt.ItemFlag.ItemIsEnabled)
else:
# Ensure all other items are fully enabled
item.setFlags(item.flags() | Qt.ItemFlag.ItemIsEnabled)
def _set_settings_panel_enabled(self, is_enabled: bool):
"""Helper function to enable or disable all widgets in the settings panel."""
interactive_widget_types = (QPushButton, QComboBox, QCheckBox, QSlider, QLineEdit)
if hasattr(self, 'settings_tab_view'):
# CORRECTED LOGIC: Loop through each type and call findChildren separately.
for widget_type in interactive_widget_types:
# Find all widgets of a specific type within the settings area
for widget in self.settings_tab_view.findChildren(widget_type):
widget.setEnabled(is_enabled)
def _update_progress(self, percent: float, text: str):
"""Thread-safe method to update the progress bar and label."""
self.progress_bar.setValue(int(percent * 100))
self.progress_label.setText(text)
def _reset_task_settings(self, task_key: str):
"""Resets the settings of a specific task to its defaults from tasks.json."""
if task_key not in self.task_settings:
return
task_info = self.config_loader.tasks_config.get(task_key, {})
defaults = task_info.get("defaults", {})
# Update the settings dictionary
self.task_settings[task_key] = defaults.copy()
# Update the widgets on the UI
for setting_key, default_value in defaults.items():
widget = self.task_widgets.get(task_key, {}).get(setting_key)
if widget:
# We must block signals here as well to prevent loops
widget.blockSignals(True)
self._set_widget_value(setting_key, default_value, widget)
widget.blockSignals(False)
self.log("INFO", f"Settings for task '{task_info.get('label')}' have been reset.")
def _assign_task_to_selection(self, task_key: str):
"""Applies a special task's configuration and type to all selected jobs."""
selected_items = self.queue_list_widget.selectedItems()
if not selected_items:
QMessageBox.information(self, "No Job Selected", "Please select one or more jobs from the queue to assign this task.")
return
task_info = self.config_loader.tasks_config.get(task_key, {})
task_settings_from_ui = self.task_settings.get(task_key, {})
job_type_map = {'raw_output': 'R', 'upscale': 'U', 'colorize': 'C'}
job_type = job_type_map.get(task_key, '?')
for item in selected_items:
job_id = item.data(Qt.ItemDataRole.UserRole)
job_data = next((job for job in self.job_queue if job['id'] == job_id), None)
if job_data:
current_job_settings = task_settings_from_ui.copy()
if task_key == 'upscale':
# Get value from our special grid widget
upscale_value_str = current_job_settings.pop('task_upscale_grid', '2x')
# Save it under the key the backend expects ('upscale_ratio')
current_job_settings['upscale_ratio'] = int(upscale_value_str.replace('x', ''))
if task_key == 'colorize' and current_job_settings.get('restore_size_after_colorize'):
try:
source_dir = job_data['source_path']
first_image_name = next((f for f in os.listdir(source_dir) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.webp'))), None)
if first_image_name:
image_path = os.path.join(source_dir, first_image_name)
with Image.open(image_path) as img:
width, height = img.size
original_long_side = max(width, height)
target_colorize_size = int(current_job_settings.get('colorization_size', 576))
if target_colorize_size > 0 and original_long_side > target_colorize_size:
division_ratio = original_long_side / target_colorize_size
calculated_upscale_ratio = max(2, round(division_ratio))
current_job_settings['upscale_ratio'] = calculated_upscale_ratio
current_job_settings['revert_upscaling'] = False
self.log("INFO", f"Job '{job_data['name']}': Auto-calculated upscale ratio: {calculated_upscale_ratio}x")
else:
self.log("WARNING", f"Job '{job_data['name']}': Could not find an image to calculate upscale ratio. Skipping auto-upscale.")
except Exception as e:
self.log("ERROR", f"Failed to auto-calculate upscale ratio for '{job_data['name']}': {e}")
device_widget = self.tasks_processing_device_widget
button_group = device_widget.findChild(QButtonGroup)
selected_device = "CPU"
if button_group and button_group.checkedButton():
selected_device = button_group.checkedButton().text()
current_job_settings['processing_device'] = selected_device
current_job_settings['processing_mode'] = self._get_value_from_widget('processing_mode', self.setting_widgets.get('processing_mode'))
current_job_settings['batch_size'] = self._get_value_from_widget('batch_size', self.setting_widgets.get('batch_size'))
job_data['settings'] = current_job_settings
job_data['job_type'] = job_type
job_data['status'] = 'Ready'
self.log("INFO", f"Assigned task '{task_info.get('label')}' to {len(selected_items)} job(s).")
self._update_job_list_ui()
def _on_pipeline_finished(self):
"""
A dedicated, thread-safe function to call when the pipeline finishes.
This centralizes the UI reset logic.
"""
self.is_running_pipeline = False
self.currently_processing_job_id = None
self._update_progress(1.0, "Finished!")
# A brief delay before unlocking allows the progress bar to show "Finished!"
QTimer.singleShot(100, lambda: self._toggle_ui_state(False))
QTimer.singleShot(2000, lambda: self._update_progress(0, "Ready"))
def closeEvent(self, event):
"""Handles the window close event to save the application state."""
if self.is_running_pipeline:
reply = QMessageBox.question(self, "Confirm Exit",
"A process is still running. Are you sure you want to stop it and exit?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No)
if reply == QMessageBox.StandardButton.Yes:
self._stop_pipeline()
else:
event.ignore()
return
self._save_app_state()
event.accept()
def _load_app_state(self):
"""Loads application state (window geometry, last directory) from a config file."""
self.app_settings_path = os.path.join(self.project_base_dir, "MangaStudio_Data", "studio_config.json")
try:
if os.path.exists(self.app_settings_path):
with open(self.app_settings_path, 'r', encoding='utf-8') as f:
settings = json.load(f)
# Restore window geometry
geometry_hex = settings.get("window_geometry")
if geometry_hex:
self.restoreGeometry(QByteArray.fromHex(geometry_hex.encode('utf-8')))
# Restore last used directory
self.last_selected_directory = settings.get("last_directory")
print("[INFO] Application state loaded.")
except Exception as e:
print(f"[WARNING] Could not load app settings: {e}")
def _save_app_state(self):
"""Saves the current application state to a config file."""
if not hasattr(self, 'app_settings_path'):
self.app_settings_path = os.path.join(self.project_base_dir, "MangaStudio_Data", "studio_config.json")
settings = {
# Convert QByteArray to a JSON-compatible hex string
"window_geometry": self.saveGeometry().toHex().data().decode('utf-8'),
"last_directory": getattr(self, 'last_selected_directory', None)
}
try:
with open(self.app_settings_path, 'w', encoding='utf-8') as f:
json.dump(settings, f, indent=4)
print("[INFO] Application state saved.")
except Exception as e:
print(f"[ERROR] Could not save app settings: {e}")
def _create_theme_manager_widget(self) -> QWidget:
"""Creates the UI component for theme selection."""
theme_frame = QFrame()
theme_layout = QVBoxLayout(theme_frame)
theme_layout.setContentsMargins(0, 10, 0, 0)
label = QLabel("Appearance & Theme")
font = label.font()
font.setBold(True)
gitextract_jo7y9pqx/
├── .dockerignore
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ └── feature_request.yml
│ ├── run.bat
│ └── workflows/
│ ├── ci.yml
│ ├── release.yml
│ └── stale.yml
├── .gitignore
├── CHANGELOG.md
├── CHANGELOG_CN.md
├── Dockerfile
├── LICENSE
├── Makefile
├── MangaStudioMain.py
├── MangaStudioMainRun.bat
├── MangaStudio_Data/
│ ├── README.md
│ ├── app/
│ │ ├── __init__.py
│ │ ├── core/
│ │ │ ├── __init__.py
│ │ │ ├── config_loader.py
│ │ │ ├── constants.py
│ │ │ └── pipeline.py
│ │ └── ui/
│ │ ├── __init__.py
│ │ └── main_window.py
│ ├── dicts/
│ │ ├── example_post_dict.txt
│ │ └── example_pre_dict.txt
│ ├── gpt_configs/
│ │ └── my_cool_prompt.yaml
│ ├── profiles/
│ │ └── Admin Preset (Creator's custom config - hardware dependent).json
│ ├── tasks.json
│ ├── temp/
│ │ └── .gitkeep
│ ├── themes/
│ │ ├── abyssal_depths.json
│ │ ├── classic_paper.json
│ │ ├── default_dark.json
│ │ ├── dracula.json
│ │ ├── emerald_sea.json
│ │ ├── golden_sands.json
│ │ ├── graphite.json
│ │ ├── lavender_dream.json
│ │ ├── matrix_code.json
│ │ ├── minty_fresh.json
│ │ ├── nordic_noir.json
│ │ ├── ocean_breeze.json
│ │ ├── ruby_glow.json
│ │ ├── sakura_sunset.json
│ │ └── solarized_light.json
│ └── ui_map.json
├── README.md
├── README_CN.md
├── demo/
│ └── doc/
│ ├── docker-compose-local-dev.yml
│ ├── docker-compose-web-with-cpu.yml
│ └── docker-compose-web-with-gpu.yml
├── devscripts/
│ ├── make_readme.py
│ └── utils.py
├── dict/
│ ├── galtransl_dict.txt
│ ├── mit_glossary.txt
│ ├── post_dict.txt
│ ├── pre_dict.txt
│ └── sakura_dict.txt
├── docker-compose.debug.yml
├── docker-compose.yml
├── docker_prepare.py
├── examples/
│ ├── Example.env
│ ├── config-example.json
│ ├── config-example.toml
│ ├── gpt_config-example.yaml
│ ├── response.cpp
│ ├── response.rs
│ └── translator_chain_example.json
├── fonts/
│ ├── NotoSansMonoCJK-VF.ttf.ttc
│ ├── msgothic.ttc
│ └── msyh.ttc
├── front/
│ ├── .dockerignore
│ ├── .gitignore
│ ├── Dockerfile
│ ├── README.md
│ ├── README_CN.md
│ ├── app/
│ │ ├── App.tsx
│ │ ├── app.css
│ │ ├── components/
│ │ │ ├── Header.tsx
│ │ │ ├── ImageHandlingArea.tsx
│ │ │ ├── ImageQueue.tsx
│ │ │ ├── LabeledInput.tsx
│ │ │ ├── LabeledSelect.tsx
│ │ │ ├── OptionsPanel.tsx
│ │ │ ├── PreviewImage.tsx
│ │ │ └── ResultGallery.tsx
│ │ ├── config.ts
│ │ ├── root.tsx
│ │ ├── routes/
│ │ │ └── home.tsx
│ │ ├── routes.ts
│ │ ├── types.ts
│ │ └── utils/
│ │ ├── fetchStatusText.ts
│ │ ├── getTranslatorName.ts
│ │ └── localStorage.ts
│ ├── package.json
│ ├── react-router.config.ts
│ ├── tsconfig.json
│ └── vite.config.ts
├── manga_translator/
│ ├── __init__.py
│ ├── __main__.py
│ ├── args.py
│ ├── colorization/
│ │ ├── __init__.py
│ │ ├── common.py
│ │ ├── manga_colorization_v2.py
│ │ └── manga_colorization_v2_utils/
│ │ ├── denoising/
│ │ │ ├── denoiser.py
│ │ │ ├── functions.py
│ │ │ ├── models.py
│ │ │ └── utils.py
│ │ ├── networks/
│ │ │ ├── extractor.py
│ │ │ └── models.py
│ │ └── utils/
│ │ └── utils.py
│ ├── config.py
│ ├── detection/
│ │ ├── __init__.py
│ │ ├── common.py
│ │ ├── common_rust.py
│ │ ├── craft.py
│ │ ├── craft_utils/
│ │ │ ├── refiner.py
│ │ │ └── vgg16_bn.py
│ │ ├── ctd.py
│ │ ├── ctd_utils/
│ │ │ ├── __init__.py
│ │ │ ├── basemodel.py
│ │ │ ├── textmask.py
│ │ │ ├── utils/
│ │ │ │ ├── db_utils.py
│ │ │ │ ├── imgproc_utils.py
│ │ │ │ ├── io_utils.py
│ │ │ │ ├── weight_init.py
│ │ │ │ └── yolov5_utils.py
│ │ │ └── yolov5/
│ │ │ ├── common.py
│ │ │ └── yolo.py
│ │ ├── dbnet_convnext.py
│ │ ├── default.py
│ │ ├── default_utils/
│ │ │ ├── CRAFT_resnet34.py
│ │ │ ├── DBHead.py
│ │ │ ├── DBNet_resnet101.py
│ │ │ ├── DBNet_resnet34.py
│ │ │ ├── craft_utils.py
│ │ │ ├── dbnet_utils.py
│ │ │ └── imgproc.py
│ │ ├── none.py
│ │ ├── paddle_rust.py
│ │ └── panel_finder.py
│ ├── inpainting/
│ │ ├── __init__.py
│ │ ├── booru_tagger.py
│ │ ├── common.py
│ │ ├── guided_ldm_inpaint4_v15.yaml
│ │ ├── guided_ldm_inpaint9_v15.yaml
│ │ ├── guided_ldm_inpainting.py
│ │ ├── inpainting_aot.py
│ │ ├── inpainting_attn.py
│ │ ├── inpainting_lama.py
│ │ ├── inpainting_lama_mpe.py
│ │ ├── inpainting_sd.py
│ │ ├── ldm/
│ │ │ ├── __init__.py
│ │ │ ├── data/
│ │ │ │ ├── __init__.py
│ │ │ │ └── util.py
│ │ │ ├── models/
│ │ │ │ ├── autoencoder.py
│ │ │ │ └── diffusion/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── ddim.py
│ │ │ │ ├── ddpm.py
│ │ │ │ ├── dpm_solver/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── dpm_solver.py
│ │ │ │ │ └── sampler.py
│ │ │ │ ├── plms.py
│ │ │ │ └── sampling_util.py
│ │ │ ├── modules/
│ │ │ │ ├── attention.py
│ │ │ │ ├── diffusionmodules/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── model.py
│ │ │ │ │ ├── openaimodel.py
│ │ │ │ │ ├── upscaling.py
│ │ │ │ │ └── util.py
│ │ │ │ ├── distributions/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── distributions.py
│ │ │ │ ├── ema.py
│ │ │ │ ├── encoders/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── modules.py
│ │ │ │ ├── image_degradation/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── bsrgan.py
│ │ │ │ │ ├── bsrgan_light.py
│ │ │ │ │ └── utils_image.py
│ │ │ │ └── midas/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── api.py
│ │ │ │ ├── midas/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── base_model.py
│ │ │ │ │ ├── blocks.py
│ │ │ │ │ ├── dpt_depth.py
│ │ │ │ │ ├── midas_net.py
│ │ │ │ │ ├── midas_net_custom.py
│ │ │ │ │ ├── transforms.py
│ │ │ │ │ └── vit.py
│ │ │ │ └── utils.py
│ │ │ └── util.py
│ │ ├── none.py
│ │ ├── original.py
│ │ └── sd_hack.py
│ ├── manga_translator.py
│ ├── mask_refinement/
│ │ ├── __init__.py
│ │ └── text_mask_utils.py
│ ├── mode/
│ │ ├── __init__.py
│ │ ├── local.py
│ │ ├── share.py
│ │ └── ws.py
│ ├── ocr/
│ │ ├── __init__.py
│ │ ├── common.py
│ │ ├── model_32px.py
│ │ ├── model_48px.py
│ │ ├── model_48px_ctc.py
│ │ ├── model_manga_ocr.py
│ │ ├── model_ocr_large.py
│ │ └── xpos_relative_position.py
│ ├── rendering/
│ │ ├── __init__.py
│ │ ├── ballon_extractor.py
│ │ ├── gimp_render.py
│ │ ├── text_render.py
│ │ ├── text_render_eng.py
│ │ └── text_render_pillow_eng.py
│ ├── save.py
│ ├── textline_merge/
│ │ └── __init__.py
│ ├── translators/
│ │ ├── __init__.py
│ │ ├── baidu.py
│ │ ├── caiyun.py
│ │ ├── chatgpt.py
│ │ ├── chatgpt_2stage.py
│ │ ├── common.py
│ │ ├── common_gpt.py
│ │ ├── config_gpt.py
│ │ ├── custom_openai.py
│ │ ├── deepl.py
│ │ ├── deepseek.py
│ │ ├── gemini.py
│ │ ├── gemini_2stage.py
│ │ ├── google.py
│ │ ├── google_gtoken.py
│ │ ├── groq.py
│ │ ├── keys.py
│ │ ├── m2m100.py
│ │ ├── mbart50.py
│ │ ├── nllb.py
│ │ ├── none.py
│ │ ├── original.py
│ │ ├── papago.py
│ │ ├── qwen2.py
│ │ ├── sakura.py
│ │ ├── selective.py
│ │ ├── sugoi.py
│ │ ├── tokenizers/
│ │ │ ├── deepseek/
│ │ │ │ ├── tokenizer.json
│ │ │ │ └── tokenizer_config.json
│ │ │ └── token_counters.py
│ │ └── youdao.py
│ ├── upscaling/
│ │ ├── __init__.py
│ │ ├── common.py
│ │ ├── esrgan.py
│ │ ├── esrgan_pytorch.py
│ │ └── waifu2x.py
│ └── utils/
│ ├── __init__.py
│ ├── bubble.py
│ ├── generic.py
│ ├── generic2.py
│ ├── inference.py
│ ├── log.py
│ ├── panel/
│ │ ├── __init__.py
│ │ └── kumikolib.py
│ ├── sort.py
│ ├── textblock.py
│ └── threading.py
├── pip-modules/
│ └── mit-renderer/
│ ├── .gitignore
│ ├── pyproject.toml
│ └── setup.py
├── pyproject.toml
├── pytest.ini
├── requirements-dev.txt
├── requirements.txt
├── run-as-kaggle.ipynb
├── run.bat
├── run.sh
├── run_as_colab.ipynb
├── sakura_dict.txt
├── server/
│ ├── args.py
│ ├── index.html
│ ├── instance.py
│ ├── main.py
│ ├── manual.html
│ ├── myqueue.py
│ ├── request_extraction.py
│ ├── sent_data_internal.py
│ ├── streaming.py
│ └── to_json.py
├── setup.cfg
├── test/
│ ├── README.md
│ ├── api_test.html
│ ├── conftest.py
│ ├── test_render.py
│ ├── test_textline_merge.py
│ ├── test_translation.py
│ └── test_translation_manual.py
└── training/
├── all-fonts.txt
├── inpainting/
│ └── README.md
└── ocr/
├── README.md
├── custom_ctc.cc
├── custom_ctc.py
├── custom_ctc_cuda_driver.cc
├── custom_ctc_gpu.py
├── custom_ctc_kernel.cu
├── setup.py
└── test_ctc.py
Showing preview only (210K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (2540 symbols across 186 files)
FILE: MangaStudio_Data/app/core/config_loader.py
class ConfigLoader (line 8) | class ConfigLoader:
method __init__ (line 9) | def __init__(self, project_base_dir):
method _find_python_executable (line 25) | def _find_python_executable(self):
method _load_backend_schema (line 34) | def _load_backend_schema(self):
method _parse_schema_output (line 58) | def _parse_schema_output(self, stdout):
method _strip_ansi (line 73) | def _strip_ansi(self, text):
method _load_ui_map (line 77) | def _load_ui_map(self):
method _load_tasks_config (line 86) | def _load_tasks_config(self):
method _get_definition_from_ref (line 100) | def _get_definition_from_ref(self, ref_path):
method _parse_factory_defaults (line 110) | def _parse_factory_defaults(self):
method _build_full_config_data (line 126) | def _build_full_config_data(self):
method get_tasks_config (line 166) | def get_tasks_config(self):
method get_factory_defaults (line 170) | def get_factory_defaults(self):
method get_tab_order (line 173) | def get_tab_order(self):
FILE: MangaStudio_Data/app/core/pipeline.py
class Pipeline (line 13) | class Pipeline:
method __init__ (line 16) | def __init__(self, app, python_executable, temp_dir):
method run (line 25) | def run(self, job, output_path, config_dict, log_callback, is_verbose=...
method run_single_image_test (line 72) | def run_single_image_test(self, test_image_path, output_path, config_d...
method _execute_subprocess (line 118) | def _execute_subprocess(self, log_callback, command):
method stop (line 168) | def stop(self, log_callback):
method _cleanup_memory (line 182) | def _cleanup_memory(self, log_callback):
FILE: MangaStudio_Data/app/ui/main_window.py
class DynamicHeightListWidget (line 41) | class DynamicHeightListWidget(QListWidget):
method __init__ (line 49) | def __init__(self, parent=None):
method minimumSizeHint (line 53) | def minimumSizeHint(self) -> QSize:
method sizeHint (line 58) | def sizeHint(self) -> QSize:
method wheelEvent (line 63) | def wheelEvent(self, event: QEvent):
method _get_content_height (line 67) | def _get_content_height(self) -> int:
class NoScrollComboBox (line 83) | class NoScrollComboBox(QComboBox):
method wheelEvent (line 86) | def wheelEvent(self, event: QEvent):
class TranslatorStudioApp (line 91) | class TranslatorStudioApp(QMainWindow):
method __init__ (line 96) | def __init__(self):
method _initialize_app (line 142) | def _initialize_app(self):
method _create_main_layout (line 153) | def _create_main_layout(self):
method _create_left_panel (line 178) | def _create_left_panel(self) -> QWidget:
method _create_right_panel (line 266) | def _create_right_panel(self) -> QWidget:
method _create_settings_tab_container (line 292) | def _create_settings_tab_container(self) -> QWidget:
method _create_visual_compare_tab (line 325) | def _create_visual_compare_tab(self) -> QWidget:
method _build_dynamic_tab_content (line 403) | def _build_dynamic_tab_content(self, tab_name: str, settings_list: lis...
method _build_tasks_tab_content (line 491) | def _build_tasks_tab_content(self) -> QWidget:
method _create_setting_row (line 614) | def _create_setting_row(self, info: dict, context_key: str = None) -> ...
method _create_segmented_button (line 717) | def _create_segmented_button(self, info: dict) -> QWidget:
method _create_combobox (line 738) | def _create_combobox(self, info: dict) -> QComboBox:
method _create_checkbox (line 759) | def _create_checkbox(self, info: dict) -> QCheckBox:
method _create_slider (line 767) | def _create_slider(self, info: dict) -> QWidget:
method _create_entry (line 829) | def _create_entry(self, info: dict) -> QLineEdit:
method _create_language_checkbox_grid (line 842) | def _create_language_checkbox_grid(self, info: dict) -> QWidget:
method _create_font_combobox (line 875) | def _create_font_combobox(self, info: dict) -> QComboBox:
method _create_entry_with_button (line 890) | def _create_entry_with_button(self, info: dict) -> QWidget:
method _create_translator_chain_builder (line 908) | def _create_translator_chain_builder(self, info: dict) -> QWidget:
method _create_chain_step_widget (line 956) | def _create_chain_step_widget(self) -> QWidget:
method _add_chain_step (line 995) | def _add_chain_step(self):
method _remove_chain_step (line 1007) | def _remove_chain_step(self):
method _get_translator_chain_string (line 1018) | def _get_translator_chain_string(self) -> str:
method _rebuild_chain_from_string (line 1043) | def _rebuild_chain_from_string(self, chain_string: str):
method _create_grid_segmented_button (line 1072) | def _create_grid_segmented_button(self, info: dict) -> QWidget:
method _create_preset_manager (line 1102) | def _create_preset_manager(self, info: dict) -> QWidget:
method _get_font_list (line 1140) | def _get_font_list(self) -> list:
method _update_chain_ui_state (line 1156) | def _update_chain_ui_state(self):
method _update_chain_list_height (line 1186) | def _update_chain_list_height(self):
method _update_translator_tooltip (line 1208) | def _update_translator_tooltip(self, translator_name: str):
method _handle_widget_button_click (line 1235) | def _handle_widget_button_click(self, key: str, associated_widget: QWi...
method _refresh_profile_list (line 1273) | def _refresh_profile_list(self):
method _save_profile (line 1287) | def _save_profile(self):
method _load_profile (line 1316) | def _load_profile(self):
method _delete_profile (line 1364) | def _delete_profile(self):
method _create_bottom_panel (line 1387) | def _create_bottom_panel(self) -> QWidget:
method _create_font_scale_widget (line 1425) | def _create_font_scale_widget(self) -> QWidget:
method _on_font_scale_changed (line 1446) | def _on_font_scale_changed(self, text: str):
method _connect_widget_signal (line 1458) | def _connect_widget_signal(self, key: str, widget: QWidget, context_ke...
method _on_setting_changed (line 1511) | def _on_setting_changed(self, key: str, context_key: str = None):
method _on_translator_changed (line 1529) | def _on_translator_changed(self, translator_name: str):
method _filter_language_dropdown (line 1538) | def _filter_language_dropdown(self, translator_name: str, lang_combo: ...
method _get_value_from_widget (line 1577) | def _get_value_from_widget(self, key: str, widget: QWidget) -> any:
method _set_widget_value (line 1634) | def _set_widget_value(self, key: str, value: any, widget: QWidget):
method _add_job (line 1696) | def _add_job(self):
method _add_job_from_path (line 1708) | def _add_job_from_path(self, path):
method dragEnterEvent (line 1742) | def dragEnterEvent(self, event):
method dragMoveEvent (line 1748) | def dragMoveEvent(self, event):
method dropEvent (line 1754) | def dropEvent(self, event):
method _remove_selected_jobs_from_queue (line 1766) | def _remove_selected_jobs_from_queue(self):
method _duplicate_selected_jobs (line 1774) | def _duplicate_selected_jobs(self):
method _clear_queue (line 1808) | def _clear_queue(self):
method _clear_history (line 1823) | def _clear_history(self):
method _move_job (line 1838) | def _move_job(self, direction: str):
method _update_job_list_ui (line 1861) | def _update_job_list_ui(self):
method _update_history_list_ui (line 1889) | def _update_history_list_ui(self):
method _on_job_selection_changed (line 1923) | def _on_job_selection_changed(self):
method _populate_settings_panel (line 1938) | def _populate_settings_panel(self):
method _get_selected_job_index (line 1990) | def _get_selected_job_index(self) -> int | None:
method _load_test_image (line 1999) | def _load_test_image(self):
method _fit_image_to_view (line 2031) | def _fit_image_to_view(self):
method _wheel_event_zoom (line 2040) | def _wheel_event_zoom(self, event):
method _update_zoom_label (line 2079) | def _update_zoom_label(self):
method _run_visual_test_thread (line 2085) | def _run_visual_test_thread(self):
method _run_visual_test (line 2099) | def _run_visual_test(self):
method _display_test_result (line 2163) | def _display_test_result(self, image_path: str):
method _on_visual_test_finished (line 2182) | def _on_visual_test_finished(self):
method _create_log_tab (line 2187) | def _create_log_tab(self) -> QWidget:
method log (line 2210) | def log(self, level: str, message: str):
method _insert_log_text (line 2237) | def _insert_log_text(self, color: str, message: str):
method _clear_log (line 2245) | def _clear_log(self):
method _start_pipeline_thread (line 2249) | def _start_pipeline_thread(self):
method _update_colorize_restore_ui_state (line 2263) | def _update_colorize_restore_ui_state(self):
method _build_final_config_for_job (line 2274) | def _build_final_config_for_job(self, job: dict) -> dict:
method _run_pipeline (line 2341) | def _run_pipeline(self):
method _stop_pipeline (line 2481) | def _stop_pipeline(self):
method _toggle_ui_state (line 2494) | def _toggle_ui_state(self, is_running: bool, running_job_id: str = None):
method _set_settings_panel_enabled (line 2518) | def _set_settings_panel_enabled(self, is_enabled: bool):
method _update_progress (line 2529) | def _update_progress(self, percent: float, text: str):
method _reset_task_settings (line 2534) | def _reset_task_settings(self, task_key: str):
method _assign_task_to_selection (line 2556) | def _assign_task_to_selection(self, task_key: str):
method _on_pipeline_finished (line 2625) | def _on_pipeline_finished(self):
method closeEvent (line 2637) | def closeEvent(self, event):
method _load_app_state (line 2653) | def _load_app_state(self):
method _save_app_state (line 2672) | def _save_app_state(self):
method _create_theme_manager_widget (line 2689) | def _create_theme_manager_widget(self) -> QWidget:
method _load_themes (line 2727) | def _load_themes(self):
method _apply_theme (line 2753) | def _apply_theme(self, theme_name: str):
method _show_queue_context_menu (line 2916) | def _show_queue_context_menu(self, position):
method _apply_settings_to_selection (line 2947) | def _apply_settings_to_selection(self):
method _remove_selected_jobs_from_queue (line 2965) | def _remove_selected_jobs_from_queue(self):
method _save_settings_to_job (line 2984) | def _save_settings_to_job(self):
method _load_settings_from_job (line 3006) | def _load_settings_from_job(self):
method _create_api_manager_widget (line 3022) | def _create_api_manager_widget(self, info: dict) -> QWidget:
method _handle_create_env_file (line 3039) | def _handle_create_env_file(self):
method _requeue_job (line 3102) | def _requeue_job(self):
method _show_history_context_menu (line 3130) | def _show_history_context_menu(self, position):
method _build_font_map (line 3145) | def _build_font_map(self):
method _create_font_combobox (line 3160) | def _create_font_combobox(self, info: dict) -> QComboBox:
FILE: devscripts/make_readme.py
function take_section (line 36) | def take_section(text, start=None, end=None, *, shift=0):
function apply_patch (line 43) | def apply_patch(text, patch):
FILE: devscripts/utils.py
function read_file (line 8) | def read_file(fname):
function write_file (line 13) | def write_file(fname, content, mode='w'):
function get_filename_args (line 18) | def get_filename_args(has_infile=False, default_outfile=None):
function compose_functions (line 31) | def compose_functions(*functions):
function run_process (line 35) | def run_process(*args, **kwargs):
FILE: docker_prepare.py
function download (line 17) | async def download(dict):
function main (line 32) | async def main():
FILE: examples/response.cpp
type Color (line 9) | struct Color {
type Translation (line 14) | struct Translation {
type TranslationResponse (line 27) | struct TranslationResponse {
function read_u32 (line 31) | uint32_t read_u32(const std::vector<uint8_t>& bytes, size_t& offset) {
function read_f32 (line 39) | float read_f32(const std::vector<uint8_t>& bytes, size_t& offset) {
function read_u8 (line 47) | uint8_t read_u8(const std::vector<uint8_t>& bytes, size_t& offset) {
function read_bool (line 53) | bool read_bool(const std::vector<uint8_t>& bytes, size_t& offset) {
function read_chunk (line 59) | std::vector<uint8_t> read_chunk(const std::vector<uint8_t>& bytes, size_...
function read_str (line 66) | std::string read_str(const std::vector<uint8_t>& bytes, size_t& offset) {
function read_map (line 72) | std::unordered_map<std::string, std::string> read_map(const std::vector<...
function Translation (line 83) | Translation from_bytes(const std::vector<uint8_t>& bytes, size_t& offset) {
function TranslationResponse (line 101) | TranslationResponse from_bytes_response(const std::vector<uint8_t>& byte...
function main (line 112) | int main() {
FILE: examples/response.rs
type Color (line 2) | struct Color {
type Translation (line 8) | struct Translation {
method from_bytes (line 43) | fn from_bytes(bytes: &[u8], offset: &mut usize) -> Self {
type TranslationResponse (line 22) | struct TranslationResponse {
method from_bytes (line 27) | fn from_bytes(bytes: &[u8]) -> Self {
function read_u32 (line 36) | fn read_u32(bytes: &[u8], offset: &mut usize) -> u32 {
FILE: front/app/components/Header.tsx
type Props (line 4) | type Props = {};
FILE: front/app/components/ImageHandlingArea.tsx
type ImageHandlingAreaProps (line 6) | interface ImageHandlingAreaProps {
FILE: front/app/components/ImageQueue.tsx
type ImageQueueProps (line 6) | interface ImageQueueProps {
FILE: front/app/components/LabeledInput.tsx
type LabeledInputProps (line 4) | type LabeledInputProps = {
FILE: front/app/components/LabeledSelect.tsx
type LabeledSelectOption (line 4) | type LabeledSelectOption = {
type LabeledSelectProps (line 9) | type LabeledSelectProps = {
FILE: front/app/components/OptionsPanel.tsx
type Props (line 15) | type Props = {
FILE: front/app/components/ResultGallery.tsx
type ResultGalleryProps (line 5) | interface ResultGalleryProps {
FILE: front/app/root.tsx
function Layout (line 26) | function Layout({ children }: { children: React.ReactNode }) {
function App (line 44) | function App() {
function ErrorBoundary (line 48) | function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
FILE: front/app/routes/home.tsx
function meta (line 4) | function meta({}: Route.MetaArgs) {
function Home (line 11) | function Home() {
FILE: front/app/types.ts
type StatusKey (line 1) | type StatusKey =
type ChunkProcessingResult (line 21) | interface ChunkProcessingResult {
type TranslatorKey (line 38) | type TranslatorKey =
type FileStatus (line 89) | interface FileStatus {
type QueuedImage (line 98) | interface QueuedImage {
type TranslationSettings (line 107) | interface TranslationSettings {
type FinishedImage (line 120) | interface FinishedImage {
FILE: front/app/utils/getTranslatorName.ts
function getTranslatorName (line 3) | function getTranslatorName(key: TranslatorKey): string {
FILE: front/app/utils/localStorage.ts
constant SETTINGS_KEY (line 3) | const SETTINGS_KEY = 'manga-translator-settings';
constant FINISHED_IMAGES_KEY (line 4) | const FINISHED_IMAGES_KEY = 'manga-translator-finished-images';
FILE: manga_translator/__main__.py
function dispatch (line 23) | async def dispatch(args: Namespace):
FILE: manga_translator/args.py
function url_decode (line 13) | def url_decode(s):
function path (line 20) | def path(string):
function file_path (line 28) | def file_path(string):
function dir_path (line 36) | def dir_path(string):
class HelpFormatter (line 54) | class HelpFormatter(argparse.HelpFormatter):
method __init__ (line 59) | def __init__(self, prog: str, indent_increment: int = 2, max_help_posi...
method _format_action_invocation (line 62) | def _format_action_invocation(self, action: argparse.Action) -> str:
function general_parser (line 79) | def general_parser(g_parser):
function reparse (line 105) | def reparse(arr: list):
FILE: manga_translator/colorization/__init__.py
function get_colorizer (line 12) | def get_colorizer(key: Colorizer, *args, **kwargs) -> CommonColorizer:
function prepare (line 20) | async def prepare(key: Colorizer):
function dispatch (line 25) | async def dispatch(key: Colorizer, device: str = 'cpu', **kwargs) -> Ima...
function unload (line 31) | async def unload(key: Colorizer):
FILE: manga_translator/colorization/common.py
class CommonColorizer (line 6) | class CommonColorizer(InfererModule):
method colorize (line 9) | async def colorize(self, image: Image.Image, colorization_size: int, *...
method _colorize (line 13) | async def _colorize(self, image: Image.Image, colorization_size: int, ...
class OfflineColorizer (line 16) | class OfflineColorizer(CommonColorizer, ModelWrapper):
method _colorize (line 19) | async def _colorize(self, *args, **kwargs):
method _infer (line 23) | async def _infer(self, image: Image.Image, colorization_size: int, **k...
FILE: manga_translator/colorization/manga_colorization_v2.py
class MangaColorizationV2 (line 14) | class MangaColorizationV2(OfflineColorizer):
method _load (line 30) | async def _load(self, device: str):
method _unload (line 38) | async def _unload(self):
method _infer (line 42) | async def _infer(self, image: Image.Image, colorization_size: int, den...
FILE: manga_translator/colorization/manga_colorization_v2_utils/denoising/denoiser.py
class FFDNetDenoiser (line 26) | class FFDNetDenoiser:
method __init__ (line 27) | def __init__(self, _device, _sigma = 25, _weights_dir = 'denoising/mod...
method load_weights (line 37) | def load_weights(self):
method get_denoised_image (line 51) | def get_denoised_image(self, imorig, sigma = None):
FILE: manga_translator/colorization/manga_colorization_v2_utils/denoising/functions.py
function concatenate_input_noise_map (line 16) | def concatenate_input_noise_map(input, noise_sigma):
class UpSampleFeaturesFunction (line 56) | class UpSampleFeaturesFunction(Function):
method forward (line 64) | def forward(ctx, input):
method backward (line 83) | def backward(ctx, grad_output):
FILE: manga_translator/colorization/manga_colorization_v2_utils/denoising/models.py
class UpSampleFeatures (line 17) | class UpSampleFeatures(nn.Module):
method __init__ (line 20) | def __init__(self):
method forward (line 22) | def forward(self, x):
class IntermediateDnCNN (line 25) | class IntermediateDnCNN(nn.Module):
method __init__ (line 29) | def __init__(self, input_features, middle_features, num_conv_layers):
method forward (line 64) | def forward(self, x):
class FFDNet (line 68) | class FFDNet(nn.Module):
method __init__ (line 71) | def __init__(self, num_input_channels):
method forward (line 95) | def forward(self, x, noise_sigma):
FILE: manga_translator/colorization/manga_colorization_v2_utils/denoising/utils.py
function variable_to_cv2_image (line 18) | def variable_to_cv2_image(varim):
function normalize (line 36) | def normalize(data):
function remove_dataparallel_wrapper (line 39) | def remove_dataparallel_wrapper(state_dict):
function is_rgb (line 55) | def is_rgb(im_path):
FILE: manga_translator/colorization/manga_colorization_v2_utils/networks/extractor.py
class Selayer (line 8) | class Selayer(nn.Module):
method __init__ (line 9) | def __init__(self, inplanes):
method forward (line 17) | def forward(self, x):
class BottleneckX_Origin (line 27) | class BottleneckX_Origin(nn.Module):
method __init__ (line 30) | def __init__(self, inplanes, planes, cardinality, stride=1, downsample...
method forward (line 48) | def forward(self, x):
class SEResNeXt_Origin (line 72) | class SEResNeXt_Origin(nn.Module):
method __init__ (line 73) | def __init__(self, block, layers, input_channels=3, cardinality=32, nu...
method _make_layer (line 98) | def _make_layer(self, block, planes, blocks, stride=1):
method forward (line 115) | def forward(self, x):
FILE: manga_translator/colorization/manga_colorization_v2_utils/networks/models.py
function l2normalize (line 13) | def l2normalize(v, eps=1e-12):
class SpectralNorm (line 17) | class SpectralNorm(nn.Module):
method __init__ (line 18) | def __init__(self, module, name='weight', power_iterations=1):
method _update_u_v (line 26) | def _update_u_v(self):
method _made_params (line 40) | def _made_params(self):
method _make_params (line 50) | def _make_params(self):
method forward (line 68) | def forward(self, *args):
class Selayer (line 72) | class Selayer(nn.Module):
method __init__ (line 73) | def __init__(self, inplanes):
method forward (line 81) | def forward(self, x):
class SelayerSpectr (line 90) | class SelayerSpectr(nn.Module):
method __init__ (line 91) | def __init__(self, inplanes):
method forward (line 99) | def forward(self, x):
class ResNeXtBottleneck (line 108) | class ResNeXtBottleneck(nn.Module):
method __init__ (line 109) | def __init__(self, in_channels=256, out_channels=256, stride=1, cardin...
method forward (line 125) | def forward(self, x):
class SpectrResNeXtBottleneck (line 136) | class SpectrResNeXtBottleneck(nn.Module):
method __init__ (line 137) | def __init__(self, in_channels=256, out_channels=256, stride=1, cardin...
method forward (line 153) | def forward(self, x):
class FeatureConv (line 164) | class FeatureConv(nn.Module):
method __init__ (line 165) | def __init__(self, input_dim=512, output_dim=512):
method forward (line 182) | def forward(self, x):
class Generator (line 185) | class Generator(nn.Module):
method __init__ (line 186) | def __init__(self, ngf=64):
method _make_encoder_block (line 270) | def _make_encoder_block(self, inplanes, planes):
method _make_encoder_block_first (line 278) | def _make_encoder_block_first(self, inplanes, planes):
method forward (line 286) | def forward(self, sketch):
class Colorizer (line 311) | class Colorizer(nn.Module):
method __init__ (line 312) | def __init__(self):
method forward (line 317) | def forward(self, x, extractor_grad = False):
FILE: manga_translator/colorization/manga_colorization_v2_utils/utils/utils.py
function resize_pad (line 4) | def resize_pad(img, size = 256):
FILE: manga_translator/config.py
class TranslatorChain (line 12) | class TranslatorChain:
method __init__ (line 13) | def __init__(self, string: str):
method has_offline (line 33) | def has_offline(self) -> bool:
method __eq__ (line 40) | def __eq__(self, __o: object) -> bool:
function translator_chain (line 46) | def translator_chain(string):
function hex2rgb (line 55) | def hex2rgb(h):
class Renderer (line 59) | class Renderer(str, Enum):
class Alignment (line 65) | class Alignment(str, Enum):
class Direction (line 71) | class Direction(str, Enum):
class InpaintPrecision (line 76) | class InpaintPrecision(str, Enum):
method __str__ (line 81) | def __str__(self):
class Detector (line 84) | class Detector(str, Enum):
class Inpainter (line 92) | class Inpainter(str, Enum):
class Colorizer (line 100) | class Colorizer(str, Enum):
class Ocr (line 104) | class Ocr(str, Enum):
class Translator (line 110) | class Translator(str, Enum):
method __str__ (line 138) | def __str__(self):
method _missing_ (line 143) | def _missing_(cls, value):
class Upscaler (line 149) | class Upscaler(str, Enum):
class RenderConfig (line 154) | class RenderConfig(BaseModel):
method font_color_fg (line 186) | def font_color_fg(self):
method font_color_bg (line 198) | def font_color_bg(self):
class UpscaleConfig (line 209) | class UpscaleConfig(BaseModel):
class TranslatorConfig (line 217) | class TranslatorConfig(BaseModel):
method translator_gen (line 247) | def translator_gen(self):
method chatgpt_config (line 263) | def chatgpt_config(self):
class DetectorConfig (line 270) | class DetectorConfig(BaseModel):
class InpainterConfig (line 291) | class InpainterConfig(BaseModel):
class ColorizerConfig (line 299) | class ColorizerConfig(BaseModel):
class OcrConfig (line 307) | class OcrConfig(BaseModel):
class Config (line 319) | class Config(BaseModel):
method re_filter_text (line 347) | def re_filter_text(self):
FILE: manga_translator/detection/__init__.py
function get_detector (line 22) | def get_detector(key: Detector, *args, **kwargs) -> CommonDetector:
function prepare (line 30) | async def prepare(detector_key: Detector):
function dispatch (line 35) | async def dispatch(detector_key: Detector, image: np.ndarray, detect_siz...
function unload (line 42) | async def unload(detector_key: Detector):
FILE: manga_translator/detection/common.py
class CommonDetector (line 10) | class CommonDetector(InfererModule):
method detect (line 12) | async def detect(self, image: np.ndarray, detect_size: int, text_thres...
method _detect (line 67) | async def _detect(self, image: np.ndarray, detect_size: int, text_thre...
method _add_border (line 71) | def _add_border(self, image: np.ndarray, target_side_length: int):
method _remove_border (line 81) | def _remove_border(self, image: np.ndarray, old_w: int, old_h: int, te...
method _add_rotation (line 101) | def _add_rotation(self, image: np.ndarray):
method _remove_rotation (line 104) | def _remove_rotation(self, textlines, raw_mask, mask, img_w, img_h):
method _add_inversion (line 115) | def _add_inversion(self, image: np.ndarray):
method _add_gamma_correction (line 118) | def _add_gamma_correction(self, image: np.ndarray):
method _add_histogram_equalization (line 126) | def _add_histogram_equalization(self, image: np.ndarray):
class OfflineDetector (line 137) | class OfflineDetector(CommonDetector, ModelWrapper):
method _detect (line 140) | async def _detect(self, *args, **kwargs):
method _infer (line 144) | async def _infer(self, image: np.ndarray, detect_size: int, text_thres...
FILE: manga_translator/detection/common_rust.py
function get_session (line 7) | def get_session():
class RustDetector (line 14) | class RustDetector:
method __init__ (line 15) | def __init__(self, det):
method parse_args (line 18) | def parse_args(self, args: TranslatorConfig):
method is_downloaded (line 22) | def is_downloaded(self) -> bool:
method download (line 26) | async def download(self, force=False):
method model_dir (line 31) | def model_dir(self):
method infer (line 35) | async def infer(self, *args, **kwargs):
method detect (line 39) | async def detect(self, image: np.ndarray, detect_size: int, text_thres...
method reload (line 52) | async def reload(self, device: str, *args, **kwargs):
method load (line 56) | async def load(self, device: str, *args, **kwargs):
method unload (line 59) | async def unload(self):
method is_loaded (line 62) | def is_loaded(self) -> bool:
FILE: manga_translator/detection/craft.py
class double_conv (line 29) | class double_conv(nn.Module):
method __init__ (line 30) | def __init__(self, in_ch, mid_ch, out_ch):
method forward (line 41) | def forward(self, x):
class CRAFT (line 46) | class CRAFT(nn.Module):
method __init__ (line 47) | def __init__(self, pretrained=False, freeze=False):
method forward (line 74) | def forward(self, x):
function copyStateDict (line 100) | def copyStateDict(state_dict):
class CRAFTDetector (line 111) | class CRAFTDetector(OfflineDetector):
method __init__ (line 125) | def __init__(self, *args, **kwargs):
method _load (line 133) | async def _load(self, device: str):
method _unload (line 147) | async def _unload(self):
method _infer (line 150) | async def _infer(self, image: np.ndarray, detect_size: int, text_thres...
FILE: manga_translator/detection/craft_utils/refiner.py
class RefineNet (line 14) | class RefineNet(nn.Module):
method __init__ (line 15) | def __init__(self):
method forward (line 54) | def forward(self, y, upconv4):
FILE: manga_translator/detection/craft_utils/vgg16_bn.py
function init_weights (line 8) | def init_weights(modules):
class vgg16_bn (line 21) | class vgg16_bn(torch.nn.Module):
method __init__ (line 22) | def __init__(self, pretrained=True, freeze=True):
method forward (line 58) | def forward(self, X):
FILE: manga_translator/detection/ctd.py
function preprocess_img (line 17) | def preprocess_img(img, input_size=(1024, 1024), device='cpu', bgr2rgb=T...
function postprocess_mask (line 30) | def postprocess_mask(img: Union[torch.Tensor, np.ndarray], thresh=None):
function postprocess_yolo (line 46) | def postprocess_yolo(det, conf_thresh, nms_thresh, resize_ratio, sort_fu...
class ComicTextDetector (line 62) | class ComicTextDetector(OfflineDetector):
method __init__ (line 76) | def __init__(self, *args, **kwargs):
method _load (line 84) | async def _load(self, device: str, input_size=1024, half=False, nms_th...
method _unload (line 104) | async def _unload(self):
method det_batch_forward_ctd (line 107) | def det_batch_forward_ctd(self, batch: np.ndarray, device: str) -> Tup...
method _infer (line 130) | async def _infer(self, image: np.ndarray, detect_size: int, text_thres...
FILE: manga_translator/detection/ctd_utils/basemodel.py
class double_conv_up_c3 (line 15) | class double_conv_up_c3(nn.Module):
method __init__ (line 16) | def __init__(self, in_ch, mid_ch, out_ch, act=True):
method forward (line 25) | def forward(self, x):
class double_conv_c3 (line 28) | class double_conv_c3(nn.Module):
method __init__ (line 29) | def __init__(self, in_ch, out_ch, stride=1, act=True):
method forward (line 35) | def forward(self, x):
class UnetHead (line 41) | class UnetHead(nn.Module):
method __init__ (line 42) | def __init__(self, act=True) -> None:
method forward (line 56) | def forward(self, f160, f80, f40, f20, f3, forward_mode=TEXTDET_MASK):
method init_weight (line 74) | def init_weight(self, init_func):
class DBHead (line 77) | class DBHead(nn.Module):
method __init__ (line 78) | def __init__(self, in_channels, k = 50, shrink_with_sigmoid=True, act=...
method forward (line 100) | def forward(self, f80, f40, u40, shrink_with_sigmoid=True, step_eval=F...
method init_weight (line 121) | def init_weight(self, init_func):
method _init_thresh (line 124) | def _init_thresh(self, inner_channels, serial=False, smooth=False, bia...
method _init_upsample (line 139) | def _init_upsample(self, in_channels, out_channels, smooth=False, bias...
method step_function (line 153) | def step_function(self, x, y):
class TextDetector (line 156) | class TextDetector(nn.Module):
method __init__ (line 157) | def __init__(self, weights, map_location='cpu', forward_mode=TEXTDET_M...
method train_mask (line 171) | def train_mask(self):
method initialize_db (line 176) | def initialize_db(self, unet_weights):
method train_db (line 188) | def train_db(self):
method forward (line 194) | def forward(self, x):
function get_base_det_models (line 205) | def get_base_det_models(model_path, device='cpu', half=False, act='leaky'):
class TextDetBase (line 216) | class TextDetBase(nn.Module):
method __init__ (line 217) | def __init__(self, model_path, device='cpu', half=False, fuse=False, a...
method fuse (line 223) | def fuse(self):
method forward (line 234) | def forward(self, features):
class TextDetBaseDNN (line 240) | class TextDetBaseDNN:
method __init__ (line 241) | def __init__(self, input_size, model_path):
method __call__ (line 246) | def __call__(self, im_in):
FILE: manga_translator/detection/ctd_utils/textmask.py
function get_topk_color (line 16) | def get_topk_color(color_list, bins, k=3, color_var=10, bin_tol=0.001):
function minxor_thresh (line 29) | def minxor_thresh(threshed, mask, dilate=False):
function get_otsuthresh_masklist (line 43) | def get_otsuthresh_masklist(img, pred_mask, per_channel=False) -> List[n...
function get_topk_masklist (line 56) | def get_topk_masklist(im_grey, pred_mask):
function merge_mask_list (line 73) | def merge_mask_list(mask_list, pred_mask, blk: Quadrilateral = None, pre...
function refine_undetected_mask (line 135) | def refine_undetected_mask(img: np.ndarray, mask_pred: np.ndarray, mask_...
function refine_mask (line 158) | def refine_mask(img: np.ndarray, pred_mask: np.ndarray, blk_list: List[Q...
FILE: manga_translator/detection/ctd_utils/utils/db_utils.py
function iou_rotate (line 11) | def iou_rotate(box_a, box_b, method='union'):
class SegDetectorRepresenter (line 32) | class SegDetectorRepresenter():
method __init__ (line 33) | def __init__(self, thresh=0.3, box_thresh=0.7, max_candidates=1000, un...
method __call__ (line 40) | def __call__(self, batch, pred, is_output_polygon=False, height=None, ...
method binarize (line 75) | def binarize(self, pred) -> np.ndarray:
method polygons_from_bitmap (line 78) | def polygons_from_bitmap(self, pred, _bitmap, dest_width, dest_height):
method boxes_from_bitmap (line 127) | def boxes_from_bitmap(self, pred, _bitmap, dest_width, dest_height):
method unclip (line 173) | def unclip(self, box, unclip_ratio=1.5):
method get_mini_boxes (line 181) | def get_mini_boxes(self, contour):
method box_score_fast (line 202) | def box_score_fast(self, bitmap, _box):
class AverageMeter (line 218) | class AverageMeter(object):
method __init__ (line 221) | def __init__(self):
method reset (line 224) | def reset(self):
method update (line 230) | def update(self, val, n=1):
class DetectionIoUEvaluator (line 238) | class DetectionIoUEvaluator(object):
method __init__ (line 239) | def __init__(self, is_output_polygon=False, iou_constraint=0.5, area_p...
method evaluate_image (line 244) | def evaluate_image(self, gt, pred):
method combine_results (line 420) | def combine_results(self, results):
class QuadMetric (line 442) | class QuadMetric():
method __init__ (line 443) | def __init__(self, is_output_polygon=False):
method measure (line 447) | def measure(self, batch, output, box_thresh=0.6):
method validate_measure (line 478) | def validate_measure(self, batch, output, box_thresh=0.6):
method evaluate_measure (line 481) | def evaluate_measure(self, batch, output):
method gather_measure (line 484) | def gather_measure(self, raw_metrics):
function shrink_polygon_py (line 506) | def shrink_polygon_py(polygon, shrink_ratio):
function shrink_polygon_pyclipper (line 517) | def shrink_polygon_pyclipper(polygon, shrink_ratio):
class MakeShrinkMap (line 532) | class MakeShrinkMap():
method __init__ (line 538) | def __init__(self, min_text_size=4, shrink_ratio=0.4, shrink_type='pyc...
method __call__ (line 544) | def __call__(self, data: dict) -> dict:
method validate_polygons (line 577) | def validate_polygons(self, polygons, ignore_tags, h, w):
method polygon_area (line 596) | def polygon_area(self, polygon):
class MakeBorderMap (line 600) | class MakeBorderMap():
method __init__ (line 601) | def __init__(self, shrink_ratio=0.4, thresh_min=0.3, thresh_max=0.7):
method __call__ (line 606) | def __call__(self, data: dict) -> dict:
method draw_border_map (line 629) | def draw_border_map(self, polygon, canvas, mask):
method distance (line 679) | def distance(self, xs, ys, point_1, point_2):
method extend_line (line 699) | def extend_line(self, point_1, point_2, result):
FILE: manga_translator/detection/ctd_utils/utils/imgproc_utils.py
function hex2bgr (line 6) | def hex2bgr(hex):
function union_area (line 14) | def union_area(bboxa, bboxb):
function get_yololabel_strings (line 23) | def get_yololabel_strings(clslist, labellist):
function xywh2xyxypoly (line 32) | def xywh2xyxypoly(xywh, to_int=True):
function xyxy2yolo (line 40) | def xyxy2yolo(xyxy, w: int, h: int):
function yolo_xywh2xyxy (line 54) | def yolo_xywh2xyxy(xywh: np.array, w: int, h: int, to_int=True):
function letterbox (line 69) | def letterbox(im, new_shape=(640, 640), color=(0, 0, 0), auto=False, sca...
function resize_keepasp (line 102) | def resize_keepasp(im, new_shape=640, scaleup=True, interpolation=cv2.IN...
function enlarge_window (line 134) | def enlarge_window(rect, im_w, im_h, ratio=2.5, aspect_ratio=1.0) -> List:
function draw_connected_labels (line 152) | def draw_connected_labels(num_labels, labels, stats, centroids, names="d...
FILE: manga_translator/detection/ctd_utils/utils/io_utils.py
class NumpyEncoder (line 16) | class NumpyEncoder(json.JSONEncoder):
method default (line 17) | def default(self, obj):
function find_all_imgs (line 29) | def find_all_imgs(img_dir, abs_path=False):
function imread (line 42) | def imread(imgpath, read_type=cv2.IMREAD_COLOR):
function imwrite (line 48) | def imwrite(img_path, img, ext='.png'):
FILE: manga_translator/detection/ctd_utils/utils/weight_init.py
function constant_init (line 4) | def constant_init(module, val, bias=0):
function xavier_init (line 9) | def xavier_init(module, gain=1, bias=0, distribution='normal'):
function normal_init (line 19) | def normal_init(module, mean=0, std=1, bias=0):
function uniform_init (line 25) | def uniform_init(module, a=0, b=1, bias=0):
function kaiming_init (line 31) | def kaiming_init(module,
function bilinear_kernel (line 75) | def bilinear_kernel(in_channels, out_channels, kernel_size):
function init_weights (line 91) | def init_weights(m):
FILE: manga_translator/detection/ctd_utils/utils/yolov5_utils.py
function scale_img (line 10) | def scale_img(img, ratio=1.0, same_shape=False, gs=32): # img(16,3,256,...
function fuse_conv_and_bn (line 22) | def fuse_conv_and_bn(conv, bn):
function check_anchor_order (line 44) | def check_anchor_order(m):
function initialize_weights (line 52) | def initialize_weights(model):
function make_divisible (line 63) | def make_divisible(x, divisor):
function intersect_dicts (line 69) | def intersect_dicts(da, db, exclude=()):
function check_version (line 73) | def check_version(current='0.0.0', minimum='0.0.0', name='version ', pin...
class Colors (line 83) | class Colors:
method __init__ (line 85) | def __init__(self):
method __call__ (line 92) | def __call__(self, i, bgr=False):
method hex2rgb (line 97) | def hex2rgb(h): # rgb order (PIL)
function box_iou (line 100) | def box_iou(box1, box2):
function non_max_suppression (line 124) | def non_max_suppression(prediction, conf_thres=0.25, iou_thres=0.45, cla...
function xywh2xyxy (line 220) | def xywh2xyxy(x):
function draw_bbox (line 230) | def draw_bbox(pred, img, lang_list=None):
FILE: manga_translator/detection/ctd_utils/yolov5/common.py
function autopad (line 24) | def autopad(k, p=None): # kernel, padding
class Conv (line 30) | class Conv(nn.Module):
method __init__ (line 32) | def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in,...
method forward (line 45) | def forward(self, x):
method forward_fuse (line 48) | def forward_fuse(self, x):
class DWConv (line 52) | class DWConv(Conv):
method __init__ (line 54) | def __init__(self, c1, c2, k=1, s=1, act=True): # ch_in, ch_out, kern...
class TransformerLayer (line 58) | class TransformerLayer(nn.Module):
method __init__ (line 60) | def __init__(self, c, num_heads):
method forward (line 69) | def forward(self, x):
class TransformerBlock (line 75) | class TransformerBlock(nn.Module):
method __init__ (line 77) | def __init__(self, c1, c2, num_heads, num_layers):
method forward (line 86) | def forward(self, x):
class Bottleneck (line 94) | class Bottleneck(nn.Module):
method __init__ (line 96) | def __init__(self, c1, c2, shortcut=True, g=1, e=0.5, act=True): # ch...
method forward (line 103) | def forward(self, x):
class BottleneckCSP (line 107) | class BottleneckCSP(nn.Module):
method __init__ (line 109) | def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ...
method forward (line 120) | def forward(self, x):
class C3 (line 126) | class C3(nn.Module):
method __init__ (line 128) | def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5, act=True): ...
method forward (line 137) | def forward(self, x):
class C3TR (line 141) | class C3TR(C3):
method __init__ (line 143) | def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
class C3SPP (line 149) | class C3SPP(C3):
method __init__ (line 151) | def __init__(self, c1, c2, k=(5, 9, 13), n=1, shortcut=True, g=1, e=0.5):
class C3Ghost (line 157) | class C3Ghost(C3):
method __init__ (line 159) | def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
class SPP (line 165) | class SPP(nn.Module):
method __init__ (line 167) | def __init__(self, c1, c2, k=(5, 9, 13)):
method forward (line 174) | def forward(self, x):
class SPPF (line 181) | class SPPF(nn.Module):
method __init__ (line 183) | def __init__(self, c1, c2, k=5): # equivalent to SPP(k=(5, 9, 13))
method forward (line 190) | def forward(self, x):
class Focus (line 199) | class Focus(nn.Module):
method __init__ (line 201) | def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in,...
method forward (line 206) | def forward(self, x): # x(b,c,w,h) -> y(b,4c,w/2,h/2)
class GhostConv (line 211) | class GhostConv(nn.Module):
method __init__ (line 213) | def __init__(self, c1, c2, k=1, s=1, g=1, act=True): # ch_in, ch_out,...
method forward (line 219) | def forward(self, x):
class GhostBottleneck (line 224) | class GhostBottleneck(nn.Module):
method __init__ (line 226) | def __init__(self, c1, c2, k=3, s=1): # ch_in, ch_out, kernel, stride
method forward (line 235) | def forward(self, x):
class Contract (line 239) | class Contract(nn.Module):
method __init__ (line 241) | def __init__(self, gain=2):
method forward (line 245) | def forward(self, x):
class Expand (line 253) | class Expand(nn.Module):
method __init__ (line 255) | def __init__(self, gain=2):
method forward (line 259) | def forward(self, x):
class Concat (line 267) | class Concat(nn.Module):
method __init__ (line 269) | def __init__(self, dimension=1):
method forward (line 273) | def forward(self, x):
class Classify (line 277) | class Classify(nn.Module):
method __init__ (line 279) | def __init__(self, c1, c2, k=1, s=1, p=None, g=1): # ch_in, ch_out, k...
method forward (line 285) | def forward(self, x):
FILE: manga_translator/detection/ctd_utils/yolov5/yolo.py
class Detect (line 7) | class Detect(nn.Module):
method __init__ (line 11) | def __init__(self, nc=80, anchors=(), ch=(), inplace=True): # detecti...
method forward (line 23) | def forward(self, x):
method _make_grid (line 46) | def _make_grid(self, nx=20, ny=20, i=0):
class Model (line 57) | class Model(nn.Module):
method __init__ (line 58) | def __init__(self, cfg='yolov5s.yaml', ch=3, nc=None, anchors=None): ...
method forward (line 96) | def forward(self, x, augment=False, profile=False, visualize=False, de...
method _forward_once (line 115) | def _forward_once(self, x, profile=False, visualize=False, detect=False):
method _descale_pred (line 136) | def _descale_pred(self, p, flips, scale, img_size):
method _clip_augmented (line 153) | def _clip_augmented(self, y):
method _profile_one_layer (line 164) | def _profile_one_layer(self, m, x, dt):
method _initialize_biases (line 170) | def _initialize_biases(self, cf=None): # initialize biases into Detec...
method _print_biases (line 180) | def _print_biases(self):
method fuse (line 185) | def fuse(self): # fuse model Conv2d() + BatchNorm2d() layers
method _apply (line 197) | def _apply(self, fn):
function parse_model (line 208) | def parse_model(d, ch): # model_dict, input_channels(3)
function load_yolov5 (line 261) | def load_yolov5(weights, map_location='cuda', fuse=True, inplace=True, o...
function load_yolov5_ckpt (line 286) | def load_yolov5_ckpt(weights, map_location='cpu', fuse=True, inplace=Tru...
FILE: manga_translator/detection/dbnet_convnext.py
class Downsample (line 20) | class Downsample(nn.Module):
method __init__ (line 22) | def __init__(self, in_chs, out_chs, stride=1, dilation=1):
method forward (line 36) | def forward(self, x):
class ConvNeXtBlock (line 42) | class ConvNeXtBlock(nn.Module):
method __init__ (line 53) | def __init__(
method forward (line 112) | def forward(self, x):
class ConvNeXtStage (line 130) | class ConvNeXtStage(nn.Module):
method __init__ (line 132) | def __init__(
method forward (line 190) | def forward(self, x):
class ConvNeXt (line 196) | class ConvNeXt(nn.Module):
method __init__ (line 201) | def __init__(
method group_matcher (line 323) | def group_matcher(self, coarse=False):
method set_grad_checkpointing (line 334) | def set_grad_checkpointing(self, enable=True):
method get_classifier (line 339) | def get_classifier(self):
method forward_features (line 342) | def forward_features(self, x):
function _init_weights (line 347) | def _init_weights(module, name=None, head_init_scale=1.0):
class UpconvSkip (line 359) | class UpconvSkip(nn.Module) :
method __init__ (line 360) | def __init__(self, ch1, ch2, out_ch) -> None:
method forward (line 377) | def forward(self, x) :
class DBHead (line 382) | class DBHead(nn.Module):
method __init__ (line 383) | def __init__(self, in_channels, k = 50):
method forward (line 400) | def forward(self, x):
method weights_init (line 410) | def weights_init(self, m):
method _init_thresh (line 418) | def _init_thresh(self, inner_channels, serial=False, smooth=False, bia...
method _init_upsample (line 433) | def _init_upsample(self, in_channels, out_channels, smooth=False, bias...
method step_function (line 447) | def step_function(self, x, y):
class DBNetConvNext (line 450) | class DBNetConvNext(nn.Module) :
method __init__ (line 451) | def __init__(self) :
method forward (line 474) | def forward(self, x) :
function det_batch_forward_default (line 499) | def det_batch_forward_default(batch: np.ndarray, device: str):
class DBConvNextDetector (line 512) | class DBConvNextDetector(OfflineDetector):
method __init__ (line 521) | def __init__(self, *args, **kwargs):
method _load (line 527) | async def _load(self, device: str):
method _unload (line 538) | async def _unload(self):
method _infer (line 541) | async def _infer(self, image: np.ndarray, detect_size: int, text_thres...
FILE: manga_translator/detection/default.py
function det_batch_forward_default (line 15) | def det_batch_forward_default(batch: np.ndarray, device: str):
class DefaultDetector (line 27) | class DefaultDetector(OfflineDetector):
method __init__ (line 36) | def __init__(self, *args, **kwargs):
method _load (line 42) | async def _load(self, device: str):
method _unload (line 53) | async def _unload(self):
method _infer (line 56) | async def _infer(self, image: np.ndarray, detect_size: int, text_thres...
FILE: manga_translator/detection/default_utils/CRAFT_resnet34.py
class ImageMultiheadSelfAttention (line 12) | class ImageMultiheadSelfAttention(nn.Module):
method __init__ (line 13) | def __init__(self, planes):
method forward (line 16) | def forward(self, x):
class double_conv (line 24) | class double_conv(nn.Module):
method __init__ (line 25) | def __init__(self, in_ch, mid_ch, out_ch, stride = 1, planes = 256):
method forward (line 47) | def forward(self, x):
class CRAFT_net (line 53) | class CRAFT_net(nn.Module):
method __init__ (line 54) | def __init__(self):
method forward_train (line 96) | def forward_train(self, x):
method forward (line 123) | def forward(self, x):
FILE: manga_translator/detection/default_utils/DBHead.py
class DBHead (line 7) | class DBHead(nn.Module):
method __init__ (line 8) | def __init__(self, in_channels, out_channels, k = 50):
method forward (line 25) | def forward(self, x):
method weights_init (line 35) | def weights_init(self, m):
method _init_thresh (line 43) | def _init_thresh(self, inner_channels, serial=False, smooth=False, bia...
method _init_upsample (line 58) | def _init_upsample(self, in_channels, out_channels, smooth=False, bias...
method step_function (line 72) | def step_function(self, x, y):
FILE: manga_translator/detection/default_utils/DBNet_resnet101.py
class ImageMultiheadSelfAttention (line 11) | class ImageMultiheadSelfAttention(nn.Module):
method __init__ (line 12) | def __init__(self, planes):
method forward (line 15) | def forward(self, x):
class double_conv (line 23) | class double_conv(nn.Module):
method __init__ (line 24) | def __init__(self, in_ch, mid_ch, out_ch, stride = 1, planes = 256):
method forward (line 46) | def forward(self, x):
class double_conv_up (line 52) | class double_conv_up(nn.Module):
method __init__ (line 53) | def __init__(self, in_ch, mid_ch, out_ch, stride = 1, planes = 256):
method forward (line 78) | def forward(self, x):
class TextDetection (line 84) | class TextDetection(nn.Module):
method __init__ (line 85) | def __init__(self, pretrained=None):
method forward (line 116) | def forward(self, x):
FILE: manga_translator/detection/default_utils/DBNet_resnet34.py
class ImageMultiheadSelfAttention (line 11) | class ImageMultiheadSelfAttention(nn.Module):
method __init__ (line 12) | def __init__(self, planes):
method forward (line 15) | def forward(self, x):
class double_conv (line 23) | class double_conv(nn.Module):
method __init__ (line 24) | def __init__(self, in_ch, mid_ch, out_ch, stride = 1, planes = 256):
method forward (line 49) | def forward(self, x):
class double_conv_up (line 55) | class double_conv_up(nn.Module):
method __init__ (line 56) | def __init__(self, in_ch, mid_ch, out_ch, planes = 256):
method forward (line 72) | def forward(self, x):
class TextDetection (line 76) | class TextDetection(nn.Module):
method __init__ (line 77) | def __init__(self, pretrained=None):
method forward (line 103) | def forward(self, x):
FILE: manga_translator/detection/default_utils/craft_utils.py
function warpCoord (line 13) | def warpCoord(Minv, pt):
function getDetBoxes_core (line 19) | def getDetBoxes_core(textmap, linkmap, text_threshold, link_threshold, l...
function getPoly_core (line 82) | def getPoly_core(boxes, labels, mapper, linkmap):
function getDetBoxes (line 228) | def getDetBoxes(textmap, linkmap, text_threshold, link_threshold, low_te...
function adjustResultCoordinates (line 238) | def adjustResultCoordinates(polys, ratio_w, ratio_h, ratio_net = 2):
FILE: manga_translator/detection/default_utils/dbnet_utils.py
class SegDetectorRepresenter (line 8) | class SegDetectorRepresenter():
method __init__ (line 9) | def __init__(self, thresh=0.6, box_thresh=0.8, max_candidates=1000, un...
method __call__ (line 16) | def __call__(self, batch, pred, is_output_polygon=False):
method binarize (line 45) | def binarize(self, pred):
method polygons_from_bitmap (line 48) | def polygons_from_bitmap(self, pred, _bitmap, dest_width, dest_height):
method boxes_from_bitmap (line 97) | def boxes_from_bitmap(self, pred, _bitmap, dest_width, dest_height):
method unclip (line 146) | def unclip(self, box, unclip_ratio=1.8):
method get_mini_boxes (line 154) | def get_mini_boxes(self, contour):
method box_score_fast (line 175) | def box_score_fast(self, bitmap, _box):
FILE: manga_translator/detection/default_utils/imgproc.py
function loadImage (line 11) | def loadImage(img_file):
function normalizeMeanVariance (line 20) | def normalizeMeanVariance(in_img, mean=(0.485, 0.456, 0.406), variance=(...
function denormalizeMeanVariance (line 28) | def denormalizeMeanVariance(in_img, mean=(0.485, 0.456, 0.406), variance...
function resize_aspect_ratio (line 37) | def resize_aspect_ratio(img, square_size, interpolation, mag_ratio=1):
function cvt2HeatmapImg (line 72) | def cvt2HeatmapImg(img):
FILE: manga_translator/detection/none.py
class NoneDetector (line 5) | class NoneDetector(CommonDetector):
method _detect (line 6) | async def _detect(self, image: np.ndarray, detect_size: int, text_thre...
FILE: manga_translator/detection/paddle_rust.py
class PaddleDetector (line 4) | class PaddleDetector(RustDetector):
method __init__ (line 5) | def __init__(self):
FILE: manga_translator/detection/panel_finder.py
function panel_process_image (line 12) | def panel_process_image(img: Image.Image):
function remove_contained_contours (line 41) | def remove_contained_contours(polygons):
function calc_panel_contours (line 82) | def calc_panel_contours(im: Image.Image):
function determine_panel_order_from_contours (line 101) | def determine_panel_order_from_contours(contours):
function draw_contours (line 108) | def draw_contours(im, contours):
function save_draw_contours (line 139) | def save_draw_contours(pth: Path | str):
function order_panels (line 160) | def order_panels(contours, img_gray):
function generate_vertical_bounding_box_groups_indices (line 195) | def generate_vertical_bounding_box_groups_indices(bounding_boxes):
function check_overlap (line 237) | def check_overlap(range1, range2):
FILE: manga_translator/inpainting/__init__.py
function get_inpainter (line 23) | def get_inpainter(key: Inpainter, *args, **kwargs) -> CommonInpainter:
function prepare (line 31) | async def prepare(inpainter_key: Inpainter, device: str = 'cpu'):
function dispatch (line 37) | async def dispatch(inpainter_key: Inpainter, image: np.ndarray, mask: np...
function unload (line 44) | async def unload(inpainter_key: Inpainter):
FILE: manga_translator/inpainting/booru_tagger.py
function make_square (line 15) | def make_square(img, target_size):
function smart_resize (line 32) | def smart_resize(img, size):
class Tagger (line 40) | class Tagger :
method __init__ (line 41) | def __init__(self, filename) -> None:
method label (line 47) | def label(self, image: Image) -> Dict[str, float] :
method label_cv2_bgr (line 80) | def label_cv2_bgr(self, image: np.ndarray) -> Dict[str, float] :
FILE: manga_translator/inpainting/common.py
class CommonInpainter (line 7) | class CommonInpainter(InfererModule):
method inpaint (line 9) | async def inpaint(self, image: np.ndarray, mask: np.ndarray, config: I...
method _inpaint (line 13) | async def _inpaint(self, image: np.ndarray, mask: np.ndarray, config: ...
class OfflineInpainter (line 16) | class OfflineInpainter(CommonInpainter, ModelWrapper):
method _inpaint (line 19) | async def _inpaint(self, *args, **kwargs):
method _infer (line 23) | async def _infer(self, image: np.ndarray, mask: np.ndarray, config: In...
FILE: manga_translator/inpainting/guided_ldm_inpainting.py
class GuidedDDIMSample (line 26) | class GuidedDDIMSample(DDIMSampler) :
method __init__ (line 27) | def __init__(self, *args, **kwargs):
method p_sample_ddim (line 31) | def p_sample_ddim(self, x, c, t, index, repeat_noise=False, use_origin...
method decode (line 88) | def decode(self, x_latent, cond, t_start, init_latent=None, nmask=None...
function get_inpainting_image_condition (line 113) | def get_inpainting_image_condition(model, image, mask) :
function get_empty_image_condition (line 130) | def get_empty_image_condition(latent) :
function fill_mask_input (line 135) | def fill_mask_input(image, mask):
class GuidedLDM (line 152) | class GuidedLDM(LatentDiffusion):
method __init__ (line 153) | def __init__(self, *args, **kwargs):
method img2img_inpaint (line 157) | def img2img_inpaint(
function get_state_dict (line 236) | def get_state_dict(d):
function load_state_dict (line 240) | def load_state_dict(ckpt_path, location='cpu'):
function create_model (line 251) | def create_model(config_path):
FILE: manga_translator/inpainting/inpainting_aot.py
class AotInpainter (line 11) | class AotInpainter(LamaMPEInpainter):
method __init__ (line 20) | def __init__(self, *args, **kwargs):
method _load (line 26) | async def _load(self, device: str):
function relu_nf (line 36) | def relu_nf(x):
function gelu_nf (line 39) | def gelu_nf(x):
function silu_nf (line 42) | def silu_nf(x):
class LambdaLayer (line 45) | class LambdaLayer(nn.Module):
method __init__ (line 46) | def __init__(self, f):
method forward (line 50) | def forward(self, x):
class ScaledWSConv2d (line 53) | class ScaledWSConv2d(nn.Conv2d):
method __init__ (line 55) | def __init__(self, in_channels, out_channels, kernel_size,
method get_weight (line 70) | def get_weight(self):
method forward (line 79) | def forward(self, x):
class ScaledWSTransposeConv2d (line 84) | class ScaledWSTransposeConv2d(nn.ConvTranspose2d):
method __init__ (line 86) | def __init__(self, in_channels: int,
method get_weight (line 105) | def get_weight(self):
method forward (line 114) | def forward(self, x, output_size: Optional[List[int]] = None):
class GatedWSConvPadded (line 120) | class GatedWSConvPadded(nn.Module):
method __init__ (line 121) | def __init__(self, in_ch, out_ch, ks, stride = 1, dilation = 1):
method forward (line 129) | def forward(self, x):
class GatedWSTransposeConvPadded (line 135) | class GatedWSTransposeConvPadded(nn.Module):
method __init__ (line 136) | def __init__(self, in_ch, out_ch, ks, stride = 1):
method forward (line 143) | def forward(self, x):
class ResBlock (line 148) | class ResBlock(nn.Module):
method __init__ (line 149) | def __init__(self, ch, alpha = 0.2, beta = 1.0, dilation = 1):
method forward (line 156) | def forward(self, x):
function my_layer_norm (line 163) | def my_layer_norm(feat):
class AOTBlock (line 170) | class AOTBlock(nn.Module):
method __init__ (line 171) | def __init__(self, dim, rates = [2, 4, 8, 16]):
method forward (line 188) | def forward(self, x):
class ResBlockDis (line 196) | class ResBlockDis(nn.Module):
method __init__ (line 197) | def __init__(self, in_planes, planes, stride=1):
method forward (line 213) | def forward(self, x):
class Discriminator (line 219) | class Discriminator(nn.Module):
method __init__ (line 220) | def __init__(self, in_ch = 3, in_planes = 64, blocks = [2, 2, 2], alph...
method forward (line 236) | def forward(self, x):
class AOTGenerator (line 240) | class AOTGenerator(nn.Module):
method __init__ (line 241) | def __init__(self, in_ch = 4, out_ch = 3, ch = 32, alpha = 0.0):
method forward (line 266) | def forward(self, img, mask):
function test (line 276) | def test():
FILE: manga_translator/inpainting/inpainting_attn.py
function relu_nf (line 8) | def relu_nf(x):
function gelu_nf (line 11) | def gelu_nf(x):
function silu_nf (line 14) | def silu_nf(x):
class LambdaLayer (line 17) | class LambdaLayer(nn.Module):
method __init__ (line 18) | def __init__(self, f):
method forward (line 22) | def forward(self, x):
class ScaledWSConv2d (line 25) | class ScaledWSConv2d(nn.Conv2d):
method __init__ (line 27) | def __init__(self, in_channels, out_channels, kernel_size,
method get_weight (line 42) | def get_weight(self):
method forward (line 51) | def forward(self, x):
class ScaledWSTransposeConv2d (line 56) | class ScaledWSTransposeConv2d(nn.ConvTranspose2d):
method __init__ (line 58) | def __init__(self, in_channels: int,
method get_weight (line 77) | def get_weight(self):
method forward (line 86) | def forward(self, x, output_size: Optional[List[int]] = None):
class GatedWSConvPadded (line 92) | class GatedWSConvPadded(nn.Module):
method __init__ (line 93) | def __init__(self, in_ch, out_ch, ks, stride = 1, dilation = 1):
method forward (line 101) | def forward(self, x):
class GatedWSTransposeConvPadded (line 107) | class GatedWSTransposeConvPadded(nn.Module):
method __init__ (line 108) | def __init__(self, in_ch, out_ch, ks, stride = 1):
method forward (line 115) | def forward(self, x):
class ResBlock (line 120) | class ResBlock(nn.Module):
method __init__ (line 121) | def __init__(self, ch, alpha = 0.2, beta = 1.0, dilation = 1):
method forward (line 128) | def forward(self, x):
class GlobalAttention (line 136) | class GlobalAttention(nn.Module):
method __init__ (line 139) | def __init__(self, in_dim):
method forward (line 150) | def forward(self, a, b, c):
class CoarseGenerator (line 168) | class CoarseGenerator(nn.Module):
method __init__ (line 169) | def __init__(self, in_ch = 4, out_ch = 3, ch = 32, alpha = 0.2):
method forward (line 230) | def forward(self, img, mask):
class InpaintingVanilla (line 244) | class InpaintingVanilla(nn.Module):
method __init__ (line 245) | def __init__(self):
method forward (line 249) | def forward(self, x, mask):
FILE: manga_translator/inpainting/inpainting_lama.py
class LamaInpainter (line 17) | class LamaInpainter(LamaMPEInpainter):
method _load (line 26) | async def _load(self, device: str):
class DepthWiseSeparableConv (line 36) | class DepthWiseSeparableConv(nn.Module):
method __init__ (line 37) | def __init__(self, in_dim, out_dim, *args, **kwargs):
method forward (line 46) | def forward(self, x):
class MultidilatedConv (line 51) | class MultidilatedConv(nn.Module):
method __init__ (line 52) | def __init__(self, in_dim, out_dim, kernel_size, dilation_num=3, comb_...
method forward (line 118) | def forward(self, x):
class BaseDiscriminator (line 145) | class BaseDiscriminator(nn.Module):
method forward (line 147) | def forward(self, x: torch.Tensor) -> Tuple[torch.Tensor, List[torch.T...
function get_conv_block_ctor (line 155) | def get_conv_block_ctor(kind='default'):
function get_norm_layer (line 167) | def get_norm_layer(kind='bn'):
function get_activation (line 177) | def get_activation(kind='tanh'):
class LearnableSpatialTransformWrapper (line 186) | class LearnableSpatialTransformWrapper(nn.Module):
method __init__ (line 187) | def __init__(self, impl, pad_coef=0.5, angle_init_range=80, train_angl...
method forward (line 195) | def forward(self, x):
method transform (line 205) | def transform(self, x):
method inverse_transform (line 212) | def inverse_transform(self, y_padded_rotated, orig_x):
class FFCSE_block (line 221) | class FFCSE_block(nn.Module):
method __init__ (line 223) | def __init__(self, channels, ratio_g):
method forward (line 239) | def forward(self, x):
class SELayer (line 253) | class SELayer(nn.Module):
method __init__ (line 254) | def __init__(self, channel, reduction=16):
method forward (line 264) | def forward(self, x):
class FourierUnit (line 271) | class FourierUnit(nn.Module):
method __init__ (line 273) | def __init__(self, in_channels, out_channels, groups=1, spatial_scale_...
method forward (line 298) | def forward(self, x):
class SpectralTransform (line 338) | class SpectralTransform(nn.Module):
method __init__ (line 340) | def __init__(self, in_channels, out_channels, stride=1, groups=1, enab...
method forward (line 364) | def forward(self, x):
class FFC (line 388) | class FFC(nn.Module):
method __init__ (line 390) | def __init__(self, in_channels, out_channels, kernel_size,
method forward (line 427) | def forward(self, x):
class FFC_BN_ACT (line 450) | class FFC_BN_ACT(nn.Module):
method __init__ (line 452) | def __init__(self, in_channels, out_channels,
method forward (line 473) | def forward(self, x):
class FFCResnetBlock (line 480) | class FFCResnetBlock(nn.Module):
method __init__ (line 481) | def __init__(self, dim, padding_type, norm_layer, activation_layer=nn....
method forward (line 499) | def forward(self, x):
class ConcatTupleLayer (line 517) | class ConcatTupleLayer(nn.Module):
method forward (line 518) | def forward(self, x):
class FFCResNetGenerator (line 527) | class FFCResNetGenerator(nn.Module):
method __init__ (line 528) | def __init__(self, input_nc, output_nc, ngf=64, n_downsampling=3, n_bl...
method forward (line 588) | def forward(self, input, mask = None):
class FFCNLayerDiscriminator (line 594) | class FFCNLayerDiscriminator(BaseDiscriminator):
method __init__ (line 595) | def __init__(self, input_nc, ndf=64, n_layers=3, norm_layer=nn.BatchNo...
method get_all_activations (line 640) | def get_all_activations(self, x):
method forward (line 647) | def forward(self, x):
function get_generator (line 659) | def get_generator(n_blocks: int = 9):
function get_discriminator (line 677) | def get_discriminator():
function test_model (line 692) | def test_model():
FILE: manga_translator/inpainting/inpainting_lama_mpe.py
class LamaMPEInpainter (line 26) | class LamaMPEInpainter(OfflineInpainter):
method __init__ (line 40) | def __init__(self, *args, **kwargs):
method _load (line 46) | async def _load(self, device: str):
method _unload (line 53) | async def _unload(self):
method _infer (line 56) | async def _infer(self, image: np.ndarray, mask: np.ndarray, config: In...
class LamaLargeInpainter (line 121) | class LamaLargeInpainter(LamaMPEInpainter):
method _load (line 131) | async def _load(self, device: str):
function set_requires_grad (line 140) | def set_requires_grad(module, value):
function get_activation (line 144) | def get_activation(kind='tanh'):
class FFCSE_block (line 154) | class FFCSE_block(nn.Module):
method __init__ (line 156) | def __init__(self, channels, ratio_g):
method forward (line 172) | def forward(self, x):
class FourierUnit (line 187) | class FourierUnit(nn.Module):
method __init__ (line 189) | def __init__(self, in_channels, out_channels, groups=1, spatial_scale_...
method forward (line 214) | def forward(self, x):
class SpectralTransform (line 260) | class SpectralTransform(nn.Module):
method __init__ (line 262) | def __init__(self, in_channels, out_channels, stride=1, groups=1, enab...
method forward (line 286) | def forward(self, x):
class FFC (line 310) | class FFC(nn.Module):
method __init__ (line 312) | def __init__(self, in_channels, out_channels, kernel_size,
method forward (line 349) | def forward(self, x):
class FFC_BN_ACT (line 372) | class FFC_BN_ACT(nn.Module):
method __init__ (line 374) | def __init__(self, in_channels, out_channels,
method forward (line 395) | def forward(self, x):
class FFCResnetBlock (line 402) | class FFCResnetBlock(nn.Module):
method __init__ (line 403) | def __init__(self, dim, padding_type, norm_layer, activation_layer=nn....
method forward (line 421) | def forward(self, x):
class MaskedSinusoidalPositionalEmbedding (line 439) | class MaskedSinusoidalPositionalEmbedding(nn.Embedding):
method __init__ (line 442) | def __init__(self, num_embeddings: int, embedding_dim: int):
method _init_weight (line 447) | def _init_weight(out: nn.Parameter):
method forward (line 464) | def forward(self, input_ids):
class MultiLabelEmbedding (line 469) | class MultiLabelEmbedding(nn.Module):
method __init__ (line 470) | def __init__(self, num_positions: int, embedding_dim: int):
method reset_parameters (line 475) | def reset_parameters(self):
method forward (line 478) | def forward(self, input_ids):
class NLayerDiscriminator (line 484) | class NLayerDiscriminator(nn.Module):
method __init__ (line 485) | def __init__(self, input_nc=3, ndf=64, n_layers=4, norm_layer=nn.Batch...
method get_all_activations (line 523) | def get_all_activations(self, x):
method forward (line 530) | def forward(self, x):
class ConcatTupleLayer (line 535) | class ConcatTupleLayer(nn.Module):
method forward (line 536) | def forward(self, x):
class FFCResNetGenerator (line 545) | class FFCResNetGenerator(nn.Module):
method __init__ (line 546) | def __init__(self, input_nc=4, output_nc=3, ngf=64, n_downsampling=3, ...
method forward (line 603) | def forward(self, img, mask, rel_pos=None, direct=None) -> Tensor:
class MPE (line 616) | class MPE(nn.Module):
method __init__ (line 617) | def __init__(self):
method forward (line 625) | def forward(self, rel_pos=None, direct=None):
class LamaFourier (line 636) | class LamaFourier:
method __init__ (line 637) | def __init__(self, build_discriminator=True, use_mpe=False, large_arch...
method train_generator (line 668) | def train_generator(self):
method train_discriminator (line 679) | def train_discriminator(self):
method to (line 690) | def to(self, device):
method eval (line 698) | def eval(self):
method cuda (line 705) | def cuda(self):
method __call__ (line 713) | def __call__(self, img: Tensor, mask: Tensor, rel_pos=None, direct=None):
method load_masked_position_encoding (line 751) | def load_masked_position_encoding(self, mask):
function load_lama_mpe (line 818) | def load_lama_mpe(model_path, device, use_mpe: bool = True, large_arch: ...
FILE: manga_translator/inpainting/inpainting_sd.py
function get_state_dict (line 19) | def get_state_dict(d):
function load_state_dict (line 22) | def load_state_dict(ckpt_path, location='cpu'):
function create_model (line 33) | def create_model(config_path):
function load_ldm_sd (line 39) | def load_ldm_sd(model, path) :
class StableDiffusionInpainter (line 46) | class StableDiffusionInpainter(OfflineInpainter):
method __init__ (line 65) | def __init__(self, *args, **kwargs):
method _load (line 69) | async def _load(self, device: str):
method _unload (line 78) | async def _unload(self):
method _infer (line 82) | async def _infer(self, image: np.ndarray, mask: np.ndarray, inpainting...
FILE: manga_translator/inpainting/ldm/data/util.py
class AddMiDaS (line 6) | class AddMiDaS(object):
method __init__ (line 7) | def __init__(self, model_type):
method pt2np (line 11) | def pt2np(self, x):
method np2pt (line 15) | def np2pt(self, x):
method __call__ (line 19) | def __call__(self, sample):
FILE: manga_translator/inpainting/ldm/models/autoencoder.py
class VQModel (line 12) | class VQModel(pl.LightningModule):
method __init__ (line 13) | def __init__(self,
method ema_scope (line 62) | def ema_scope(self, context=None):
method init_from_ckpt (line 76) | def init_from_ckpt(self, path, ignore_keys=list()):
method on_train_batch_end (line 90) | def on_train_batch_end(self, *args, **kwargs):
method encode (line 94) | def encode(self, x):
method encode_to_prequant (line 100) | def encode_to_prequant(self, x):
method decode (line 105) | def decode(self, quant):
method decode_code (line 110) | def decode_code(self, code_b):
method forward (line 115) | def forward(self, input, return_pred_indices=False):
method get_input (line 122) | def get_input(self, batch, k):
method training_step (line 140) | def training_step(self, batch, batch_idx, optimizer_idx):
method validation_step (line 162) | def validation_step(self, batch, batch_idx):
method _validation_step (line 168) | def _validation_step(self, batch, batch_idx, suffix=""):
method configure_optimizers (line 195) | def configure_optimizers(self):
method get_last_layer (line 228) | def get_last_layer(self):
method log_images (line 231) | def log_images(self, batch, only_inputs=False, plot_ema=False, **kwargs):
method to_rgb (line 253) | def to_rgb(self, x):
class VQModelInterface (line 261) | class VQModelInterface(VQModel):
method __init__ (line 262) | def __init__(self, embed_dim, *args, **kwargs):
method encode (line 266) | def encode(self, x):
method decode (line 271) | def decode(self, h, force_not_quantize=False):
class AutoencoderKL (line 281) | class AutoencoderKL(pl.LightningModule):
method __init__ (line 282) | def __init__(self,
method init_from_ckpt (line 320) | def init_from_ckpt(self, path, ignore_keys=list()):
method ema_scope (line 332) | def ema_scope(self, context=None):
method on_train_batch_end (line 346) | def on_train_batch_end(self, *args, **kwargs):
method encode (line 350) | def encode(self, x):
method decode (line 356) | def decode(self, z):
method forward (line 361) | def forward(self, input, sample_posterior=True):
method get_input (line 370) | def get_input(self, batch, k):
method training_step (line 377) | def training_step(self, batch, batch_idx, optimizer_idx):
method validation_step (line 398) | def validation_step(self, batch, batch_idx):
method _validation_step (line 404) | def _validation_step(self, batch, batch_idx, postfix=""):
method configure_optimizers (line 418) | def configure_optimizers(self):
method get_last_layer (line 431) | def get_last_layer(self):
method log_images (line 435) | def log_images(self, batch, only_inputs=False, log_ema=False, **kwargs):
method to_rgb (line 460) | def to_rgb(self, x):
class IdentityFirstStage (line 469) | class IdentityFirstStage(torch.nn.Module):
method __init__ (line 470) | def __init__(self, *args, vq_interface=False, **kwargs):
method encode (line 474) | def encode(self, x, *args, **kwargs):
method decode (line 477) | def decode(self, x, *args, **kwargs):
method quantize (line 480) | def quantize(self, x, *args, **kwargs):
method forward (line 485) | def forward(self, x, *args, **kwargs):
FILE: manga_translator/inpainting/ldm/models/diffusion/ddim.py
class DDIMSampler (line 10) | class DDIMSampler(object):
method __init__ (line 11) | def __init__(self, model, schedule="linear", **kwargs):
method register_buffer (line 17) | def register_buffer(self, name, attr):
method make_schedule (line 23) | def make_schedule(self, ddim_num_steps, ddim_discretize="uniform", ddi...
method sample (line 55) | def sample(self,
method ddim_sampling (line 123) | def ddim_sampling(self, cond, shape,
method p_sample_ddim (line 181) | def p_sample_ddim(self, x, c, t, index, repeat_noise=False, use_origin...
method encode (line 254) | def encode(self, x0, c, t_enc, use_original_steps=False, return_interm...
method stochastic_encode (line 301) | def stochastic_encode(self, x0, t, use_original_steps=False, noise=None):
method decode (line 317) | def decode(self, x_latent, cond, t_start, unconditional_guidance_scale...
FILE: manga_translator/inpainting/ldm/models/diffusion/ddpm.py
function disabled_train (line 36) | def disabled_train(self, mode=True):
function uniform_on_device (line 42) | def uniform_on_device(r1, r2, shape, device):
class DDPM (line 46) | class DDPM(pl.LightningModule):
method __init__ (line 48) | def __init__(self,
method register_schedule (line 138) | def register_schedule(self, given_betas=None, beta_schedule="linear", ...
method ema_scope (line 195) | def ema_scope(self, context=None):
method init_from_ckpt (line 210) | def init_from_ckpt(self, path, ignore_keys=list(), only_model=False):
method q_mean_variance (line 272) | def q_mean_variance(self, x_start, t):
method predict_start_from_noise (line 284) | def predict_start_from_noise(self, x_t, t, noise):
method predict_start_from_z_and_v (line 290) | def predict_start_from_z_and_v(self, x_t, t, v):
method predict_eps_from_z_and_v (line 298) | def predict_eps_from_z_and_v(self, x_t, t, v):
method q_posterior (line 304) | def q_posterior(self, x_start, x_t, t):
method p_mean_variance (line 313) | def p_mean_variance(self, x, t, clip_denoised: bool):
method p_sample (line 326) | def p_sample(self, x, t, clip_denoised=True, repeat_noise=False):
method p_sample_loop (line 335) | def p_sample_loop(self, shape, return_intermediates=False):
method sample (line 350) | def sample(self, batch_size=16, return_intermediates=False):
method q_sample (line 356) | def q_sample(self, x_start, t, noise=None):
method get_v (line 361) | def get_v(self, x, noise, t):
method get_loss (line 367) | def get_loss(self, pred, target, mean=True):
method p_losses (line 382) | def p_losses(self, x_start, t, noise=None):
method forward (line 413) | def forward(self, x, *args, **kwargs):
method get_input (line 419) | def get_input(self, batch, k):
method shared_step (line 427) | def shared_step(self, batch):
method training_step (line 432) | def training_step(self, batch, batch_idx):
method validation_step (line 457) | def validation_step(self, batch, batch_idx):
method on_train_batch_end (line 465) | def on_train_batch_end(self, *args, **kwargs):
method _get_rows_from_list (line 469) | def _get_rows_from_list(self, samples):
method log_images (line 477) | def log_images(self, batch, N=8, n_row=2, sample=True, return_keys=Non...
method configure_optimizers (line 514) | def configure_optimizers(self):
class LatentDiffusion (line 523) | class LatentDiffusion(DDPM):
method __init__ (line 526) | def __init__(self,
method make_cond_schedule (line 584) | def make_cond_schedule(self, ):
method on_train_batch_start (line 591) | def on_train_batch_start(self, batch, batch_idx, dataloader_idx):
method register_schedule (line 606) | def register_schedule(self,
method instantiate_first_stage (line 615) | def instantiate_first_stage(self, config):
method instantiate_cond_stage (line 622) | def instantiate_cond_stage(self, config):
method _get_denoise_row_from_list (line 643) | def _get_denoise_row_from_list(self, samples, desc='', force_no_decode...
method get_first_stage_encoding (line 655) | def get_first_stage_encoding(self, encoder_posterior):
method get_learned_conditioning (line 664) | def get_learned_conditioning(self, c):
method meshgrid (line 677) | def meshgrid(self, h, w):
method delta_border (line 684) | def delta_border(self, h, w):
method get_weighting (line 698) | def get_weighting(self, h, w, Ly, Lx, device):
method get_fold_unfold (line 714) | def get_fold_unfold(self, x, kernel_size, stride, uf=1, df=1): # todo...
method get_input (line 767) | def get_input(self, batch, k, return_first_stage_outputs=False, force_...
method decode_first_stage (line 820) | def decode_first_stage(self, z, predict_cids=False, force_not_quantize...
method decode_first_stage_with_grad (line 830) | def decode_first_stage_with_grad(self, z, predict_cids=False, force_no...
method encode_first_stage (line 893) | def encode_first_stage(self, x):
method shared_step (line 896) | def shared_step(self, batch, **kwargs):
method forward (line 901) | def forward(self, x, c, *args, **kwargs):
method apply_model (line 912) | def apply_model(self, x_noisy, t, cond, return_ids=False):
method _predict_eps_from_xstart (line 929) | def _predict_eps_from_xstart(self, x_t, t, pred_xstart):
method _prior_bpd (line 933) | def _prior_bpd(self, x_start):
method p_losses (line 947) | def p_losses(self, x_start, cond, t, noise=None):
method p_mean_variance (line 984) | def p_mean_variance(self, x, c, t, clip_denoised: bool, return_codeboo...
method p_sample (line 1016) | def p_sample(self, x, c, t, clip_denoised=False, repeat_noise=False,
method progressive_denoising (line 1047) | def progressive_denoising(self, cond, shape, verbose=True, callback=No...
method p_sample_loop (line 1103) | def p_sample_loop(self, cond, shape, return_intermediates=False,
method sample (line 1154) | def sample(self, cond, batch_size=16, return_intermediates=False, x_T=...
method sample_log (line 1172) | def sample_log(self, cond, batch_size, ddim, ddim_steps, **kwargs):
method get_unconditional_conditioning (line 1186) | def get_unconditional_conditioning(self, batch_size, null_label=None):
method log_images (line 1211) | def log_images(self, batch, N=8, n_row=4, sample=True, ddim_steps=50, ...
method configure_optimizers (line 1340) | def configure_optimizers(self):
method to_rgb (line 1365) | def to_rgb(self, x):
class DiffusionWrapper (line 1374) | class DiffusionWrapper(pl.LightningModule):
method __init__ (line 1375) | def __init__(self, diff_model_config, conditioning_key):
method forward (line 1382) | def forward(self, x, t, c_concat: list = None, c_crossattn: list = Non...
class LatentUpscaleDiffusion (line 1416) | class LatentUpscaleDiffusion(LatentDiffusion):
method __init__ (line 1417) | def __init__(self, *args, low_scale_config, low_scale_key="LR", noise_...
method instantiate_low_stage (line 1425) | def instantiate_low_stage(self, config):
method get_input (line 1433) | def get_input(self, batch, k, cond_key=None, bs=None, log_mode=False):
method log_images (line 1455) | def log_images(self, batch, N=8, n_row=4, sample=True, ddim_steps=200,...
class LatentFinetuneDiffusion (line 1554) | class LatentFinetuneDiffusion(LatentDiffusion):
method __init__ (line 1560) | def __init__(self,
method init_from_ckpt (line 1583) | def init_from_ckpt(self, path, ignore_keys=list(), only_model=False):
method log_images (line 1615) | def log_images(self, batch, N=8, n_row=4, sample=True, ddim_steps=200,...
class LatentInpaintDiffusion (line 1696) | class LatentInpaintDiffusion(LatentFinetuneDiffusion):
method __init__ (line 1703) | def __init__(self,
method get_input (line 1713) | def get_input(self, batch, k, cond_key=None, bs=None, return_first_sta...
method log_images (line 1739) | def log_images(self, *args, **kwargs):
class LatentDepth2ImageDiffusion (line 1746) | class LatentDepth2ImageDiffusion(LatentFinetuneDiffusion):
method __init__ (line 1751) | def __init__(self, depth_stage_config, concat_keys=("midas_in",), *arg...
method get_input (line 1757) | def get_input(self, batch, k, cond_key=None, bs=None, return_first_sta...
method log_images (line 1790) | def log_images(self, *args, **kwargs):
class LatentUpscaleFinetuneDiffusion (line 1799) | class LatentUpscaleFinetuneDiffusion(LatentFinetuneDiffusion):
method __init__ (line 1803) | def __init__(self, concat_keys=("lr",), reshuffle_patch_size=None,
method instantiate_low_stage (line 1814) | def instantiate_low_stage(self, config):
method get_input (line 1822) | def get_input(self, batch, k, cond_key=None, bs=None, return_first_sta...
method log_images (line 1856) | def log_images(self, *args, **kwargs):
FILE: manga_translator/inpainting/ldm/models/diffusion/dpm_solver/dpm_solver.py
class NoiseScheduleVP (line 7) | class NoiseScheduleVP:
method __init__ (line 8) | def __init__(
method marginal_log_mean_coeff (line 106) | def marginal_log_mean_coeff(self, t):
method marginal_alpha (line 120) | def marginal_alpha(self, t):
method marginal_std (line 126) | def marginal_std(self, t):
method marginal_lambda (line 132) | def marginal_lambda(self, t):
method inverse_lambda (line 140) | def inverse_lambda(self, lamb):
function model_wrapper (line 161) | def model_wrapper(
class DPM_Solver (line 319) | class DPM_Solver:
method __init__ (line 320) | def __init__(self, model_fn, noise_schedule, predict_x0=False, thresho...
method noise_prediction_fn (line 346) | def noise_prediction_fn(self, x, t):
method data_prediction_fn (line 352) | def data_prediction_fn(self, x, t):
method model_fn (line 367) | def model_fn(self, x, t):
method get_time_steps (line 376) | def get_time_steps(self, skip_type, t_T, t_0, N, device):
method get_orders_and_timesteps_for_singlestep_solver (line 405) | def get_orders_and_timesteps_for_singlestep_solver(self, steps, order,...
method denoise_to_zero_fn (line 463) | def denoise_to_zero_fn(self, x, s):
method dpm_solver_first_update (line 469) | def dpm_solver_first_update(self, x, s, t, model_s=None, return_interm...
method singlestep_dpm_solver_second_update (line 515) | def singlestep_dpm_solver_second_update(self, x, s, t, r1=0.5, model_s...
method singlestep_dpm_solver_third_update (line 599) | def singlestep_dpm_solver_third_update(self, x, s, t, r1=1. / 3., r2=2...
method multistep_dpm_solver_second_update (line 723) | def multistep_dpm_solver_second_update(self, x, model_prev_list, t_pre...
method multistep_dpm_solver_third_update (line 780) | def multistep_dpm_solver_third_update(self, x, model_prev_list, t_prev...
method singlestep_dpm_solver_update (line 827) | def singlestep_dpm_solver_update(self, x, s, t, order, return_intermed...
method multistep_dpm_solver_update (line 855) | def multistep_dpm_solver_update(self, x, model_prev_list, t_prev_list,...
method dpm_solver_adaptive (line 878) | def dpm_solver_adaptive(self, x, order, t_T, t_0, h_init=0.05, atol=0....
method sample (line 939) | def sample(self, x, steps=20, t_start=None, t_end=None, order=3, skip_...
function interpolate_fn (line 1104) | def interpolate_fn(x, xp, yp):
function expand_dims (line 1145) | def expand_dims(v, dims):
FILE: manga_translator/inpainting/ldm/models/diffusion/dpm_solver/sampler.py
class DPMSolverSampler (line 13) | class DPMSolverSampler(object):
method __init__ (line 14) | def __init__(self, model, **kwargs):
method register_buffer (line 20) | def register_buffer(self, name, attr):
method sample (line 27) | def sample(self,
FILE: manga_translator/inpainting/ldm/models/diffusion/plms.py
class PLMSSampler (line 12) | class PLMSSampler(object):
method __init__ (line 13) | def __init__(self, model, schedule="linear", **kwargs):
method register_buffer (line 19) | def register_buffer(self, name, attr):
method make_schedule (line 25) | def make_schedule(self, ddim_num_steps, ddim_discretize="uniform", ddi...
method sample (line 59) | def sample(self,
method plms_sampling (line 118) | def plms_sampling(self, cond, shape,
method p_sample_plms (line 178) | def p_sample_plms(self, x, c, t, index, repeat_noise=False, use_origin...
FILE: manga_translator/inpainting/ldm/models/diffusion/sampling_util.py
function append_dims (line 5) | def append_dims(x, target_dims):
function norm_thresholding (line 14) | def norm_thresholding(x0, value):
function spatial_norm_thresholding (line 19) | def spatial_norm_thresholding(x0, value):
FILE: manga_translator/inpainting/ldm/modules/attention.py
function exists (line 23) | def exists(val):
function uniq (line 27) | def uniq(arr):
function default (line 31) | def default(val, d):
function max_neg_value (line 37) | def max_neg_value(t):
function init_ (line 41) | def init_(tensor):
class GEGLU (line 49) | class GEGLU(nn.Module):
method __init__ (line 50) | def __init__(self, dim_in, dim_out):
method forward (line 54) | def forward(self, x):
class FeedForward (line 59) | class FeedForward(nn.Module):
method __init__ (line 60) | def __init__(self, dim, dim_out=None, mult=4, glu=False, dropout=0.):
method forward (line 75) | def forward(self, x):
function zero_module (line 79) | def zero_module(module):
function Normalize (line 88) | def Normalize(in_channels):
class SpatialSelfAttention (line 92) | class SpatialSelfAttention(nn.Module):
method __init__ (line 93) | def __init__(self, in_channels):
method forward (line 119) | def forward(self, x):
class CrossAttention (line 145) | class CrossAttention(nn.Module):
method __init__ (line 146) | def __init__(self, query_dim, context_dim=None, heads=8, dim_head=64, ...
method forward (line 163) | def forward(self, x, context=None, mask=None):
class MemoryEfficientCrossAttention (line 197) | class MemoryEfficientCrossAttention(nn.Module):
method __init__ (line 199) | def __init__(self, query_dim, context_dim=None, heads=8, dim_head=64, ...
method forward (line 216) | def forward(self, x, context=None, mask=None):
class BasicTransformerBlock (line 246) | class BasicTransformerBlock(nn.Module):
method __init__ (line 251) | def __init__(self, dim, n_heads, d_head, dropout=0., context_dim=None,...
method forward (line 268) | def forward(self, x, context=None):
method _forward (line 271) | def _forward(self, x, context=None):
class SpatialTransformer (line 278) | class SpatialTransformer(nn.Module):
method __init__ (line 287) | def __init__(self, in_channels, n_heads, d_head,
method forward (line 321) | def forward(self, x, context=None):
FILE: manga_translator/inpainting/ldm/modules/diffusionmodules/model.py
function get_timestep_embedding (line 20) | def get_timestep_embedding(timesteps, embedding_dim):
function nonlinearity (line 41) | def nonlinearity(x):
function Normalize (line 46) | def Normalize(in_channels, num_groups=32):
class Upsample (line 50) | class Upsample(nn.Module):
method __init__ (line 51) | def __init__(self, in_channels, with_conv):
method forward (line 61) | def forward(self, x):
class Downsample (line 68) | class Downsample(nn.Module):
method __init__ (line 69) | def __init__(self, in_channels, with_conv):
method forward (line 80) | def forward(self, x):
class ResnetBlock (line 90) | class ResnetBlock(nn.Module):
method __init__ (line 91) | def __init__(self, *, in_channels, out_channels=None, conv_shortcut=Fa...
method forward (line 129) | def forward(self, x, temb):
class AttnBlock (line 152) | class AttnBlock(nn.Module):
method __init__ (line 153) | def __init__(self, in_channels):
method forward (line 179) | def forward(self, x):
class MemoryEfficientAttnBlock (line 205) | class MemoryEfficientAttnBlock(nn.Module):
method __init__ (line 212) | def __init__(self, in_channels):
method forward (line 239) | def forward(self, x):
class MemoryEfficientCrossAttentionWrapper (line 271) | class MemoryEfficientCrossAttentionWrapper(MemoryEfficientCrossAttention):
method forward (line 272) | def forward(self, x, context=None, mask=None):
function make_attn (line 280) | def make_attn(in_channels, attn_type="vanilla", attn_kwargs=None):
class Model (line 300) | class Model(nn.Module):
method __init__ (line 301) | def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks,
method forward (line 400) | def forward(self, x, t=None, context=None):
method get_last_layer (line 448) | def get_last_layer(self):
class Encoder (line 452) | class Encoder(nn.Module):
method __init__ (line 453) | def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks,
method forward (line 518) | def forward(self, x):
class Decoder (line 546) | class Decoder(nn.Module):
method __init__ (line 547) | def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks,
method forward (line 619) | def forward(self, z):
class SimpleDecoder (line 655) | class SimpleDecoder(nn.Module):
method __init__ (line 656) | def __init__(self, in_channels, out_channels, *args, **kwargs):
method forward (line 678) | def forward(self, x):
class UpsampleDecoder (line 691) | class UpsampleDecoder(nn.Module):
method __init__ (line 692) | def __init__(self, in_channels, out_channels, ch, num_res_blocks, reso...
method forward (line 725) | def forward(self, x):
class LatentRescaler (line 739) | class LatentRescaler(nn.Module):
method __init__ (line 740) | def __init__(self, factor, in_channels, mid_channels, out_channels, de...
method forward (line 764) | def forward(self, x):
class MergedRescaleEncoder (line 776) | class MergedRescaleEncoder(nn.Module):
method __init__ (line 777) | def __init__(self, in_channels, ch, resolution, out_ch, num_res_blocks,
method forward (line 789) | def forward(self, x):
class MergedRescaleDecoder (line 795) | class MergedRescaleDecoder(nn.Module):
method __init__ (line 796) | def __init__(self, z_channels, out_ch, resolution, num_res_blocks, att...
method forward (line 806) | def forward(self, x):
class Upsampler (line 812) | class Upsampler(nn.Module):
method __init__ (line 813) | def __init__(self, in_size, out_size, in_channels, out_channels, ch_mu...
method forward (line 825) | def forward(self, x):
class Resize (line 831) | class Resize(nn.Module):
method __init__ (line 832) | def __init__(self, in_channels=None, learned=False, mode="bilinear"):
method forward (line 847) | def forward(self, x, scale_factor=1.0):
FILE: manga_translator/inpainting/ldm/modules/diffusionmodules/openaimodel.py
function convert_module_to_f16 (line 23) | def convert_module_to_f16(x):
function convert_module_to_f32 (line 26) | def convert_module_to_f32(x):
class AttentionPool2d (line 31) | class AttentionPool2d(nn.Module):
method __init__ (line 36) | def __init__(
method forward (line 50) | def forward(self, x):
class TimestepBlock (line 61) | class TimestepBlock(nn.Module):
method forward (line 67) | def forward(self, x, emb):
class TimestepEmbedSequential (line 73) | class TimestepEmbedSequential(nn.Sequential, TimestepBlock):
method forward (line 79) | def forward(self, x, emb, context=None):
class Upsample (line 90) | class Upsample(nn.Module):
method __init__ (line 99) | def __init__(self, channels, use_conv, dims=2, out_channels=None, padd...
method forward (line 108) | def forward(self, x):
class TransposedUpsample (line 120) | class TransposedUpsample(nn.Module):
method __init__ (line 122) | def __init__(self, channels, out_channels=None, ks=5):
method forward (line 129) | def forward(self,x):
class Downsample (line 133) | class Downsample(nn.Module):
method __init__ (line 142) | def __init__(self, channels, use_conv, dims=2, out_channels=None,paddi...
method forward (line 157) | def forward(self, x):
class ResBlock (line 162) | class ResBlock(TimestepBlock):
method __init__ (line 178) | def __init__(
method forward (line 242) | def forward(self, x, emb):
method _forward (line 254) | def _forward(self, x, emb):
class AttentionBlock (line 277) | class AttentionBlock(nn.Module):
method __init__ (line 284) | def __init__(
method forward (line 313) | def forward(self, x):
method _forward (line 317) | def _forward(self, x):
function count_flops_attn (line 326) | def count_flops_attn(model, _x, y):
class QKVAttentionLegacy (line 346) | class QKVAttentionLegacy(nn.Module):
method __init__ (line 351) | def __init__(self, n_heads):
method forward (line 355) | def forward(self, qkv):
method count_flops (line 374) | def count_flops(model, _x, y):
class QKVAttention (line 378) | class QKVAttention(nn.Module):
method __init__ (line 383) | def __init__(self, n_heads):
method forward (line 387) | def forward(self, qkv):
method count_flops (line 408) | def count_flops(model, _x, y):
class UNetModel (line 412) | class UNetModel(nn.Module):
method __init__ (line 442) | def __init__(
method convert_to_fp16 (line 738) | def convert_to_fp16(self):
method convert_to_fp32 (line 746) | def convert_to_fp32(self):
method forward (line 754) | def forward(self, x, timesteps=None, context=None, y=None,**kwargs):
FILE: manga_translator/inpainting/ldm/modules/diffusionmodules/upscaling.py
class AbstractLowScaleModel (line 10) | class AbstractLowScaleModel(nn.Module):
method __init__ (line 12) | def __init__(self, noise_schedule_config=None):
method register_schedule (line 17) | def register_schedule(self, beta_schedule="linear", timesteps=1000,
method q_sample (line 44) | def q_sample(self, x_start, t, noise=None):
method forward (line 49) | def forward(self, x):
method decode (line 52) | def decode(self, x):
class SimpleImageConcat (line 56) | class SimpleImageConcat(AbstractLowScaleModel):
method __init__ (line 58) | def __init__(self):
method forward (line 62) | def forward(self, x):
class ImageConcatWithNoiseAugmentation (line 67) | class ImageConcatWithNoiseAugmentation(AbstractLowScaleModel):
method __init__ (line 68) | def __init__(self, noise_schedule_config, max_noise_level=1000, to_cud...
method forward (line 72) | def forward(self, x, noise_level=None):
FILE: manga_translator/inpainting/ldm/modules/diffusionmodules/util.py
function make_beta_schedule (line 21) | def make_beta_schedule(schedule, n_timestep, linear_start=1e-4, linear_e...
function make_ddim_timesteps (line 46) | def make_ddim_timesteps(ddim_discr_method, num_ddim_timesteps, num_ddpm_...
function make_ddim_sampling_parameters (line 63) | def make_ddim_sampling_parameters(alphacums, ddim_timesteps, eta, verbos...
function betas_for_alpha_bar (line 77) | def betas_for_alpha_bar(num_diffusion_timesteps, alpha_bar, max_beta=0.9...
function extract_into_tensor (line 96) | def extract_into_tensor(a, t, x_shape):
function checkpoint (line 102) | def checkpoint(func, inputs, params, flag):
class CheckpointFunction (line 119) | class CheckpointFunction(torch.autograd.Function):
method forward (line 121) | def forward(ctx, run_function, length, *args):
method backward (line 133) | def backward(ctx, *output_grads):
function timestep_embedding (line 154) | def timestep_embedding(timesteps, dim, max_period=10000, repeat_only=Fal...
function zero_module (line 177) | def zero_module(module):
function scale_module (line 186) | def scale_module(module, scale):
function mean_flat (line 195) | def mean_flat(tensor):
function normalization (line 202) | def normalization(channels):
class SiLU (line 212) | class SiLU(nn.Module):
method forward (line 213) | def forward(self, x):
class GroupNorm32 (line 217) | class GroupNorm32(nn.GroupNorm):
method forward (line 218) | def forward(self, x):
function conv_nd (line 221) | def conv_nd(dims, *args, **kwargs):
function linear (line 234) | def linear(*args, **kwargs):
function avg_pool_nd (line 241) | def avg_pool_nd(dims, *args, **kwargs):
class HybridConditioner (line 254) | class HybridConditioner(nn.Module):
method __init__ (line 256) | def __init__(self, c_concat_config, c_crossattn_config):
method forward (line 261) | def forward(self, c_concat, c_crossattn):
function noise_like (line 267) | def noise_like(shape, device, repeat=False):
FILE: manga_translator/inpainting/ldm/modules/distributions/distributions.py
class AbstractDistribution (line 5) | class AbstractDistribution:
method sample (line 6) | def sample(self):
method mode (line 9) | def mode(self):
class DiracDistribution (line 13) | class DiracDistribution(AbstractDistribution):
method __init__ (line 14) | def __init__(self, value):
method sample (line 17) | def sample(self):
method mode (line 20) | def mode(self):
class DiagonalGaussianDistribution (line 24) | class DiagonalGaussianDistribution(object):
method __init__ (line 25) | def __init__(self, parameters, deterministic=False):
method sample (line 35) | def sample(self):
method kl (line 39) | def kl(self, other=None):
method nll (line 53) | def nll(self, sample, dims=[1,2,3]):
method mode (line 61) | def mode(self):
function normal_kl (line 65) | def normal_kl(mean1, logvar1, mean2, logvar2):
FILE: manga_translator/inpainting/ldm/modules/ema.py
class LitEma (line 5) | class LitEma(nn.Module):
method __init__ (line 6) | def __init__(self, model, decay=0.9999, use_num_updates=True):
method reset_num_updates (line 25) | def reset_num_updates(self):
method forward (line 29) | def forward(self, model):
method copy_to (line 50) | def copy_to(self, model):
method store (line 59) | def store(self, parameters):
method restore (line 68) | def restore(self, parameters):
FILE: manga_translator/inpainting/ldm/modules/encoders/modules.py
class AbstractEncoder (line 11) | class AbstractEncoder(nn.Module):
method __init__ (line 12) | def __init__(self):
method encode (line 15) | def encode(self, *args, **kwargs):
class IdentityEncoder (line 19) | class IdentityEncoder(AbstractEncoder):
method encode (line 21) | def encode(self, x):
class ClassEmbedder (line 25) | class ClassEmbedder(nn.Module):
method __init__ (line 26) | def __init__(self, embed_dim, n_classes=1000, key='class', ucg_rate=0.1):
method forward (line 33) | def forward(self, batch, key=None, disable_dropout=False):
method get_unconditional_conditioning (line 45) | def get_unconditional_conditioning(self, bs, device="cuda"):
function disabled_train (line 52) | def disabled_train(self, mode=True):
class FrozenT5Embedder (line 58) | class FrozenT5Embedder(AbstractEncoder):
method __init__ (line 60) | def __init__(self, version="google/t5-v1_1-large", device="cuda", max_...
method freeze (line 69) | def freeze(self):
method forward (line 75) | def forward(self, text):
method encode (line 84) | def encode(self, text):
class FrozenCLIPEmbedder (line 88) | class FrozenCLIPEmbedder(AbstractEncoder):
method __init__ (line 95) | def __init__(self, version="openai/clip-vit-large-patch14", device="cu...
method freeze (line 111) | def freeze(self):
method forward (line 117) | def forward(self, text):
method encode (line 130) | def encode(self, text):
class FrozenOpenCLIPEmbedder (line 134) | class FrozenOpenCLIPEmbedder(AbstractEncoder):
method __init__ (line 143) | def __init__(self, arch="ViT-H-14", version="laion2b_s32b_b79k", devic...
method freeze (line 163) | def freeze(self):
method forward (line 168) | def forward(self, text):
method encode_with_transformer (line 173) | def encode_with_transformer(self, text):
method text_transformer_forward (line 182) | def text_transformer_forward(self, x: torch.Tensor, attn_mask = None):
method encode (line 192) | def encode(self, text):
class FrozenCLIPT5Encoder (line 196) | class FrozenCLIPT5Encoder(AbstractEncoder):
method __init__ (line 197) | def __init__(self, clip_version="openai/clip-vit-large-patch14", t5_ve...
method encode (line 205) | def encode(self, text):
method forward (line 208) | def forward(self, text):
FILE: manga_translator/inpainting/ldm/modules/image_degradation/bsrgan.py
function modcrop_np (line 29) | def modcrop_np(img, sf):
function analytic_kernel (line 49) | def analytic_kernel(k):
function anisotropic_Gaussian (line 65) | def anisotropic_Gaussian(ksize=15, theta=np.pi, l1=6, l2=6):
function gm_blur_kernel (line 86) | def gm_blur_kernel(mean, cov, size=15):
function shift_pixel (line 99) | def shift_pixel(x, sf, upper_left=True):
function blur (line 128) | def blur(x, k):
function gen_kernel (line 145) | def gen_kernel(k_size=np.array([15, 15]), scale_factor=np.array([4, 4]),...
function fspecial_gaussian (line 187) | def fspecial_gaussian(hsize, sigma):
function fspecial_laplacian (line 201) | def fspecial_laplacian(alpha):
function fspecial (line 210) | def fspecial(filter_type, *args, **kwargs):
function bicubic_degradation (line 228) | def bicubic_degradation(x, sf=3):
function srmd_degradation (line 240) | def srmd_degradation(x, k, sf=3):
function dpsr_degradation (line 262) | def dpsr_degradation(x, k, sf=3):
function classical_degradation (line 284) | def classical_degradation(x, k, sf=3):
function add_sharpening (line 299) | def add_sharpening(img, weight=0.5, radius=50, threshold=10):
function add_blur (line 325) | def add_blur(img, sf=4):
function add_resize (line 339) | def add_resize(img, sf=4):
function add_Gaussian_noise (line 369) | def add_Gaussian_noise(img, noise_level1=2, noise_level2=25):
function add_speckle_noise (line 386) | def add_speckle_noise(img, noise_level1=2, noise_level2=25):
function add_Poisson_noise (line 404) | def add_Poisson_noise(img):
function add_JPEG_noise (line 418) | def add_JPEG_noise(img):
function random_crop (line 427) | def random_crop(lq, hq, sf=4, lq_patchsize=64):
function degradation_bsrgan (line 438) | def degradation_bsrgan(img, sf=4, lq_patchsize=72, isp_model=None):
function degradation_bsrgan_variant (line 530) | def degradation_bsrgan_variant(image, sf=4, isp_model=None):
function degradation_bsrgan_plus (line 617) | def degradation_bsrgan_plus(img, sf=4, shuffle_prob=0.5, use_sharp=True,...
FILE: manga_translator/inpainting/ldm/modules/image_degradation/bsrgan_light.py
function modcrop_np (line 28) | def modcrop_np(img, sf):
function analytic_kernel (line 48) | def analytic_kernel(k):
function anisotropic_Gaussian (line 64) | def anisotropic_Gaussian(ksize=15, theta=np.pi, l1=6, l2=6):
function gm_blur_kernel (line 85) | def gm_blur_kernel(mean, cov, size=15):
function shift_pixel (line 98) | def shift_pixel(x, sf, upper_left=True):
function blur (line 127) | def blur(x, k):
function gen_kernel (line 144) | def gen_kernel(k_size=np.array([15, 15]), scale_factor=np.array([4, 4]),...
function fspecial_gaussian (line 186) | def fspecial_gaussian(hsize, sigma):
function fspecial_laplacian (line 200) | def fspecial_laplacian(alpha):
function fspecial (line 209) | def fspecial(filter_type, *args, **kwargs):
function bicubic_degradation (line 227) | def bicubic_degradation(x, sf=3):
function srmd_degradation (line 239) | def srmd_degradation(x, k, sf=3):
function dpsr_degradation (line 261) | def dpsr_degradation(x, k, sf=3):
function classical_degradation (line 283) | def classical_degradation(x, k, sf=3):
function add_sharpening (line 298) | def add_sharpening(img, weight=0.5, radius=50, threshold=10):
function add_blur (line 324) | def add_blur(img, sf=4):
function add_resize (line 342) | def add_resize(img, sf=4):
function add_Gaussian_noise (line 372) | def add_Gaussian_noise(img, noise_level1=2, noise_level2=25):
function add_speckle_noise (line 389) | def add_speckle_noise(img, noise_level1=2, noise_level2=25):
function add_Poisson_noise (line 407) | def add_Poisson_noise(img):
function add_JPEG_noise (line 421) | def add_JPEG_noise(img):
function random_crop (line 430) | def random_crop(lq, hq, sf=4, lq_patchsize=64):
function degradation_bsrgan (line 441) | def degradation_bsrgan(img, sf=4, lq_patchsize=72, isp_model=None):
function degradation_bsrgan_variant (line 533) | def degradation_bsrgan_variant(image, sf=4, isp_model=None, up=False):
FILE: manga_translator/inpainting/ldm/modules/image_degradation/utils_image.py
function is_image_file (line 29) | def is_image_file(filename):
function get_timestamp (line 33) | def get_timestamp():
function imshow (line 37) | def imshow(x, title=None, cbar=False, figsize=None):
function surf (line 47) | def surf(Z, cmap='rainbow', figsize=None):
function get_image_paths (line 67) | def get_image_paths(dataroot):
function _get_paths_from_images (line 74) | def _get_paths_from_images(path):
function patches_from_image (line 93) | def patches_from_image(img, p_size=512, p_overlap=64, p_max=800):
function imssave (line 112) | def imssave(imgs, img_path):
function split_imageset (line 125) | def split_imageset(original_dataroot, target_dataroot, n_channels=3, p_s...
function mkdir (line 153) | def mkdir(path):
function mkdirs (line 158) | def mkdirs(paths):
function mkdir_and_rename (line 166) | def mkdir_and_rename(path):
function imread_uint (line 185) | def imread_uint(path, n_channels=3):
function imsave (line 203) | def imsave(img, img_path):
function imwrite (line 209) | def imwrite(img, img_path):
function read_img (line 220) | def read_img(path):
function uint2single (line 249) | def uint2single(img):
function single2uint (line 254) | def single2uint(img):
function uint162single (line 259) | def uint162single(img):
function single2uint16 (line 264) | def single2uint16(img):
function uint2tensor4 (line 275) | def uint2tensor4(img):
function uint2tensor3 (line 282) | def uint2tensor3(img):
function tensor2uint (line 289) | def tensor2uint(img):
function single2tensor3 (line 302) | def single2tensor3(img):
function single2tensor4 (line 307) | def single2tensor4(img):
function tensor2single (line 312) | def tensor2single(img):
function tensor2single3 (line 320) | def tensor2single3(img):
function single2tensor5 (line 329) | def single2tensor5(img):
function single32tensor5 (line 333) | def single32tensor5(img):
function single42tensor4 (line 337) | def single42tensor4(img):
function tensor2img (line 342) | def tensor2img(tensor, out_type=np.uint8, min_max=(0, 1)):
function augment_img (line 380) | def augment_img(img, mode=0):
function augment_img_tensor4 (line 401) | def augment_img_tensor4(img, mode=0):
function augment_img_tensor (line 422) | def augment_img_tensor(img, mode=0):
function augment_img_np3 (line 441) | def augment_img_np3(img, mode=0):
function augment_imgs (line 469) | def augment_imgs(img_list, hflip=True, rot=True):
function modcrop (line 494) | def modcrop(img_in, scale):
function shave (line 510) | def shave(img_in, border=0):
function rgb2ycbcr (line 529) | def rgb2ycbcr(img, only_y=True):
function ycbcr2rgb (line 553) | def ycbcr2rgb(img):
function bgr2ycbcr (line 573) | def bgr2ycbcr(img, only_y=True):
function channel_convert (line 597) | def channel_convert(in_c, tar_type, img_list):
function calculate_psnr (line 621) | def calculate_psnr(img1, img2, border=0):
function calculate_ssim (line 642) | def calculate_ssim(img1, img2, border=0):
function ssim (line 669) | def ssim(img1, img2):
function cubic (line 700) | def cubic(x):
function calculate_weights_indices (line 708) | def calculate_weights_indices(in_length, out_length, scale, kernel, kern...
function imresize (line 766) | def imresize(img, scale, antialiasing=True):
function imresize_np (line 839) | def imresize_np(img, scale, antialiasing=True):
FILE: manga_translator/inpainting/ldm/modules/midas/api.py
function disabled_train (line 22) | def disabled_train(self, mode=True):
function load_midas_transform (line 28) | def load_midas_transform(model_type):
function load_model (line 73) | def load_model(model_type):
class MiDaSInference (line 137) | class MiDaSInference(nn.Module):
method __init__ (line 150) | def __init__(self, model_type):
method forward (line 157) | def forward(self, x):
FILE: manga_translator/inpainting/ldm/modules/midas/midas/base_model.py
class BaseModel (line 4) | class BaseModel(torch.nn.Module):
method load (line 5) | def load(self, path):
FILE: manga_translator/inpainting/ldm/modules/midas/midas/blocks.py
function _make_encoder (line 11) | def _make_encoder(backbone, features, use_pretrained, groups=1, expand=F...
function _make_scratch (line 49) | def _make_scratch(in_shape, out_shape, groups=1, expand=False):
function _make_pretrained_efficientnet_lite3 (line 78) | def _make_pretrained_efficientnet_lite3(use_pretrained, exportable=False):
function _make_efficientnet_backbone (line 88) | def _make_efficientnet_backbone(effnet):
function _make_resnet_backbone (line 101) | def _make_resnet_backbone(resnet):
function _make_pretrained_resnext101_wsl (line 114) | def _make_pretrained_resnext101_wsl(use_pretrained):
class Interpolate (line 120) | class Interpolate(nn.Module):
method __init__ (line 124) | def __init__(self, scale_factor, mode, align_corners=False):
method forward (line 138) | def forward(self, x):
class ResidualConvUnit (line 155) | class ResidualConvUnit(nn.Module):
method __init__ (line 159) | def __init__(self, features):
method forward (line 177) | def forward(self, x):
class FeatureFusionBlock (line 194) | class FeatureFusionBlock(nn.Module):
method __init__ (line 198) | def __init__(self, features):
method forward (line 209) | def forward(self, *xs):
class ResidualConvUnit_custom (line 231) | class ResidualConvUnit_custom(nn.Module):
method __init__ (line 235) | def __init__(self, features, activation, bn):
method forward (line 263) | def forward(self, x):
class FeatureFusionBlock_custom (line 291) | class FeatureFusionBlock_custom(nn.Module):
method __init__ (line 295) | def __init__(self, features, activation, deconv=False, bn=False, expan...
method forward (line 320) | def forward(self, *xs):
FILE: manga_translator/inpainting/ldm/modules/midas/midas/dpt_depth.py
function _make_fusion_block (line 15) | def _make_fusion_block(features, use_bn):
class DPT (line 26) | class DPT(BaseModel):
method __init__ (line 27) | def __init__(
method forward (line 67) | def forward(self, x):
class DPTDepthModel (line 88) | class DPTDepthModel(DPT):
method __init__ (line 89) | def __init__(self, path=None, non_negative=True, **kwargs):
method forward (line 107) | def forward(self, x):
FILE: manga_translator/inpainting/ldm/modules/midas/midas/midas_net.py
class MidasNet (line 12) | class MidasNet(BaseModel):
method __init__ (line 16) | def __init__(self, path=None, features=256, non_negative=True):
method forward (line 49) | def forward(self, x):
FILE: manga_translator/inpainting/ldm/modules/midas/midas/midas_net_custom.py
class MidasNet_small (line 12) | class MidasNet_small(BaseModel):
method __init__ (line 16) | def __init__(self, path=None, features=64, backbone="efficientnet_lite...
method forward (line 73) | def forward(self, x):
function fuse_model (line 109) | def fuse_model(m):
FILE: manga_translator/inpainting/ldm/modules/midas/midas/transforms.py
function apply_min_size (line 6) | def apply_min_size(sample, size, image_interpolation_method=cv2.INTER_AR...
class Resize (line 48) | class Resize(object):
method __init__ (line 52) | def __init__(
method constrain_to_multiple_of (line 94) | def constrain_to_multiple_of(self, x, min_val=0, max_val=None):
method get_size (line 105) | def get_size(self, width, height):
method __call__ (line 162) | def __call__(self, sample):
class NormalizeImage (line 197) | class NormalizeImage(object):
method __init__ (line 201) | def __init__(self, mean, std):
method __call__ (line 205) | def __call__(self, sample):
class PrepareForNet (line 211) | class PrepareForNet(object):
method __init__ (line 215) | def __init__(self):
method __call__ (line 218) | def __call__(self, sample):
FILE: manga_translator/inpainting/ldm/modules/midas/midas/vit.py
class Slice (line 9) | class Slice(nn.Module):
method __init__ (line 10) | def __init__(self, start_index=1):
method forward (line 14) | def forward(self, x):
class AddReadout (line 18) | class AddReadout(nn.Module):
method __init__ (line 19) | def __init__(self, start_index=1):
method forward (line 23) | def forward(self, x):
class ProjectReadout (line 31) | class ProjectReadout(nn.Module):
method __init__ (line 32) | def __init__(self, in_features, start_index=1):
method forward (line 38) | def forward(self, x):
class Transpose (line 45) | class Transpose(nn.Module):
method __init__ (line 46) | def __init__(self, dim0, dim1):
method forward (line 51) | def forward(self, x):
function forward_vit (line 56) | def forward_vit(pretrained, x):
function _resize_pos_embed (line 100) | def _resize_pos_embed(self, posemb, gs_h, gs_w):
function forward_flex (line 117) | def forward_flex(self, x):
function get_activation (line 159) | def get_activation(name):
function get_readout_oper (line 166) | def get_readout_oper(vit_features, features, use_readout, start_index=1):
function _make_vit_b16_backbone (line 183) | def _make_vit_b16_backbone(
function _make_pretrained_vitl16_384 (line 297) | def _make_pretrained_vitl16_384(pretrained, use_readout="ignore", hooks=...
function _make_pretrained_vitb16_384 (line 310) | def _make_pretrained_vitb16_384(pretrained, use_readout="ignore", hooks=...
function _make_pretrained_deitb16_384 (line 319) | def _make_pretrained_deitb16_384(pretrained, use_readout="ignore", hooks...
function _make_pretrained_deitb16_distil_384 (line 328) | def _make_pretrained_deitb16_distil_384(pretrained, use_readout="ignore"...
function _make_vit_b_rn50_backbone (line 343) | def _make_vit_b_rn50_backbone(
function _make_pretrained_vitb_rn50_384 (line 478) | def _make_pretrained_vitb_rn50_384(
FILE: manga_translator/inpainting/ldm/modules/midas/utils.py
function read_pfm (line 9) | def read_pfm(path):
function write_pfm (line 58) | def write_pfm(path, image, scale=1):
function read_image (line 97) | def read_image(path):
function resize_image (line 116) | def resize_image(img):
function resize_depth (line 146) | def resize_depth(depth, width, height):
function write_depth (line 165) | def write_depth(path, depth, bits=1):
FILE: manga_translator/inpainting/ldm/util.py
function log_txt_as_img (line 11) | def log_txt_as_img(wh, xc, size=10):
function ismap (line 35) | def ismap(x):
function isimage (line 41) | def isimage(x):
function exists (line 47) | def exists(x):
function default (line 51) | def default(val, d):
function mean_flat (line 57) | def mean_flat(tensor):
function count_params (line 65) | def count_params(model, verbose=False):
function instantiate_from_config (line 72) | def instantiate_from_config(config):
function get_obj_from_str (line 82) | def get_obj_from_str(string, reload=False):
class AdamWwithEMAandWings (line 90) | class AdamWwithEMAandWings(optim.Optimizer):
method __init__ (line 92) | def __init__(self, params, lr=1.e-3, betas=(0.9, 0.999), eps=1.e-8, #...
method __setstate__ (line 113) | def __setstate__(self, state):
method step (line 119) | def step(self, closure=None):
FILE: manga_translator/inpainting/none.py
class NoneInpainter (line 7) | class NoneInpainter(CommonInpainter):
method _inpaint (line 9) | async def _inpaint(self, image: np.ndarray, mask: np.ndarray, config: ...
FILE: manga_translator/inpainting/original.py
class OriginalInpainter (line 7) | class OriginalInpainter(CommonInpainter):
method _inpaint (line 9) | async def _inpaint(self, image: np.ndarray, mask: np.ndarray, config: ...
FILE: manga_translator/inpainting/sd_hack.py
function disable_verbosity (line 10) | def disable_verbosity():
function enable_sliced_attention (line 15) | def enable_sliced_attention():
function hack_everything (line 21) | def hack_everything(clip_skip=0):
function _hacked_clip_forward (line 29) | def _hacked_clip_forward(self, text):
function _hacked_sliced_attentin_forward (line 71) | def _hacked_sliced_attentin_forward(self, x, context=None, mask=None):
FILE: manga_translator/manga_translator.py
function set_main_logger (line 52) | def set_main_logger(l):
class TranslationInterrupt (line 56) | class TranslationInterrupt(Exception):
function load_dictionary (line 63) | def load_dictionary(file_path):
function apply_dictionary (line 87) | def apply_dictionary(text, dictionary):
class MangaTranslator (line 95) | class MangaTranslator:
method __init__ (line 106) | def __init__(self, params: dict = None):
method _setup_log_file (line 154) | def _setup_log_file(self):
method parse_init_params (line 273) | def parse_init_params(self, params: dict):
method _set_image_context (line 313) | def _set_image_context(self, config: Config, image=None):
method _get_image_subfolder (line 338) | def _get_image_subfolder(self) -> str:
method _save_current_image_context (line 344) | def _save_current_image_context(self, image_md5: str):
method _restore_image_context (line 349) | def _restore_image_context(self, image_md5: str):
method using_gpu (line 357) | def using_gpu(self):
method translate (line 360) | async def translate(self, image: Image.Image, config: Config, image_na...
method _translate (line 432) | async def _translate(self, config: Config, ctx: Context) -> Context:
method _revert_upscale (line 626) | async def _revert_upscale(self, config: Config, ctx: Context):
method _run_colorizer (line 667) | async def _run_colorizer(self, config: Config, ctx: Context):
method _run_upscaling (line 680) | async def _run_upscaling(self, config: Config, ctx: Context):
method _run_detection (line 685) | async def _run_detection(self, config: Config, ctx: Context):
method _unload_model (line 695) | async def _unload_model(self, tool: str, model: str):
method _detector_cleanup_job (line 714) | async def _detector_cleanup_job(self):
method _run_ocr (line 726) | async def _run_ocr(self, config: Config, ctx: Context):
method _run_textline_merge (line 770) | async def _run_textline_merge(self, config: Config, ctx: Context):
method _build_prev_context (line 919) | def _build_prev_context(self, use_original_text=False, current_page_in...
method _dispatch_with_context (line 996) | async def _dispatch_with_context(self, config: Config, texts: list[str...
method _run_text_translation (line 1057) | async def _run_text_translation(self, config: Config, ctx: Context):
method _run_mask_refinement (line 1354) | async def _run_mask_refinement(self, config: Config, ctx: Context):
method _run_inpainting (line 1358) | async def _run_inpainting(self, config: Config, ctx: Context):
method _run_text_rendering (line 1364) | async def _run_text_rendering(self, config: Config, ctx: Context):
method _result_path (line 1381) | def _result_path(self, path: str) -> str:
method add_progress_hook (line 1417) | def add_progress_hook(self, ph):
method _report_progress (line 1420) | async def _report_progress(self, state: str, finished: bool = False):
method _add_logger_hook (line 1424) | def _add_logger_hook(self):
method translate_batch (line 1456) | async def translate_batch(self, images_with_configs: List[tuple], batc...
method _translate_until_translation (line 1659) | async def _translate_until_translation(self, image: Image.Image, confi...
method _batch_translate_contexts (line 1804) | async def _batch_translate_contexts(self, contexts_with_configs: List[...
method _concurrent_translate_contexts (line 2012) | async def _concurrent_translate_contexts(self, contexts_with_configs: ...
method _batch_translate_texts (line 2212) | async def _batch_translate_texts(self, texts: List[str], config: Confi...
method _apply_post_translation_processing (line 2343) | async def _apply_post_translation_processing(self, ctx: Context, confi...
method _complete_translation_pipeline (line 2481) | async def _complete_translation_pipeline(self, ctx: Context, config: C...
method _check_repetition_hallucination (line 2576) | async def _check_repetition_hallucination(self, text: str, threshold: ...
method _check_target_language_ratio (line 2632) | async def _check_target_language_ratio(self, text_regions: List, targe...
method _validate_translation (line 2690) | async def _validate_translation(self, original_text: str, translation:...
method _retry_translation_with_validation (line 2729) | async def _retry_translation_with_validation(self, region, config: Con...
FILE: manga_translator/mask_refinement/__init__.py
function dispatch (line 9) | async def dispatch(text_regions: List[TextBlock], raw_image: np.ndarray,...
FILE: manga_translator/mask_refinement/text_mask_utils.py
function save_rgb (line 18) | def save_rgb(fn, img):
function area_overlap (line 24) | def area_overlap(x1, y1, w1, h1, x2, y2, w2, h2): # returns None if rec...
function dist (line 29) | def dist(x1, y1, x2, y2):
function rect_distance (line 32) | def rect_distance(x1, y1, x1b, y1b, x2, y2, x2b, y2b):
function extend_rect (line 56) | def extend_rect(x, y, w, h, max_x, max_y, extend_size):
function complete_mask_fill (line 63) | def complete_mask_fill(text_lines: List[Tuple[int, int, int, int]]):
function refine_mask (line 71) | def refine_mask(rgbimg, rawmask):
function complete_mask (line 96) | def complete_mask(img: np.ndarray, mask: np.ndarray, textlines: List[Qua...
function unsharp (line 197) | def unsharp(image):
FILE: manga_translator/mode/local.py
function play_completion_sound (line 25) | def play_completion_sound():
function safe_get_memory_info (line 40) | def safe_get_memory_info():
function force_cleanup (line 49) | def force_cleanup():
class MangaTranslatorLocal (line 70) | class MangaTranslatorLocal(MangaTranslator):
method __init__ (line 71) | def __init__(self, params: dict = None):
method translate_path (line 85) | async def translate_path(self, path: str, dest: str = None, params: di...
method translate_file (line 201) | async def translate_file(self, path: str, dest: str, params: dict, con...
method _translate_file (line 237) | async def _translate_file(self, path: str, dest: str, config: Config, ...
method _save_text_to_file (line 295) | def _save_text_to_file(self, image_path: str, ctx: Context):
method _translate_folder_batch (line 330) | async def _translate_folder_batch(self, path: str, dest: str, params: ...
FILE: manga_translator/mode/share.py
class RestrictedUnpickler (line 27) | class RestrictedUnpickler(pickle.Unpickler):
method find_class (line 28) | def find_class(self, module: str, name: str):
function restricted_loads (line 36) | def restricted_loads(data: bytes):
class MethodCall (line 39) | class MethodCall(BaseModel):
class MangaShare (line 47) | class MangaShare:
method __init__ (line 48) | def __init__(self, params: dict = None):
method progress_stream (line 72) | async def progress_stream(self):
method run_method (line 82) | async def run_method(self, method, **attributes):
method check_nonce (line 111) | def check_nonce(self, request: Request):
method check_lock (line 117) | def check_lock(self):
method get_fn (line 121) | def get_fn(self, method_name: str):
method listen (line 129) | async def listen(self, translation_params: dict = None):
FILE: manga_translator/mode/ws.py
class MangaTranslatorWS (line 14) | class MangaTranslatorWS(MangaTranslator):
method __init__ (line 15) | def __init__(self, params: dict = None):
method listen (line 24) | async def listen(self, translation_params: dict = None):
method _run_text_translation (line 227) | async def _run_text_translation(self, config: Config, ctx: Context):
method _run_text_rendering (line 246) | async def _run_text_rendering(self, config: Config, ctx: Context):
FILE: manga_translator/ocr/__init__.py
function get_ocr (line 19) | def get_ocr(key: Ocr, *args, **kwargs) -> CommonOCR:
function prepare (line 27) | async def prepare(ocr_key: Ocr, device: str = 'cpu'):
function dispatch (line 33) | async def dispatch(ocr_key: Ocr, image: np.ndarray, regions: List[Quadri...
function unload (line 40) | async def unload(ocr_key: Ocr):
FILE: manga_translator/ocr/common.py
class CommonOCR (line 11) | class CommonOCR(InfererModule):
method _generate_text_direction (line 12) | def _generate_text_direction(self, bboxes: List[Union[Quadrilateral, T...
method recognize (line 41) | async def recognize(self, image: np.ndarray, textlines: List[Quadrilat...
method _recognize (line 49) | async def _recognize(self, image: np.ndarray, textlines: List[Quadrila...
class OfflineOCR (line 53) | class OfflineOCR(CommonOCR, ModelWrapper):
method _recognize (line 56) | async def _recognize(self, *args, **kwargs):
method _infer (line 60) | async def _infer(self, image: np.ndarray, textlines: List[Quadrilatera...
FILE: manga_translator/ocr/model_32px.py
class Model32pxOCR (line 19) | class Model32pxOCR(OfflineOCR):
method __init__ (line 31) | def __init__(self, *args, **kwargs):
method _load (line 39) | async def _load(self, device: str):
method _unload (line 55) | async def _unload(self):
method _infer (line 58) | async def _infer(self, image: np.ndarray, textlines: List[Quadrilatera...
class ResNet (line 143) | class ResNet(nn.Module):
method __init__ (line 145) | def __init__(self, input_channel, output_channel, block, layers):
method _make_layer (line 184) | def _make_layer(self, block, planes, blocks, stride=1):
method forward (line 201) | def forward(self, x):
class BasicBlock (line 236) | class BasicBlock(nn.Module):
method __init__ (line 239) | def __init__(self, inplanes, planes, stride=1, downsample=None):
method _conv3x3 (line 248) | def _conv3x3(self, in_planes, out_planes, stride=1):
method forward (line 253) | def forward(self, x):
function conv3x3 (line 269) | def conv3x3(in_planes, out_planes, stride=1, groups=1, dilation=1):
function conv1x1 (line 275) | def conv1x1(in_planes, out_planes, stride=1):
class ResNet_FeatureExtractor (line 279) | class ResNet_FeatureExtractor(nn.Module):
method __init__ (line 282) | def __init__(self, input_channel, output_channel=128):
method forward (line 286) | def forward(self, input):
class PositionalEncoding (line 289) | class PositionalEncoding(nn.Module):
method __init__ (line 290) | def __init__(self, d_model, dropout=0.1, max_len=5000):
method forward (line 302) | def forward(self, x, offset = 0):
function generate_square_subsequent_mask (line 306) | def generate_square_subsequent_mask(sz):
class AddCoords (line 311) | class AddCoords(nn.Module):
method __init__ (line 313) | def __init__(self, with_r=False):
method forward (line 317) | def forward(self, input_tensor):
class Beam (line 347) | class Beam:
method __init__ (line 348) | def __init__(self, char_seq = [], logprobs = []):
method avg_logprob (line 357) | def avg_logprob(self):
method sort_key (line 360) | def sort_key(self):
method seq_end (line 363) | def seq_end(self, end_tok):
method extend (line 366) | def extend(self, idx, logprob):
class Hypothesis (line 374) | class Hypothesis:
method __init__ (line 375) | def __init__(self, device, start_tok: int, end_tok: int, padding_tok: ...
method seq_end (line 389) | def seq_end(self):
method logprob (line 392) | def logprob(self):
method sort_key (line 395) | def sort_key(self):
method prob (line 398) | def prob(self):
method __len__ (line 401) | def __len__(self):
method extend (line 404) | def extend(self, idx, logprob):
method output (line 412) | def output(self):
function next_token_batch (line 415) | def next_token_batch(
class OCR (line 467) | class OCR(nn.Module):
method __init__ (line 468) | def __init__(self, dictionary, max_len):
method forward (line 491) | def forward(self,
method infer_beam_batch (line 518) | def infer_beam_batch(self, img: torch.FloatTensor, img_widths: List[in...
method infer_beam (line 597) | def infer_beam(self, img: torch.FloatTensor, beams_k: int = 5, start_t...
function test (line 663) | def test():
function test_inference (line 672) | def test_inference():
FILE: manga_translator/ocr/model_48px.py
class Model48pxOCR (line 27) | class Model48pxOCR(OfflineOCR):
method __init__ (line 39) | def __init__(self, *args, **kwargs):
method _load (line 47) | async def _load(self, device: str):
method _unload (line 64) | async def _unload(self):
method _infer (line 67) | async def _infer(self, image: np.ndarray, textlines: List[Quadrilatera...
class ConvNeXtBlock (line 182) | class ConvNeXtBlock(nn.Module):
method __init__ (line 193) | def __init__(self, dim, layer_scale_init_value=1e-6, ks = 7, padding =...
method forward (line 203) | def forward(self, x):
class ConvNext_FeatureExtractor (line 216) | class ConvNext_FeatureExtractor(nn.Module) :
method __init__ (line 217) | def __init__(self, img_height = 48, in_dim = 3, dim = 512, n_layers = ...
method make_layers (line 256) | def make_layers(self, dim, n, ks = 7, padding = 3) :
method forward (line 262) | def forward(self, x) :
function transformer_encoder_forward (line 278) | def transformer_encoder_forward(
class XposMultiheadAttention (line 294) | class XposMultiheadAttention(nn.Module):
method __init__ (line 295) | def __init__(
method reset_parameters (line 320) | def reset_parameters(self):
method forward (line 327) | def forward(
function generate_square_subsequent_mask (line 396) | def generate_square_subsequent_mask(sz):
class Beam (line 401) | class Beam:
method __init__ (line 402) | def __init__(self, char_seq = [], logprobs = []):
method avg_logprob (line 411) | def avg_logprob(self):
method sort_key (line 414) | def sort_key(self):
method seq_end (line 417) | def seq_end(self, end_tok):
method extend (line 420) | def extend(self, idx, logprob):
class Hypothesis (line 428) | class Hypothesis:
method __init__ (line 429) | def __init__(self, device, start_tok: int, end_tok: int, padding_tok: ...
method seq_end (line 443) | def seq_end(self):
method logprob (line 446) | def logprob(self):
method sort_key (line 449) | def sort_key(self):
method prob (line 452) | def prob(self):
method __len__ (line 455) | def __len__(self):
method extend (line 458) | def extend(self, idx, logprob):
method output (line 466) | def output(self):
function next_token_batch (line 469) | def next_token_batch(
class OCR (line 505) | class OCR(nn.Module):
method __init__ (line 506) | def __init__(self, dictionary, max_len):
method encoder_forward (line 543) | def encoder_forward(self, memory, encoder_mask):
method decoder_forward (line 548) | def decoder_forward(
method forward (line 574) | def forward(self,
method infer_beam_batch (line 600) | def infer_beam_batch(self, img: torch.FloatTensor, img_widths: List[in...
method infer_beam_batch_tensor (line 678) | def infer_beam_batch_tensor(self, img: torch.FloatTensor, img_widths: ...
function convert_pl_model (line 805) | def convert_pl_model(filename: str) :
function test_LocalViT_FeatureExtractor (line 814) | def test_LocalViT_FeatureExtractor() :
function test_infer (line 820) | def test_infer() :
FILE: manga_translator/ocr/model_48px_ctc.py
class Model48pxCTCOCR (line 18) | class Model48pxCTCOCR(OfflineOCR):
method __init__ (line 30) | def __init__(self, *args, **kwargs):
method _load (line 38) | async def _load(self, device: str):
method _unload (line 59) | async def _unload(self):
method _infer (line 62) | async def _infer(self, image: np.ndarray, textlines: List[Quadrilatera...
class PositionalEncoding (line 163) | class PositionalEncoding(nn.Module):
method __init__ (line 164) | def __init__(self, d_model, dropout=0.1, max_len=5000):
method forward (line 176) | def forward(self, x, offset = 0):
class CustomTransformerEncoderLayer (line 180) | class CustomTransformerEncoderLayer(nn.Module):
method __init__ (line 212) | def __init__(self, d_model, nhead, dim_feedforward=2048, dropout=0.1, ...
method __setstate__ (line 233) | def __setstate__(self, state):
method forward (line 238) | def forward(self, src: torch.Tensor, src_mask: Optional[torch.Tensor] ...
method _sa_block (line 263) | def _sa_block(self, x: torch.Tensor,
method _ff_block (line 272) | def _ff_block(self, x: torch.Tensor) -> torch.Tensor:
class ResNet (line 277) | class ResNet(nn.Module):
method __init__ (line 279) | def __init__(self, input_channel, output_channel, block, layers):
method _make_layer (line 318) | def _make_layer(self, block, planes, blocks, stride=1):
method forward (line 335) | def forward(self, x):
class BasicBlock (line 372) | class BasicBlock(nn.Module):
method __init__ (line 375) | def __init__(self, inplanes, planes, stride=1, downsample=None):
method _conv3x3 (line 384) | def _conv3x3(self, in_planes, out_planes, stride=1):
method forward (line 389) | def forward(self, x):
function conv3x3 (line 405) | def conv3x3(in_planes, out_planes, stride=1, groups=1, dilation=1):
function conv1x1 (line 411) | def conv1x1(in_planes, out_planes, stride=1):
class ResNet_FeatureExtractor (line 415) | class ResNet_FeatureExtractor(nn.Module):
method __init__ (line 418) | def __init__(self, input_channel, output_channel=128):
method forward (line 422) | def forward(self, input):
class OCR (line 425) | class OCR(nn.Module):
method __init__ (line 426) | def __init__(self, dictionary, max_len):
method forward (line 438) | def forward(self,
method decode (line 447) | def decode(self, img: torch.Tensor, img_widths: List[int], blank, verb...
method decode_ctc_top1 (line 456) | def decode_ctc_top1(self, pred_char_logits, pred_color_values, blank, ...
method eval_ocr (line 496) | def eval_ocr(self, input_lengths, target_lengths, pred_char_logits, pr...
function test2 (line 522) | def test2():
function test_inference (line 533) | def test_inference():
FILE: manga_translator/ocr/model_manga_ocr.py
function merge_bboxes (line 25) | async def merge_bboxes(bboxes: List[Quadrilateral], width: int, height: ...
class ModelMangaOCR (line 90) | class ModelMangaOCR(OfflineOCR):
method __init__ (line 102) | def __init__(self, *args, **kwargs):
method _load (line 110) | async def _load(self, device: str):
method _unload (line 128) | async def _unload(self):
method _infer (line 132) | async def _infer(self, image: np.ndarray, textlines: List[Quadrilatera...
FILE: manga_translator/ocr/model_ocr_large.py
class ResNet (line 9) | class ResNet(nn.Module):
method __init__ (line 11) | def __init__(self, input_channel, output_channel, block, layers):
method _make_layer (line 50) | def _make_layer(self, block, planes, blocks, stride=1):
method forward (line 67) | def forward(self, x):
class BasicBlock (line 102) | class BasicBlock(nn.Module):
method __init__ (line 105) | def __init__(self, inplanes, planes, stride=1, downsample=None):
method _conv3x3 (line 114) | def _conv3x3(self, in_planes, out_planes, stride=1):
method forward (line 119) | def forward(self, x):
function conv3x3 (line 135) | def conv3x3(in_planes, out_planes, stride=1, groups=1, dilation=1):
function conv1x1 (line 141) | def conv1x1(in_planes, out_planes, stride=1):
class ResNet_FeatureExtractor (line 145) | class ResNet_FeatureExtractor(nn.Module):
method __init__ (line 148) | def __init__(self, input_channel, output_channel=128):
method forward (line 152) | def forward(self, input):
class PositionalEncoding (line 155) | class PositionalEncoding(nn.Module):
method __init__ (line 156) | def __init__(self, d_model, dropout=0.1, max_len=5000):
method forward (line 168) | def forward(self, x, offset = 0):
function generate_square_subsequent_mask (line 172) | def generate_square_subsequent_mask(sz):
class AddCoords (line 177) | class AddCoords(nn.Module):
method __init__ (line 179) | def __init__(self, with_r=False):
method forward (line 183) | def forward(self, input_tensor):
class Beam (line 213) | class Beam:
method __init__ (line 214) | def __init__(self, char_seq = [], logprobs = []):
method avg_logprob (line 223) | def avg_logprob(self):
method sort_key (line 226) | def sort_key(self):
method seq_end (line 229) | def seq_end(self, end_tok):
method extend (line 232) | def extend(self, idx, logprob):
class Hypothesis (line 240) | class Hypothesis:
method __init__ (line 241) | def __init__(self, device, start_tok: int, end_tok: int, padding_tok: ...
method seq_end (line 255) | def seq_end(self):
method logprob (line 258) | def logprob(self):
method sort_key (line 261) | def sort_key(self):
method prob (line 264) | def prob(self):
method __len__ (line 267) | def __len__(self):
method extend (line 270) | def extend(self, idx, logprob):
method output (line 278) | def output(self):
function next_token_batch (line 281) | def next_token_batch(
class OCR (line 333) | class OCR(nn.Module):
method __init__ (line 334) | def __init__(self, dictionary, max_len):
method forward (line 357) | def forward(self,
method infer_beam_batch (line 384) | def infer_beam_batch(self, img: torch.FloatTensor, img_widths: List[in...
method infer_beam (line 463) | def infer_beam(self, img: torch.FloatTensor, beams_k: int = 5, start_t...
function test (line 529) | def test():
function test_inference (line 538) | def test_inference():
FILE: manga_translator/ocr/xpos_relative_position.py
function fixed_pos_embedding (line 9) | def fixed_pos_embedding(x):
function rotate_every_two (line 17) | def rotate_every_two(x):
function duplicate_interleave (line 23) | def duplicate_interleave(m):
function apply_rotary_pos_emb (line 33) | def apply_rotary_pos_emb(x, sin, cos, scale=1):
function apply_rotary_pos_emb2d (line 38) | def apply_rotary_pos_emb2d(x, sin, cos, scale=1):
class XPOS (line 44) | class XPOS(nn.Module):
method __init__ (line 45) | def __init__(
method forward (line 55) | def forward(self, x, offset=0, downscale=False):
class XPOS2D (line 74) | class XPOS2D(nn.Module):
method __init__ (line 75) | def __init__(
method forward (line 81) | def forward(self, x: torch.Tensor, offset_x = 0, offset_y = 0, downsca...
function test (line 96) | def test() :
FILE: manga_translator/rendering/__init__.py
function parse_font_paths (line 23) | def parse_font_paths(path: str, default: List[str] = None) -> List[str]:
function fg_bg_compare (line 31) | def fg_bg_compare(fg, bg):
function count_text_length (line 37) | def count_text_length(text: str) -> float:
function resize_regions_to_font_size (line 48) | def resize_regions_to_font_size(img: np.ndarray, text_regions: List['Tex...
function dispatch (line 235) | async def dispatch(
function render (line 264) | def render(
function dispatch_eng_render (line 412) | async def dispatch_eng_render(img_canvas: np.ndarray, original_img: np.n...
function dispatch_eng_render_pillow (line 422) | async def dispatch_eng_render_pillow(img_canvas: np.ndarray, original_im...
FILE: manga_translator/rendering/ballon_extractor.py
function enlarge_window (line 8) | def enlarge_window(rect, im_w, im_h, ratio=2.5, aspect_ratio=1.0) -> List:
function extract_ballon_region (line 31) | def extract_ballon_region(img: np.ndarray, ballon_rect: List, enlarge_ra...
FILE: manga_translator/rendering/gimp_render.py
function gimp_render (line 70) | def gimp_render(out_file, ctx: Context):
function gimp_console_executable (line 153) | def gimp_console_executable():
function gimp_batch (line 170) | def gimp_batch(script):
FILE: manga_translator/rendering/text_render.py
function CJK_Compatibility_Forms_translate (line 118) | def CJK_Compatibility_Forms_translate(cdpt: str, direction: int):
function compact_special_symbols (line 136) | def compact_special_symbols(text: str) -> str:
function rotate_image (line 144) | def rotate_image(image, angle):
function add_color (line 159) | def add_color(bw_char_map, color, stroke_char_map, stroke_color):
function get_cached_font (line 217) | def get_cached_font(path: str) -> freetype.Face:
function set_font (line 225) | def set_font(font_path: str):
class namespace (line 233) | class namespace:
class Glyph (line 236) | class Glyph:
method __init__ (line 237) | def __init__(self, glyph):
function get_char_glyph (line 256) | def get_char_glyph(cdpt: str, font_size: int, direction: int) -> Glyph:
function get_char_border (line 269) | def get_char_border(cdpt: str, font_size: int, direction: int):
function calc_vertical (line 296) | def calc_vertical(font_size: int, text: str, max_height: int):
function put_char_vertical (line 339) | def put_char_vertical(font_size: int, cdpt: str, pen_l: Tuple[int, int],...
function put_text_vertical (line 541) | def put_text_vertical(font_size: int, text: str, h: int, alignment: str,...
function select_hyphenator (line 582) | def select_hyphenator(lang: str):
function get_char_offset_x (line 597) | def get_char_offset_x(font_size: int, cdpt: str):
function get_string_width (line 609) | def get_string_width(font_size: int, text: str):
function calc_horizontal (line 612) | def calc_horizontal(font_size: int, text: str, max_width: int, max_heigh...
function put_char_horizontal (line 881) | def put_char_horizontal(font_size: int, cdpt: str, pen_l: Tuple[int, int...
function put_text_horizontal (line 1100) | def put_text_horizontal(font_size: int, text: str, width: int, height: i...
function test (line 1162) | def test():
FILE: manga_translator/rendering/text_render_eng.py
class Textline (line 15) | class Textline:
method __init__ (line 16) | def __init__(self, text: str = '', pos_x: int = 0, pos_y: int = 0, len...
method append_right (line 27) | def append_right(self, word: str, w_len: int, delimiter: str = ''):
method append_left (line 33) | def append_left(self, word: str, w_len: int, delimiter: str = ''):
method add_spacing (line 39) | def add_spacing(self, spacing: int):
method strip_spacing (line 44) | def strip_spacing(self):
function render_lines (line 49) | def render_lines(
function seg_eng (line 96) | def seg_eng(text: str) -> List[str]:
function layout_lines_aligncenter (line 158) | def layout_lines_aligncenter(
function render_textblock_list_eng (line 336) | def render_textblock_list_eng(
FILE: manga_translator/rendering/text_render_pillow_eng.py
function merge_seg_eng (line 10) | def merge_seg_eng(text: str, font, bbox_width, size_ratio=1.2) -> List[s...
function widen_mask_opencv_round (line 30) | def widen_mask_opencv_round(mask, width):
function _check_bbox_collision (line 38) | def _check_bbox_collision(b1, b2):
function _spiral_points_generator (line 42) | def _spiral_points_generator(anchor_x, anchor_y, limit):
function _find_collision_free_position (line 55) | def _find_collision_free_position(bbox_idx, bboxes, anchors, image_bound...
function solve_collisions_spiral_xyxy (line 80) | def solve_collisions_spiral_xyxy(image_shape, initial_bboxes_xyxy, max_i...
function render_textblock_list_eng (line 107) | def render_textblock_list_eng(
FILE: manga_translator/save.py
class FormatNotSupportedException (line 9) | class FormatNotSupportedException(Exception):
method __init__ (line 10) | def __init__(self, fmt: str):
function register_format (line 14) | def register_format(format_cls):
class ExportFormat (line 21) | class ExportFormat():
method __init_subclass__ (line 25) | def __init_subclass__(cls, **kwargs):
method save (line 29) | def save(self, result: Image.Image, dest: str, ctx: Context):
method _save (line 33) | def _save(self, result: Image.Image, dest: str, ctx: Context):
function save_result (line 36) | def save_result(result: Image.Image, dest: str, ctx: Context):
class ImageFormat (line 48) | class ImageFormat(ExportFormat):
method _save (line 51) | def _save(self, result: Image.Image, dest: str, ctx: Context):
class JPGFormat (line 54) | class JPGFormat(ExportFormat):
method _save (line 57) | def _save(self, result: Image.Image, dest: str, ctx: Context):
class GIMPFormat (line 62) | class GIMPFormat(ExportFormat):
method _save (line 65) | def _save(self, result: Image.Image, dest: str, ctx: Context):
FILE: manga_translator/textline_merge/__init__.py
function split_text_region (line 10) | def split_text_region(
function merge_bboxes_text_region (line 110) | def merge_bboxes_text_region(bboxes: List[Quadrilateral], width, height):
function dispatch (line 184) | async def dispatch(textlines: List[Quadrilateral], width: int, height: i...
FILE: manga_translator/translators/__init__.py
function get_translator (line 71) | def get_translator(key: Translator, *args, **kwargs) -> CommonTranslator:
function prepare (line 81) | async def prepare(chain: TranslatorChain):
function dispatch (line 89) | async def dispatch(chain: TranslatorChain, queries: List[str], translato...
function dispatch_batch (line 131) | async def dispatch_batch(chain: TranslatorChain, batch_queries: List[Lis...
function unload (line 193) | async def unload(key: Translator):
FILE: manga_translator/translators/baidu.py
class BaiduTranslator (line 17) | class BaiduTranslator(CommonTranslator):
method __init__ (line 42) | def __init__(self) -> None:
method _translate (line 47) | async def _translate(self, from_lang, to_lang, queries):
method _modify_invalid_translation_query (line 76) | def _modify_invalid_translation_query(self, query: str, trans: str) ->...
method get_url (line 81) | def get_url(from_lang, to_lang, query_text):
FILE: manga_translator/translators/caiyun.py
class CaiyunTranslator (line 8) | class CaiyunTranslator(CommonTranslator):
method __init__ (line 26) | def __init__(self):
method _translate (line 31) | async def _translate(self, from_lang, to_lang, queries):
method _truncate (line 45) | def _truncate(self, q):
method _do_request (line 51) | async def _do_request(self, data):
FILE: manga_translator/translators/chatgpt.py
class OpenAITranslator (line 20) | class OpenAITranslator(ConfigGPT, CommonTranslator):
method __init__ (line 35) | def __init__(self, check_openai_key=True):
method set_prev_context (line 84) | def set_prev_context(self, text: str = ""):
method parse_args (line 87) | def parse_args(self, args: CommonTranslator):
method _ratelimit_sleep (line 91) | async def _ratelimit_sleep(self):
method _assemble_prompts (line 111) | def _assemble_prompts(self, from_lang: str, to_lang: str, queries: Lis...
method _translate (line 149) | async def _translate(self, from_lang: str, to_lang: str, queries: List...
method _try_fallback_model (line 177) | async def _try_fallback_model(self, to_lang: str, prompt: str, batch_q...
method _translate_batch (line 272) | async def _translate_batch(
method _request_with_retry (line 600) | async def _request_with_retry(self, to_lang: str, prompt: str) -> str:
method _request_translation (line 659) | async def _request_translation(self, to_lang: str, prompt: str) -> str:
method _fix_prefix_spacing (line 788) | def _fix_prefix_spacing(self, text_to_fix):
method print_boxed (line 810) | def print_boxed(self, text, border_color="blue", title="OpenAITranslat...
method load_glossary (line 832) | def load_glossary(self, path):
method detect_type (line 853) | def detect_type(self, dic_path):
method load_mit_dict (line 926) | def load_mit_dict(self, dic_path):
method load_galtransl_dic (line 1030) | def load_galtransl_dic(self, dic_path):
method load_sakura_dict (line 1070) | def load_sakura_dict(self, dic_path):
method extract_relevant_terms (line 1110) | def extract_relevant_terms(self, text):
FILE: manga_translator/translators/chatgpt_2stage.py
function encode_image (line 13) | def encode_image(image):
class RefusalMessageError (line 26) | class RefusalMessageError(Exception):
class ChatGPT2StageTranslator (line 31) | class ChatGPT2StageTranslator(OpenAITranslator):
method _contains_refusal (line 192) | def _contains_refusal(cls, text: str) -> bool:
method _attempt_fallback_stage1 (line 199) | async def _attempt_fallback_stage1(self, refine_prompt: str, base64_im...
method _attempt_batch_fallback_stage1 (line 247) | async def _attempt_batch_fallback_stage1(self, batch_refine_prompt: st...
method __init__ (line 308) | def __init__(self, max_tokens=16000, refine_temperature=0.0, translate...
method _translate (line 334) | async def _translate(self, from_lang: str, to_lang: str, queries: List...
method _translate_2stage (line 354) | async def _translate_2stage(self, from_lang: str, to_lang: str, querie...
method _process_refine_output (line 524) | def _process_refine_output(self, refine_output: List[str]) -> List[str]:
method _get_refine_prompt (line 547) | def _get_refine_prompt(self, text_regions, width: int, height: int, ne...
method _get_refine_system_instruction (line 569) | def _get_refine_system_instruction(self, from_lang: str):
method _parse_json_response (line 619) | def _parse_json_response(self, raw_content: str, fallback_queries: Lis...
method _remap_translations_to_original_positions (line 747) | def _remap_translations_to_original_positions(self, reordered_translat...
method _request_translation (line 787) | async def _request_translation(self, to_lang: str, prompt: str) -> str:
method translate (line 998) | async def translate(self, from_lang: str, to_lang: str, queries: List[...
method _translate_batch_2stage (line 1062) | async def _translate_batch_2stage(self, from_lang: str, to_lang: str, ...
method _get_batch_refine_prompt (line 1265) | def _get_batch_refine_prompt(self, batch_query_regions: List[List], ba...
method _get_batch_refine_system_instruction (line 1290) | def _get_batch_refine_system_instruction(self, from_lang: str):
method _parse_batch_json_response (line 1372) | def _parse_batch_json_response(self, raw_content: str, fallback_querie...
method _generate_batch_debug_images (line 1423) | async def _generate_batch_debug_images(self, batch_contexts: List[Cont...
FILE: manga_translator/translators/common.py
class InvalidServerResponse (line 72) | class InvalidServerResponse(Exception):
class MissingAPIKeyException (line 75) | class MissingAPIKeyException(Exception):
class LanguageUnsupportedException (line 78) | class LanguageUnsupportedException(Exception):
method __init__ (line 79) | def __init__(self, language_code: str, translator: str = None, support...
class MTPEAdapter (line 85) | class MTPEAdapter():
method dispatch (line 86) | async def dispatch(self, queries: List[str], translations: List[str]) ...
class CommonTranslator (line 105) | class CommonTranslator(InfererModule):
method __init__ (line 118) | def __init__(self):
method supports_languages (line 123) | def supports_languages(self, from_lang: str, to_lang: str, fatal: bool...
method parse_language_codes (line 137) | def parse_language_codes(self, from_lang: str, to_lang: str, fatal: bo...
method translate (line 147) | async def translate(self, from_lang: str, to_lang: str, queries: List[...
method _translate (line 227) | async def _translate(self, from_lang: str, to_lang: str, queries: List...
method _ratelimit_sleep (line 230) | async def _ratelimit_sleep(self):
method _is_translation_invalid (line 239) | def _is_translation_invalid(self, query: str, trans: str) -> bool:
method _modify_invalid_translation_query (line 251) | def _modify_invalid_translation_query(self, query: str, trans: str) ->...
method _clean_translation_output (line 258) | def _clean_translation_output(self, query: str, trans: str, to_lang: s...
class OfflineTranslator (line 303) | class OfflineTranslator(CommonTranslator, ModelWrapper):
method _translate (line 306) | async def _translate(self, *args, **kwargs):
method _infer (line 310) | async def _infer(self, from_lang: str, to_lang: str, queries: List[str...
method load (line 313) | async def load(self, from_lang: str, to_lang: str, device: str):
method _load (line 317) | async def _load(self, from_lang: str, to_lang: str, device: str):
method reload (line 320) | async def reload(self, from_lang: str, to_lang: str, device: str):
method _load (line 324) | async def _load(self, from_lang: str, to_lang: str, device: str):
method unload (line 327) | async def unload(self, device: str):
FILE: manga_translator/translators/common_gpt.py
class CommonGPTTranslator (line 13) | class CommonGPTTranslator(ConfigGPT, CommonTranslator):
method __init__ (line 39) | def __init__(self, config_key: str):
method parse_args (line 57) | def parse_args(self, args: CommonTranslator):
method count_tokens (line 61) | def count_tokens(self, text: str) -> int:
method withinTokenLimit (line 102) | def withinTokenLimit(self, text: str) -> bool:
method supports_languages (line 128) | def supports_languages(self, from_lang: str, to_lang: str, fatal: bool...
method fallback_fewShot (line 133) | def fallback_fewShot(self) -> str:
method _assemble_prompts (line 164) | def _assemble_prompts(self, from_lang: str, to_lang: str, queries: Lis...
method _assemble_request (line 227) | def _assemble_request(self, to_lang: str, prompt: str) -> Dict:
method _parse_response (line 249) | def _parse_response(self, response: str, queries: List):
method _ratelimit_sleep (line 265) | async def _ratelimit_sleep(self):
class _CommonGPTTranslator_JSON (line 280) | class _CommonGPTTranslator_JSON:
method __init__ (line 286) | def __init__(self, translator: CommonGPTTranslator):
method ppJSON (line 291) | def ppJSON(self, jsonText: str) -> str:
method _assemble_prompts (line 311) | def _assemble_prompts(self, from_lang: str, to_lang: str, queries: Lis...
method _assemble_request (line 361) | def _assemble_request(self, to_lang: str, prompt: str, response_format...
method _parse_response (line 407) | def _parse_response(self, response: json, queries: List[str]) -> List[...
method text2json (line 459) | def text2json(text: str) -> TranslationList:
method _list2json (line 485) | def _list2json(self, vals: List[str]) -> TranslationList:
FILE: manga_translator/translators/config_gpt.py
class TextValue (line 9) | class TextValue(BaseModel):
class TranslationList (line 13) | class TranslationList(BaseModel):
class ConfigGPT (line 17) | class ConfigGPT:
method __init__ (line 171) | def __init__(self, config_key: str):
method _config_get (line 178) | def _config_get(self, key: str, default=None):
method include_template (line 197) | def include_template(self) -> str:
method prompt_template (line 201) | def prompt_template(self) -> str:
method chat_system_template (line 205) | def chat_system_template(self) -> str:
method chat_sample (line 209) | def chat_sample(self) -> Dict[str, List[str]]:
method _closest_sample_match (line 231) | def _closest_sample_match(self, all_samples: Dict, to_lang: str, max_d...
method get_chat_sample (line 283) | def get_chat_sample(self, to_lang: str) -> List[str]:
method json_mode (line 292) | def json_mode(self) -> bool:
method json_sample (line 296) | def json_sample(self) -> Dict[str, List[TranslationList]]:
method get_json_sample (line 327) | def get_json_sample(self, to_lang: str) -> List[TranslationList]:
method get_sample (line 335) | def get_sample(self, to_lang: str) -> List:
method rgx_capture (line 347) | def rgx_capture(self) -> str:
method temperature (line 351) | def temperature(self) -> float:
method top_p (line 355) | def top_p(self) -> float:
method verbose_logging (line 359) | def verbose_logging(self) -> bool:
method glossary_system_template (line 363) | def glossary_system_template(self) -> str:
method extract_capture_groups (line 366) | def extract_capture_groups(self, text, regex=r"(.*)"):
FILE: manga_translator/translators/custom_openai.py
class CustomOpenAiTranslator (line 17) | class CustomOpenAiTranslator(ConfigGPT, CommonTranslator):
method __init__ (line 34) | def __init__(self, model=None, api_base=None, api_key=None, check_open...
method parse_args (line 49) | def parse_args(self, args: TranslatorConfig):
method extract_capture_groups (line 53) | def extract_capture_groups(self, text, regex=r"(.*)"):
method _assemble_prompts (line 71) | def _assemble_prompts(self, from_lang: str, to_lang: str, queries: Lis...
method _format_prompt_log (line 101) | def _format_prompt_log(self, to_lang: str, prompt: str) -> str:
method _translate (line 121) | async def _translate(self, from_lang: str, to_lang: str, queries: List...
method _request_translation (line 217) | async def _request_translation(self, to_lang: str, prompt: str) -> str:
FILE: manga_translator/translators/deepl.py
class DeeplTranslator (line 6) | class DeeplTranslator(CommonTranslator):
method __init__ (line 45) | def __init__(self):
method _translate (line 51) | async def _translate(self, from_lang, to_lang, queries):
FILE: manga_translator/translators/deepseek.py
class DeepseekTranslator (line 18) | class DeepseekTranslator(CommonGPTTranslator):
method __init__ (line 47) | def __init__(self, check_openai_key=True):
method count_tokens (line 65) | def count_tokens(self, text: str):
method _format_prompt_log (line 84) | def _format_prompt_log(self, to_lang: str, prompt: str) -> str:
method _translate (line 105) | async def _translate(self, from_lang: str, to_lang: str, queries: List...
method _request_translation (line 233) | async def _request_translation(self, to_lang: str, prompt: str) -> str:
FILE: manga_translator/translators/gemini.py
class GeminiTranslator (line 19) | class GeminiTranslator(CommonGPTTranslator):
method __init__ (line 88) | def __init__(self):
method useCache (line 212) | def useCache(self) -> bool:
method parse_args (line 244) | def parse_args(self, args: CommonGPTTranslator):
method _init_json_mode (line 253) | def _init_json_mode(self):
method _init_standard_mode (line 262) | def _init_standard_mode(self):
method count_tokens (line 267) | def count_tokens(self, text: str) -> int:
method _createContext (line 272) | def _createContext(self, to_lang: str):
method _needRecache (line 299) | def _needRecache(self) -> bool:
method _translate (line 316) | async def _translate(self, from_lang: str, to_lang: str, queries: List...
method formatLog (line 421) | def formatLog(self, vals: dict) -> str:
method _request_translation (line 426) | async def _request_translation(self, to_lang: str, prompt: str) -> str:
class _GeminiTranslator_json (line 491) | class _GeminiTranslator_json (_CommonGPTTranslator_JSON):
method __init__ (line 496) | def __init__(self, translator: GeminiTranslator):
method _createContext (line 503) | def _createContext(self, to_lang: str):
method _request_translation (line 531) | async def _request_translation(self, to_lang: str, prompt: str) -> str:
FILE: manga_translator/translators/gemini_2stage.py
function encode_image (line 15) | def encode_image(image):
class TextBoundingBox (line 28) | class TextBoundingBox(BaseModel):
class TextBoundingBoxes (line 35) | class TextBoundingBoxes(BaseModel):
class TranslatedText (line 39) | class TranslatedText(BaseModel):
class TranslatedTexts (line 45) | class TranslatedTexts(BaseModel):
class Gemini2StageTranslator (line 49) | class Gemini2StageTranslator(CommonTranslator):
method __init__ (line 74) | def __init__(self, max_tokens = 16000, refine_temperature = 0.0, trans...
method supports_languages (line 83) | def supports_languages(self, from_lang: str, to_lang: str, fatal: bool...
method translate (line 91) | async def translate(self, from_lang: str, to_lang: str, queries: List[...
method _translate (line 158) | async def _translate(self, from_lang: str, to_lang: str, query_indices...
method _translate_2stage (line 161) | async def _translate_2stage(self, from_lang: str, to_lang: str, query_...
method process_refine_output (line 200) | def process_refine_output(self, refine_output: List[str]) -> List[str]:
method get_prompt (line 220) | def get_prompt(self, text_regions, width: int, height: int, new_width:...
method _get_refine_system_instruction (line 236) | def _get_refine_system_instruction(self, from_lang: str):
method get_translate_system_instruction (line 249) | def get_translate_system_instruction(self, from_lang: str, to_lang: str):
FILE: manga_translator/translators/google.py
class GoogleTranslator (line 50) | class GoogleTranslator(CommonTranslator):
method __init__ (line 109) | def __init__(self, service_urls=DEFAULT_CLIENT_SERVICE_URLS, user_agen...
method _translate (line 141) | async def _translate(self, from_lang: str, to_lang: str, queries: List...
method _translate_query (line 168) | async def _translate_query(self, from_lang: str, to_lang: str, query: ...
method _request_and_parse_translation (line 202) | async def _request_and_parse_translation(self, query, to_lang, from_la...
method _build_rpc_request (line 286) | def _build_rpc_request(self, text: str, dest: str, src: str):
method _pick_service_url (line 296) | def _pick_service_url(self):
method _request_translation (line 301) | async def _request_translation(self, text: str, dest: str, src: str):
method _translate_legacy (line 323) | async def _translate_legacy(self, text, dest, src, override):
method _parse_extra_data (line 345) | def _parse_extra_data(self, data):
method translate_legacy (line 368) | async def translate_legacy(self, text, dest='en', src='auto', **kwargs):
method detect (line 471) | async def detect(self, text: str):
method detect_legacy (line 476) | async def detect_legacy(self, text, **kwargs):
FILE: manga_translator/translators/google_gtoken.py
class TokenAcquirer (line 12) | class TokenAcquirer:
method __init__ (line 41) | def __init__(self, client: httpx.AsyncClient, tkk='0', host='translate...
method _update (line 46) | async def _update(self):
method _lazy (line 112) | def _lazy(self, value):
method _xr (line 130) | def _xr(self, a, b):
method acquire (line 142) | def acquire(self, text):
method do (line 198) | async def do(self, text):
FILE: manga_translator/translators/groq.py
class GroqTranslator (line 8) | class GroqTranslator(CommonTranslator):
method __init__ (line 49) | def __init__(self, check_groq_key=True):
method parse_args (line 64) | def parse_args(self, args):
method _config_get (line 68) | def _config_get(self, key: str, default=None):
method chat_system_template (line 74) | def chat_system_template(self) -> str:
method chat_sample (line 78) | def chat_sample(self):
method temperature (line 82) | def temperature(self) -> float:
method top_p (line 86) | def top_p(self) -> float:
method _format_prompt_log (line 89) | def _format_prompt_log(self, to_lang: str, prompt: str) -> str:
method _translate (line 101) | async def _translate(self, from_lang: str, to_lang: str, queries: List...
method _request_translation (line 111) | async def _request_translation(self, to_lang: str, prompt: str) -> str:
FILE: manga_translator/translators/m2m100.py
class M2M100Translator (line 12) | class M2M100Translator(OfflineTranslator):
method _load (line 54) | async def _load(self, from_lang: str, to_lang: str, device: str):
method _unload (line 68) | async def _unload(self):
method _infer (line 73) | async def _infer(self, from_lang: str, to_lang: str, queries: List[str...
method tokenize (line 88) | def tokenize(self, queries, lang):
method detokenize (line 95) | def detokenize(self, queries, lang):
class M2M100BigTranslator (line 102) | class M2M100BigTranslator(M2M100Translator):
FILE: manga_translator/translators/mbart50.py
class MBart50Translator (line 31) | class MBart50Translator(OfflineTranslator):
method _load (line 61) | async def _load(self, from_lang: str, to_lang: str, device: str):
method _unload (line 75) | async def _unload(self):
method _infer (line 79) | async def _infer(self, from_lang: str, to_lang: str, queries: list[str...
method _translate_sentence (line 91) | def _translate_sentence(self, from_lang: str, to_lang: str, query: str...
method _map_detected_lang_to_translator (line 113) | def _map_detected_lang_to_translator(self, lang):
method _download (line 119) | async def _download(self):
method _check_downloaded (line 124) | def _check_downloaded(self) -> bool:
FILE: manga_translator/translators/nllb.py
class NLLBTranslator (line 34) | class NLLBTranslator(OfflineTranslator):
method _load (line 64) | async def _load(self, from_lang: str, to_lang: str, device: str):
method _unload (line 73) | async def _unload(self):
method _infer (line 77) | async def _infer(self, from_lang: str, to_lang: str, queries: List[str...
method _translate_sentence (line 89) | def _translate_sentence(self, from_lang: str, to_lang: str, query: str...
method _map_detected_lang_to_translator (line 115) | def _map_detected_lang_to_translator(self, lang):
method _download (line 121) | async def _download(self):
method _check_downloaded (line 127) | def _check_downloaded(self) -> bool:
class NLLBBigTranslator (line 131) | class NLLBBigTranslator(NLLBTranslator):
FILE: manga_translator/translators/none.py
class NoneTranslator (line 5) | class NoneTranslator(CommonTranslator):
method supports_languages (line 7) | def supports_languages(self, from_lang: str, to_lang: str, fatal: bool...
method _translate (line 10) | async def _translate(self, from_lang: str, to_lang: str, queries: List...
FILE: manga_translator/translators/original.py
class OriginalTranslator (line 5) | class OriginalTranslator(CommonTranslator):
method supports_languages (line 7) | def supports_languages(self, from_lang: str, to_lang: str, fatal: bool...
method _translate (line 10) | async def _translate(self, from_lang: str, to_lang: str, queries: List...
FILE: manga_translator/translators/papago.py
class PapagoTranslator (line 13) | class PapagoTranslator(CommonTranslator):
method _translate (line 32) | async def _translate(self, from_lang, to_lang, queries):
method _version_key (line 45) | def _version_key(self):
method _do_request (line 52) | async def _do_request(self, data, version_key):
FILE: manga_translator/translators/qwen2.py
class Qwen2Translator (line 14) | class Qwen2Translator(OfflineTranslator, ConfigGPT):
method __init__ (line 47) | def __init__(self):
method parse_args (line 51) | def parse_args(self, args: TranslatorConfig):
method _load (line 54) | async def _load(self, from_lang: str, to_lang: str, device: str):
method _unload (line 71) | async def _unload(self):
method _infer (line 75) | async def _infer(self, from_lang: str, to_lang: str, queries: List[str...
method tokenize (line 112) | def tokenize(self, queries, to_lang):
class Qwen2BigTranslator (line 154) | class Qwen2BigTranslator(Qwen2Translator):
FILE: manga_translator/translators/sakura.py
class SakuraDict (line 18) | class SakuraDict():
method __init__ (line 19) | def __init__(self, path: str, logger: logging.Logger, version: str = "...
method load_galtransl_dic (line 34) | def load_galtransl_dic(self, dic_path: str):
method load_sakura_dict (line 86) | def load_sakura_dict(self, dic_path: str):
method detect_type (line 130) | def detect_type(self, dic_path: str):
method get_dict_str (line 172) | def get_dict_str(self):
method get_dict_from_file (line 189) | def get_dict_from_file(self, dic_path: str):
class SakuraTranslator (line 203) | class SakuraTranslator(CommonTranslator):
method __init__ (line 223) | def __init__(self):
method get_sakura_version (line 239) | def get_sakura_version(self):
method get_dict_path (line 242) | def get_dict_path(self):
method detect_and_caculate_repeats (line 245) | def detect_and_caculate_repeats(self, s: str, threshold: int = _REPEAT...
method enlarge_small_kana (line 287) | def enlarge_small_kana(text, ignore=''):
method _format_prompt_log (line 329) | def _format_prompt_log(self, prompt: str) -> str:
method _split_text (line 352) | def _split_text(self, text: str) -> List[str]:
method _preprocess_queries (line 360) | def _preprocess_queries(self, queries: List[str]) -> List[str]:
method _check_translation_quality (line 371) | async def _check_translation_quality(self, queries: List[str], respons...
method _detect_repeats (line 406) | def _detect_repeats(self, text: str, threshold: int = _REPEAT_DETECT_T...
method _get_repeat_count (line 413) | def _get_repeat_count(self, text: str, threshold: int = _REPEAT_DETECT...
method _check_align (line 420) | def _check_align(self, queries: List[str], response: str) -> bool:
method _translate_single_lines (line 430) | async def _translate_single_lines(self, queries: List[str]) -> List[str]:
method _delete_quotation_mark (line 444) | def _delete_quotation_mark(self, texts: List[str]) -> List[str]:
method _translate (line 454) | async def _translate(self, from_lang: str, to_lang: str, queries: List...
method _handle_translation_request (line 472) | async def _handle_translation_request(self, prompt: str) -> str:
method _request_translation (line 504) | async def _request_translation(self, input_text_list) -> str:
method _set_gpt_style (line 561) | def _set_gpt_style(self, style_name: str):
FILE: manga_translator/translators/selective.py
function prepare (line 11) | def prepare(translator_supplicant: Callable[[str], OfflineTranslator]):
class SelectiveOfflineTranslator (line 15) | class SelectiveOfflineTranslator(OfflineTranslator):
method __init__ (line 27) | def __init__(self):
method select_translator (line 32) | def select_translator(self, from_lang: str, to_lang: str) -> OfflineTr...
method translate (line 39) | async def translate(self, from_lang: str, to_lang: str, queries: List[...
method load (line 54) | async def load(self, from_lang: str, to_lang: str, device: str):
method reload (line 57) | async def reload(self, from_lang: str, to_lang: str, device: str):
method _load (line 60) | async def _load(self, from_lang: str, to_lang: str, device: str):
method _unload (line 63) | async def _unload(self):
method _infer (line 66) | async def _infer(self, from_lang: str, to_lang: str, queries: List[str...
FILE: manga_translator/translators/sugoi.py
class JparacrawlTranslator (line 9) | class JparacrawlTranslator(OfflineTranslator):
method _load (line 59) | async def _load(self, from_lang: str, to_lang: str, device: str):
method _unload (line 82) | async def _unload(self):
method infer (line 87) | async def infer(self, from_lang: str, to_lang: str, queries: List[str]...
method _infer (line 98) | async def _infer(self, from_lang: str, to_lang: str, queries: List[str...
method tokenize (line 112) | def tokenize(self, queries, lang):
method detokenize (line 119) | def detokenize(self, queries, lang):
class JparacrawlBigTranslator (line 124) | class JparacrawlBigTranslator(JparacrawlTranslator):
class SugoiTranslator (line 142) | class SugoiTranslator(JparacrawlBigTranslator):
method __init__ (line 163) | def __init__(self):
method _load (line 167) | async def _load(self, from_lang: str, to_lang: str, device: str):
method tokenize (line 172) | def tokenize(self, queries, lang):
method detokenize (line 190) | def detokenize(self, queries, lang):
FILE: manga_translator/translators/tokenizers/token_counters.py
class deepseekTokenCounter (line 6) | class deepseekTokenCounter():
method __init__ (line 19) | def __init__(self):
method count_tokens (line 26) | def count_tokens(self, text: str) -> int:
class ChatGPTTokenCounter (line 34) | class ChatGPTTokenCounter():
method __init__ (line 46) | def __init__(self, CHATGPT_MODEL: str):
method _get_encoder_for_model (line 49) | def _get_encoder_for_model(self, CHATGPT_MODEL: str) -> str:
method count_tokens (line 67) | def count_tokens(self, text: str) -> int:
FILE: manga_translator/translators/youdao.py
function sha256_encode (line 12) | def sha256_encode(signStr):
class YoudaoTranslator (line 17) | class YoudaoTranslator(CommonTranslator):
method __init__ (line 41) | def __init__(self):
method _translate (line 46) | async def _translate(self, from_lang, to_lang, queries):
method _truncate (line 71) | def _truncate(self, q):
method _do_request (line 77) | async def _do_request(self, data):
FILE: manga_translator/upscaling/__init__.py
function get_upscaler (line 17) | def get_upscaler(key: Upscaler, *args, **kwargs) -> CommonUpscaler:
function prepare (line 25) | async def prepare(upscaler_key: Upscaler):
function dispatch (line 30) | async def dispatch(upscaler_key: Upscaler, image_batch: List[Image.Image...
function unload (line 38) | async def unload(upscaler_key: Upscaler):
FILE: manga_translator/upscaling/common.py
class CommonUpscaler (line 7) | class CommonUpscaler(InfererModule):
method upscale (line 10) | async def upscale(self, image_batch: List[Image.Image], upscale_ratio:...
method _upscale (line 36) | async def _upscale(self, image_batch: List[Image.Image], upscale_ratio...
class OfflineUpscaler (line 39) | class OfflineUpscaler(CommonUpscaler, ModelWrapper):
method _upscale (line 42) | async def _upscale(self, *args, **kwargs):
method _infer (line 46) | async def _infer(self, image_batch: List[Image.Image], upscale_ratio: ...
FILE: manga_translator/upscaling/esrgan.py
class ESRGANUpscaler (line 57) | class ESRGANUpscaler(OfflineUpscaler):
method _load (line 61) | async def _load(self, device: str):
method _unload (line 64) | async def _unload(self):
method _infer (line 67) | async def _infer(self, image_batch: List[Image.Image], upscale_ratio: ...
method _run_esrgan_executable (line 95) | def _run_esrgan_executable(self, image_directory: str, output_director...
FILE: manga_translator/upscaling/esrgan_pytorch.py
class RRDBNet (line 28) | class RRDBNet(nn.Module):
method __init__ (line 29) | def __init__(self, in_nc, out_nc, nf, nb, nr=3, gc=32, upscale=4, norm...
method forward (line 67) | def forward(self, x, outm=None):
class RRDB (line 78) | class RRDB(nn.Module):
method __init__ (line 84) | def __init__(self, nf, nr=3, kernel_size=3, gc=32, stride=1, bias=1, p...
method forward (line 105) | def forward(self, x):
class ResidualDenseBlock_5C (line 115) | class ResidualDenseBlock_5C(nn.Module):
method __init__ (line 126) | def __init__(self, nf=64, kernel_size=3, gc=32, stride=1, bias=1, pad_...
method forward (line 154) | def forward(self, x):
class GaussianNoise (line 174) | class GaussianNoise(nn.Module):
method __init__ (line 175) | def __init__(self, sigma=0.1, is_relative_detach=False):
method forward (line 181) | def forward(self, x):
function conv1x1 (line 189) | def conv1x1(in_planes, out_planes, stride=1):
class SRVGGNetCompact (line 197) | class SRVGGNetCompact(nn.Module):
method __init__ (line 202) | def __init__(self, num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=16...
method forward (line 240) | def forward(self, x):
class Upsample (line 256) | class Upsample(nn.Module):
method __init__ (line 262) | def __init__(self, size=None, scale_factor=None, mode="nearest", align...
method forward (line 272) | def forward(self, x):
method extra_repr (line 275) | def extra_repr(self):
function pixel_unshuffle (line 284) | def pixel_unshuffle(x, scale):
function pixelshuffle_block (line 301) | def pixelshuffle_block(in_nc, out_nc, upscale_factor=2, kernel_size=3, s...
function upconv_block (line 317) | def upconv_block(in_nc, out_nc, upscale_factor=2, kernel_size=3, stride=...
function make_layer (line 338) | def make_layer(basic_block, num_basic_block, **kwarg):
function act (line 352) | def act(act_type, inplace=True, neg_slope=0.2, n_prelu=1, beta=1.0):
class Identity (line 370) | class Identity(nn.Module):
method __init__ (line 371) | def __init__(self, *kwargs):
method forward (line 374) | def forward(self, x, *kwargs):
function norm (line 378) | def norm(norm_type, nc):
function pad (line 392) | def pad(pad_type, padding):
function get_valid_padding (line 408) | def get_valid_padding(kernel_size, dilation):
class ShortcutBlock (line 414) | class ShortcutBlock(nn.Module):
method __init__ (line 416) | def __init__(self, submodule):
method forward (line 420) | def forward(self, x):
method __repr__ (line 424) | def __repr__(self):
function sequential (line 428) | def sequential(*args):
function conv_block (line 444) | def conv_block(in_nc, out_nc, kernel_size, stride=1, dilation=1, groups=...
function infer_params (line 479) | def infer_params(state_dict):
class ESRGANUpscalerPytorch (line 512) | class ESRGANUpscalerPytorch(OfflineUpscaler):
method _load (line 521) | async def _load(self, device: str):
method _unload (line 534) | async def _unload(self):
method _infer (line 537) | async def _infer(self, image_batch: List[Image.Image], upscale_ratio: ...
function test (line 551) | def test() :
FILE: manga_translator/upscaling/waifu2x.py
class Waifu2xUpscaler (line 52) | class Waifu2xUpscaler(OfflineUpscaler): # ~2GB of vram
method __init__ (line 56) | def __init__(self, *args, **kwargs):
method _load (line 62) | async def _load(self, device: str):
method _unload (line 65) | async def _unload(self):
method _infer (line 68) | async def _infer(self, image_batch: List[Image.Image], upscale_ratio: ...
method _run_waifu2x_executable (line 96) | def _run_waifu2x_executable(self, image_directory: str, output_directo...
FILE: manga_translator/utils/bubble.py
function check_color (line 4) | def check_color(image):
function is_ignore (line 28) | def is_ignore(region_img, ignore_bubble = 0):
FILE: manga_translator/utils/generic.py
class Context (line 28) | class Context(dict):
method __init__ (line 29) | def __init__(self, **kwargs):
method __getattr__ (line 33) | def __getattr__(self, item):
method __delattr__ (line 36) | def __delattr__(self, key) -> None:
method __setattr__ (line 39) | def __setattr__(self, key, value):
method __getstate__ (line 42) | def __getstate__(self):
method __setstate__ (line 45) | def __setstate__(self, state):
method __eq__ (line 48) | def __eq__(self, other):
method __contains__ (line 53) | def __contains__(self, key):
method __repr__ (line 56) | def __repr__(self):
method _get_kwargs (line 71) | def _get_kwargs(self):
method _get_args (line 74) | def _get_args(self):
function atoi (line 79) | def atoi(text: str) -> int | str:
function natural_sort (line 82) | def natural_sort(l: List[str]):
function repeating_sequence (line 85) | def repeating_sequence(s: str):
function count_valuable_text (line 93) | def count_valuable_text(text: str) -> int:
function replace_prefix (line 96) | def replace_prefix(s: str, old: str, new: str):
function chunks (line 101) | def chunks(lst, n):
function get_digest (line 106) | def get_digest(file_path: str) -> str:
function get_image_md5 (line 119) | def get_image_md5(image) -> str:
function get_filename_from_url (line 142) | def get_filename_from_url(url: str, default: str = '') -> str:
function download_url_with_progressbar (line 148) | def download_url_with_progressbar(url: str, path: str):
function prompt_yes_no (line 193) | def prompt_yes_no(query: str, default: bool = None) -> bool:
class AvgMeter (line 206) | class AvgMeter():
method __init__ (line 207) | def __init__(self):
method reset (line 210) | def reset(self):
method __call__ (line 214) | def __call__(self, val = None):
function load_image (line 223) | def load_image(img: Image.Image) -> Tuple[np.ndarray, Optional[Image.Ima...
function dump_image (line 241) | def dump_image(img_pil: Image.Image, img: np.ndarray, alpha_ch: Image.Im...
function resize_keep_aspect (line 251) | def resize_keep_aspect(img, size):
function image_resize (line 257) | def image_resize(image, width = None, height = None, inter = cv2.INTER_A...
function resize_polygon (line 288) | def resize_polygon(pts, xfact, yfact, origin='center'):
class BBox (line 294) | class BBox(object):
method __init__ (line 295) | def __init__(self, x: int, y: int, w: int, h: int, text: str, prob: fl...
method width (line 309) | def width(self):
method height (line 312) | def height(self):
method to_points (line 315) | def to_points(self):
method xywh (line 320) | def xywh(self):
function sort_pnts (line 324) | def sort_pnts(pts: np.ndarray):
class Quadrilateral (line 356) | class Quadrilateral(object):
method __init__ (line 360) | def __init__(self, pts: np.ndarray, text: str, prob: float, fg_r: int ...
method structure (line 378) | def structure(self) -> List[np.ndarray]:
method valid (line 386) | def valid(self) -> bool:
method fg_colors (line 397) | def fg_colors(self):
method bg_colors (line 401) | def bg_colors(self):
method aspect_ratio (line 405) | def aspect_ratio(self) -> float:
method font_size (line 413) | def font_size(self) -> float:
method width (line 419) | def width(self) -> int:
method height (line 422) | def height(self) -> int:
method xyxy (line 426) | def xyxy(self):
method clip (line 429) | def clip(self, width, height):
method aabb (line 439) | def aabb(self) -> BBox:
method get_transformed_region (line 445) | def get_transformed_region(self, img, direction, textheight) -> np.nda...
method is_axis_aligned (line 484) | def is_axis_aligned(self) -> bool:
method is_approximate_axis_aligned (line 497) | def is_approximate_axis_aligned(self) -> bool:
method cosangle (line 510) | def cosangle(self) -> float:
method angle (line 518) | def angle(self) -> float:
method centroid (line 522) | def centroid(self) -> np.ndarray:
method distance_to_point (line 525) | def distance_to_point(self, p: np.ndarray) -> float:
method polygon (line 533) | def polygon(self) -> Polygon:
method area (line 537) | def area(self) -> float:
method poly_distance (line 540) | def poly_distance(self, other) -> float:
method distance (line 543) | def distance(self, other, rho = 0.5) -> float:
method distance_impl (line 546) | def distance_impl(self, other, rho = 0.5) -> float:
method copy (line 598) | def copy(self, new_pts: np.ndarray):
function distance_point_point (line 616) | def distance_point_point(a: np.ndarray, b: np.ndarray) -> float:
function distance_point_lineseg (line 620) | def distance_point_lineseg(p: np.ndarray, p1: np.ndarray, p2: np.ndarray):
function quadrilateral_can_merge_region (line 653) | def quadrilateral_can_merge_region(a: Quadrilateral, b: Quadrilateral, r...
function quadrilateral_can_merge_region_coarse (line 700) | def quadrilateral_can_merge_region_coarse(a: Quadrilateral, b: Quadrilat...
function findNextPowerOf2 (line 716) | def findNextPowerOf2(n):
class Point (line 723) | class Point:
method __init__ (line 724) | def __init__(self, x = 0, y = 0):
method length2 (line 728) | def length2(self) -> float:
method length (line 731) | def length(self) -> float:
method __str__ (line 734) | def __str__(self):
method __add__ (line 737) | def __add__(self, other):
method __sub__ (line 742) | def __sub__(self, other):
method __mul__ (line 747) | def __mul__(self, other):
method __truediv__ (line 753) | def __truediv__(self, other):
method neg (line 756) | def neg(self):
method normalize (line 759) | def normalize(self):
function center_of_points (line 762) | def center_of_points(pts: List[Point]) -> Point:
function support_impl (line 771) | def support_impl(pts: List[Point], d: Point) -> Point:
function support (line 781) | def support(a: List[Point], b: List[Point], d: Point) -> Point:
function cross (line 784) | def cross(a: Point, b: Point, c: Point) -> Point:
function closest_point_to_origin (line 787) | def closest_point_to_origin(a: Point, b: Point) -> Point:
function dcmp (line 799) | def dcmp(a) -> bool:
function gjk_distance (line 804) | def gjk_distance(s1: List[Point], s2: List[Point]) -> float:
function rgb2hex (line 830) | def rgb2hex(r,g,b):
function hex2rgb (line 833) | def hex2rgb(h):
function get_color_name (line 837) | def get_color_name(rgb: List[int]) -> str:
function square_pad_resize (line 849) | def square_pad_resize(img: np.ndarray, tgt_size: int):
function det_rearrange_forward (line 876) | def det_rearrange_forward(
function main (line 1000) | def main():
FILE: manga_translator/utils/generic2.py
function color_difference (line 10) | def color_difference(rgb1: List, rgb2: List) -> float:
function is_punctuation (line 21) | def is_punctuation(ch):
function is_whitespace (line 37) | def is_whitespace(ch):
function is_control (line 49) | def is_control(ch):
function is_valuable_char (line 61) | def is_valuable_char(ch):
function is_valuable_text (line 66) | def is_valuable_text(text):
function dist (line 73) | def dist(x1, y1, x2, y2):
function rect_distance (line 77) | def rect_distance(x1, y1, x1b, y1b, x2, y2, x2b, y2b):
function is_right_to_left_char (line 102) | def is_right_to_left_char(ch):
FILE: manga_translator/utils/inference.py
class InfererModule (line 24) | class InfererModule(ABC):
method __init__ (line 25) | def __init__(self):
method parse_args (line 29) | def parse_args(self, args: TranslatorConfig):
class ModelVerificationException (line 54) | class ModelVerificationException(Exception):
class InvalidModelMappingException (line 57) | class InvalidModelMappingException(ValueError):
method __init__ (line 58) | def __init__(self, cls: str, map_key: str, error_msg: str):
class ModelWrapper (line 62) | class ModelWrapper(ABC):
method __init__ (line 99) | def __init__(self):
method is_loaded (line 106) | def is_loaded(self) -> bool:
method is_downloaded (line 109) | def is_downloaded(self) -> bool:
method model_dir (line 113) | def model_dir(self):
method _get_file_path (line 116) | def _get_file_path(self, *args) -> str:
method _get_used_gpu_memory (line 119) | def _get_used_gpu_memory(self) -> bool:
method _check_for_malformed_model_mapping (line 127) | def _check_for_malformed_model_mapping(self):
method _download_file (line 138) | async def _download_file(self, url: str, path: str):
method _verify_file (line 142) | async def _verify_file(self, sha256_pre_calculated: str, path: str):
method _on_verify_failure (line 152) | def _on_verify_failure(self, sha256_calculated: str, sha256_pre_calcul...
method _temp_working_directory (line 157) | def _temp_working_directory(self):
method download (line 162) | async def download(self, force=False):
method _download (line 177) | async def _download(self):
method _on_download_finished (line 265) | def _on_download_finished(self, map_key):
method _check_downloaded (line 271) | def _check_downloaded(self) -> bool:
method _check_downloaded_map (line 281) | def _check_downloaded_map(self, map_key: str) -> bool:
method _grant_execute_permissions (line 310) | def _grant_execute_permissions(self, map_key: str):
method reload (line 324) | async def reload(self, device: str, *args, **kwargs):
method load (line 328) | async def load(self, device: str, *args, **kwargs):
method unload (line 338) | async def unload(self):
method infer (line 343) | async def infer(self, *args, **kwargs):
method _load (line 353) | async def _load(self, device: str, *args, **kwargs):
method _unload (line 357) | async def _unload(self):
method _infer (line 361) | async def _infer(self, *args, **kwargs):
FILE: manga_translator/utils/log.py
class Formatter (line 8) | class Formatter(logging.Formatter):
method formatMessage (line 9) | def formatMessage(self, record: logging.LogRecord) -> str:
class Filter (line 20) | class Filter(logging.Filter):
method filter (line 21) | def filter(self, record: logging.LogRecord) -> bool:
function init_logging (line 31) | def init_logging():
function set_log_level (line 37) | def set_log_level(level):
function get_logger (line 40) | def get_logger(name: str):
function add_file_logger (line 45) | def add_file_logger(path: str):
function remove_file_logger (line 51) | def remove_file_logger(path: str):
FILE: manga_translator/utils/panel/__init__.py
function get_panels_from_array (line 4) | def get_panels_from_array(img_rgb, rtl=True):
FILE: manga_translator/utils/panel/kumikolib.py
class Kumiko (line 14) | class Kumiko:
method __init__ (line 18) | def __init__(self, options = None):
method parse_url_list (line 32) | def parse_url_list(self, urls):
method parse_pdf_file (line 57) | def parse_pdf_file(self, pdf_filename):
method parse_dir (line 71) | def parse_dir(self, directory, urls = None):
method parse_images (line 77) | def parse_images(self, filenames, urls = None):
method parse_image (line 93) | def parse_image(self, filename, url = None):
method get_infos (line 104) | def get_infos(self):
method save_panels (line 107) | def save_panels(self, output_base_path = 'auto', output_format = "jpg"):
FILE: manga_translator/utils/sort.py
function sort_regions (line 10) | def sort_regions(
function _simple_sort (line 122) | def _simple_sort(regions: List[TextBlock], right_to_left: bool) -> List[...
function _sort_panels_fill (line 153) | def _sort_panels_fill(panels: List[Tuple[int, int, int, int]], right_to_...
function visualize_textblocks (line 199) | def visualize_textblocks(canvas: np.ndarray, blk_list: List[TextBlock], ...
FILE: manga_translator/utils/textblock.py
class TextBlock (line 39) | class TextBlock(object):
method __init__ (line 44) | def __init__(self, lines: List[Tuple[int, int, int, int]],
method xyxy (line 121) | def xyxy(self):
method xywh (line 130) | def xywh(self):
method center (line 135) | def center(self) -> np.ndarray:
method unrotated_polygons (line 140) | def unrotated_polygons(self) -> np.ndarray:
method unrotated_min_rect (line 147) | def unrotated_min_rect(self) -> np.ndarray:
method min_rect (line 157) | def min_rect(self) -> np.ndarray:
method polygon_aspect_ratio (line 169) | def polygon_aspect_ratio(self) -> float:
method unrotated_size (line 178) | def unrotated_size(self) -> Tuple[int, int]:
method aspect_ratio (line 186) | def aspect_ratio(self) -> float:
method polygon_object (line 191) | def polygon_object(self) -> Polygon:
method area (line 196) | def area(self) -> float:
method real_area (line 200) | def real_area(self) -> float:
method normalized_width_list (line 204) | def normalized_width_list(self) -> List[float]:
method __len__ (line 213) | def __len__(self):
method __getitem__ (line 216) | def __getitem__(self, idx):
method to_dict (line 219) | def to_dict(self):
method get_transformed_region (line 223) | def get_transformed_region(self, img: np.ndarray, line_idx: int, texth...
method source_lang (line 286) | def source_lang(self):
method get_translation_for_rendering (line 291) | def get_translation_for_rendering(self):
method is_bulleted_list (line 321) | def is_bulleted_list(self):
method set_font_colors (line 343) | def set_font_colors(self, fg_colors, bg_colors):
method update_font_colors (line 347) | def update_font_colors(self, fg_colors: np.ndarray, bg_colors: np.ndar...
method get_font_colors (line 353) | def get_font_colors(self, bgr=False):
method direction (line 370) | def direction(self):
method vertical (line 412) | def vertical(self):
method horizontal (line 416) | def horizontal(self):
method alignment (line 420) | def alignment(self):
method stroke_width (line 459) | def stroke_width(self):
function rotate_polygons (line 466) | def rotate_polygons(center, polygons, rotation, new_center=None, to_int=...
FILE: manga_translator/utils/threading.py
class PriorityLock (line 3) | class PriorityLock:
class _Context (line 8) | class _Context:
method __init__ (line 9) | def __init__(self, lock: 'PriorityLock', priority: int):
method __aenter__ (line 13) | async def __aenter__(self):
method __aexit__ (line 16) | async def __aexit__(self, exc_type, exc_val, exc_tb):
method __init__ (line 19) | def __init__(self):
method acquire (line 24) | async def acquire(self, priority: int):
method release (line 35) | async def release(self):
method __call__ (line 45) | def __call__(self, priority: int):
class Throttler (line 48) | class Throttler:
method __init__ (line 69) | def __init__(self, rate):
method wrap (line 75) | def wrap(self, func):
method __call__ (line 80) | async def __call__(self, func, *args, **kwargs):
method flush (line 101) | async def flush(self):
FILE: pip-modules/mit-renderer/setup.py
function fix_imports (line 21) | def fix_imports(content):
function build_files (line 29) | def build_files():
class build_py (line 47) | class build_py(_build_py):
method run (line 48) | def run(self):
FILE: server/args.py
function url_decode (line 5) | def url_decode(s):
function path (line 12) | def path(string):
function file_path (line 20) | def file_path(string):
function dir_path (line 28) | def dir_path(string):
function parse_arguments (line 36) | def parse_arguments():
FILE: server/instance.py
class ExecutorInstance (line 10) | class ExecutorInstance(BaseModel):
method free_executor (line 15) | def free_executor(self):
method sent (line 18) | async def sent(self, image: Image, config: Config):
method sent_stream (line 21) | async def sent_stream(self, image: Image, config: Config, sender: Noti...
method sent_batch (line 24) | async def sent_batch(self, images: List[Image.Image], config: Config, ...
method sent_batch_stream (line 29) | async def sent_batch_stream(self, images: List[Image.Image], config: C...
class Executors (line 34) | class Executors:
method __init__ (line 35) | def __init__(self):
method register (line 40) | def register(self, instance: ExecutorInstance):
method free_executors (line 43) | def free_executors(self) -> int:
method _find_instance (line 46) | async def _find_instance(self):
method find_executor (line 54) | async def find_executor(self) -> ExecutorInstance:
method free_executor (line 60) | async def free_executor(self, instance: ExecutorInstance):
FILE: server/main.py
function register_instance (line 46) | async def register_instance(instance: ExecutorInstance, req: Request, re...
function transform_to_image (line 52) | def transform_to_image(ctx):
function transform_to_json (line 65) | def transform_to_json(ctx):
function transform_to_bytes (line 68) | def transform_to_bytes(ctx):
function json (line 72) | async def json(req: Request, data: TranslateRequest):
function bytes (line 77) | async def bytes(req: Request, data: TranslateRequest):
function image (line 82) | async def image(req: Request, data: TranslateRequest) -> StreamingResponse:
function stream_json (line 91) | async def stream_json(req: Request, data: TranslateRequest) -> Streaming...
function stream_bytes (line 95) | async def stream_bytes(req: Request, data: TranslateRequest)-> Streaming...
function stream_image (line 99) | async def stream_image(req: Request, data: TranslateRequest) -> Streamin...
function json_form (line 103) | async def json_form(req: Request, image: UploadFile = File(...), config:...
function bytes_form (line 110) | async def bytes_form(req: Request, image: UploadFile = File(...), config...
function image_form (line 117) | async def image_form(req: Request, image: UploadFile = File(...), config...
function stream_json_form (line 128) | async def stream_json_form(req: Request, image: UploadFile = File(...), ...
function stream_bytes_form (line 138) | async def stream_bytes_form(req: Request, image: UploadFile = File(...),...
function stream_image_form (line 144)
Copy disabled (too large)
Download .json
Condensed preview — 304 files, each showing path, character count, and a content snippet. Download the .json file for the full structured content (10,501K chars).
[
{
"path": ".dockerignore",
"chars": 92,
"preview": "result\n*.ckpt\n*.pt\n.vscode\n*.onnx\n__pycache__\nocrs\nmodels/*\ntest/testdata/bboxes\n/venv\n.git\n"
},
{
"path": ".github/FUNDING.yml",
"chars": 87,
"preview": "# These are supported funding model platforms\r\n\r\nko_fi: voilelabs\r\npatreon: voilelabs\r\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.yml",
"chars": 558,
"preview": "name: Bug Report\ndescription: You think somethings is broken\ntitle: \"[Bug]: \"\nlabels: [\"bug\"]\n\nbody:\n - type: textarea\n"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.yml",
"chars": 555,
"preview": "name: Feature request\ndescription: Suggest an idea for this project\ntitle: \"[Feature Request]: \"\nlabels: [\"enhancement\"]"
},
{
"path": ".github/run.bat",
"chars": 77,
"preview": "@echo off\n\npushd \"%~dp0\"\ngit pull --quiet\npython -m manga_translator %*\npopd\n"
},
{
"path": ".github/workflows/ci.yml",
"chars": 1166,
"preview": "name: CI\n\non:\n push:\n branches: [main]\n paths:\n - '.github/workflows/ci.yml'\n - 'manga_translator/**'\n "
},
{
"path": ".github/workflows/release.yml",
"chars": 1211,
"preview": "name: Publish Release docker image\n\non:\n workflow_dispatch:\n release:\n types: [published]\n\njobs:\n push_to_registry"
},
{
"path": ".github/workflows/stale.yml",
"chars": 1041,
"preview": "name: Mark stale issues\n\non:\n schedule:\n - cron: \"0 0 * * *\" # Runs once per day at midnight UTC\n workflow_dispatch"
},
{
"path": ".gitignore",
"chars": 544,
"preview": "result\r\n*.ckpt\r\n*.pt\r\n.vscode\r\n*.onnx\r\n__pycache__\r\nocrs\r\nManga\r\nManga-translated\r\n/models\r\n.env\r\n*.local\r\n*.local.*\r\nte"
},
{
"path": "CHANGELOG.md",
"chars": 3046,
"preview": "# Changelogs\n\n### 2023-11-11\n\n1. Added new OCR model `48px`\n\n### 2023-05-08\n\n1. Added [4x-UltraSharp](https://mega.nz/fo"
},
{
"path": "CHANGELOG_CN.md",
"chars": 1630,
"preview": "# 更新日志 (中文)\n\n### 2023-11-11\n\n1. 添加了新的OCR模型`48px`\n\n### 2023-05-08\n\n1. 添加了[4x-UltraSharp](https://mega.nz/folder/qZRBmaIY#"
},
{
"path": "Dockerfile",
"chars": 1236,
"preview": "FROM pytorch/pytorch:2.5.1-cuda11.8-cudnn9-runtime\n\n# not apt update: most effective code in pytorch base image is in /o"
},
{
"path": "LICENSE",
"chars": 35149,
"preview": " GNU GENERAL PUBLIC LICENSE\n Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
},
{
"path": "Makefile",
"chars": 570,
"preview": "build-image:\n\tdocker rmi manga-image-translator || true\n\tdocker build . --tag=manga-image-translator\n\nrun-web-server:\n\td"
},
{
"path": "MangaStudioMain.py",
"chars": 3052,
"preview": "# ===============================================================\n# Manga Translation Studio - Main Entry Point\n#\n# Auth"
},
{
"path": "MangaStudioMainRun.bat",
"chars": 1401,
"preview": "@echo off\nREM =================================================================\nREM == "
},
{
"path": "MangaStudio_Data/README.md",
"chars": 6985,
"preview": "# Manga Translation Studio\n\nThis document describes the **Manga Translation Studio**, a comprehensive graphical user int"
},
{
"path": "MangaStudio_Data/app/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "MangaStudio_Data/app/core/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "MangaStudio_Data/app/core/config_loader.py",
"chars": 7118,
"preview": "import os\nimport json\nimport subprocess\nimport sys\nimport re\n\n\nclass ConfigLoader:\n def __init__(self, project_base_d"
},
{
"path": "MangaStudio_Data/app/core/constants.py",
"chars": 4295,
"preview": "# ===============================================================\n# Application Constants\n#\n# Description: This file hol"
},
{
"path": "MangaStudio_Data/app/core/pipeline.py",
"chars": 7524,
"preview": "import os\nimport json\nimport subprocess\nimport gc\nimport shutil\n\ntry:\n import torch\nexcept ImportError:\n torch = N"
},
{
"path": "MangaStudio_Data/app/ui/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "MangaStudio_Data/app/ui/main_window.py",
"chars": 139102,
"preview": "# ===============================================================\n# Main Application Window (PySide6 Version)\n#\n# Author"
},
{
"path": "MangaStudio_Data/dicts/example_post_dict.txt",
"chars": 1201,
"preview": "# =========================================================\n# POST-TRANSLATION DICTIONARY (TRANSLATION CORRECTION)\n# ==="
},
{
"path": "MangaStudio_Data/dicts/example_pre_dict.txt",
"chars": 893,
"preview": "# =========================================================\n# PRE-TRANSLATION DICTIONARY (OCR CORRECTION)\n# ============"
},
{
"path": "MangaStudio_Data/gpt_configs/my_cool_prompt.yaml",
"chars": 2579,
"preview": "# =========================================================\n# GPT (AI TRANSLATOR) CONFIGURATION FILE\n# ================="
},
{
"path": "MangaStudio_Data/profiles/Admin Preset (Creator's custom config - hardware dependent).json",
"chars": 1583,
"preview": "{\n \"filter_text\": \"\",\n \"renderer\": \"default\",\n \"alignment\": \"auto\",\n \"disable_font_border\": false,\n \"font"
},
{
"path": "MangaStudio_Data/tasks.json",
"chars": 2209,
"preview": "{\n \"raw_output\": {\n \"label\": \"RAW Output\",\n \"description\": \"Removes text from the images, leaving blank speech bu"
},
{
"path": "MangaStudio_Data/temp/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "MangaStudio_Data/themes/abyssal_depths.json",
"chars": 431,
"preview": "{\n \"name\": \"Abyssal Depths\",\n \"author\": \"Gemini\",\n \"style\": {\n \"colors\": {\n \"background_main\": \"#000020\",\n "
},
{
"path": "MangaStudio_Data/themes/classic_paper.json",
"chars": 430,
"preview": "{\n \"name\": \"Classic Paper\",\n \"author\": \"Gemini\",\n \"style\": {\n \"colors\": {\n \"background_main\": \"#FDF5E6\",\n "
},
{
"path": "MangaStudio_Data/themes/default_dark.json",
"chars": 434,
"preview": "{\n \"name\": \"Default Dark\",\n \"author\": \"MangaStudio\",\n \"style\": {\n \"colors\": {\n \"background_main\": \"#242424\",\n"
},
{
"path": "MangaStudio_Data/themes/dracula.json",
"chars": 424,
"preview": "{\n \"name\": \"Dracula\",\n \"author\": \"Gemini\",\n \"style\": {\n \"colors\": {\n \"background_main\": \"#282a36\",\n \"bac"
},
{
"path": "MangaStudio_Data/themes/emerald_sea.json",
"chars": 428,
"preview": "{\n \"name\": \"Emerald Sea\",\n \"author\": \"Gemini\",\n \"style\": {\n \"colors\": {\n \"background_main\": \"#F0FFF0\",\n "
},
{
"path": "MangaStudio_Data/themes/golden_sands.json",
"chars": 429,
"preview": "{\n \"name\": \"Golden Sands\",\n \"author\": \"Gemini\",\n \"style\": {\n \"colors\": {\n \"background_main\": \"#FFF8E1\",\n "
},
{
"path": "MangaStudio_Data/themes/graphite.json",
"chars": 425,
"preview": "{\n \"name\": \"Graphite\",\n \"author\": \"Gemini\",\n \"style\": {\n \"colors\": {\n \"background_main\": \"#1E1E1E\",\n \"ba"
},
{
"path": "MangaStudio_Data/themes/lavender_dream.json",
"chars": 431,
"preview": "{\n \"name\": \"Lavender Dream\",\n \"author\": \"Gemini\",\n \"style\": {\n \"colors\": {\n \"background_main\": \"#F3E5F5\",\n "
},
{
"path": "MangaStudio_Data/themes/matrix_code.json",
"chars": 428,
"preview": "{\n \"name\": \"Matrix Code\",\n \"author\": \"Gemini\",\n \"style\": {\n \"colors\": {\n \"background_main\": \"#0D0208\",\n "
},
{
"path": "MangaStudio_Data/themes/minty_fresh.json",
"chars": 428,
"preview": "{\n \"name\": \"Minty Fresh\",\n \"author\": \"Gemini\",\n \"style\": {\n \"colors\": {\n \"background_main\": \"#E8F5E9\",\n "
},
{
"path": "MangaStudio_Data/themes/nordic_noir.json",
"chars": 428,
"preview": "{\n \"name\": \"Nordic Noir\",\n \"author\": \"Gemini\",\n \"style\": {\n \"colors\": {\n \"background_main\": \"#2E3440\",\n "
},
{
"path": "MangaStudio_Data/themes/ocean_breeze.json",
"chars": 429,
"preview": "{\n \"name\": \"Ocean Breeze\",\n \"author\": \"Gemini\",\n \"style\": {\n \"colors\": {\n \"background_main\": \"#E1F5FE\",\n "
},
{
"path": "MangaStudio_Data/themes/ruby_glow.json",
"chars": 426,
"preview": "{\n \"name\": \"Ruby Glow\",\n \"author\": \"Gemini\",\n \"style\": {\n \"colors\": {\n \"background_main\": \"#2B2B2B\",\n \"b"
},
{
"path": "MangaStudio_Data/themes/sakura_sunset.json",
"chars": 430,
"preview": "{\n \"name\": \"Sakura Sunset\",\n \"author\": \"Gemini\",\n \"style\": {\n \"colors\": {\n \"background_main\": \"#FFF0F5\",\n "
},
{
"path": "MangaStudio_Data/themes/solarized_light.json",
"chars": 437,
"preview": "{\n \"name\": \"Solarized Light\",\n \"author\": \"MangaStudio\",\n \"style\": {\n \"colors\": {\n \"background_main\": \"#fdf6e3"
},
{
"path": "MangaStudio_Data/ui_map.json",
"chars": 21001,
"preview": "{\n \"__tab_order__\": [\n \"General & Translator\",\n \"Detector & OCR\",\n \"Image & Inpainter\",\n \"Render & Output\","
},
{
"path": "README.md",
"chars": 61851,
"preview": "# Manga/Image Translator (English Readme)\nLast Updated: 2025/05/10\n---\n \n最后更新时间:2025年5月10日\n---\n \r\n\\\\ Galtransl Format Glossary (Format: source term[Tab/Four spaces]target term"
},
{
"path": "dict/mit_glossary.txt",
"chars": 936,
"preview": "# MIT 格式术语表 (格式:源词[至少一个Tab/空格]目标词[至少一个Tab/空格][#注释 或 //注释]) \n# MIT Format Glossary (Format: source term[at least one Tab"
},
{
"path": "dict/post_dict.txt",
"chars": 825,
"preview": "# 格式:源词[至少一个Tab/空格]目标词[至少一个Tab/空格][#注释 或 //注释]\r\n# Format: source term[at least one Tab/Space]target term[at least one Ta"
},
{
"path": "dict/pre_dict.txt",
"chars": 823,
"preview": "# 格式:源词[至少一个Tab/空格]目标词[至少一个Tab/空格][#注释 或 //注释]\r\n# Format: source term[at least one Tab/Space]target term[at least one Ta"
},
{
"path": "dict/sakura_dict.txt",
"chars": 279,
"preview": "// 示例字典,可自行添加或修改\n\\\\Sakura Format Glossary (Format: source term->target term #comment) \n安芸倫也->安艺伦也 #名字,男性,学生\n倫也->伦也 #名字,男"
},
{
"path": "docker-compose.debug.yml",
"chars": 251,
"preview": "services:\n front:\n image: front\n build:\n context: front\n dockerfile: ./Dockerfile\n environment:\n "
},
{
"path": "docker-compose.yml",
"chars": 173,
"preview": "\nservices:\n front:\n image: front\n build:\n context: front\n dockerfile: ./Dockerfile\n environment:\n "
},
{
"path": "docker_prepare.py",
"chars": 1464,
"preview": "import asyncio\nfrom argparse import ArgumentParser\nfrom manga_translator.utils import ModelWrapper\nfrom manga_translator"
},
{
"path": "examples/Example.env",
"chars": 3074,
"preview": "# This is a list of the current keys, used by translation services.\n# If you wish to use one of these services, you must"
},
{
"path": "examples/config-example.json",
"chars": 1379,
"preview": "{\n \"filter_text\": null,\n \"render\": {\n \"renderer\": \"default\",\n \"alignment\": \"auto\",\n \"disable_font_border\": fa"
},
{
"path": "examples/config-example.toml",
"chars": 840,
"preview": "mask_dilation_offset = 30\n\n[render]\nrenderer = \"default\"\nalignment = \"auto\"\ndisable_font_border = false\nfont_size_offset"
},
{
"path": "examples/gpt_config-example.yaml",
"chars": 9054,
"preview": "# Values will be search for upwards. \n# \n# If you wish to set a global default: \n# Set it as a top-level entry.\n# If"
},
{
"path": "examples/response.cpp",
"chars": 3366,
"preview": "#include <fstream>\n#include <iostream>\n#include <vector>\n#include <string>\n#include <unordered_map>\n#include <cstring>\n#"
},
{
"path": "examples/response.rs",
"chars": 3052,
"preview": "#[derive(Debug)]\nstruct Color {\n fg: [u8; 3],\n bg: [u8; 3],\n}\n\n#[derive(Debug)]\nstruct Translation {\n min_x: u3"
},
{
"path": "examples/translator_chain_example.json",
"chars": 1369,
"preview": "{\n \"filter_text\": null,\n \"render\": {\n \"renderer\": \"default\",\n \"alignment\": \"auto\",\n \"disable_font_border\": fa"
},
{
"path": "front/.dockerignore",
"chars": 309,
"preview": "**/.classpath\n**/.dockerignore\n**/.env\n**/.git\n**/.gitignore\n**/.project\n**/.settings\n**/.toolstarget\n**/.vs\n**/.vscode\n"
},
{
"path": "front/.gitignore",
"chars": 65,
"preview": ".DS_Store\n/node_modules/\n\n# React Router\n/.react-router/\n/build/\n"
},
{
"path": "front/Dockerfile",
"chars": 279,
"preview": "FROM node:lts-alpine\nENV NODE_ENV=production\nWORKDIR /usr/src/app\nCOPY [\"package.json\", \"package-lock.json*\", \"npm-shrin"
},
{
"path": "front/README.md",
"chars": 1237,
"preview": "[中文说明](README_CN.md)\n\n## Features\n\n- 🖼️ Multi-image upload support (drag & drop, paste, file picker)\n- 🔄 Real-time trans"
},
{
"path": "front/README_CN.md",
"chars": 629,
"preview": "[ENGLISH](README.md)\n## 特性\n\n- 🚀 服务器端渲染\n- ⚡️ 热模块替换 (HMR)\n- 📦 资源打包和优化\n- 🔄 数据加载和变更\n- 🔒 默认使用 TypeScript\n- 🎉 使用 TailwindCSS 进"
},
{
"path": "front/app/App.tsx",
"chars": 16229,
"preview": "import React, { useState, useEffect, useMemo } from \"react\";\nimport {\n type StatusKey,\n processingStatuses,\n type Tra"
},
{
"path": "front/app/app.css",
"chars": 300,
"preview": "@import \"tailwindcss\";\n\n@theme {\n --font-sans: \"Inter\", ui-sans-serif, system-ui, sans-serif,\n \"Apple Color Emoji\", "
},
{
"path": "front/app/components/Header.tsx",
"chars": 1435,
"preview": "import { Disclosure } from \"@headlessui/react\";\nimport React from \"react\";\n\ntype Props = {};\n\nexport const Header: React"
},
{
"path": "front/app/components/ImageHandlingArea.tsx",
"chars": 5632,
"preview": "import React from \"react\";\nimport { Icon } from \"@iconify/react\";\nimport { fetchStatusText } from \"@/utils/fetchStatusTe"
},
{
"path": "front/app/components/ImageQueue.tsx",
"chars": 5012,
"preview": "import React from 'react';\nimport { Icon } from '@iconify/react';\nimport type { QueuedImage } from '@/types';\nimport Pre"
},
{
"path": "front/app/components/LabeledInput.tsx",
"chars": 1174,
"preview": "import React from \"react\";\nimport { Icon } from \"@iconify/react\";\n\nexport type LabeledInputProps = {\n id: string;\n lab"
},
{
"path": "front/app/components/LabeledSelect.tsx",
"chars": 1266,
"preview": "import React from \"react\";\nimport { Icon } from \"@iconify/react\";\n\ntype LabeledSelectOption = {\n label: string;\n value"
},
{
"path": "front/app/components/OptionsPanel.tsx",
"chars": 5517,
"preview": "import React from \"react\";\nimport type { TranslatorKey } from \"@/types\";\nimport { validTranslators } from \"@/types\";\nimp"
},
{
"path": "front/app/components/PreviewImage.tsx",
"chars": 676,
"preview": "import React, { useState, useEffect } from \"react\";\n\nconst ImagePreview = React.memo(\n ({ file, result }: { file: File;"
},
{
"path": "front/app/components/ResultGallery.tsx",
"chars": 6539,
"preview": "import React, { useState } from 'react';\nimport { Icon } from '@iconify/react';\nimport type { FinishedImage } from '@/ty"
},
{
"path": "front/app/config.ts",
"chars": 1789,
"preview": "export const languageOptions = [ \n { value: \"CHS\", label: \"简体中文\" }, \n { value: \"CHT\", label: \"繁體中文\" }, \n { value: "
},
{
"path": "front/app/root.tsx",
"chars": 1787,
"preview": "import {\n isRouteErrorResponse,\n Links,\n Meta,\n Outlet,\n Scripts,\n ScrollRestoration,\n} from \"react-router\";\n\nimpo"
},
{
"path": "front/app/routes/home.tsx",
"chars": 299,
"preview": "import type { Route } from \"./+types/home\";\nimport { App } from \"../App\";\n\nexport function meta({}: Route.MetaArgs) {\n "
},
{
"path": "front/app/routes.ts",
"chars": 134,
"preview": "import { type RouteConfig, index } from \"@react-router/dev/routes\";\n\nexport default [index(\"routes/home.tsx\")] satisfies"
},
{
"path": "front/app/types.ts",
"chars": 2226,
"preview": "export type StatusKey =\n | \"upload\"\n | \"pending\"\n | \"detection\"\n | \"ocr\"\n | \"textline_merge\"\n | \"mask-generation\"\n"
},
{
"path": "front/app/utils/fetchStatusText.ts",
"chars": 1396,
"preview": "import type { StatusKey } from \"@/types\";\n\nexport const fetchStatusText = (\n status: StatusKey | null,\n progress: stri"
},
{
"path": "front/app/utils/getTranslatorName.ts",
"chars": 199,
"preview": "import type { TranslatorKey } from \"@/types\";\n\nexport function getTranslatorName(key: TranslatorKey): string {\n if (key"
},
{
"path": "front/app/utils/localStorage.ts",
"chars": 1718,
"preview": "import type { TranslationSettings, FinishedImage } from '@/types';\n\nconst SETTINGS_KEY = 'manga-translator-settings';\nco"
},
{
"path": "front/package.json",
"chars": 998,
"preview": "{\n \"name\": \"front\",\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": {\n \"build\": \"react-router build\",\n \"dev\":"
},
{
"path": "front/react-router.config.ts",
"chars": 292,
"preview": "import type { Config } from \"@react-router/dev/config\";\nimport { vercelPreset } from \"@vercel/react-router/vite\";\n\nexpor"
},
{
"path": "front/tsconfig.json",
"chars": 631,
"preview": "{\n \"include\": [\n \"**/*\",\n \"**/.server/**/*\",\n \"**/.client/**/*\",\n \".react-router/types/**/*\"\n ],\n \"compil"
},
{
"path": "front/vite.config.ts",
"chars": 554,
"preview": "import { reactRouter } from \"@react-router/dev/vite\";\nimport tailwindcss from \"@tailwindcss/vite\";\nimport { defineConfig"
},
{
"path": "manga_translator/__init__.py",
"chars": 125,
"preview": "import colorama\nfrom dotenv import load_dotenv\n\ncolorama.init(autoreset=True)\nload_dotenv()\n\nfrom .manga_translator impo"
},
{
"path": "manga_translator/__main__.py",
"chars": 3904,
"preview": "import os\nimport sys\nimport asyncio\nimport logging\nfrom argparse import Namespace\n\nfrom manga_translator import Config\nf"
},
{
"path": "manga_translator/args.py",
"chars": 8493,
"preview": "import argparse\nimport os\nfrom urllib.parse import unquote\n\nfrom .detection import DETECTORS\nfrom .ocr import OCRS\nfrom "
},
{
"path": "manga_translator/colorization/__init__.py",
"chars": 1095,
"preview": "from PIL import Image\n\nfrom .common import CommonColorizer, OfflineColorizer\nfrom .manga_colorization_v2 import MangaCol"
},
{
"path": "manga_translator/colorization/common.py",
"chars": 801,
"preview": "from PIL import Image\nfrom abc import abstractmethod\n\nfrom ..utils import InfererModule, ModelWrapper\n\nclass CommonColor"
},
{
"path": "manga_translator/colorization/manga_colorization_v2.py",
"chars": 3069,
"preview": "import os\nimport torch\nimport numpy as np\nfrom PIL import Image\nfrom torchvision.transforms import ToTensor\n\nfrom .commo"
},
{
"path": "manga_translator/colorization/manga_colorization_v2_utils/denoising/denoiser.py",
"chars": 4100,
"preview": "\"\"\"\nDenoise an image with the FFDNet denoising method\n\nCopyright (C) 2018, Matias Tassano <matias.tassano@parisdescartes"
},
{
"path": "manga_translator/colorization/manga_colorization_v2_utils/denoising/functions.py",
"chars": 3694,
"preview": "\"\"\"\nFunctions implementing custom NN layers\n\nCopyright (C) 2018, Matias Tassano <matias.tassano@parisdescartes.fr>\n\nThis"
},
{
"path": "manga_translator/colorization/manga_colorization_v2_utils/denoising/models.py",
"chars": 4137,
"preview": "\"\"\"\nDefinition of the FFDNet model and its custom layers\n\nCopyright (C) 2018, Matias Tassano <matias.tassano@parisdescar"
},
{
"path": "manga_translator/colorization/manga_colorization_v2_utils/denoising/utils.py",
"chars": 2062,
"preview": "\"\"\"\nDifferent utilities such as orthogonalization of weights, initialization of\nloggers, etc\n\nCopyright (C) 2018, Matias"
},
{
"path": "manga_translator/colorization/manga_colorization_v2_utils/networks/extractor.py",
"chars": 4116,
"preview": "import torch\nimport torch.nn as nn\nimport math\n\n'''https://github.com/blandocs/Tag2Pix/blob/master/model/pretrained.py''"
},
{
"path": "manga_translator/colorization/manga_colorization_v2_utils/networks/models.py",
"chars": 13263,
"preview": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torchvision.models as M\nimport math\nfrom torch"
},
{
"path": "manga_translator/colorization/manga_colorization_v2_utils/utils/utils.py",
"chars": 1228,
"preview": "import numpy as np\nimport cv2\n\ndef resize_pad(img, size = 256):\n \n if len(img.shape) == 2:\n img = n"
},
{
"path": "manga_translator/config.py",
"chars": 13484,
"preview": "import argparse\nimport re\nfrom enum import Enum\n\nfrom typing import Optional, Any, Literal, List\n\nfrom omegaconf import "
},
{
"path": "manga_translator/detection/__init__.py",
"chars": 1819,
"preview": "import numpy as np\r\n\r\nfrom .default import DefaultDetector\r\nfrom .dbnet_convnext import DBConvNextDetector\r\nfrom .ctd im"
},
{
"path": "manga_translator/detection/common.py",
"chars": 6331,
"preview": "from abc import abstractmethod\nfrom typing import List, Tuple\nfrom collections import Counter\nimport numpy as np\nimport "
},
{
"path": "manga_translator/detection/common_rust.py",
"chars": 2029,
"preview": "import numpy as np\nfrom rusty_manga_image_translator import Session, PyPreprocessorOptions, PyDefaultOptions, PyImage\n\nf"
},
{
"path": "manga_translator/detection/craft.py",
"chars": 7698,
"preview": "\"\"\" \r\nCopyright (c) 2019-present NAVER Corp.\r\nMIT License\r\n\"\"\"\r\n\r\nimport numpy as np\r\nimport torch\r\nimport torch.nn as "
},
{
"path": "manga_translator/detection/craft_utils/refiner.py",
"chars": 2610,
"preview": "\"\"\" \r\nCopyright (c) 2019-present NAVER Corp.\r\nMIT License\r\n\"\"\"\r\n\r\n# -*- coding: utf-8 -*-\r\nimport torch\r\nimport torch.n"
},
{
"path": "manga_translator/detection/craft_utils/vgg16_bn.py",
"chars": 2722,
"preview": "from collections import namedtuple\r\n\r\nimport torch\r\nimport torch.nn as nn\r\nimport torch.nn.init as init\r\nfrom torchvisio"
},
{
"path": "manga_translator/detection/ctd.py",
"chars": 8079,
"preview": "import os\nimport shutil\nimport numpy as np\nimport einops\nfrom typing import Union, Tuple\nimport cv2\nimport torch\n\nfrom ."
},
{
"path": "manga_translator/detection/ctd_utils/__init__.py",
"chars": 303,
"preview": "from .basemodel import TextDetBase, TextDetBaseDNN\r\nfrom .utils.yolov5_utils import non_max_suppression\r\nfrom .utils.db_"
},
{
"path": "manga_translator/detection/ctd_utils/basemodel.py",
"chars": 10305,
"preview": "import cv2\r\nimport copy\r\nimport torch\r\nimport torch.nn as nn\r\n\r\nfrom .utils.yolov5_utils import fuse_conv_and_bn\r\nfrom ."
},
{
"path": "manga_translator/detection/ctd_utils/textmask.py",
"chars": 8232,
"preview": "from typing import List\r\nimport cv2\r\nimport numpy as np\r\n\r\nfrom .utils.imgproc_utils import union_area, enlarge_window\r\n"
},
{
"path": "manga_translator/detection/ctd_utils/utils/db_utils.py",
"chars": 28558,
"preview": "import cv2\r\nimport numpy as np\r\nimport pyclipper\r\nfrom shapely.geometry import Polygon\r\nfrom collections import namedtup"
},
{
"path": "manga_translator/detection/ctd_utils/utils/imgproc_utils.py",
"chars": 6639,
"preview": "import numpy as np\r\nimport cv2\r\nimport random\r\nfrom typing import List\r\n\r\ndef hex2bgr(hex):\r\n gmask = 254 << 8\r\n r"
},
{
"path": "manga_translator/detection/ctd_utils/utils/io_utils.py",
"chars": 1813,
"preview": "import os\r\nimport os.path as osp\r\nimport glob\r\nfrom pathlib import Path\r\nimport cv2\r\nimport numpy as np\r\nimport json\r\n\r\n"
},
{
"path": "manga_translator/detection/ctd_utils/utils/weight_init.py",
"chars": 3888,
"preview": "import torch.nn as nn\r\nimport torch\r\n\r\ndef constant_init(module, val, bias=0):\r\n nn.init.constant_(module.weight, val"
},
{
"path": "manga_translator/detection/ctd_utils/utils/yolov5_utils.py",
"chars": 10456,
"preview": "import math\r\nimport torch\r\nimport torch.nn as nn\r\nimport torch.nn.functional as F\r\nimport cv2\r\nimport numpy as np\r\nimpor"
},
{
"path": "manga_translator/detection/ctd_utils/yolov5/common.py",
"chars": 11143,
"preview": "# YOLOv5 🚀 by Ultralytics, GPL-3.0 license\r\n\"\"\"\r\nCommon modules\r\n\"\"\"\r\n\r\nimport json\r\nimport math\r\nimport platform\r\nimpor"
},
{
"path": "manga_translator/detection/ctd_utils/yolov5/yolo.py",
"chars": 14698,
"preview": "from operator import mod\r\nfrom cv2 import imshow\r\n# from utils.yolov5_utils import scale_img\r\nfrom copy import deepcopy\r"
},
{
"path": "manga_translator/detection/dbnet_convnext.py",
"chars": 20687,
"preview": "\r\nfrom functools import partial\r\nimport shutil\r\nfrom typing import Callable, Optional, Tuple, Union\r\nimport cv2\r\nimport "
},
{
"path": "manga_translator/detection/default.py",
"chars": 4541,
"preview": "import os\nimport shutil\nimport numpy as np\nimport torch\nimport cv2\nimport einops\nfrom typing import List, Tuple\n\nfrom .d"
},
{
"path": "manga_translator/detection/default_utils/CRAFT_resnet34.py",
"chars": 7225,
"preview": "\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.nn.init as init\n\nfrom torchvision.model"
},
{
"path": "manga_translator/detection/default_utils/DBHead.py",
"chars": 2965,
"preview": "# -*- coding: utf-8 -*-\n# @Time : 2019/12/4 14:54\n# @Author : zhoujun\nimport torch\nfrom torch import nn\n\nclass DBHea"
},
{
"path": "manga_translator/detection/default_utils/DBNet_resnet101.py",
"chars": 5912,
"preview": "\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nfrom torchvision.models import resnet101\n\nimport DB"
},
{
"path": "manga_translator/detection/default_utils/DBNet_resnet34.py",
"chars": 5327,
"preview": "\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nfrom torchvision.models import resnet34\n\nfrom . imp"
},
{
"path": "manga_translator/detection/default_utils/craft_utils.py",
"chars": 9163,
"preview": "\"\"\" \nCopyright (c) 2019-present NAVER Corp.\nMIT License\n\"\"\"\n\n# -*- coding: utf-8 -*-\nimport numpy as np\nimport cv2\nimpo"
},
{
"path": "manga_translator/detection/default_utils/dbnet_utils.py",
"chars": 7505,
"preview": "\nimport pyclipper\nimport cv2\nimport numpy as np\nfrom shapely.geometry import Polygon\nimport torch\n\nclass SegDetectorRepr"
},
{
"path": "manga_translator/detection/default_utils/imgproc.py",
"chars": 2332,
"preview": "\"\"\" \nCopyright (c) 2019-present NAVER Corp.\nMIT License\n\"\"\"\n\n# -*- coding: utf-8 -*-\nimport numpy as np\nfrom skimage im"
},
{
"path": "manga_translator/detection/none.py",
"chars": 294,
"preview": "import numpy as np\n\nfrom .common import CommonDetector\n\nclass NoneDetector(CommonDetector):\n async def _detect(self, "
},
{
"path": "manga_translator/detection/paddle_rust.py",
"chars": 197,
"preview": "from manga_translator.detection.common_rust import RustDetector, get_session\n\n\nclass PaddleDetector(RustDetector):\n d"
},
{
"path": "manga_translator/detection/panel_finder.py",
"chars": 7387,
"preview": "from pathlib import Path\r\nimport sys\r\n\r\nimport cv2 as cv\r\nimport numpy as np\r\nfrom PIL import Image\r\n\r\nKERNEL_SIZE = 7\r\n"
},
{
"path": "manga_translator/inpainting/__init__.py",
"chars": 1882,
"preview": "from typing import Optional\r\n\r\nimport numpy as np\r\n\r\nfrom .common import CommonInpainter, OfflineInpainter\r\nfrom .inpain"
},
{
"path": "manga_translator/inpainting/booru_tagger.py",
"chars": 3468,
"preview": "import os\r\nimport gc\r\nimport pandas as pd\r\nimport numpy as np\r\nfrom onnxruntime import InferenceSession\r\nfrom typing imp"
},
{
"path": "manga_translator/inpainting/common.py",
"chars": 984,
"preview": "import numpy as np\nfrom abc import abstractmethod\n\nfrom ..config import InpainterConfig\nfrom ..utils import InfererModul"
},
{
"path": "manga_translator/inpainting/guided_ldm_inpaint4_v15.yaml",
"chars": 1647,
"preview": "model:\r\n target: manga_translator.inpainting.guided_ldm_inpainting.GuidedLDM\r\n params:\r\n linear_start: 0.00085\r\n "
},
{
"path": "manga_translator/inpainting/guided_ldm_inpaint9_v15.yaml",
"chars": 1644,
"preview": "model:\r\n target: manga_translator.inpainting.guided_ldm_inpainting.GuidedLDM\r\n params:\r\n linear_start: 0.00085\r\n "
},
{
"path": "manga_translator/inpainting/guided_ldm_inpainting.py",
"chars": 11553,
"preview": "\r\n\r\nimport einops\r\nimport torch\r\nimport torch as th\r\nimport torch.nn as nn\r\nimport numpy as np\r\nfrom tqdm import tqdm\r\ni"
},
{
"path": "manga_translator/inpainting/inpainting_aot.py",
"chars": 11003,
"preview": "from typing import List, Optional\r\nimport numpy as np\r\nimport os\r\nimport shutil\r\nimport torch\r\nimport torch.nn as nn\r\nim"
},
{
"path": "manga_translator/inpainting/inpainting_attn.py",
"chars": 10486,
"preview": "from typing import List, Optional\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nimport numpy as np"
},
{
"path": "manga_translator/inpainting/inpainting_lama.py",
"chars": 27210,
"preview": "# Fast Fourier Convolution NeurIPS 2020\n# original implementation https://github.com/pkumivision/FFC/blob/main/model_zoo"
},
{
"path": "manga_translator/inpainting/inpainting_lama_mpe.py",
"chars": 32251,
"preview": "# Lama with Masking Positional Encoding\n# original implementation https://github.com/DQiaole/ZITS_inpainting.git\n# paper"
},
{
"path": "manga_translator/inpainting/inpainting_sd.py",
"chars": 5479,
"preview": "import torch\r\nimport numpy as np\r\nimport cv2\r\nimport os\r\nimport einops\r\nimport safetensors\r\nimport safetensors.torch\r\nfr"
},
{
"path": "manga_translator/inpainting/ldm/__init__.py",
"chars": 58,
"preview": "import sys\nsys.path.append('manga_translator/inpainting')\n"
},
{
"path": "manga_translator/inpainting/ldm/data/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "manga_translator/inpainting/ldm/data/util.py",
"chars": 652,
"preview": "import torch\r\n\r\nfrom ldm.modules.midas.api import load_midas_transform\r\n\r\n\r\nclass AddMiDaS(object):\r\n def __init__(se"
},
{
"path": "manga_translator/inpainting/ldm/models/autoencoder.py",
"chars": 19440,
"preview": "import torch\nimport pytorch_lightning as pl\nimport torch.nn.functional as F\nfrom contextlib import contextmanager\n\nfrom "
},
{
"path": "manga_translator/inpainting/ldm/models/diffusion/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "manga_translator/inpainting/ldm/models/diffusion/ddim.py",
"chars": 17304,
"preview": "\"\"\"SAMPLING ONLY.\"\"\"\n\nimport torch\nimport numpy as np\nfrom tqdm import tqdm\n\nfrom ldm.modules.diffusionmodules.util impo"
},
{
"path": "manga_translator/inpainting/ldm/models/diffusion/ddpm.py",
"chars": 87728,
"preview": "\"\"\"\nwild mixture of\nhttps://github.com/lucidrains/denoising-diffusion-pytorch/blob/7706bdfc6f527f58d33f84b7b522e61e6e316"
},
{
"path": "manga_translator/inpainting/ldm/models/diffusion/dpm_solver/__init__.py",
"chars": 37,
"preview": "from .sampler import DPMSolverSampler"
},
{
"path": "manga_translator/inpainting/ldm/models/diffusion/dpm_solver/dpm_solver.py",
"chars": 65968,
"preview": "import torch\nimport torch.nn.functional as F\nimport math\nfrom tqdm import tqdm\n\n\nclass NoiseScheduleVP:\n def __init__"
},
{
"path": "manga_translator/inpainting/ldm/models/diffusion/dpm_solver/sampler.py",
"chars": 2990,
"preview": "\"\"\"SAMPLING ONLY.\"\"\"\nimport torch\n\nfrom .dpm_solver import NoiseScheduleVP, model_wrapper, DPM_Solver\n\n\nMODEL_TYPES = {\n"
},
{
"path": "manga_translator/inpainting/ldm/models/diffusion/plms.py",
"chars": 12927,
"preview": "\"\"\"SAMPLING ONLY.\"\"\"\n\nimport torch\nimport numpy as np\nfrom tqdm import tqdm\nfrom functools import partial\n\nfrom ldm.modu"
},
{
"path": "manga_translator/inpainting/ldm/models/diffusion/sampling_util.py",
"chars": 753,
"preview": "import torch\nimport numpy as np\n\n\ndef append_dims(x, target_dims):\n \"\"\"Appends dimensions to the end of a tensor unti"
},
{
"path": "manga_translator/inpainting/ldm/modules/attention.py",
"chars": 12152,
"preview": "from inspect import isfunction\r\nimport math\r\nimport torch\r\nimport torch.nn.functional as F\r\nfrom torch import nn, einsum"
},
{
"path": "manga_translator/inpainting/ldm/modules/diffusionmodules/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "manga_translator/inpainting/ldm/modules/diffusionmodules/model.py",
"chars": 35240,
"preview": "# pytorch_diffusion + derived encoder decoder\r\nimport math\r\nimport torch\r\nimport torch.nn as nn\r\nimport numpy as np\r\nfro"
},
{
"path": "manga_translator/inpainting/ldm/modules/diffusionmodules/openaimodel.py",
"chars": 31112,
"preview": "from abc import abstractmethod\r\nimport math\r\n\r\nimport numpy as np\r\nimport torch as th\r\nimport torch.nn as nn\r\nimport tor"
},
{
"path": "manga_translator/inpainting/ldm/modules/diffusionmodules/upscaling.py",
"chars": 3505,
"preview": "import torch\r\nimport torch.nn as nn\r\nimport numpy as np\r\nfrom functools import partial\r\n\r\nfrom ldm.modules.diffusionmodu"
},
{
"path": "manga_translator/inpainting/ldm/modules/diffusionmodules/util.py",
"chars": 10136,
"preview": "# adopted from\r\n# https://github.com/openai/improved-diffusion/blob/main/improved_diffusion/gaussian_diffusion.py\r\n# and"
},
{
"path": "manga_translator/inpainting/ldm/modules/distributions/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "manga_translator/inpainting/ldm/modules/distributions/distributions.py",
"chars": 3062,
"preview": "import torch\r\nimport numpy as np\r\n\r\n\r\nclass AbstractDistribution:\r\n def sample(self):\r\n raise NotImplementedEr"
},
{
"path": "manga_translator/inpainting/ldm/modules/ema.py",
"chars": 3192,
"preview": "import torch\r\nfrom torch import nn\r\n\r\n\r\nclass LitEma(nn.Module):\r\n def __init__(self, model, decay=0.9999, use_num_up"
},
{
"path": "manga_translator/inpainting/ldm/modules/encoders/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "manga_translator/inpainting/ldm/modules/encoders/modules.py",
"chars": 7824,
"preview": "import torch\r\nimport torch.nn as nn\r\nfrom torch.utils.checkpoint import checkpoint\r\n\r\nfrom transformers import T5Tokeniz"
},
{
"path": "manga_translator/inpainting/ldm/modules/image_degradation/__init__.py",
"chars": 210,
"preview": "from ldm.modules.image_degradation.bsrgan import degradation_bsrgan_variant as degradation_fn_bsr\r\nfrom ldm.modules.imag"
},
{
"path": "manga_translator/inpainting/ldm/modules/image_degradation/bsrgan.py",
"chars": 25929,
"preview": "# -*- coding: utf-8 -*-\r\n\"\"\"\r\n# --------------------------------------------\r\n# Super-Resolution\r\n# --------------------"
},
{
"path": "manga_translator/inpainting/ldm/modules/image_degradation/bsrgan_light.py",
"chars": 22992,
"preview": "# -*- coding: utf-8 -*-\r\nimport numpy as np\r\nimport cv2\r\nimport torch\r\n\r\nfrom functools import partial\r\nimport random\r\nf"
},
{
"path": "manga_translator/inpainting/ldm/modules/image_degradation/utils_image.py",
"chars": 29938,
"preview": "import os\r\nimport math\r\nimport random\r\nimport numpy as np\r\nimport torch\r\nimport cv2\r\nfrom torchvision.utils import make_"
},
{
"path": "manga_translator/inpainting/ldm/modules/midas/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "manga_translator/inpainting/ldm/modules/midas/api.py",
"chars": 5508,
"preview": "# based on https://github.com/isl-org/MiDaS\r\n\r\nimport cv2\r\nimport torch\r\nimport torch.nn as nn\r\nfrom torchvision.transfo"
},
{
"path": "manga_translator/inpainting/ldm/modules/midas/midas/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "manga_translator/inpainting/ldm/modules/midas/midas/base_model.py",
"chars": 383,
"preview": "import torch\r\n\r\n\r\nclass BaseModel(torch.nn.Module):\r\n def load(self, path):\r\n \"\"\"Load model from file.\r\n\r\n "
},
{
"path": "manga_translator/inpainting/ldm/modules/midas/midas/blocks.py",
"chars": 9584,
"preview": "import torch\r\nimport torch.nn as nn\r\n\r\nfrom .vit import (\r\n _make_pretrained_vitb_rn50_384,\r\n _make_pretrained_vit"
},
{
"path": "manga_translator/inpainting/ldm/modules/midas/midas/dpt_depth.py",
"chars": 3263,
"preview": "import torch\r\nimport torch.nn as nn\r\nimport torch.nn.functional as F\r\n\r\nfrom .base_model import BaseModel\r\nfrom .blocks "
},
{
"path": "manga_translator/inpainting/ldm/modules/midas/midas/midas_net.py",
"chars": 2785,
"preview": "\"\"\"MidashNet: Network for monocular depth estimation trained by mixing several datasets.\r\nThis file contains code that i"
},
{
"path": "manga_translator/inpainting/ldm/modules/midas/midas/midas_net_custom.py",
"chars": 5334,
"preview": "\"\"\"MidashNet: Network for monocular depth estimation trained by mixing several datasets.\r\nThis file contains code that i"
},
{
"path": "manga_translator/inpainting/ldm/modules/midas/midas/transforms.py",
"chars": 8103,
"preview": "import numpy as np\r\nimport cv2\r\nimport math\r\n\r\n\r\ndef apply_min_size(sample, size, image_interpolation_method=cv2.INTER_A"
},
{
"path": "manga_translator/inpainting/ldm/modules/midas/midas/vit.py",
"chars": 15116,
"preview": "import torch\r\nimport torch.nn as nn\r\nimport timm\r\nimport types\r\nimport math\r\nimport torch.nn.functional as F\r\n\r\n\r\nclass "
},
{
"path": "manga_translator/inpainting/ldm/modules/midas/utils.py",
"chars": 4771,
"preview": "\"\"\"Utils for monoDepth.\"\"\"\r\nimport sys\r\nimport re\r\nimport numpy as np\r\nimport cv2\r\nimport torch\r\n\r\n\r\ndef read_pfm(path):"
},
{
"path": "manga_translator/inpainting/ldm/util.py",
"chars": 7425,
"preview": "import importlib\r\n\r\nimport torch\r\nfrom torch import optim\r\nimport numpy as np\r\n\r\nfrom inspect import isfunction\r\nfrom PI"
},
{
"path": "manga_translator/inpainting/none.py",
"chars": 430,
"preview": "import numpy as np\n\nfrom .common import CommonInpainter\nfrom ..config import InpainterConfig\n\n\nclass NoneInpainter(Commo"
},
{
"path": "manga_translator/inpainting/original.py",
"chars": 326,
"preview": "import numpy as np\n\nfrom .common import CommonInpainter\nfrom ..config import InpainterConfig\n\n\nclass OriginalInpainter(C"
},
{
"path": "manga_translator/inpainting/sd_hack.py",
"chars": 3615,
"preview": "import torch\r\nimport einops\r\n\r\nfrom transformers import logging\r\n\r\nfrom . import ldm\r\nfrom .ldm.modules.attention import"
},
{
"path": "manga_translator/manga_translator.py",
"chars": 130038,
"preview": "import asyncio\nimport cv2\nimport json\nimport langcodes\nimport os\nimport regex as re\nimport time\nimport torch\nimport logg"
},
{
"path": "manga_translator/mask_refinement/__init__.py",
"chars": 2633,
"preview": "from typing import List\r\nimport cv2\r\nimport numpy as np\r\n\r\nfrom .text_mask_utils import complete_mask_fill, complete_mas"
},
{
"path": "manga_translator/mask_refinement/text_mask_utils.py",
"chars": 8602,
"preview": "\r\nfrom typing import Tuple, List\r\nimport numpy as np\r\nimport cv2\r\nimport math\r\n\r\nfrom tqdm import tqdm\r\nfrom shapely.geo"
},
{
"path": "manga_translator/mode/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "manga_translator/mode/local.py",
"chars": 25466,
"preview": "import json\nimport os\nimport gc\nimport copy\nfrom typing import Union, List\nimport time \n\nfrom PIL import Image\nimport p"
},
{
"path": "manga_translator/mode/share.py",
"chars": 6382,
"preview": "import asyncio\nimport pickle\nimport io\nimport secrets\nfrom threading import Lock\n\nimport uvicorn\nfrom fastapi import Fas"
},
{
"path": "manga_translator/mode/ws.py",
"chars": 11198,
"preview": "import asyncio\nimport logging\nimport os\nfrom typing import Tuple\n\nimport cv2\nimport numpy as np\nfrom PIL import Image\n\nf"
},
{
"path": "manga_translator/ocr/__init__.py",
"chars": 1460,
"preview": "import numpy as np\r\nfrom typing import List, Optional\r\nfrom .common import CommonOCR, OfflineOCR\r\nfrom .model_32px impor"
},
{
"path": "manga_translator/ocr/common.py",
"chars": 2801,
"preview": "import numpy as np\nfrom abc import abstractmethod\nfrom typing import List, Union\nfrom collections import Counter\nimport "
}
]
// ... and 104 more files (download for full content)
About this extraction
This page contains the full source code of the zyddnys/manga-image-translator GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 304 files (63.2 MB), approximately 2.4M tokens, and a symbol index with 2540 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.