68 lines
1.9 KiB
Python
68 lines
1.9 KiB
Python
from __future__ import annotations
|
|
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
from PIL import Image
|
|
|
|
ROOT = Path(__file__).resolve().parents[3]
|
|
CANVAS = (900, 1125)
|
|
MALE_PERSONAS = ("P5", "P7")
|
|
MALE_EMPTY_HAIR_SLOTS = (
|
|
"hair-back-base",
|
|
"hair-back-left",
|
|
"hair-back-right",
|
|
"hair-side-left-2",
|
|
"hair-side-right-2",
|
|
)
|
|
GENERATED_PERSONAS = ("P4", "P5", "P6", "P7")
|
|
|
|
|
|
def parts_dir(persona: str) -> Path:
|
|
return ROOT / f"apps/web/public/avatar/{persona.lower()}-live2d-generated/parts"
|
|
|
|
|
|
def write_empty(path: Path) -> None:
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
Image.new("RGBA", CANVAS, (0, 0, 0, 0)).save(path, optimize=True)
|
|
|
|
|
|
def clean_green_edge_spill(path: Path) -> int:
|
|
image = Image.open(path).convert("RGBA")
|
|
pixels = image.load()
|
|
changed = 0
|
|
for y in range(image.height):
|
|
for x in range(image.width):
|
|
r, g, b, a = pixels[x, y]
|
|
if a <= 80 and g > 135 and g > r + 35 and g > b + 35:
|
|
if a <= 48:
|
|
pixels[x, y] = (r, g, b, 0)
|
|
else:
|
|
pixels[x, y] = (r, min(g, max(r, b) + 18), b, a)
|
|
changed += 1
|
|
if changed:
|
|
image.save(path, optimize=True)
|
|
return changed
|
|
|
|
|
|
def main() -> int:
|
|
for persona in MALE_PERSONAS:
|
|
directory = parts_dir(persona)
|
|
for part_id in MALE_EMPTY_HAIR_SLOTS:
|
|
write_empty(directory / f"{part_id}.png")
|
|
|
|
p7_outfit = parts_dir("P7") / "outfit.png"
|
|
p5_outfit = parts_dir("P5") / "outfit.png"
|
|
if p7_outfit.exists():
|
|
shutil.copyfile(p7_outfit, p5_outfit)
|
|
|
|
cleaned = 0
|
|
for persona in GENERATED_PERSONAS:
|
|
for path in parts_dir(persona).glob("*.png"):
|
|
cleaned += clean_green_edge_spill(path)
|
|
print(f"applied visual overrides for P5/P7 male silhouette; cleaned {cleaned} green edge pixels")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|