Metadata-Version: 2.4
Name: lemon-manga-translator
Version: 0.0.11
Summary: Koharu-inspired manga translation pipeline (JA->KO) in Python
Keywords: manga,translation,japanese,korean,ocr,inpainting
Author: LemonDouble
Author-email: LemonDouble <contact@lemondouble.com>
License-Expression: GPL-3.0-only
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Graphics
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Dist: numpy
Requires-Dist: pillow
Requires-Dist: opencv-python-headless
Requires-Dist: httpx
Requires-Dist: onnxruntime>=1.17
Requires-Dist: huggingface-hub>=0.20
Requires-Python: >=3.11
Project-URL: Homepage, https://github.com/LemonDouble/lemon-manga-translator
Project-URL: Repository, https://github.com/LemonDouble/lemon-manga-translator
Project-URL: Issues, https://github.com/LemonDouble/lemon-manga-translator/issues
Description-Content-Type: text/markdown

# lemon-manga-translator

일본어 → 한국어 만화(망가) 자동 번역 파이프라인.

한 페이지를 받아 텍스트 영역을 검출하고, Vision LLM으로 SFX 분류 + 번역을 동시에 수행한 뒤, AOT 인페인팅으로 원문을 지우고 그 위에 한국어 텍스트를 렌더한다.

## Quick Start

```bash
pip install lemon-manga-translator
```

```python
from lemon_manga_translator import MangaTranslator
from PIL import Image

mt = MangaTranslator(
    openrouter_api_key="sk-or-...",
    openrouter_model="google/gemini-3.1-flash-lite-preview",
)
mt.prepare()  # HuggingFace에서 ONNX 모델 자동 다운로드

image = Image.open("page.jpg")
result = mt.translate(image)
result.save("output.png")
```

`prepare()` 호출 시 [lemondouble/lemon-manga-translator](https://huggingface.co/lemondouble/lemon-manga-translator)에서 ONNX 모델(CTD 77 MB + AOT 23 MB)을 다운로드한다. 이후 캐시되어 재다운로드하지 않는다.

### 로컬 모델 사용

```python
mt = MangaTranslator(
    openrouter_api_key="sk-or-...",
    openrouter_model="google/gemini-3.1-flash-lite-preview",
    model_source="/path/to/local/models",  # HF 대신 로컬 경로
)
```

## 파이프라인

```
원본 이미지 ──▶ 1) CTD 검출 ──▶ 2) 마스크 제한 ──▶ 3) Vision LLM ──▶ 4) SFX 드롭
                                                                         │
                                                                         ▼
   최종 이미지 ◀── 7) 텍스트 렌더 ◀── 6) 말풍선 확장 ◀── 5) AOT 인페인트
```

| 단계 | 설명 |
|------|------|
| **1) CTD 검출** | YOLOv5 bbox + UNet 마스크로 텍스트 영역을 검출한다. UNet이 잡고 YOLO가 놓친 영역은 connected component로 보조 복구. |
| **2) 마스크 제한** | YOLO bbox 밖의 마스크 픽셀을 제거해 페이지 번호·잡글자를 인페인트 대상에서 뺀다. |
| **3) Vision LLM** | 페이지에 번호 박스를 그려 OpenRouter Vision API에 **한 번만** 호출. 각 블록에 대해 SFX 여부 + 한국어 번역을 JSON으로 받는다. 호출당 wall-clock 60초 상한 · 최대 5회 재시도. |
| **4) SFX 드롭** | SFX(의성어/효과음)로 분류된 블록을 마스크에서 제거. 그림의 일부이므로 지우지 않는다. |
| **5) AOT 인페인트** | AOT-GAN으로 마스크된 텍스트를 자연스러운 배경으로 채운다. |
| **6) 말풍선 확장** | 인페인트 결과에 flood fill을 걸어 말풍선 전체 영역을 찾고, 렌더 영역을 확장한다. |
| **7) 텍스트 렌더** | 확장된 영역 안에 한국어 번역을 최대 폰트 크기로 이진탐색하여 렌더한다. 폰트: Jua(primary) + PretendardJP(fallback). |

