P-Q-Image-2.1-Remove-Object / draw_outline.py
gsprochette's picture
P-Q-Image-2.1-Remove-Object v0.1: LoRAs, model card, examples
fa5b4fe
Raw History Blame Contribute Delete
2.03 kB
"""Draw the red outline the LoRA expects around the object to remove.
python draw_outline.py photo.jpg mask.png photo_outlined.png
The mask is white (> 127) on the object, at any size (it is resized to the photo with nearest
neighbour). The outline is a pure red ring (255, 0, 0) starting 2 px outside the mask, 3 px wide,
or 4 px when the photo's short side is over 900 px: the style the model expects. Save the
result losslessly (png) if you keep it, so the red stays pure.
Needs numpy, opencv-python(-headless) and pillow.
"""
from __future__ import annotations
import sys
import cv2
import numpy as np
from PIL import Image
RED = (255, 0, 0)
OUTLINE_SUFFIX = "The area to edit is outlined in red. Remove the red outline as well."
def _within(mask: np.ndarray, r: float) -> np.ndarray:
"""Pixels within distance r of the mask (Euclidean distance transform)."""
dist = cv2.distanceTransform((~mask).astype(np.uint8), cv2.DIST_L2, 5)
return dist <= r
def outline_ring(mask: np.ndarray, width: int | None = None, gap: int = 2) -> np.ndarray:
"""Boolean ring from `gap` to `gap + width` px outside the boolean mask."""
h, w = mask.shape
width = width or (3 if min(h, w) <= 900 else 4)
return _within(mask, gap + width) & ~_within(mask, gap)
def draw_outline(photo: Image.Image, mask: Image.Image) -> Image.Image:
"""The photo with the mask's outline drawn in pure red. An empty mask returns the photo."""
photo = photo.convert("RGB")
m = np.array(mask.convert("L").resize(photo.size, Image.NEAREST)) > 127
out = np.array(photo)
if m.any():
out[outline_ring(m)] = RED
return Image.fromarray(out)
def outline_prompt(instruction: str) -> str:
"""The instruction followed by the sentence the model expects with outlined images."""
return f"{instruction.rstrip()} {OUTLINE_SUFFIX}"
if __name__ == "__main__":
if len(sys.argv) != 4:
sys.exit(__doc__)
draw_outline(Image.open(sys.argv[1]), Image.open(sys.argv[2])).save(sys.argv[3])