manga_translation / utils /polygon_utils.py
qqwjq1981's picture
Update utils/polygon_utils.py
78699c5 verified
raw
history blame
3.75 kB
from shapely.geometry import Polygon, MultiPoint
import os
FONT_PATH = os.path.join(os.path.dirname(__file__), "..", "NotoSansSC-Regular.ttf")
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
import textwrap
def shrink_polygon(polygon, shrink_ratio=0.9):
cx = sum(x for x, _ in polygon) / len(polygon)
cy = sum(y for _, y in polygon) / len(polygon)
shrunk = [((x - cx) * shrink_ratio + cx, (y - cy) * shrink_ratio + cy) for x, y in polygon]
return [(int(x), int(y)) for x, y in shrunk]
import numpy as np
import cv2
from PIL import Image, ImageDraw
def inpaint_polygon(img: Image.Image, polygon, mode="auto", fallback_color=(255, 255, 255)):
np_img = np.array(img.convert("RGB"))
mask = np.zeros((np_img.shape[0], np_img.shape[1]), dtype=np.uint8)
pts = np.array(polygon, np.int32).reshape((-1, 1, 2))
cv2.fillPoly(mask, [pts], 255)
if mode == "fill":
img_copy = img.copy()
draw = ImageDraw.Draw(img_copy)
draw.polygon(polygon, fill=fallback_color)
return img_copy
# try:
# inpainted = cv2.inpaint(np_img, mask, inpaintRadius=3, flags=cv2.INPAINT_TELEA)
# return Image.fromarray(inpainted)
# except Exception as e:
# print("⚠️ Inpaint failed, falling back to solid fill:", e)
img_copy = img.copy()
draw = ImageDraw.Draw(img_copy)
draw.polygon(polygon, fill=fallback_color)
return img_copy
def merge_polygons_to_convex_hull(polygons):
points = [pt for poly in polygons for pt in poly]
hull = MultiPoint(points).convex_hull
return list(hull.exterior.coords)
def render_translated_chunk(img: Image.Image, translations, font_path="NotoSansSC-Regular.ttf", font_scale=1.0):
from PIL import ImageDraw
img_copy = img.copy()
for entry in translations:
polygon = entry.get("polygon") or entry.get("polygons")
text = entry.get("translated", "")
if polygon and text:
img_copy = draw_translated_text_convex(img_copy, polygon, text, font_path, font_scale)
return img_copy
def draw_translated_text_convex(img, polygon_coords, text, font_path="NotoSansSC-Regular.ttf", font_scale=1.0):
from PIL import ImageDraw
font_polygon = polygon_coords
render_polygon = shrink_polygon(polygon_coords, shrink_ratio=0.9)
img = inpaint_polygon(img, render_polygon, mode="auto", fallback_color=(255, 255, 255))
debug_color = (180, 255, 180)
draw = ImageDraw.Draw(img)
draw.line(render_polygon + [render_polygon[0]], fill=debug_color, width=1)
draw_wrapped_text(img, render_polygon, text, font_path, polygon_for_size=font_polygon, font_scale=font_scale)
return img
def draw_wrapped_text(img, polygon, text, font_path, polygon_for_size=None, font_scale=1.0):
from PIL import ImageDraw, ImageFont
import textwrap
polygon_for_size = polygon_for_size or polygon
draw = ImageDraw.Draw(img)
xs, ys = zip(*polygon_for_size)
x_min, x_max = min(xs), max(xs)
y_min, y_max = min(ys), max(ys)
box_width = x_max - x_min
box_height = y_max - y_min
avg_char_width = 0.4
estimated_size = int(min(box_height / 1.2, box_width / (len(text) * avg_char_width)))
estimated_size = max(6, estimated_size)
font_size = int(estimated_size * font_scale)
font = ImageFont.truetype(font_path, font_size)
max_chars = max(1, int(box_width / (font.getbbox("A")[2] + 1)))
wrapped = textwrap.fill(text, width=max_chars)
bbox = draw.textbbox((0, 0), wrapped, font=font)
text_w, text_h = bbox[2] - bbox[0], bbox[3] - bbox[1]
x = x_min + (box_width - text_w) / 2
y = y_min + (box_height - text_h) / 2
draw.text((x, y), wrapped, font=font, fill="black", align="center")