Added improvements
This commit is contained in:
Binary file not shown.
Binary file not shown.
BIN
fonts/Komika.ttf
Normal file
BIN
fonts/Komika.ttf
Normal file
Binary file not shown.
BIN
fonts/Laffayette_Comic_Pro.ttf
Normal file
BIN
fonts/Laffayette_Comic_Pro.ttf
Normal file
Binary file not shown.
BIN
fonts/animeace2_bld.ttf
Normal file
BIN
fonts/animeace2_bld.ttf
Normal file
Binary file not shown.
BIN
fonts/animeace2_ital.ttf
Normal file
BIN
fonts/animeace2_ital.ttf
Normal file
Binary file not shown.
BIN
fonts/animeace2_reg.ttf
Normal file
BIN
fonts/animeace2_reg.ttf
Normal file
Binary file not shown.
@@ -11,8 +11,8 @@ Strategy:
|
|||||||
2. Detect the original font size from the OCR bounding boxes.
|
2. Detect the original font size from the OCR bounding boxes.
|
||||||
3. Dynamically wrap and scale down the translated text if it exceeds the bubble dimensions.
|
3. Dynamically wrap and scale down the translated text if it exceeds the bubble dimensions.
|
||||||
4. Render the translated text centered inside the bubble bounding box.
|
4. Render the translated text centered inside the bubble bounding box.
|
||||||
5. Uses uniform line heights to prevent accent collisions.
|
5. Uses uniform line heights (ascent + descent) to prevent Catalan accent collisions (È, À).
|
||||||
6. Adds a white stroke to the text to cover any residual original characters.
|
6. Adds a dynamic white stroke to the text to cover any residual original characters.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
@@ -25,26 +25,19 @@ from typing import Dict, List, Tuple, Optional, Set, Any
|
|||||||
# ============================================================
|
# ============================================================
|
||||||
# CONFIG — edit these paths to match your setup
|
# CONFIG — edit these paths to match your setup
|
||||||
# ============================================================
|
# ============================================================
|
||||||
IMAGE_PATH = "003.jpg"
|
IMAGE_PATH = "004.png"
|
||||||
BUBBLES_PATH = "bubbles.json"
|
BUBBLES_PATH = "bubbles_004.json"
|
||||||
TRANSLATIONS_PATH = "output_003.txt"
|
TRANSLATIONS_PATH = "output_004.txt"
|
||||||
OUTPUT_PATH = "translated_page_003.png"
|
OUTPUT_PATH = "translated_page_004.png"
|
||||||
|
|
||||||
# Font candidates — first one that loads wins
|
# Font candidates — Prioritizes Laffayette for Catalan, with safe fallbacks
|
||||||
FONT_CANDIDATES = [
|
FONT_CANDIDATES = [
|
||||||
|
"fonts/animeace2_reg.ttf",
|
||||||
"fonts/ComicNeue-Bold.ttf",
|
"fonts/ComicNeue-Bold.ttf",
|
||||||
# Mac fallbacks
|
|
||||||
"/System/Library/Fonts/Supplemental/Comic Sans MS Bold.ttf",
|
|
||||||
"/System/Library/Fonts/Supplemental/Arial Bold.ttf",
|
|
||||||
# Windows fallbacks
|
|
||||||
"C:\\Windows\\Fonts\\comicbd.ttf",
|
|
||||||
"C:\\Windows\\Fonts\\arialbd.ttf",
|
|
||||||
# Linux fallbacks
|
|
||||||
"/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
DEFAULT_FONT_SIZE = 24
|
DEFAULT_FONT_SIZE = 18
|
||||||
MIN_FONT_SIZE = 12
|
MIN_FONT_SIZE = 8
|
||||||
QUAD_PAD = 4 # extra pixels added around each quad before white-fill
|
QUAD_PAD = 4 # extra pixels added around each quad before white-fill
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
@@ -105,9 +98,7 @@ def parse_translations(filepath: str) -> Dict[int, str]:
|
|||||||
return translations
|
return translations
|
||||||
|
|
||||||
def parse_bubbles(filepath: str):
|
def parse_bubbles(filepath: str):
|
||||||
"""
|
"""Returns the full JSON data."""
|
||||||
Returns the full JSON data.
|
|
||||||
"""
|
|
||||||
with open(filepath, "r", encoding="utf-8") as f:
|
with open(filepath, "r", encoding="utf-8") as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
return data
|
return data
|
||||||
@@ -205,7 +196,7 @@ def fit_text_dynamically(
|
|||||||
|
|
||||||
wrapped_lines = textwrap.wrap(text, width=chars_per_line)
|
wrapped_lines = textwrap.wrap(text, width=chars_per_line)
|
||||||
|
|
||||||
# Use uniform font metrics for height instead of per-line bounding boxes
|
# Use uniform font metrics for height to protect accents like È
|
||||||
line_spacing = max(2, int(font_size * 0.15))
|
line_spacing = max(2, int(font_size * 0.15))
|
||||||
if hasattr(font, 'getmetrics'):
|
if hasattr(font, 'getmetrics'):
|
||||||
ascent, descent = font.getmetrics()
|
ascent, descent = font.getmetrics()
|
||||||
@@ -246,7 +237,7 @@ def render_text(
|
|||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Draws the translated text centered in the line_union_bbox of each bubble.
|
Draws the translated text centered in the line_union_bbox of each bubble.
|
||||||
Adds a white stroke (outline) to cover any residual original characters.
|
Adds a dynamic white stroke (outline) to cover any residual original characters.
|
||||||
"""
|
"""
|
||||||
image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)
|
image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)
|
||||||
pil_img = Image.fromarray(image_rgb)
|
pil_img = Image.fromarray(image_rgb)
|
||||||
@@ -280,7 +271,7 @@ def render_text(
|
|||||||
target_size = get_original_font_size(val)
|
target_size = get_original_font_size(val)
|
||||||
wrapped_lines, font, line_spacing, final_size = fit_text_dynamically(text, font_path, bw, bh, target_size)
|
wrapped_lines, font, line_spacing, final_size = fit_text_dynamically(text, font_path, bw, bh, target_size)
|
||||||
|
|
||||||
# Use uniform typographic line height for rendering
|
# Use uniform typographic line height for rendering to protect accents
|
||||||
if hasattr(font, 'getmetrics'):
|
if hasattr(font, 'getmetrics'):
|
||||||
ascent, descent = font.getmetrics()
|
ascent, descent = font.getmetrics()
|
||||||
line_h = ascent + descent
|
line_h = ascent + descent
|
||||||
@@ -290,6 +281,8 @@ def render_text(
|
|||||||
total_text_height = (line_h * len(wrapped_lines)) + (line_spacing * max(0, len(wrapped_lines) - 1))
|
total_text_height = (line_h * len(wrapped_lines)) + (line_spacing * max(0, len(wrapped_lines) - 1))
|
||||||
|
|
||||||
current_y = by + (bh - total_text_height) // 2
|
current_y = by + (bh - total_text_height) // 2
|
||||||
|
|
||||||
|
# Dynamic outline thickness based on the final scaled font size
|
||||||
outline_thickness = max(2, int(final_size * 0.10))
|
outline_thickness = max(2, int(final_size * 0.10))
|
||||||
|
|
||||||
for i, line in enumerate(wrapped_lines):
|
for i, line in enumerate(wrapped_lines):
|
||||||
@@ -301,6 +294,7 @@ def render_text(
|
|||||||
|
|
||||||
current_x = bx + (bw - lw) // 2
|
current_x = bx + (bw - lw) // 2
|
||||||
|
|
||||||
|
# Draw text with white stroke for artifact coverage
|
||||||
draw.text(
|
draw.text(
|
||||||
(current_x, current_y),
|
(current_x, current_y),
|
||||||
line,
|
line,
|
||||||
|
|||||||
@@ -1108,8 +1108,8 @@ def translate_manga_text(
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
translate_manga_text(
|
translate_manga_text(
|
||||||
image_path="003.jpg",
|
image_path="004.png",
|
||||||
source_lang="es",
|
source_lang="en",
|
||||||
target_lang="ca",
|
target_lang="ca",
|
||||||
confidence_threshold=0.05,
|
confidence_threshold=0.05,
|
||||||
min_text_length=1,
|
min_text_length=1,
|
||||||
|
|||||||
Reference in New Issue
Block a user