Fixed issues with sprite overlap, fixed naming of files, added id parameter to CLI, removed numpy dependency
All checks were successful
All checks were successful
This commit is contained in:
parent
941b4c5614
commit
db406adfdc
3 changed files with 40 additions and 52 deletions
|
@ -1,7 +1,7 @@
|
||||||
import pathlib
|
import pathlib
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
from collections import Counter
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from tqdm.contrib import itertools as tqdm_itertools
|
from tqdm.contrib import itertools as tqdm_itertools
|
||||||
|
|
||||||
|
@ -44,10 +44,10 @@ def gen_main_sprite(folder: pathlib.Path):
|
||||||
|
|
||||||
def process_sprite(images_folder: pathlib.Path, configdata: SpritesheetData, outputfolder: pathlib.Path):
|
def process_sprite(images_folder: pathlib.Path, configdata: SpritesheetData, outputfolder: pathlib.Path):
|
||||||
main_sprite = gen_main_sprite(images_folder)
|
main_sprite = gen_main_sprite(images_folder)
|
||||||
save_sprite(main_sprite, outputfolder, f"{images_folder.stem}")
|
image_idx = save_sprite(main_sprite, outputfolder, f"{images_folder.stem}")
|
||||||
|
|
||||||
for i in images_folder.iterdir():
|
for i in images_folder.iterdir():
|
||||||
initial_row, index = 0, int(i.stem)
|
initial_row = 0
|
||||||
expressions = Image.open(i)
|
expressions = Image.open(i)
|
||||||
|
|
||||||
rowcount, colcount = calculate_counts(*expressions.size, configdata["facesize"])
|
rowcount, colcount = calculate_counts(*expressions.size, configdata["facesize"])
|
||||||
|
@ -61,7 +61,7 @@ def process_sprite(images_folder: pathlib.Path, configdata: SpritesheetData, out
|
||||||
for x, y in tqdm_itertools.product(range(initial_row, rowcount), range(0, colcount), ascii="-="):
|
for x, y in tqdm_itertools.product(range(initial_row, rowcount), range(0, colcount), ascii="-="):
|
||||||
img = generate_sprite(main_sprite, expressions, x, y, configdata)
|
img = generate_sprite(main_sprite, expressions, x, y, configdata)
|
||||||
if img is not None:
|
if img is not None:
|
||||||
save_sprite(img, outputfolder, f"{images_folder.stem}", (x, y, colcount, index))
|
image_idx = save_sprite(img, outputfolder, f"{images_folder.stem}", image_idx)
|
||||||
|
|
||||||
|
|
||||||
def generate_sprite(main_sprite: Image.Image, expressions: Image.Image, row: int, col: int, configdata: SpritesheetData) -> Image.Image | None:
|
def generate_sprite(main_sprite: Image.Image, expressions: Image.Image, row: int, col: int, configdata: SpritesheetData) -> Image.Image | None:
|
||||||
|
@ -69,53 +69,34 @@ def generate_sprite(main_sprite: Image.Image, expressions: Image.Image, row: int
|
||||||
roi = (
|
roi = (
|
||||||
col * facesize[0],
|
col * facesize[0],
|
||||||
row * facesize[1],
|
row * facesize[1],
|
||||||
(col + 1) * facesize[0] - 1,
|
(col + 1) * facesize[0],
|
||||||
(row + 1) * facesize[1] - 1
|
(row + 1) * facesize[1]
|
||||||
)
|
)
|
||||||
|
|
||||||
expression = expressions.crop(roi)
|
expression = expressions.crop(roi)
|
||||||
|
|
||||||
if is_empty(expression):
|
if is_empty(expression):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
mask = Image.new("RGBA", (facesize[0], facesize[1]), (255,255,255,255))
|
||||||
composition = main_sprite.copy()
|
composition = main_sprite.copy()
|
||||||
composition.paste(expression, position, expression)
|
composition.paste(expression, position, mask)
|
||||||
return composition
|
return composition
|
||||||
|
|
||||||
|
|
||||||
def save_sprite(image: Image.Image, outputfolder: pathlib.Path, name: str, info: tuple | None = None):
|
def save_sprite(image: Image.Image, outputfolder: pathlib.Path, name: str, idx: int = 0) -> int:
|
||||||
savefolder = outputfolder / name
|
savefolder = outputfolder / name
|
||||||
if not savefolder.is_dir():
|
if not savefolder.is_dir():
|
||||||
savefolder.mkdir()
|
savefolder.mkdir()
|
||||||
|
outfile = savefolder / f"{idx}.png"
|
||||||
postfix = "0"
|
|
||||||
|
|
||||||
if info is not None:
|
|
||||||
(row, col, column_count, file_idx) = info
|
|
||||||
|
|
||||||
if file_idx == 0 and column_count == 4:
|
|
||||||
postfix = str(col + 1)
|
|
||||||
elif file_idx == 0:
|
|
||||||
raise ValueError("Should not have any faces")
|
|
||||||
elif column_count == 4:
|
|
||||||
postfix = str((column_count * row + col + 1) + pow(column_count, 2) * (file_idx - 1) + column_count)
|
|
||||||
elif column_count == 1:
|
|
||||||
postfix = str((file_idx - 1) * 16 + 1 if file_idx >= 2 else file_idx * 4 + 1)
|
|
||||||
elif column_count < 4:
|
|
||||||
postfix = str((column_count * row + col + 1) + pow(column_count, 2) * (file_idx - 1))
|
|
||||||
else:
|
|
||||||
raise ValueError("Unaccounted case")
|
|
||||||
|
|
||||||
outfile = savefolder / f"{postfix}.png"
|
|
||||||
|
|
||||||
with open(outfile, 'wb') as file:
|
with open(outfile, 'wb') as file:
|
||||||
image.save(file)
|
image.save(file)
|
||||||
|
|
||||||
|
idx += 1
|
||||||
|
return idx
|
||||||
|
|
||||||
def is_empty(img: Image.Image):
|
def is_empty(img: Image.Image):
|
||||||
data = np.asarray(img.crop((96, 96, 160, 160)).convert('LA'))
|
data = Counter(img.crop((96, 96, 160, 160)).convert('LA').getdata())
|
||||||
np.reshape(data, (-1, 1))
|
if len(data) < 10:
|
||||||
_, count_unique = np.unique(data, return_counts=True)
|
|
||||||
if count_unique.size < 10:
|
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -15,6 +15,7 @@ class Arguments(argparse.Namespace):
|
||||||
Default Arguments when calling the CLI
|
Default Arguments when calling the CLI
|
||||||
"""
|
"""
|
||||||
output: str
|
output: str
|
||||||
|
id: str
|
||||||
cacheclear: bool
|
cacheclear: bool
|
||||||
filter: List[str]
|
filter: List[str]
|
||||||
timeout: int
|
timeout: int
|
||||||
|
@ -31,9 +32,10 @@ def parse_arguments():
|
||||||
|
|
||||||
parser.add_argument("--version", action="version", version=f"atlasimagecomposer {__version__}")
|
parser.add_argument("--version", action="version", version=f"atlasimagecomposer {__version__}")
|
||||||
parser.add_argument("--output", action="store", type=str, default=None, dest="output", help="Set the output location. This can be an absolute or relative path")
|
parser.add_argument("--output", action="store", type=str, default=None, dest="output", help="Set the output location. This can be an absolute or relative path")
|
||||||
|
parser.add_argument("--id", action="store", type=str, default=None, dest="id", help="Set the servantId/charaId - Skips user prompt when provided")
|
||||||
parser.add_argument("--filter", action="extend", nargs="+", dest="filter", help='Specify one or more spritesheet ids that will be fetched')
|
parser.add_argument("--filter", action="extend", nargs="+", dest="filter", help='Specify one or more spritesheet ids that will be fetched')
|
||||||
parser.add_argument("--timeout", action="store", type=int, default=None, dest="timeout", help="Set the timeout for all requests towards AtlasAcademy, default is 10s")
|
parser.add_argument("--timeout", action="store", type=int, default=None, dest="timeout", help="Set the timeout for all requests towards AtlasAcademy, default is 10s")
|
||||||
parser.add_argument("--clear-cache", action="store_true", default=False, dest="cacheclear", help="Clear cached assets before downloading files for a servant")
|
parser.add_argument("--clear-cache", action="store_true", default=False, dest="cacheclear", help="Clear cached assets before downloading files")
|
||||||
|
|
||||||
|
|
||||||
args = Arguments()
|
args = Arguments()
|
||||||
|
@ -56,7 +58,10 @@ def run_cli():
|
||||||
|
|
||||||
welcome()
|
welcome()
|
||||||
|
|
||||||
|
input_id = args.id
|
||||||
|
if not input_id:
|
||||||
input_id = input("Enter servantId/charaId: ")
|
input_id = input("Enter servantId/charaId: ")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
t = int(input_id)
|
t = int(input_id)
|
||||||
if t <= 0:
|
if t <= 0:
|
||||||
|
@ -65,6 +70,8 @@ def run_cli():
|
||||||
print("Servant ID has to be a valid integer above 0")
|
print("Servant ID has to be a valid integer above 0")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
input_id = int(input_id)
|
||||||
|
|
||||||
if args.cacheclear:
|
if args.cacheclear:
|
||||||
cachepath = Paths.IMAGES / str(input_id)
|
cachepath = Paths.IMAGES / str(input_id)
|
||||||
if input_id > 10000:
|
if input_id > 10000:
|
||||||
|
@ -75,4 +82,4 @@ def run_cli():
|
||||||
else:
|
else:
|
||||||
print("No cache to clear was found, continuing")
|
print("No cache to clear was found, continuing")
|
||||||
|
|
||||||
compose(int(input_id), args.filter)
|
compose(input_id, args.filter)
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
[project]
|
[project]
|
||||||
name = "atlasimagecomposer"
|
name = "atlasimagecomposer"
|
||||||
version = "0.1.0-c.2"
|
version = "0.1.0-c.3"
|
||||||
dependencies = [
|
|
||||||
"numpy~=2.0.1",
|
|
||||||
"pillow~=10.4.0",
|
|
||||||
"requests~=2.32.3",
|
|
||||||
"tqdm~=4.66.5",
|
|
||||||
]
|
|
||||||
requires-python = ">= 3.10"
|
requires-python = ">= 3.10"
|
||||||
authors = [{name = "Firq", email = "firelp42@gmail.com"}]
|
authors = [{name = "Firq", email = "firelp42@gmail.com"}]
|
||||||
maintainers = [{name = "Firq", email = "firelp42@gmail.com"}]
|
maintainers = [{name = "Firq", email = "firelp42@gmail.com"}]
|
||||||
|
@ -19,15 +13,11 @@ classifiers = [
|
||||||
"Programming Language :: Python :: 3.12",
|
"Programming Language :: Python :: 3.12",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
dependencies = [
|
||||||
atlasimagecomposer = "atlasimagecomposer.cli.cli:run_cli"
|
"pillow~=10.4.0",
|
||||||
|
"requests~=2.32.3",
|
||||||
[tool.setuptools.packages.find]
|
"tqdm~=4.66.5",
|
||||||
where = ["."]
|
]
|
||||||
include = ["atlasimagecomposer*"]
|
|
||||||
|
|
||||||
[tool.setuptools.package-data]
|
|
||||||
"*" = ["py.typed"]
|
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
lint = [
|
lint = [
|
||||||
|
@ -39,6 +29,16 @@ typing = [
|
||||||
"types-requests~=2.32.0",
|
"types-requests~=2.32.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
atlasimagecomposer = "atlasimagecomposer.cli.cli:run_cli"
|
||||||
|
|
||||||
|
[tool.setuptools.packages.find]
|
||||||
|
where = ["."]
|
||||||
|
include = ["atlasimagecomposer*"]
|
||||||
|
|
||||||
|
[tool.setuptools.package-data]
|
||||||
|
"*" = ["py.typed"]
|
||||||
|
|
||||||
[tool.pylint."MAIN"]
|
[tool.pylint."MAIN"]
|
||||||
disable = [
|
disable = [
|
||||||
"line-too-long",
|
"line-too-long",
|
||||||
|
|
Loading…
Reference in a new issue