lemon-manga-translator
일본어 → 한국어 만화(망가) 자동 번역 파이프라인.
학습된 검출 모델로 글자와 말풍선을 찾고, Vision LLM으로 읽기와 번역을 함께 처리합니다. 번역문을 배치할 수 있는 영역의 원문을 지우고 한국어를 그립니다.
Quick Start
pip install lemon-manga-translator
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",
)
mt.prepare() # HuggingFace에서 ONNX 모델 자동 다운로드
image = Image.open("page.jpg")
result = mt.translate(image)
result.save("output.png")
prepare()는 Koharu Layout ONNX의 글자·말풍선 검출 모델과 AOT를 내려받아 캐시합니다. 검출 모델 버전은 고정되어 있습니다. 모델의 사용 조건은 각각의 모델 카드를 확인해야 합니다.
- 기본 번역 범위는 대사·내레이션·제목·이름표·표지판입니다. 색상이 있거나 손글씨·장식체여도 이 범위에 해당하면 번역합니다.
- 효과음·저작권 표시·읽을 수 없는 글자와 상품 포장에 인쇄된 상품명·작은 장식 글자는 보존합니다. 포장의 상품명·작은 장식 글자는 읽혀도
no_translate=true,ko_translation=null로 처리합니다. - 작은 글자와 검출이 누락된 글자의 번역은 보장하지 않습니다. 원문 색상은 자동 복원하지 않습니다.
- 기존 CTD 검출은
detector="ctd"로 선택할 수 있습니다.
로컬 모델 사용
mt = MangaTranslator(
openrouter_api_key="sk-or-...",
openrouter_model="google/gemini-3.1-flash-lite",
model_source="/path/to/local/models", # HF 대신 로컬 경로
)
로컬 모델은 onnx/layout/rfdetr-seg-2xlarge.onnx, onnx/aot-inpainting/aot_folded.onnx, pytorch/aot-inpainting/config.json에 둡니다. CTD를 선택하면 검출 모델 경로는 onnx/comic-text-detector/ctd.onnx입니다.
파이프라인
원본 → 글자·말풍선 검출 → Vision 읽기·번역 → 응답·배치 검증 → 원문 삭제 → 한국어 합성
| 단계 | 설명 |
|---|---|
| 검출 | RF-DETR의 글자 mask와 말풍선 mask를 연결합니다. 같은 말풍선의 대사는 함께 읽으며 효과음은 별도 후보로 유지합니다. |
| 읽기·번역 | 번호가 있는 페이지를 한 번 보내고 JSON 응답을 검증합니다. 기본 모델 최대 2회, fallback 최대 1회까지 호출하며 전체 사용량을 합산합니다. |
| 배치 검증 | ID와 타입을 확인하고 실제 폰트·폴백·stroke의 픽셀을 검사합니다. 원문을 보존하는 영역에는 번역 글자를 놓지 않습니다. |
| 삭제 | 유효한 번역과 배치가 있는 블록만 지웁니다. 학습된 말풍선의 균일한 배경은 같은 색으로 채우고 나머지는 AOT로 처리합니다. API 실패·번역 누락·배치 실패 때 원문을 남깁니다. |
| 합성 | 검증한 글자 레이어를 그대로 그립니다. 말풍선 글자 크기는 페이지의 중앙값으로 상한을 제한합니다. 같은 크기에서는 단어를 덜 나누는 줄바꿈을 먼저 고르고, 중앙에 가까운 배치를 선택합니다. Jua와 PretendardJP를 사용합니다. |
GPU 사용
device="cuda"를 지정하면 ONNX 추론을 GPU에서 실행합니다. CUDA provider 초기화에 실패하면 오류를 반환합니다.
mt = MangaTranslator(
openrouter_api_key="sk-or-...",
openrouter_model="google/gemini-3.1-flash-lite",
device="cuda",
)
GPU 사용 시 onnxruntime 대신 환경의 CUDA 버전에 맞는 onnxruntime-gpu를 직접 설치해야 한다. 자세한 호환성은 onnxruntime 공식 문서 참고.
튜닝
각 단계의 하이퍼파라미터는 dataclass로 노출되어 있다. MangaTranslator 생성자에 주입하면 단계 동작을 바꿀 수 있고, 미지정 시 라이브러리 기본값을 사용한다.
from lemon_manga_translator import (
MangaTranslator,
LayoutConfig,
CtdConfig,
BubbleConfig,
PipelineConfig,
RenderConfig,
)
mt = MangaTranslator(
openrouter_api_key="sk-or-...",
openrouter_model="google/gemini-3.1-flash-lite",
layout_config=LayoutConfig(text_confidence_threshold=0.25),
pipeline_config=PipelineConfig(expand_bubbles=True),
render_config=RenderConfig(min_font_size=12, max_font_size=64),
vision_system_prompt="path/to/custom_prompt.txt", # 파일 경로 또는 문자열
)
| Config | 다루는 것 |
|---|---|
LayoutConfig |
학습 검출 confidence와 글자·말풍선 mask 연결 기준 |
CtdConfig, BubbleConfig |
CTD를 선택할 때 사용하는 기존 검출·flood 설정 |
PipelineConfig |
drop_bbox_pad, skip_llm, expand_bubbles |
RenderConfig |
텍스트 색/스트로크, 패딩, mask 여백, 최소·최대 글자 크기. 최소 크기 기본값은 페이지 크기에 따라 정합니다. |
기본 Vision 시스템 프롬프트는 lemon_manga_translator/prompts/vision_system.txt에 동봉되어 있다.
- 기본
openrouter_response_mode="standard"는 각 블록의id,text_kind,no_translate,ko_translation을 받습니다.openrouter_response_mode="compact_reading"는 같은 호출에서 일본어를 먼저 읽고[id, text_kind, ja_source, ko_translation]배열을 JSON schema로 받습니다. 번역할 때 일본어 원문도 비어 있지 않아야 하며, 보존할 때는 번역을null로 반환합니다. 공개 결과 형식은 같습니다. - 사용자 정의 프롬프트는 선택한 모드의 응답 형식을 따라야 합니다.
text_kind는 기본 프롬프트의 분류 값 중 하나이며, 포장 상품명·장식 글자·효과음·기타·불확실한 글자는 코드에서 원문을 보존합니다. 수동 번역의VisionBlockResult형식은 세 필드를 유지합니다.
배경 마스킹
Vision LLM에 보내는 이미지에서 텍스트 bbox 바깥 영역을 마스킹할 수 있다. 배경 정보를 줄여 LLM이 텍스트에 집중하도록 유도하는 옵션.
from lemon_manga_translator import MangaTranslator
mt = MangaTranslator(
openrouter_api_key="sk-or-...",
openrouter_model="google/gemini-3.1-flash-lite",
bg_mask_mode="blur", # "none" | "solid" | "blur" | "overlay"
)
| 모드 | 설명 |
|---|---|
none |
기본값. 원본 이미지 그대로 전송. |
solid |
bbox 밖을 흰색으로 채움. |
blur |
bbox 밖을 강한 가우시안 블러 처리. |
overlay |
bbox 밖을 반투명 회색 오버레이. |
테스트 스크립트에서도 --bg-mask 플래그로 사용 가능:
uv run python scripts/test_pipeline.py image.jpg --bg-mask blur
배치 번역
여러 페이지를 병렬로 번역할 수 있다. 내부적으로 ThreadPoolExecutor를 사용하며, 입력 순서가 보장된다.
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라 단일 인스턴스를 스레드 간 공유해도 안전하다.
진행 상황 콜백
on_progress를 넘기면 각 단계를 시작할 때 PipelineStage로 호출된다. 텍스트가 없는 페이지는 DETECT 이후 바로 끝난다.
from lemon_manga_translator import PipelineStage
def on_progress(stage: PipelineStage) -> None:
print(stage) # detect → translate → inpaint → render
result = mt.translate(image, on_progress=on_progress)
| 단계 | 파이프라인 |
|---|---|
DETECT |
1~2 |
TRANSLATE |
3~4 |
INPAINT |
5 |
RENDER |
6~7 |
on_stage_done을 넘기면 각 단계가 끝날 때 StageResult로 중간 결과를 받는다.
| 단계 | 채워지는 필드 |
|---|---|
DETECT |
text_blocks (bbox) |
TRANSLATE |
vision_results (블록별 번역, no_translate) |
INPAINT |
image (원문을 지운 이미지) |
RENDER |
image (최종 이미지) |
번역 수정 후 다시 렌더
translations에 VisionBlockResult 목록을 넘기면 Vision LLM을 호출하지 않고 그 번역으로 인페인트·렌더만 다시 한다. 같은 이미지는 검출 결과가 같으므로 block_index가 그대로 유지된다. no_translate=True인 블록은 지우지 않고 원문을 남긴다.
text_boxes로 추가하거나 바꾼 박스는 해당 사각형을 삭제·배치 영역으로 사용합니다. 좌표를 바꾸지 않은 박스는 학습 검출의 mask를 유지합니다. block_index는 박스 순서입니다. 수동 번역에서 no_translate=False와 빈 번역문을 함께 보내면 해당 원문만 지웁니다.
from dataclasses import replace
first = []
mt.translate(image, on_stage_done=lambda result: first.extend(result.vision_results or []))
edited = [replace(first[0], ko_translation="수정한 번역")] + first[1:]
result = mt.translate(image, translations=edited)
로깅
라이브러리는 표준 logging 모듈로 단계별 진행 상황을 INFO 레벨로 기록한다. 호출자가 핸들러를 설정하면 실시간 로그가 보인다.
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 검출 완료: 텍스트 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()로 빌드 시 모델을 미리 캐싱하면 컨테이너 시작 시 다운로드 없이 바로 사용할 수 있다.
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"]
# server.py — prepare()는 캐시에서 즉시 로드
mt = MangaTranslator(
openrouter_api_key=os.environ["OPENROUTER_API_KEY"],
openrouter_model="google/gemini-3.1-flash-lite",
)
mt.prepare()
Debug
from lemon_manga_translator.api.debug_translator import DebugTranslator
dt = DebugTranslator(
openrouter_api_key="sk-or-...",
openrouter_model="google/gemini-3.1-flash-lite",
)
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로 정렬되어 저장된다.
디버그 산출물 목록
| 파일 | 의미 |
|---|---|
00_input.png |
원본 입력 이미지 |
00_params.json |
실행 하이퍼파라미터 스냅샷 |
01_mask_raw.png |
UNet 원본 float 마스크 |
01_mask_refined.png |
임계화 + 모폴로지 후처리 후 마스크 |
01b_mask_bbox_restricted.png |
최종 텍스트 bbox 밖 마스크 제거 후 |
02_bboxes.png |
원본 위에 최종 텍스트 박스 + 박스별 YOLO/UNet 신뢰도 라벨 |
02_text_blocks.json |
검출된 텍스트 블록 메타(bbox, yolo_confidence 등) JSON |
02a_balloon_flood_scope.png |
YOLO seed 기반 CTD flood fill 영역 |
02a_balloon_flood_overlay.png |
CTD flood fill 영역을 원본 위에 하늘색 반투명 오버레이 |
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(초록) + 말풍선 확장 status/expansion ratio 라벨 |
06_final.png |
최종 결과 |
Dev Playground
파라미터를 바꿔가며 단계별로 결과를 확인하는 로컬 디버깅 GUI(dev-tools/playground)가 별도로 들어 있다 — 라이브러리 PyPI 패키지에는 포함되지 않는다. 사용법은 dev-tools/README.md 참고.
모델·코드·폰트 출처
모델 가중치는 lemondouble/lemon-manga-translator에서 PyTorch(safetensors) + ONNX 형태로 제공된다.
| 구성 요소 | 출처 | 라이선스 |
|---|---|---|
| Comic Text Detector | 원 저자 dmMaze/comic-text-detector · 배포 manga-image-translator beta-0.3 | GPL-3.0 |
| AOT Inpainting | 아키텍처 AOT-GAN (Zeng et al., 2021) · 파인튜닝 manga-image-translator beta-0.3 · 리팩 mayocream/aot-inpainting | GPL-3.0 |
detection/ctd_utils/ |
zyddnys/manga-image-translator manga_translator/detection/ctd_utils (벤더) |
GPL-3.0 |
assets/fonts/Jua-Regular.ttf |
The Jua Project Authors / Woowahan Brothers · Google Fonts: Jua | SIL OFL 1.1 |
assets/fonts/PretendardJP-Regular.otf |
orioncactus/pretendard (Kil Hyung-jin) | SIL OFL 1.1 |
| Vision LLM (런타임 API) | OpenRouter 경유 호출 | 번들되지 않음. 사용 모델 제공자 ToS |
License
Metadata
Release files for lemon-manga-translator 0.0.21
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| lemon_manga_translator-0.0.21.tar.gz | 3.5 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| lemon_manga_translator-0.0.21-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 7.1 MB
Release files / lemon_manga_translator-0.0.21.tar.gz
| Download URL | lemon_manga_translator-0.0.21.tar.gz |
|---|---|
| Size | 3.5 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8ad20322b805dc07f7e05915547606efbc313684fa3ee5e5e7a5c81364d926c2
|
|
BLAKE2b-256 checksum How to use checksums |
4c86b67c6cca9b0b55db908b62cfddcd351cdfe07b2b208b31c8dee22d3abc92
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.
Transparency logRelease files / lemon_manga_translator-0.0.21-py3-none-any.whl
| Download URL | lemon_manga_translator-0.0.21-py3-none-any.whl |
|---|---|
| Size | 3.6 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d9cd2e65719263e168d713cbe79c3a88a402de0a79c853c14aa8451ec62d972c
|
|
BLAKE2b-256 checksum How to use checksums |
60e82fd70e04d94e972ea42ed6f6d7371677a0d1e6561e6ab650bcc93ec0e0e2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.
Transparency log