skyeweave/atlasimagecomposer/compose.py

124 lines
4 KiB
Python
Raw Normal View History

2024-10-04 17:30:40 +00:00
import math
2024-08-09 16:57:33 +00:00
import pathlib
import sys
import numpy as np
from PIL import Image
from tqdm.contrib import itertools as tqdm_itertools
from .atlas import fetch_data, fetch_image
2024-10-04 17:30:40 +00:00
from .cli import ExpressionDefaults, Paths
2024-08-09 16:57:33 +00:00
def welcome():
print("-------------------------------------------------")
print(" Welcome to the FGO Sprite loader and composer ")
print(" developed by Firq ")
print("-------------------------------------------------")
def run():
welcome()
Paths.IMAGES.mkdir(exist_ok=True)
Paths.OUTPUT.mkdir(exist_ok=True)
servantid = input("Enter servant ID: ")
try:
t = int(servantid)
2024-08-09 17:09:05 +00:00
if t <= 0:
2024-08-09 16:57:33 +00:00
raise ValueError
except ValueError:
print("Servant ID has to be a valid integer above 0")
sys.exit(1)
sprites = fetch_data(servantid)
for sprite in sprites:
fetch_image(servantid, sprite)
path = Paths.IMAGES / servantid
for f in path.iterdir():
2024-10-04 17:30:40 +00:00
process_sprite(f, sprites[f.stem]["position"], sprites[f.stem]["faceSize"], servantid)
2024-08-09 17:14:12 +00:00
print(f"Files have been saved at: {(Paths.OUTPUT / servantid).absolute()}")
2024-08-09 16:57:33 +00:00
2024-10-04 17:30:40 +00:00
def process_sprite(filepath: pathlib.Path, position: tuple, faceSize: int, servantid: str):
2024-08-09 16:57:33 +00:00
im = Image.open(filepath)
width, height = im.size
main_sprite = im.crop((0, 0, width, 768))
save_sprite(main_sprite, servantid, filepath.stem)
expressions = im.crop((0, 768, width, height))
2024-10-04 17:30:40 +00:00
expressionwidth, expressionheight = expressions.size
rows = expressionheight // faceSize
expressions_p_row = expressionwidth // faceSize
exp_per_row = 4
if faceSize != ExpressionDefaults.faceSize:
w, h = expressions.size
composites = []
per_composite = (1280 - ExpressionDefaults.faceSize) // faceSize
exp_per_row = per_composite
offset = (faceSize * ExpressionDefaults.faceSize) / 1280
composite_count = math.ceil((h - 1280) / (offset + faceSize * per_composite)) + 1
expressions = expressions.crop((0, 256, w, h))
floating_offset = 0
for count in range(0, composite_count + 1):
composites.append((
0,
3 * count * faceSize + floating_offset,
w,
(3 * count + 3) * faceSize + floating_offset - 1
))
floating_offset += offset
overrides = Image.new("RGBA", (w, faceSize * composite_count * 3), (255, 0, 0, 0))
for idx, c in enumerate(composites):
crop = expressions.crop(c)
pos = (
0,
idx * 3 * faceSize,
)
overrides.paste(crop, pos, crop)
expressions = overrides.copy()
for x, y in tqdm_itertools.product(range(0, rows), range(0, expressions_p_row, 1), ascii="-="):
img = generate_sprite(main_sprite, expressions, x, y, position, faceSize)
2024-08-09 16:57:33 +00:00
if img is not None:
2024-10-04 17:30:40 +00:00
save_sprite(img, servantid, filepath.stem, (x, y, exp_per_row))
2024-08-09 16:57:33 +00:00
def is_empty(img: Image.Image):
data = np.asarray(img.crop((96, 96, 160, 160)).convert('LA'))
np.reshape(data, (-1, 1))
_, count_unique = np.unique(data, return_counts=True)
if count_unique.size < 10:
return True
return False
2024-10-04 17:30:40 +00:00
def generate_sprite(sprite: Image.Image, expressions: Image.Image, row: int, col: int, position: tuple, faceSize: int) -> Image.Image | None:
2024-08-09 16:57:33 +00:00
area = (
2024-10-04 17:30:40 +00:00
col * faceSize,
row * faceSize,
(col + 1) * faceSize - 1,
(row + 1) * faceSize - 1
2024-08-09 16:57:33 +00:00
)
expression = expressions.crop(area)
if is_empty(expression):
return None
compose = sprite.copy()
compose.paste(expression, position, expression)
return compose
def save_sprite(image: Image.Image, folder: str, name: str, rowcol: tuple | None = None):
savefolder = Paths.OUTPUT / folder
if not savefolder.is_dir():
savefolder.mkdir()
2024-10-04 17:30:40 +00:00
postfix = f"_{rowcol[0] * rowcol[2] + rowcol[1] + 1}" if rowcol is not None else "_0"
2024-08-09 16:57:33 +00:00
outfile = savefolder / f"{name}{postfix}.png"
with open(outfile, 'wb') as file:
image.save(file)