Nekochu commited on
Commit
736cf48
·
1 Parent(s): 18070fb

Z-Anime 6B CPU: distill 8-step Q5_0, Qwen3-4B Q8_0, euler_a, beta schedule

Browse files
Files changed (3) hide show
  1. Dockerfile +61 -0
  2. README.md +41 -5
  3. app.py +145 -0
Dockerfile ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM ubuntu:22.04 AS builder
2
+
3
+ ENV DEBIAN_FRONTEND=noninteractive
4
+
5
+ RUN apt-get update && apt-get install -y --no-install-recommends \
6
+ build-essential cmake git ca-certificates libopenblas-dev pkg-config \
7
+ && rm -rf /var/lib/apt/lists/*
8
+
9
+ WORKDIR /build
10
+
11
+ RUN git clone --depth 1 https://github.com/leejet/stable-diffusion.cpp.git . \
12
+ && git submodule update --init --depth 1
13
+
14
+ RUN mkdir build && cd build \
15
+ && cmake .. -DGGML_BLAS=ON -DSD_BUILD_SHARED_LIBS=OFF \
16
+ && cmake --build . --config Release -j1
17
+
18
+ RUN mkdir -p /artifacts \
19
+ && cp /build/build/bin/sd-cli /artifacts/ \
20
+ && (cp -a /build/build/bin/lib*.so* /artifacts/ 2>/dev/null || true)
21
+
22
+ # ---------------------------------------------------------------------------
23
+ # Runtime image
24
+ # ---------------------------------------------------------------------------
25
+ FROM ubuntu:22.04
26
+
27
+ ENV DEBIAN_FRONTEND=noninteractive
28
+
29
+ RUN apt-get update && apt-get install -y --no-install-recommends \
30
+ libopenblas0 libgomp1 ca-certificates curl \
31
+ python3 python3-pip \
32
+ && rm -rf /var/lib/apt/lists/*
33
+
34
+ WORKDIR /app
35
+
36
+ COPY --from=builder /artifacts/ /app/
37
+ ENV LD_LIBRARY_PATH=/app:${LD_LIBRARY_PATH}
38
+ RUN chmod +x /app/sd-cli
39
+
40
+ RUN mkdir -p /app/models
41
+
42
+ # Download Z-Anime distill 8-step Q5_0 GGUF (~4.51GB)
43
+ RUN curl -fL --retry 3 --retry-delay 5 -o /app/models/z-anime-8steps-q5_0.gguf \
44
+ "https://huggingface.co/DaNS2025/Z-Anime_8-steps.GGUF/resolve/main/Z-Anime-8steps.q5_0.gguf"
45
+
46
+ # Download Qwen3-4B text encoder Q8_0 GGUF (~4.28GB)
47
+ RUN curl -fL --retry 3 --retry-delay 5 -o /app/models/qwen3_4b_q8_0.gguf \
48
+ "https://huggingface.co/worstplayer/Z-Image_Qwen_3_4b_text_encoder_GGUF/resolve/main/Qwen_3_4b-Q8_0.gguf"
49
+
50
+ # Download VAE (~168MB)
51
+ RUN curl -fL --retry 3 --retry-delay 5 -o /app/models/ae.safetensors \
52
+ "https://huggingface.co/SeeSee21/Z-Anime/resolve/main/vae/ae.safetensors"
53
+
54
+ # Install Python deps
55
+ RUN pip3 install --no-cache-dir gradio Pillow
56
+
57
+ COPY app.py /app/app.py
58
+
59
+ EXPOSE 7860
60
+
61
+ CMD ["python3", "/app/app.py"]
README.md CHANGED
@@ -1,10 +1,46 @@
1
  ---
2
- title: Z Anime CPU
3
- emoji: 👁
4
- colorFrom: purple
5
- colorTo: blue
6
  sdk: docker
7
  pinned: false
 
 
 
 
 
 
 
 
 
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Z-Anime Image Generation (CPU)
3
+ emoji: 🎨
4
+ colorFrom: blue
5
+ colorTo: pink
6
  sdk: docker
7
  pinned: false
8
+ license: apache-2.0
9
+ tags:
10
+ - image-generation
11
+ - z-anime
12
+ - gguf
13
+ - cpu
14
+ - anime
15
+ short_description: Z-Anime 6B - CPU anime image generation via sd.cpp
16
+ models:
17
+ - SeeSee21/Z-Anime
18
+ startup_duration_timeout: 1h
19
  ---
20
 
21
+ # Z-Anime Image Generation (CPU)
22
+
23
+ Generate anime images with [Z-Anime 6B](https://huggingface.co/SeeSee21/Z-Anime) (S3-DiT, distill 8-step) via [stable-diffusion.cpp](https://github.com/leejet/stable-diffusion.cpp). Runs on free CPU Spaces.
24
+
25
+ ## Models
26
+
27
+ | Component | File | Size |
28
+ |-----------|------|------|
29
+ | Diffusion (DiT) | Z-Anime-8steps Q5_0 GGUF | 4.51 GB |
30
+ | Text Encoder | Qwen3-4B Q8_0 GGUF | 4.28 GB |
31
+ | VAE | ae.safetensors | 168 MB |
32
+
33
+ ## Settings
34
+
35
+ - **Steps:** 8 (distilled)
36
+ - **CFG:** 1.0
37
+ - **Sampler:** euler_ancestral
38
+ - **Resolution:** 512x512 (recommended on CPU)
39
+
40
+ ## Credits
41
+
42
+ - [Z-Anime](https://huggingface.co/SeeSee21/Z-Anime) by SeeSee21
43
+ - [Z-Image](https://github.com/Tongyi-MAI/Z-Image) by Alibaba Tongyi Lab
44
+ - [stable-diffusion.cpp](https://github.com/leejet/stable-diffusion.cpp) by leejet
45
+ - [Z-Anime 8-step GGUF](https://huggingface.co/DaNS2025/Z-Anime_8-steps.GGUF) by DaNS2025
46
+ - [Qwen3-4B Text Encoder GGUF](https://huggingface.co/worstplayer/Z-Image_Qwen_3_4b_text_encoder_GGUF) by worstplayer
app.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Z-Anime 6B Image Generation (CPU) via sd-cli binary"""
2
+
3
+ import os, time, subprocess, tempfile, threading
4
+ from PIL import Image
5
+ import gradio as gr
6
+
7
+ # ---------------------------------------------------------------------------
8
+ # Model paths (downloaded at build time)
9
+ # ---------------------------------------------------------------------------
10
+ DIFFUSION = "/app/models/z-anime-8steps-q5_0.gguf"
11
+ LLM = "/app/models/qwen3_4b_q8_0.gguf"
12
+ VAE = "/app/models/ae.safetensors"
13
+
14
+ RESOLUTIONS = ["512x512", "768x512", "512x768"]
15
+ STEPS = 8
16
+ CFG = 1.0
17
+ TIMEOUT = 10800
18
+
19
+ _active_proc = None
20
+ _proc_lock = threading.Lock()
21
+
22
+ # ---------------------------------------------------------------------------
23
+ # Inference
24
+ # ---------------------------------------------------------------------------
25
+
26
+ def generate(prompt, negative_prompt, resolution, seed):
27
+ global _active_proc
28
+
29
+ if not prompt or not prompt.strip():
30
+ raise gr.Error("Please enter a prompt.")
31
+
32
+ prompt = prompt.strip()[:500]
33
+ w, h = (int(x) for x in resolution.split("x"))
34
+ seed = int(seed or -1) if seed is not None else -1
35
+
36
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f:
37
+ output_path = f.name
38
+
39
+ cmd = [
40
+ "/app/sd-cli",
41
+ "--diffusion-model", DIFFUSION,
42
+ "--llm", LLM,
43
+ "--vae", VAE,
44
+ "-p", prompt,
45
+ "-n", negative_prompt or "",
46
+ "-W", str(w),
47
+ "-H", str(h),
48
+ "--steps", str(STEPS),
49
+ "--cfg-scale", str(CFG),
50
+ "--sampling-method", "euler_a",
51
+ "--schedule", "beta",
52
+ "-o", output_path,
53
+ "--diffusion-fa",
54
+ "--vae-tiling",
55
+ "-v",
56
+ ]
57
+ if seed >= 0:
58
+ cmd += ["-s", str(seed)]
59
+
60
+ print(f"[gen] {w}x{h} steps={STEPS} seed={seed} prompt={prompt[:80]}")
61
+ t0 = time.time()
62
+
63
+ try:
64
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
65
+ with _proc_lock:
66
+ _active_proc = proc
67
+
68
+ try:
69
+ stdout, stderr = proc.communicate(timeout=TIMEOUT)
70
+ except subprocess.TimeoutExpired:
71
+ proc.kill()
72
+ proc.wait()
73
+ raise
74
+
75
+ elapsed = time.time() - t0
76
+
77
+ with _proc_lock:
78
+ _active_proc = None
79
+
80
+ if proc.returncode != 0:
81
+ err = stderr.decode(errors="replace")[-500:] if stderr else "Unknown error"
82
+ if proc.returncode == -9:
83
+ raise gr.Error("Out of memory (killed by OS). Try 512x512.")
84
+ raise gr.Error(f"sd-cli failed (code {proc.returncode}): {err}")
85
+
86
+ if not os.path.exists(output_path) or os.path.getsize(output_path) == 0:
87
+ raise gr.Error("No output image generated")
88
+
89
+ img = Image.open(output_path)
90
+ status = f"Generated in {elapsed:.1f}s ({w}x{h}, {STEPS} steps)"
91
+ print(f"[gen] {status}")
92
+ return img, status
93
+
94
+ except subprocess.TimeoutExpired:
95
+ with _proc_lock:
96
+ _active_proc = None
97
+ raise gr.Error(f"Generation timed out ({TIMEOUT//60} min limit)")
98
+ except gr.Error:
99
+ with _proc_lock:
100
+ _active_proc = None
101
+ raise
102
+ except Exception as e:
103
+ with _proc_lock:
104
+ _active_proc = None
105
+ raise gr.Error(f"Error: {e}")
106
+
107
+ # ---------------------------------------------------------------------------
108
+ # Gradio UI
109
+ # ---------------------------------------------------------------------------
110
+ with gr.Blocks(title="Z-Anime (CPU)") as demo:
111
+ gr.Markdown(
112
+ "**[Z-Anime 6B](https://huggingface.co/SeeSee21/Z-Anime)** S3-DiT Q5_0 GGUF "
113
+ "(distill 8-step) via [sd.cpp](https://github.com/leejet/stable-diffusion.cpp) | "
114
+ "Free CPU inference"
115
+ )
116
+ with gr.Row():
117
+ with gr.Column():
118
+ prompt_input = gr.Textbox(label="Prompt", lines=3,
119
+ placeholder="anime girl with silver hair, fantasy armor, dramatic lighting")
120
+ neg_input = gr.Textbox(label="Negative Prompt", lines=2,
121
+ value="lowres, bad anatomy, bad hands, text, error, worst quality, blurry")
122
+ with gr.Row():
123
+ res_input = gr.Dropdown(choices=RESOLUTIONS, value="512x512",
124
+ label="Resolution")
125
+ seed_input = gr.Number(value=-1, label="Seed (-1=random)", precision=0)
126
+ gen_btn = gr.Button("Generate (8 steps, CFG 1)", variant="primary", size="lg")
127
+ with gr.Column():
128
+ output_img = gr.Image(type="pil", label="Output")
129
+ status_box = gr.Textbox(label="Status", interactive=False)
130
+
131
+ gen_btn.click(fn=generate,
132
+ inputs=[prompt_input, neg_input, res_input, seed_input],
133
+ outputs=[output_img, status_box],
134
+ concurrency_limit=1)
135
+
136
+ def _on_unload():
137
+ with _proc_lock:
138
+ proc = _active_proc
139
+ if proc and proc.poll() is None:
140
+ print("[cleanup] User disconnected, killing sd-cli process")
141
+ proc.kill()
142
+
143
+ demo.unload(_on_unload)
144
+
145
+ demo.launch(server_name="0.0.0.0", server_port=7860, show_error=True)