Nekochu commited on
Commit
6da7294
·
1 Parent(s): d528a08

Switch to n-Arno AIO Q4_K GGUF (single 1.79 GB file, restores GGUF speed)

Browse files
Files changed (2) hide show
  1. README.md +6 -6
  2. app.py +36 -50
README.md CHANGED
@@ -15,11 +15,11 @@ tags:
15
 
16
  # Anima Preview3 Image Generation (CPU)
17
 
18
- Generate anime images with [Anima Preview3](https://huggingface.co/circlestone-labs/Anima) + new Turbo LoRA on free CPU hardware.
19
 
20
  - Engine: stable-diffusion.cpp (compiled from source)
21
- - Diffusion model: anima-preview3-base.safetensors (4.2 GB, fp16)
22
- - Text encoder: qwen_3_06b_base.safetensors
23
- - VAE: qwen_image_vae.safetensors
24
- - Turbo LoRA: [dummy9996/Anima-Turbo-LoRA](https://huggingface.co/dummy9996/Anima-Turbo-LoRA) (HF mirror of civitai.com/models/2560840), 8-12 steps at CFG 1
25
- - Hardware: CPU Basic — ~50 min per 512×512 generation
 
15
 
16
  # Anima Preview3 Image Generation (CPU)
17
 
18
+ Generate anime images with [Anima Preview3](https://huggingface.co/circlestone-labs/Anima) + Turbo LoRA, packaged as a single Q4_K GGUF for sd.cpp.
19
 
20
  - Engine: stable-diffusion.cpp (compiled from source)
21
+ - Model: [n-Arno/Anima-P3-Turbo-AIO-Q4_K](https://huggingface.co/n-Arno/Anima-P3-Turbo-AIO-Q4_K) — 1.79 GB
22
+ - Concatenates VAE + LLM (Qwen3-0.6B text encoder) + DiT + Turbo LoRA (from civitai.com/models/2560840)
23
+ - Quantized to Q4_K via `sd-cli -M convert`
24
+ - Settings: CFG 1, sampler `er_sde`, scheduler `smoothstep`, 16 steps, `--cache-mode spectrum`, `--offload-to-cpu`
25
+ - Hardware: CPU Basic — ~30 min per 512×512 generation
app.py CHANGED
@@ -1,48 +1,35 @@
1
- """Anima Preview3 Image Generation (CPU) via sd-cli binary"""
2
 
3
  import os, time, subprocess, tempfile, shutil, threading
4
- from pathlib import Path
5
  from PIL import Image
6
  from huggingface_hub import hf_hub_download
7
  import gradio as gr
8
 
9
  # ---------------------------------------------------------------------------
10
- # Download models
11
  # ---------------------------------------------------------------------------
12
  MODELS_DIR = "/tmp/anima_models"
13
- LORA_DIR = "/tmp/loras"
14
  os.makedirs(MODELS_DIR, exist_ok=True)
15
- os.makedirs(LORA_DIR, exist_ok=True)
16
 
17
- def ensure_model(repo_id, filename, subdir=""):
 
18
  path = os.path.join(MODELS_DIR, filename)
19
  if os.path.exists(path):
20
  return path
21
- print(f"[init] Downloading {repo_id}/{subdir}/{filename}...")
22
- src = hf_hub_download(
23
- repo_id=repo_id,
24
- filename=f"{subdir}/{filename}" if subdir else filename,
25
- )
26
  shutil.copy2(src, path)
27
  return path
28
 
29
- print("[init] Ensuring model files...")
 
30
  t0 = time.time()
31
- diffusion_path = ensure_model(
32
- "circlestone-labs/Anima",
33
- "anima-preview3-base.safetensors",
34
- "split_files/diffusion_models",
35
  )
36
- llm_path = ensure_model("circlestone-labs/Anima", "qwen_3_06b_base.safetensors", "split_files/text_encoders")
37
- vae_path = ensure_model("circlestone-labs/Anima", "qwen_image_vae.safetensors", "split_files/vae")
38
-
39
- # Turbo LoRA trained on Preview3 (HF mirror of civitai.com/models/2560840)
40
- lora_src = hf_hub_download("dummy9996/Anima-Turbo-LoRA", "anima-turbo-lora-v0.1.safetensors")
41
- lora_path = os.path.join(LORA_DIR, "anima_preview3_turbo.safetensors")
42
- if not os.path.exists(lora_path):
43
- shutil.copy2(lora_src, lora_path)
44
-
45
- print(f"[init] Models ready in {time.time()-t0:.1f}s")
46
 
47
  # ---------------------------------------------------------------------------
48
  # Active process tracking (kill on user disconnect)
@@ -53,14 +40,14 @@ _proc_lock = threading.Lock()
53
  # ---------------------------------------------------------------------------
54
  # Inference via sd-cli binary
55
  # ---------------------------------------------------------------------------
56
- RESOLUTIONS = ["512x512", "768x512", "512x768"]
57
  CFG = 1.0
58
  TIMEOUT = 10800
59
- # Coefficient calibrated for Preview3 fp16 safetensors on CPU Basic
60
- # (current GGUF was ~15 min/512x512/8steps; fp16 is ~3-4x slower)
61
- EST_MIN_PER_512_8STEPS = 50
62
 
63
- def generate(prompt, negative_prompt, resolution, steps, lora_strength, seed):
 
64
  global _active_proc
65
 
66
  if not prompt or not prompt.strip():
@@ -68,13 +55,12 @@ def generate(prompt, negative_prompt, resolution, steps, lora_strength, seed):
68
 
69
  prompt = prompt.strip()[:500]
70
  steps = int(steps)
71
- lora_strength = float(lora_strength)
72
 
73
  w, h = (int(x) for x in resolution.split("x"))
74
  pixels = w * h
75
- est_min = int(pixels / 512 / 512 * steps / 8 * EST_MIN_PER_512_8STEPS)
76
 
77
- if est_min > 120:
78
  raise gr.Error(f"Estimated ~{est_min} min — too slow for CPU. Use 512x512 or fewer steps.")
79
 
80
  seed = int(seed or -1) if seed is not None else -1
@@ -84,26 +70,26 @@ def generate(prompt, negative_prompt, resolution, steps, lora_strength, seed):
84
 
85
  cmd = [
86
  "sd-cli",
87
- "--diffusion-model", diffusion_path,
88
- "--llm", llm_path,
89
- "--vae", vae_path,
90
- "--lora-model-dir", LORA_DIR,
91
- "-p", f"<lora:anima_preview3_turbo:{lora_strength}> {prompt}",
92
  "-n", negative_prompt or "",
93
  "-W", str(w),
94
  "-H", str(h),
95
  "--steps", str(steps),
96
  "--cfg-scale", str(CFG),
97
- "--sampling-method", "dpm++2m",
98
- "-o", output_path,
99
- "--diffusion-fa",
100
  "--vae-tiling",
 
 
 
101
  "-v",
102
  ]
103
  if seed >= 0:
104
  cmd += ["-s", str(seed)]
105
 
106
- print(f"[gen] {w}x{h} steps={steps} lora={lora_strength} seed={seed} est~{est_min}min prompt={prompt[:80]}")
107
  t0 = time.time()
108
 
109
  try:
@@ -133,7 +119,7 @@ def generate(prompt, negative_prompt, resolution, steps, lora_strength, seed):
133
  raise gr.Error("No output image generated")
134
 
135
  img = Image.open(output_path)
136
- status = f"Generated in {elapsed:.1f}s ({w}x{h}, {steps} steps, lora={lora_strength})"
137
  print(f"[gen] {status}")
138
  return img, status
139
 
@@ -150,15 +136,16 @@ def generate(prompt, negative_prompt, resolution, steps, lora_strength, seed):
150
  _active_proc = None
151
  raise gr.Error(f"Error: {e}")
152
 
 
153
  # ---------------------------------------------------------------------------
154
  # Gradio UI
155
  # ---------------------------------------------------------------------------
156
  with gr.Blocks(title="Anima Preview3 (CPU)") as demo:
157
  gr.Markdown(
158
- "**[Anima Preview3](https://huggingface.co/circlestone-labs/Anima)** safetensors "
159
- "+ [Turbo LoRA v0.1](https://huggingface.co/dummy9996/Anima-Turbo-LoRA) (CFG 1, 8-12 steps) "
160
  "via [sd.cpp](https://github.com/leejet/stable-diffusion.cpp) | "
161
- "**~50 min** at 512x512 on free CPU. Lower LoRA strength = more variety, slightly less stability."
162
  )
163
  with gr.Row():
164
  with gr.Column():
@@ -168,8 +155,7 @@ with gr.Blocks(title="Anima Preview3 (CPU)") as demo:
168
  value="worst quality, low quality, score_1, score_2, score_3, artist name")
169
  with gr.Row():
170
  res_input = gr.Dropdown(choices=RESOLUTIONS, value="512x512", label="Resolution")
171
- steps_input = gr.Slider(8, 12, value=8, step=1, label="Steps")
172
- strength_input = gr.Slider(0.5, 1.0, value=1.0, step=0.05, label="LoRA strength")
173
  seed_input = gr.Number(value=-1, label="Seed (-1=random)", precision=0)
174
  gen_btn = gr.Button("Generate", variant="primary", size="lg")
175
  with gr.Column():
@@ -177,7 +163,7 @@ with gr.Blocks(title="Anima Preview3 (CPU)") as demo:
177
  status_box = gr.Textbox(label="Status", interactive=False)
178
 
179
  gen_btn.click(fn=generate,
180
- inputs=[prompt_input, neg_input, res_input, steps_input, strength_input, seed_input],
181
  outputs=[output_img, status_box],
182
  concurrency_limit=1)
183
 
 
1
+ """Anima Preview3 + Turbo (AIO GGUF) Image Generation (CPU) via sd-cli binary"""
2
 
3
  import os, time, subprocess, tempfile, shutil, threading
 
4
  from PIL import Image
5
  from huggingface_hub import hf_hub_download
6
  import gradio as gr
7
 
8
  # ---------------------------------------------------------------------------
9
+ # Download single AIO GGUF model file (VAE + LLM + DiT + Turbo LoRA, all merged)
10
  # ---------------------------------------------------------------------------
11
  MODELS_DIR = "/tmp/anima_models"
 
12
  os.makedirs(MODELS_DIR, exist_ok=True)
 
13
 
14
+
15
+ def ensure_model(repo_id, filename):
16
  path = os.path.join(MODELS_DIR, filename)
17
  if os.path.exists(path):
18
  return path
19
+ print(f"[init] Downloading {repo_id}/{filename}...")
20
+ src = hf_hub_download(repo_id=repo_id, filename=filename)
 
 
 
21
  shutil.copy2(src, path)
22
  return path
23
 
24
+
25
+ print("[init] Ensuring AIO GGUF model file...")
26
  t0 = time.time()
27
+ # n-Arno's "All In One" build: VAE + LLM (text encoder) + DiT + Turbo LoRA
28
+ # concatenated and quantized to Q4_K. 1.79 GB. Made specifically for sd.cpp.
29
+ aio_path = ensure_model(
30
+ "n-Arno/Anima-P3-Turbo-AIO-Q4_K", "Anima-P3-Turbo-AIO-Q4_K.gguf"
31
  )
32
+ print(f"[init] Model ready in {time.time()-t0:.1f}s")
 
 
 
 
 
 
 
 
 
33
 
34
  # ---------------------------------------------------------------------------
35
  # Active process tracking (kill on user disconnect)
 
40
  # ---------------------------------------------------------------------------
41
  # Inference via sd-cli binary
42
  # ---------------------------------------------------------------------------
43
+ RESOLUTIONS = ["512x512", "768x512", "512x768", "832x1216"]
44
  CFG = 1.0
45
  TIMEOUT = 10800
46
+ # Calibration: Q4_K AIO GGUF at 16 steps + 512x512 ≈ 30 min on CPU Basic.
47
+ EST_MIN_PER_512_16STEPS = 30
 
48
 
49
+
50
+ def generate(prompt, negative_prompt, resolution, steps, seed):
51
  global _active_proc
52
 
53
  if not prompt or not prompt.strip():
 
55
 
56
  prompt = prompt.strip()[:500]
57
  steps = int(steps)
 
58
 
59
  w, h = (int(x) for x in resolution.split("x"))
60
  pixels = w * h
61
+ est_min = int(pixels / 512 / 512 * steps / 16 * EST_MIN_PER_512_16STEPS)
62
 
63
+ if est_min > 180:
64
  raise gr.Error(f"Estimated ~{est_min} min — too slow for CPU. Use 512x512 or fewer steps.")
65
 
66
  seed = int(seed or -1) if seed is not None else -1
 
70
 
71
  cmd = [
72
  "sd-cli",
73
+ "-m", aio_path,
74
+ "-p", prompt,
 
 
 
75
  "-n", negative_prompt or "",
76
  "-W", str(w),
77
  "-H", str(h),
78
  "--steps", str(steps),
79
  "--cfg-scale", str(CFG),
80
+ "--sampling-method", "er_sde",
81
+ "--scheduler", "smoothstep",
82
+ "--cache-mode", "spectrum",
83
  "--vae-tiling",
84
+ "--fa",
85
+ "--offload-to-cpu",
86
+ "-o", output_path,
87
  "-v",
88
  ]
89
  if seed >= 0:
90
  cmd += ["-s", str(seed)]
91
 
92
+ print(f"[gen] {w}x{h} steps={steps} seed={seed} est~{est_min}min prompt={prompt[:80]}")
93
  t0 = time.time()
94
 
95
  try:
 
119
  raise gr.Error("No output image generated")
120
 
121
  img = Image.open(output_path)
122
+ status = f"Generated in {elapsed:.1f}s ({w}x{h}, {steps} steps)"
123
  print(f"[gen] {status}")
124
  return img, status
125
 
 
136
  _active_proc = None
137
  raise gr.Error(f"Error: {e}")
138
 
139
+
140
  # ---------------------------------------------------------------------------
141
  # Gradio UI
142
  # ---------------------------------------------------------------------------
143
  with gr.Blocks(title="Anima Preview3 (CPU)") as demo:
144
  gr.Markdown(
145
+ "**[Anima Preview3 + Turbo AIO Q4_K](https://huggingface.co/n-Arno/Anima-P3-Turbo-AIO-Q4_K)** GGUF "
146
+ "(VAE + LLM + DiT + Turbo LoRA merged into 1.79 GB) "
147
  "via [sd.cpp](https://github.com/leejet/stable-diffusion.cpp) | "
148
+ "**~30 min** at 512x512 / 16 steps on free CPU"
149
  )
150
  with gr.Row():
151
  with gr.Column():
 
155
  value="worst quality, low quality, score_1, score_2, score_3, artist name")
156
  with gr.Row():
157
  res_input = gr.Dropdown(choices=RESOLUTIONS, value="512x512", label="Resolution")
158
+ steps_input = gr.Slider(8, 20, value=16, step=1, label="Steps")
 
159
  seed_input = gr.Number(value=-1, label="Seed (-1=random)", precision=0)
160
  gen_btn = gr.Button("Generate", variant="primary", size="lg")
161
  with gr.Column():
 
163
  status_box = gr.Textbox(label="Status", interactive=False)
164
 
165
  gen_btn.click(fn=generate,
166
+ inputs=[prompt_input, neg_input, res_input, steps_input, seed_input],
167
  outputs=[output_img, status_box],
168
  concurrency_limit=1)
169