### GPU 사용

`device="cuda"`를 지정하면 ONNX 추론(CTD, AOT)이 GPU에서 실행된다.

```python
mt = MangaTranslator(
    openrouter_api_key="sk-or-...",
    openrouter_model="google/gemini-3.1-flash-lite-preview",
    device="cuda",
)
```

GPU 사용 시 `onnxruntime` 대신 환경의 CUDA 버전에 맞는 `onnxruntime-gpu`를 직접 설치해야 한다. 자세한 호환성은 [onnxruntime 공식 문서](https://onnxruntime.ai/docs/execution-providers/CUDA-ExecutionProvider.html) 참고.

### 배경 마스킹

Vision LLM에 보내는 이미지에서 텍스트 bbox 바깥 영역을 마스킹할 수 있다. 배경 정보를 줄여 LLM이 텍스트에 집중하도록 유도하는 옵션.

```python
from lemon_manga_translator import MangaTranslator

mt = MangaTranslator(
    openrouter_api_key="sk-or-...",
    openrouter_model="google/gemini-3.1-flash-lite-preview",
    bg_mask_mode="blur",  # "none" | "solid" | "blur" | "overlay"
)
```

| 모드 | 설명 |
|------|------|
| `none` | 기본값. 원본 이미지 그대로 전송. |
| `solid` | bbox 밖을 흰색으로 채움. |
| `blur` | bbox 밖을 강한 가우시안 블러 처리. |
| `overlay` | bbox 밖을 반투명 회색 오버레이. |

테스트 스크립트에서도 `--bg-mask` 플래그로 사용 가능:

```bash
uv run python scripts/test_pipeline.py image.jpg --bg-mask blur
```

### 배치 번역

여러 페이지를 병렬로 번역할 수 있다. 내부적으로 `ThreadPoolExecutor`를 사용하며, 입력 순서가 보장된다.

```python
images = [Image.open(p) for p in page_paths]
results = mt.translate_batch(images, max_workers=4)

for i, r in enumerate(results):
    r.save(f"output_{i:03d}.png")
```

내부 컴포넌트가 stateless라 단일 인스턴스를 스레드 간 공유해도 안전하다.

### 로깅

라이브러리는 표준 `logging` 모듈로 단계별 진행 상황을 INFO 레벨로 기록한다. 호출자가 핸들러를 설정하면 실시간 로그가 보인다.

```python
import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(name)s: %(message)s",
    datefmt="%H:%M:%S",
)

mt = MangaTranslator(...)
mt.prepare()
mt.translate(image)
```

샘플 출력:

```
11:53:10 INFO lemon_manga_translator.pipeline: 페이지 처리 시작 (크기=1200x1800)
11:53:10 INFO lemon_manga_translator.pipeline: [1/7] CTD 검출 완료: YOLO 12블록
11:53:11 INFO lemon_manga_translator.pipeline: [3/7] Vision LLM 완료 (시도=1회, 대사=11, SFX/제외=3)
11:53:13 INFO lemon_manga_translator.pipeline: [7/7] 텍스트 렌더 완료 (11블록)
```

## Docker

`pre_download()`로 빌드 시 모델을 미리 캐싱하면 컨테이너 시작 시 다운로드 없이 바로 사용할 수 있다.

```dockerfile
FROM python:3.11-slim

RUN pip install lemon-manga-translator

# 빌드 시 ONNX 모델을 캐시에 다운로드 (세션 로드 없음)
RUN python -c "from lemon_manga_translator import MangaTranslator; \
    MangaTranslator.pre_download()"

COPY server.py .
CMD ["python", "server.py"]
```

```python
# server.py — prepare()는 캐시에서 즉시 로드
mt = MangaTranslator(
    openrouter_api_key=os.environ["OPENROUTER_API_KEY"],
    openrouter_model="google/gemini-3.1-flash-lite-preview",
)
mt.prepare()
```

## Debug

```python
from lemon_manga_translator.api.debug_translator import DebugTranslator

dt = DebugTranslator(
    openrouter_api_key="sk-or-...",
    openrouter_model="google/gemini-3.1-flash-lite-preview",
)
dt.prepare()

image = Image.open("page.jpg")
result = dt.translate(image, debug_dir="./debug/")

result.detection       # CTD 결과 (mask + text_blocks)
result.inpainted       # AOT 인페인트 결과
result.vision_results  # LLM 응답 (block_index, is_sfx, ko_translation)
result.save_all("./debug/")
```

`debug_dir`를 지정하면 각 단계의 중간 산출물이 파일명 prefix로 정렬되어 저장된다.

<details>
<summary>디버그 산출물 목록</summary>

| 파일 | 의미 |
|------|------|
| `00_input.png` | 원본 입력 이미지 |
| `00_params.json` | 실행 하이퍼파라미터 스냅샷 |
| `01_mask_raw.png` | UNet 원본 float 마스크 |
| `01_mask_refined.png` | 임계화 + 모폴로지 후처리 후 마스크 |
| `01b_mask_bbox_restricted.png` | YOLO bbox 밖 마스크 제거 후 |
| `02_bboxes.png` | 원본 위에 YOLO 텍스트 박스 표시 |
| `03_mask_overlay.png` | 마스크를 원본 위에 핑크색 반투명 오버레이 |
| `03b_mask_filtered.png` | SFX 드롭 후 마스크 (해당 시만) |
| `03c_mask_filtered_overlay.png` | SFX 드롭 마스크 오버레이 (해당 시만) |
| `04_inpainted.png` | AOT 인페인트 결과 |
| `05a_annotated.png` | LLM에 전송된 번호 박스 이미지 |
| `05_vision.json` | LLM 응답 JSON |
| `05_cost.json` | 토큰/비용 정보 |
| `05b_render_bboxes.png` | 원본 bbox(빨강) + 확장된 render_bbox(초록) |
| `06_final.png` | 최종 결과 |

</details>

## 모델·코드·폰트 출처

모델 가중치는 [lemondouble/lemon-manga-translator](https://huggingface.co/lemondouble/lemon-manga-translator)에서 PyTorch(safetensors) + ONNX 형태로 제공된다.

| 구성 요소 | 출처 | 라이선스 |
|----------|------|---------|
| Comic Text Detector | 원 저자 [dmMaze/comic-text-detector](https://github.com/dmMaze/comic-text-detector) · 배포 [manga-image-translator beta-0.3](https://github.com/zyddnys/manga-image-translator/releases/tag/beta-0.3) | [GPL-3.0](https://github.com/dmMaze/comic-text-detector/blob/master/LICENSE) |
| AOT Inpainting | 아키텍처 [AOT-GAN](https://arxiv.org/abs/2104.01431) (Zeng et al., 2021) · 파인튜닝 [manga-image-translator beta-0.3](https://github.com/zyddnys/manga-image-translator/releases/tag/beta-0.3) · 리팩 [mayocream/aot-inpainting](https://huggingface.co/mayocream/aot-inpainting) | [GPL-3.0](https://github.com/zyddnys/manga-image-translator/blob/main/LICENSE) |
| `detection/ctd_utils/` | [zyddnys/manga-image-translator](https://github.com/zyddnys/manga-image-translator) `manga_translator/detection/ctd_utils` (벤더) | [GPL-3.0](https://github.com/zyddnys/manga-image-translator/blob/main/LICENSE) |
| `assets/fonts/Jua-Regular.ttf` | The Jua Project Authors / Woowahan Brothers · [Google Fonts: Jua](https://fonts.google.com/specimen/Jua) | [SIL OFL 1.1](https://openfontlicense.org/) |
| `assets/fonts/PretendardJP-Regular.otf` | [orioncactus/pretendard](https://github.com/orioncactus/pretendard) (Kil Hyung-jin) | [SIL OFL 1.1](https://github.com/orioncactus/pretendard/blob/main/LICENSE) |
| Vision LLM (런타임 API) | [OpenRouter](https://openrouter.ai/) 경유 호출 | 번들되지 않음. 사용 모델 제공자 ToS |

## License

[GPL-3.0](LICENSE)
