mirror-ppr / mirrorppr /data /self_augmentation.py
multimodalart's picture
multimodalart HF Staff
Upload folder using huggingface_hub
834d1cf verified
Raw
History Blame Contribute Delete
3.9 kB
from __future__ import annotations
from dataclasses import asdict, dataclass
from pathlib import Path
import random
from PIL import Image
@dataclass
class AugmentationSpec:
width: int
height: int
crop_box: tuple[int, int, int, int]
rotation: int = 0
flip: bool = False
def _fit_crop_box(
image_size: tuple[int, int],
aspect_ratio: float,
scale: float,
required_box: tuple[int, int, int, int] | None,
rng: random.Random,
) -> tuple[int, int, int, int]:
width, height = image_size
crop_area = width * height * scale
crop_w = int((crop_area * aspect_ratio) ** 0.5)
crop_h = int(crop_w / aspect_ratio)
if crop_w > width:
crop_w = width
crop_h = int(crop_w / aspect_ratio)
if crop_h > height:
crop_h = height
crop_w = int(crop_h * aspect_ratio)
crop_w = max(16, min(width, crop_w))
crop_h = max(16, min(height, crop_h))
if required_box is None:
left = rng.randint(0, max(0, width - crop_w))
top = rng.randint(0, max(0, height - crop_h))
return left, top, left + crop_w, top + crop_h
rx, ry, rw, rh = required_box
min_left = max(0, rx + rw - crop_w)
max_left = min(rx, width - crop_w)
min_top = max(0, ry + rh - crop_h)
max_top = min(ry, height - crop_h)
if min_left > max_left or min_top > max_top:
return 0, 0, width, height
left = rng.randint(int(min_left), int(max_left))
top = rng.randint(int(min_top), int(max_top))
return left, top, left + crop_w, top + crop_h
def generate_specs(
image_size: tuple[int, int],
count: int,
seed: int = 0,
output_area: int = 1024 * 1024,
aspect_ratio_range: tuple[float, float] = (1 / 3, 3),
required_box: tuple[int, int, int, int] | None = None,
) -> list[AugmentationSpec]:
rng = random.Random(seed)
specs = []
angles = [-15, -10, -5, 0, 5, 10, 15]
for _ in range(count):
ar = rng.uniform(*aspect_ratio_range)
scale = rng.uniform(0.55, 1.0)
crop_box = _fit_crop_box(image_size, ar, scale, required_box, rng)
target_w = int((output_area * ar) ** 0.5)
target_h = int(target_w / ar)
target_w = max(16, (target_w // 16) * 16)
target_h = max(16, (target_h // 16) * 16)
specs.append(
AugmentationSpec(
width=target_w,
height=target_h,
crop_box=crop_box,
rotation=rng.choice(angles),
flip=bool(rng.getrandbits(1)),
)
)
return specs
def apply_spec(image: Image.Image, spec: AugmentationSpec) -> Image.Image:
out = image.convert("RGB")
if spec.flip:
out = out.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
out = out.crop(spec.crop_box)
if spec.rotation:
out = out.rotate(spec.rotation, resample=Image.Resampling.BICUBIC, expand=False)
return out.resize((spec.width, spec.height), Image.Resampling.LANCZOS)
def save_augmented_pair(
source: Image.Image,
target: Image.Image,
output_dir: str | Path,
count: int,
seed: int = 0,
prefix: str = "aug",
required_box: tuple[int, int, int, int] | None = None,
) -> list[dict[str, object]]:
output_dir = Path(output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
specs = generate_specs(source.size, count=count, seed=seed, required_box=required_box)
pairs = []
for idx, spec in enumerate(specs):
source_out = output_dir / f"{prefix}_{idx:03d}_source.png"
target_out = output_dir / f"{prefix}_{idx:03d}_target.png"
apply_spec(source, spec).save(source_out)
apply_spec(target, spec).save(target_out)
pairs.append(
{
"source": str(source_out),
"target": str(target_out),
"augmentation": asdict(spec),
}
)
return pairs