"""Basic retouching tools: healing / inpainting and skin softening.""" from __future__ import annotations from typing import Tuple import numpy as np from PIL import Image def heal_spot( img: Image.Image, center: Tuple[int, int], radius: int, ) -> Image.Image: """Remove blemish at *center* using OpenCV Telea inpainting.""" import cv2 arr = np.asarray(img.convert("RGB")) bgr = cv2.cvtColor(arr, cv2.COLOR_RGB2BGR) mask = np.zeros(arr.shape[:2], dtype=np.uint8) cv2.circle(mask, center, radius, 255, -1) result = cv2.inpaint(bgr, mask, inpaintRadius=radius * 2, flags=cv2.INPAINT_TELEA) result = cv2.cvtColor(result, cv2.COLOR_BGR2RGB) return Image.fromarray(result) def soften_skin( rgb_float01: np.ndarray, mask: np.ndarray, strength: float, ) -> np.ndarray: """Edge-preserving skin softening (bilateral filter) within *mask* area. *strength*: 0..100. 0 = no effect. *mask*: float01 (H, W) – typically the person/face mask. """ if strength <= 0: return rgb_float01 import cv2 img8 = np.clip(rgb_float01 * 255, 0, 255).astype(np.uint8) d = max(3, int(strength / 10)) sigma_color = 30 + strength * 0.7 sigma_space = 30 + strength * 0.7 smoothed = cv2.bilateralFilter(img8, d, sigma_color, sigma_space) smoothed_f = smoothed.astype(np.float32) / 255.0 alpha = np.clip(mask * min(strength / 100.0, 1.0), 0.0, 1.0)[..., None] result = rgb_float01 * (1.0 - alpha) + smoothed_f * alpha return np.clip(result, 0.0, 1.0).astype(np.float32)