import numpy as np from PIL import Image, ImageDraw, ImageFilter, ImageFont from . import config from .fonts import get_cached_font def render_layout_occupancy(layout, mask_shape, font_path): h, w = mask_shape canvas = Image.new("L", (w, h), 0) draw = ImageDraw.Draw(canvas) for word, size, (y, x), orient, _color in layout: font = get_cached_font(font_path, size) if orient: font = ImageFont.TransposedFont(font, orientation=orient) draw.text((x, y), word, font=font, fill=255) return (np.array(canvas) > 0).astype(np.uint8) def compute_fill_ratio_fast(layout, mask, font_path): if not layout: return 0.0, None occ = render_layout_occupancy(layout, mask.shape, font_path) free_area = np.sum(mask == 0) if free_area == 0: return 0.0, occ filled_area = np.sum((mask == 0) & (occ == 1)) return filled_area / free_area, occ def apply_dot_matrix(base_img, mask_hd): text_mask = base_img.convert("L").point(lambda x: 0 if x < 200 else 255) filter_size = max(3, (config.DOT_SAFETY_BUFFER // 2) * 2 + 1) safe_zone_mask = text_mask.filter(ImageFilter.MinFilter(size=filter_size)) safe_zone_array = np.array(safe_zone_mask) unfilled_zone = (mask_hd == 0) & (safe_zone_array > 200) draw = ImageDraw.Draw(base_img) h, w = mask_hd.shape dot_color = "white" if config.FILL_ON == "WHITE" else "black" for y in range(0, h, config.DOT_SPACING): for x in range(0, w, config.DOT_SPACING): if unfilled_zone[y, x]: if config.DOT_RADIUS > 0: draw.ellipse([x - config.DOT_RADIUS, y - config.DOT_RADIUS, x + config.DOT_RADIUS, y + config.DOT_RADIUS], fill=dot_color) else: draw.point((x, y), fill=dot_color) return base_img