Started pipelines, render not working
This commit is contained in:
159
pipeline-render.py
Normal file
159
pipeline-render.py
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
pipeline_render.py
|
||||||
|
───────────────────────────────────────────────────────────────
|
||||||
|
Standalone Rendering Pipeline
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python pipeline-render.py /path/to/chapter/folder
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
import zipfile
|
||||||
|
import importlib.util
|
||||||
|
from pathlib import Path
|
||||||
|
import cv2 # ✅ Added OpenCV to load the image
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
# CONFIG
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
DEFAULT_FONT_PATH = "fonts/ComicNeue-Regular.ttf"
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
# DYNAMIC MODULE LOADER
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
def load_module(name, filepath):
|
||||||
|
spec = importlib.util.spec_from_file_location(name, filepath)
|
||||||
|
if spec is None or spec.loader is None:
|
||||||
|
raise FileNotFoundError(f"Cannot load spec for {filepath}")
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
# HELPERS
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
def sorted_pages(chapter_dir):
|
||||||
|
exts = {".jpg", ".jpeg", ".png", ".webp"}
|
||||||
|
pages = [
|
||||||
|
p for p in Path(chapter_dir).iterdir()
|
||||||
|
if p.is_file() and p.suffix.lower() in exts
|
||||||
|
]
|
||||||
|
return sorted(pages, key=lambda p: p.stem)
|
||||||
|
|
||||||
|
def pack_rendered_cbz(chapter_dir, output_cbz, rendered_files):
|
||||||
|
if not rendered_files:
|
||||||
|
print("⚠️ No rendered pages found — CBZ not created.")
|
||||||
|
return
|
||||||
|
|
||||||
|
with zipfile.ZipFile(output_cbz, "w", compression=zipfile.ZIP_STORED) as zf:
|
||||||
|
for rp in rendered_files:
|
||||||
|
arcname = rp.name
|
||||||
|
zf.write(rp, arcname)
|
||||||
|
|
||||||
|
print(f"\n✅ Rendered CBZ saved → {output_cbz}")
|
||||||
|
print(f"📦 Contains: {len(rendered_files)} translated pages ready to read.")
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
# PER-PAGE PIPELINE
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
def process_render(page_path, workdir, renderer_module, font_path):
|
||||||
|
print(f"\n{'─' * 70}")
|
||||||
|
print(f"🎨 RENDERING: {page_path.name}")
|
||||||
|
print(f"{'─' * 70}")
|
||||||
|
|
||||||
|
txt_path = workdir / "output.txt"
|
||||||
|
json_path = workdir / "bubbles.json"
|
||||||
|
out_img = workdir / page_path.name
|
||||||
|
|
||||||
|
if not txt_path.exists() or not json_path.exists():
|
||||||
|
print(" ⚠️ Missing output.txt or bubbles.json. Did you run the OCR pipeline first?")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# ✅ FIX: Load the image into memory (as a NumPy array) before passing it
|
||||||
|
img_array = cv2.imread(str(page_path.resolve()))
|
||||||
|
if img_array is None:
|
||||||
|
print(f" ❌ Failed to load image: {page_path.name}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
orig_dir = os.getcwd()
|
||||||
|
try:
|
||||||
|
os.chdir(workdir)
|
||||||
|
|
||||||
|
# Pass the loaded image array instead of the string path
|
||||||
|
renderer_module.render_translations(
|
||||||
|
img_array, # 1st arg: Image Data (NumPy array)
|
||||||
|
str(out_img.resolve()), # 2nd arg: Output image path
|
||||||
|
str(txt_path.resolve()), # 3rd arg: Translations text
|
||||||
|
str(json_path.resolve()), # 4th arg: Bubbles JSON
|
||||||
|
font_path # 5th arg: Font Path
|
||||||
|
)
|
||||||
|
print(" ✅ Render complete")
|
||||||
|
return out_img
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ❌ Failed: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
finally:
|
||||||
|
os.chdir(orig_dir)
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
# MAIN
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Manga Rendering Pipeline")
|
||||||
|
parser.add_argument("chapter_dir", help="Path to the folder containing original manga pages")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
chapter_dir = Path(args.chapter_dir).resolve()
|
||||||
|
output_cbz = chapter_dir.parent / f"{chapter_dir.name}_rendered.cbz"
|
||||||
|
|
||||||
|
script_dir = Path(__file__).parent
|
||||||
|
absolute_font_path = str((script_dir / DEFAULT_FONT_PATH).resolve())
|
||||||
|
|
||||||
|
print("Loading renderer module...")
|
||||||
|
try:
|
||||||
|
renderer = load_module("manga_renderer", str(script_dir / "manga-renderer.py"))
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Could not load manga-renderer.py: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
pages = sorted_pages(chapter_dir)
|
||||||
|
if not pages:
|
||||||
|
print(f"❌ No images found in: {chapter_dir}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print(f"\n📖 Chapter : {chapter_dir}")
|
||||||
|
print(f" Pages : {len(pages)}\n")
|
||||||
|
|
||||||
|
succeeded, failed = [], []
|
||||||
|
rendered_files = []
|
||||||
|
|
||||||
|
for i, page_path in enumerate(pages, start=1):
|
||||||
|
print(f"[{i}/{len(pages)}] Checking data for {page_path.name}...")
|
||||||
|
workdir = Path(chapter_dir) / "translated" / page_path.stem
|
||||||
|
|
||||||
|
out_file = process_render(page_path, workdir, renderer, absolute_font_path)
|
||||||
|
if out_file:
|
||||||
|
succeeded.append(page_path.name)
|
||||||
|
rendered_files.append(out_file)
|
||||||
|
else:
|
||||||
|
failed.append(page_path.name)
|
||||||
|
|
||||||
|
print(f"\n{'═' * 70}")
|
||||||
|
print("RENDER PIPELINE COMPLETE")
|
||||||
|
print(f"✅ {len(succeeded)} page(s) rendered successfully")
|
||||||
|
if failed:
|
||||||
|
print(f"❌ {len(failed)} page(s) skipped or failed:")
|
||||||
|
for f in failed:
|
||||||
|
print(f" • {f}")
|
||||||
|
print(f"{'═' * 70}\n")
|
||||||
|
|
||||||
|
print("Packing final CBZ...")
|
||||||
|
pack_rendered_cbz(chapter_dir, output_cbz, rendered_files)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
"""
|
"""
|
||||||
pipeline.py
|
pipeline.py
|
||||||
───────────────────────────────────────────────────────────────
|
───────────────────────────────────────────────────────────────
|
||||||
Translation + render pipeline
|
Translation OCR pipeline (No Rendering)
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
python pipeline.py /path/to/chapter/folder
|
python pipeline.py /path/to/chapter/folder
|
||||||
@@ -30,14 +30,6 @@ QUALITY_THRESHOLD = 0.50
|
|||||||
READING_MODE = "ltr"
|
READING_MODE = "ltr"
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
# Renderer Settings
|
|
||||||
RENDER_ENABLED = True
|
|
||||||
RENDER_OUTPUT_NAME = "page_translated.png"
|
|
||||||
FONT_CANDIDATES = [
|
|
||||||
"fonts/ComicNeue-Regular.ttf",
|
|
||||||
"fonts/ComicRelief-Regular.ttf"
|
|
||||||
]
|
|
||||||
|
|
||||||
# ─────────────────────────────────────────────
|
# ─────────────────────────────────────────────
|
||||||
# DYNAMIC MODULE LOADER
|
# DYNAMIC MODULE LOADER
|
||||||
# ─────────────────────────────────────────────
|
# ─────────────────────────────────────────────
|
||||||
@@ -74,7 +66,7 @@ def pack_cbz(chapter_dir, translated_dir, output_cbz):
|
|||||||
)
|
)
|
||||||
|
|
||||||
txts = sorted(translated_dir.rglob("output.txt"), key=lambda p: p.parent.name)
|
txts = sorted(translated_dir.rglob("output.txt"), key=lambda p: p.parent.name)
|
||||||
rendered = sorted(translated_dir.rglob(RENDER_OUTPUT_NAME), key=lambda p: p.parent.name)
|
jsons = sorted(translated_dir.rglob("bubbles.json"), key=lambda p: p.parent.name)
|
||||||
|
|
||||||
if not pages:
|
if not pages:
|
||||||
print("⚠️ No original pages found — CBZ not created.")
|
print("⚠️ No original pages found — CBZ not created.")
|
||||||
@@ -86,23 +78,23 @@ def pack_cbz(chapter_dir, translated_dir, output_cbz):
|
|||||||
arcname = f"pages/{img.name}"
|
arcname = f"pages/{img.name}"
|
||||||
zf.write(img, arcname)
|
zf.write(img, arcname)
|
||||||
|
|
||||||
# Rendered pages
|
|
||||||
for rp in rendered:
|
|
||||||
arcname = f"rendered/{rp.parent.name}_translated.png"
|
|
||||||
zf.write(rp, arcname)
|
|
||||||
|
|
||||||
# Text outputs
|
# Text outputs
|
||||||
for txt in txts:
|
for txt in txts:
|
||||||
arcname = f"translations/{txt.parent.name}_output.txt"
|
arcname = f"translations/{txt.parent.name}_output.txt"
|
||||||
zf.write(txt, arcname)
|
zf.write(txt, arcname)
|
||||||
|
|
||||||
|
# JSON outputs
|
||||||
|
for j in jsons:
|
||||||
|
arcname = f"data/{j.parent.name}_bubbles.json"
|
||||||
|
zf.write(j, arcname)
|
||||||
|
|
||||||
print(f"\n✅ CBZ saved → {output_cbz}")
|
print(f"\n✅ CBZ saved → {output_cbz}")
|
||||||
print(f"📦 Contains: {len(pages)} original, {len(rendered)} rendered, {len(txts)} text files.")
|
print(f"📦 Contains: {len(pages)} original pages, {len(txts)} text files, {len(jsons)} JSON files.")
|
||||||
|
|
||||||
# ─────────────────────────────────────────────
|
# ─────────────────────────────────────────────
|
||||||
# PER-PAGE PIPELINE
|
# PER-PAGE PIPELINE
|
||||||
# ─────────────────────────────────────────────
|
# ─────────────────────────────────────────────
|
||||||
def process_page(page_path, workdir, translator_module, renderer_module):
|
def process_page(page_path, workdir, translator_module):
|
||||||
print(f"\n{'─' * 70}")
|
print(f"\n{'─' * 70}")
|
||||||
print(f"PAGE: {page_path.name}")
|
print(f"PAGE: {page_path.name}")
|
||||||
print(f"{'─' * 70}")
|
print(f"{'─' * 70}")
|
||||||
@@ -129,17 +121,6 @@ def process_page(page_path, workdir, translator_module, renderer_module):
|
|||||||
)
|
)
|
||||||
print(" ✅ Translator done")
|
print(" ✅ Translator done")
|
||||||
|
|
||||||
# 2) Render
|
|
||||||
if RENDER_ENABLED:
|
|
||||||
renderer_module.render_translations(
|
|
||||||
input_image=str(page_path.resolve()),
|
|
||||||
output_image=RENDER_OUTPUT_NAME,
|
|
||||||
translations_file="output.txt",
|
|
||||||
bubbles_file="bubbles.json",
|
|
||||||
font_candidates=FONT_CANDIDATES
|
|
||||||
)
|
|
||||||
print(" ✅ Renderer done")
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -153,7 +134,7 @@ def process_page(page_path, workdir, translator_module, renderer_module):
|
|||||||
# MAIN
|
# MAIN
|
||||||
# ─────────────────────────────────────────────
|
# ─────────────────────────────────────────────
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description="Manga Translation Pipeline")
|
parser = argparse.ArgumentParser(description="Manga Translation OCR Pipeline")
|
||||||
parser.add_argument("chapter_dir", help="Path to the folder containing manga pages")
|
parser.add_argument("chapter_dir", help="Path to the folder containing manga pages")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -171,12 +152,6 @@ def main():
|
|||||||
print(f"❌ Could not load manga-translator.py: {e}")
|
print(f"❌ Could not load manga-translator.py: {e}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
try:
|
|
||||||
renderer = load_module("manga_renderer", str(script_dir / "manga-renderer.py"))
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Could not load manga-renderer.py: {e}")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
pages = sorted_pages(chapter_dir)
|
pages = sorted_pages(chapter_dir)
|
||||||
if not pages:
|
if not pages:
|
||||||
print(f"❌ No images found in: {chapter_dir}")
|
print(f"❌ No images found in: {chapter_dir}")
|
||||||
@@ -184,8 +159,7 @@ def main():
|
|||||||
|
|
||||||
print(f"\n📖 Chapter : {chapter_dir}")
|
print(f"\n📖 Chapter : {chapter_dir}")
|
||||||
print(f" Pages : {len(pages)}")
|
print(f" Pages : {len(pages)}")
|
||||||
print(f" Source : {SOURCE_LANG} → Target: {TARGET_LANG}")
|
print(f" Source : {SOURCE_LANG} → Target: {TARGET_LANG}\n")
|
||||||
print(f" Render : {'ON' if RENDER_ENABLED else 'OFF'}\n")
|
|
||||||
|
|
||||||
translated_dir = chapter_dir / "translated"
|
translated_dir = chapter_dir / "translated"
|
||||||
succeeded, failed = [], []
|
succeeded, failed = [], []
|
||||||
@@ -194,7 +168,7 @@ def main():
|
|||||||
print(f"[{i}/{len(pages)}] Processing...")
|
print(f"[{i}/{len(pages)}] Processing...")
|
||||||
workdir = make_page_workdir(chapter_dir, page_path.stem)
|
workdir = make_page_workdir(chapter_dir, page_path.stem)
|
||||||
|
|
||||||
if process_page(page_path, workdir, translator, renderer):
|
if process_page(page_path, workdir, translator):
|
||||||
succeeded.append(page_path.name)
|
succeeded.append(page_path.name)
|
||||||
else:
|
else:
|
||||||
failed.append(page_path.name)
|
failed.append(page_path.name)
|
||||||
Reference in New Issue
Block a user