ab2b778f44
build / build (push) Successful in 3m36s
Extends the MDL system beyond text overrides: bitmaps/<label>.json names a model/scale and resource list, and mdlp extracts each resource's frames, runs them through an ONNX Runtime CPU upscaler baked into mdlp-image, re-quantizes to the game's palette, and writes the result back - same build-time pipeline as message overrides. Adds animgallery, a standalone viewer that renders any data directory's animated resources as an HTML/GIF gallery with a checkbox-driven MDL JSON generator, and -only-changed to preview just what a mod overrides against the base game. Fixes a real data race in hacked's ChangeNotifier.hashAll() (found via go build -race causing non-deterministic resource counts), via a new numbered patch rather than editing the vendored source. Adds engine-patches/, a general-fixes counterpart to android/engine-patches/ now staged and applied to the desktop builds too (previously unpatched): a real GL_UNPACK_ROW_LENGTH stride bug, an audio error-logging improvement, and two critter-rendering fixes - mod art was losing to the base game's own re-opened resource files, and critters rendered at the wrong size/position once that was fixed, both from how the billboard renderer derives on-screen size and placement straight from bitmap pixel dimensions. Updates README.md and AGENTS.md to document engine-patches/.
60 lines
2.3 KiB
Python
60 lines
2.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Upscales every PNG in one directory into another, via one ONNX Runtime
|
|
CPU session shared across the whole batch (same reasoning as mdlp's own
|
|
Go-side batching: the fixed per-invocation model-load cost otherwise
|
|
dominates for the many small sprite frames one upscale definition has).
|
|
|
|
Runs on CPU only, not Vulkan: this Docker image has no real GPU, and
|
|
Vulkan software emulation (Mesa lavapipe) gives wrong colors for ML
|
|
inference workloads.
|
|
|
|
Usage: upscale_batch.py <model.onnx> <in-dir> <out-dir> <scale>
|
|
"""
|
|
import sys
|
|
import os
|
|
|
|
import numpy as np
|
|
import onnxruntime as ort
|
|
from PIL import Image
|
|
|
|
|
|
def main():
|
|
model_path, in_dir, out_dir, scale_arg = sys.argv[1:5]
|
|
scale = int(scale_arg)
|
|
|
|
session = ort.InferenceSession(model_path, providers=["CPUExecutionProvider"])
|
|
input_name = session.get_inputs()[0].name
|
|
|
|
names = sorted(n for n in os.listdir(in_dir) if n.lower().endswith(".png"))
|
|
for name in names:
|
|
img = Image.open(os.path.join(in_dir, name)).convert("RGBA")
|
|
rgb = img.convert("RGB")
|
|
alpha = img.split()[3]
|
|
|
|
# The model has no concept of transparency (3-channel RGB in/out) -
|
|
# run it on the flattened RGB only, and scale the alpha mask
|
|
# separately with a plain resize. Alpha edges don't need AI
|
|
# enhancement, just to end up the right size.
|
|
arr = np.asarray(rgb, dtype=np.float32) / 255.0
|
|
arr = np.transpose(arr, (2, 0, 1))[np.newaxis, ...]
|
|
output = session.run(None, {input_name: arr})[0]
|
|
output = np.clip(output[0], 0.0, 1.0) * 255.0
|
|
output = np.transpose(output, (1, 2, 0)).astype(np.uint8)
|
|
upscaled_rgb = Image.fromarray(output, "RGB")
|
|
|
|
target_size = (img.width * scale, img.height * scale)
|
|
if upscaled_rgb.size != target_size:
|
|
# The model upscales at its own fixed native factor - resize to
|
|
# whatever scale the mod actually asked for.
|
|
upscaled_rgb = upscaled_rgb.resize(target_size, Image.LANCZOS)
|
|
upscaled_alpha = alpha.resize(target_size, Image.LANCZOS)
|
|
|
|
result = Image.merge("RGBA", (*upscaled_rgb.split(), upscaled_alpha))
|
|
result.save(os.path.join(out_dir, name))
|
|
|
|
print(f"upscaled {len(names)} frame(s)")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|