| """ |
| INFERENCE.py - run PixelModel v1 from model.safetensors, self-contained. |
| |
| Only needs torch + safetensors + pillow + numpy; no other file from this |
| repo. Produces byte-identical output to main.py (which loads model.png) |
| for the same prompt and resolution. |
| |
| Usage: |
| python INFERENCE.py "a red double decker bus" |
| python INFERENCE.py "a cat on a couch" --model model.safetensors --out cat.png --res 64 --scale 4 |
| """ |
|
|
| import argparse |
| import os |
| import sys |
|
|
| import numpy as np |
| import torch |
| from PIL import Image |
| from safetensors.torch import load_file |
|
|
| EMB_DIM = 64 |
| NATIVE_RES = 64 |
| FREQS = (1.0, 2.0, 4.0, 8.0) |
|
|
|
|
| def _fnv1a(data: bytes) -> int: |
| h = 0x811C9DC5 |
| for byte in data: |
| h ^= byte |
| h = (h * 0x01000193) & 0xFFFFFFFF |
| return h |
|
|
|
|
| def prompt_to_embedding(prompt: str) -> torch.Tensor: |
| text = "".join(c if c.isalnum() or c == " " else " " for c in prompt.lower()) |
| text = " ".join(text.split()) |
| vec = np.zeros(EMB_DIM, dtype=np.float32) |
| padded = f" {text} " |
| for i in range(len(padded) - 2): |
| h = _fnv1a(padded[i:i + 3].encode("utf-8")) |
| vec[h % EMB_DIM] += 1.0 if (h >> 16) & 1 else -1.0 |
| for word in text.split(): |
| h = _fnv1a(b"w:" + word.encode("utf-8")) |
| vec[h % EMB_DIM] += 2.0 if (h >> 16) & 1 else -2.0 |
| norm = np.linalg.norm(vec) |
| if norm > 0: |
| vec /= norm |
| return torch.from_numpy(vec) |
|
|
|
|
| def coord_features(res: int) -> torch.Tensor: |
| axis = torch.linspace(-1.0, 1.0, res) |
| yy, xx = torch.meshgrid(axis, axis, indexing="ij") |
| x, y = xx.reshape(-1), yy.reshape(-1) |
| feats = [x, y] |
| for f in FREQS: |
| feats += [torch.sin(f * torch.pi * x), torch.cos(f * torch.pi * x), |
| torch.sin(f * torch.pi * y), torch.cos(f * torch.pi * y)] |
| return torch.stack(feats, dim=1) |
|
|
|
|
| def forward(w: dict, prompt: str, res: int) -> torch.Tensor: |
| emb = prompt_to_embedding(prompt).unsqueeze(0) |
| z = torch.tanh(emb @ w["T1"].T + w["b1"]) |
| z = torch.tanh(z @ w["T2"].T + w["b2"]) |
| feats = coord_features(res) |
| P = feats.shape[0] |
| inp = torch.cat([z.expand(P, -1), feats], dim=1) |
| h = torch.tanh(inp @ w["D1"].T + w["bd1"]) |
| h = torch.tanh(h @ w["D2"].T + w["bd2"]) |
| rgb = torch.sigmoid(h @ w["D3"].T + w["bd3"]) |
| return rgb.reshape(res, res, 3) |
|
|
|
|
| def main(): |
| p = argparse.ArgumentParser(description="PixelModel v1 inference (safetensors)") |
| p.add_argument("prompt") |
| p.add_argument("--model", default="model.safetensors") |
| p.add_argument("--out", default="out.png") |
| p.add_argument("--res", type=int, default=NATIVE_RES) |
| p.add_argument("--scale", type=int, default=4) |
| args = p.parse_args() |
|
|
| if not os.path.exists(args.model): |
| sys.exit(f"Model not found: {args.model}\n" |
| f"Run: python convert_to_safetensors.py to create it from model.png.") |
|
|
| weights = load_file(args.model) |
| with torch.no_grad(): |
| result = forward(weights, args.prompt, args.res) |
|
|
| arr = (result.numpy() * 255).clip(0, 255).astype(np.uint8) |
| img = Image.fromarray(arr, mode="RGB") |
| if args.scale > 1: |
| img = img.resize((args.res * args.scale,) * 2, Image.NEAREST) |
| img.save(args.out) |
| print(f"prompt : '{args.prompt}'") |
| print(f"model : {args.model} ({os.path.getsize(args.model)} bytes, safetensors)") |
| print(f"output : {args.out} ({args.res}x{args.res} native, x{args.scale} view)") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|