Spaces:
Runtime error
Runtime error
Alexander Bagus
commited on
Commit
·
bb2d84c
1
Parent(s):
506ccf8
22
Browse files- .gitignore +16 -0
- README.md +6 -7
- app.py +228 -4
- examples/0_examples.json +7 -0
- examples/bird.jpg +0 -0
- examples/bottle.jpg +0 -0
- examples/pose1.jpg +0 -0
- examples/pose2.jpg +0 -0
- examples/room.jpg +0 -0
- requirements.txt +6 -0
- static/footer.md +13 -0
- static/header.html +14 -0
- utils/image_utils.py +76 -0
- utils/prompt_utils.py +71 -0
- utils/repo_utils.py +33 -0
.gitignore
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# project
|
| 2 |
+
/models/
|
| 3 |
+
|
| 4 |
+
# Packages
|
| 5 |
+
*.egg
|
| 6 |
+
*.egg-info
|
| 7 |
+
build
|
| 8 |
+
eggs
|
| 9 |
+
parts
|
| 10 |
+
bin
|
| 11 |
+
var
|
| 12 |
+
sdist
|
| 13 |
+
develop-eggs
|
| 14 |
+
.installed.cfg
|
| 15 |
+
lib64
|
| 16 |
+
__pycache__
|
README.md
CHANGED
|
@@ -1,14 +1,13 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: gradio
|
| 7 |
-
sdk_version: 6.
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
-
|
| 11 |
-
short_description: Train LoRA from a single image
|
| 12 |
---
|
| 13 |
|
| 14 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
| 1 |
---
|
| 2 |
+
title: STARFlow Image
|
| 3 |
+
emoji: 💻
|
| 4 |
+
colorFrom: green
|
| 5 |
+
colorTo: red
|
| 6 |
sdk: gradio
|
| 7 |
+
sdk_version: 6.0.2
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
+
short_description: Generate image from text prompt using Apple STARFlow
|
|
|
|
| 11 |
---
|
| 12 |
|
| 13 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
app.py
CHANGED
|
@@ -1,7 +1,231 @@
|
|
| 1 |
import gradio as gr
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
-
def greet(name):
|
| 4 |
-
return "Hello " + name + "!!"
|
| 5 |
|
| 6 |
-
|
| 7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
+
import numpy as np
|
| 3 |
+
import torch, random, json, spaces, time
|
| 4 |
+
# from safetensors.torch import load_file
|
| 5 |
+
# from diffusers import AutoencoderKL, FlowMatchEulerDiscreteScheduler
|
| 6 |
+
# from videox_fun.pipeline import ZImageControlPipeline
|
| 7 |
+
# from videox_fun.models import ZImageControlTransformer2DModel
|
| 8 |
+
# from transformers import AutoTokenizer, Qwen3ForCausalLM
|
| 9 |
+
# from diffusers import AutoencoderKL
|
| 10 |
+
# from controlnet_aux.processor import Processor
|
| 11 |
+
from utils import repo_utils, image_utils, prompt_utils
|
| 12 |
|
|
|
|
|
|
|
| 13 |
|
| 14 |
+
repo_utils.clone_repo_if_not_exists("https://github.com/apple/ml-starflow.git", "app/models")
|
| 15 |
+
repo_utils.clone_repo_if_not_exists("https://huggingface.co/apple/starflow", "app/models")
|
| 16 |
+
|
| 17 |
+
# MODEL_PATH = "models/Z-Image-Turbo/"
|
| 18 |
+
# CONTROLNET_PATH = "models/Z-Image-Turbo-Fun-Controlnet-Union/Z-Image-Turbo-Fun-Controlnet-Union.safetensors"
|
| 19 |
+
|
| 20 |
+
DTYPE = torch.bfloat16
|
| 21 |
+
MAX_SEED = np.iinfo(np.int32).max
|
| 22 |
+
|
| 23 |
+
# # load transformer
|
| 24 |
+
# transformer = ZImageControlTransformer2DModel.from_pretrained(
|
| 25 |
+
# MODEL_PATH,
|
| 26 |
+
# subfolder="transformer",
|
| 27 |
+
# transformer_additional_kwargs={
|
| 28 |
+
# "control_layers_places": [0, 5, 10, 15, 20, 25],
|
| 29 |
+
# "control_in_dim": 16
|
| 30 |
+
# },
|
| 31 |
+
# torch_dtype= DTYPE
|
| 32 |
+
# ).to("cuda")
|
| 33 |
+
|
| 34 |
+
# ## Load controlnet
|
| 35 |
+
# state_dict = load_file(CONTROLNET_PATH)
|
| 36 |
+
# state_dict = state_dict["state_dict"] if "state_dict" in state_dict else state_dict
|
| 37 |
+
# m, u = transformer.load_state_dict(state_dict, strict=False)
|
| 38 |
+
# print(f"missing keys: {len(m)}, unexpected keys: {len(u)}")
|
| 39 |
+
|
| 40 |
+
# # load ZImageControlPipeline
|
| 41 |
+
# vae = AutoencoderKL.from_pretrained(
|
| 42 |
+
# MODEL_PATH,
|
| 43 |
+
# subfolder="vae",
|
| 44 |
+
# device_map="cuda",
|
| 45 |
+
# torch_dtype= DTYPE
|
| 46 |
+
# )
|
| 47 |
+
|
| 48 |
+
# tokenizer = AutoTokenizer.from_pretrained(
|
| 49 |
+
# MODEL_PATH,
|
| 50 |
+
# subfolder="tokenizer"
|
| 51 |
+
# )
|
| 52 |
+
|
| 53 |
+
# text_encoder = Qwen3ForCausalLM.from_pretrained(
|
| 54 |
+
# MODEL_PATH,
|
| 55 |
+
# subfolder="text_encoder",
|
| 56 |
+
# torch_dtype=DTYPE,
|
| 57 |
+
# )
|
| 58 |
+
|
| 59 |
+
# scheduler = FlowMatchEulerDiscreteScheduler.from_pretrained(
|
| 60 |
+
# MODEL_PATH,
|
| 61 |
+
# subfolder="scheduler"
|
| 62 |
+
# )
|
| 63 |
+
|
| 64 |
+
# pipe = ZImageControlPipeline(
|
| 65 |
+
# vae=vae,
|
| 66 |
+
# tokenizer=tokenizer,
|
| 67 |
+
# text_encoder=text_encoder,
|
| 68 |
+
# transformer=transformer,
|
| 69 |
+
# scheduler=scheduler,
|
| 70 |
+
# )
|
| 71 |
+
# pipe.to("cuda", DTYPE)
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
# def prepare(prompt, is_polish_prompt):
|
| 75 |
+
# if not is_polish_prompt: return prompt, False
|
| 76 |
+
# polished_prompt = prompt_utils.polish_prompt(prompt)
|
| 77 |
+
# return polished_prompt, True
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
@spaces.GPU
|
| 81 |
+
def inference(
|
| 82 |
+
prompt,
|
| 83 |
+
negative_prompt,
|
| 84 |
+
seed=42,
|
| 85 |
+
randomize_seed=True,
|
| 86 |
+
guidance_scale=1.5,
|
| 87 |
+
num_inference_steps=8,
|
| 88 |
+
progress=gr.Progress(track_tqdm=True),
|
| 89 |
+
):
|
| 90 |
+
timestamp = time.time()
|
| 91 |
+
print(f"timestamp: {timestamp}")
|
| 92 |
+
|
| 93 |
+
# # process image
|
| 94 |
+
# print("DEBUG: process image")
|
| 95 |
+
# if input_image is None:
|
| 96 |
+
# print("Error: input_image is empty.")
|
| 97 |
+
# return None
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
# print("DEBUG: control_image_torch")
|
| 101 |
+
# orig_width, orig_height = input_image.size
|
| 102 |
+
# control_image, width, height = image_utils.rescale_image(input_image, image_scale, 16, 2048)
|
| 103 |
+
# control_image_torch = image_utils.get_image_latent(control_image, sample_size=[height, width])[:, :, 0]
|
| 104 |
+
|
| 105 |
+
# # generation
|
| 106 |
+
# if randomize_seed: seed = random.randint(0, MAX_SEED)
|
| 107 |
+
# generator = torch.Generator().manual_seed(seed)
|
| 108 |
+
|
| 109 |
+
# output_image = pipe(
|
| 110 |
+
# prompt=prompt,
|
| 111 |
+
# negative_prompt = negative_prompt,
|
| 112 |
+
# width=width,
|
| 113 |
+
# height=height,
|
| 114 |
+
# generator=generator,
|
| 115 |
+
# guidance_scale=guidance_scale,
|
| 116 |
+
# control_image=control_image_torch,
|
| 117 |
+
# num_inference_steps=num_inference_steps,
|
| 118 |
+
# control_context_scale=control_context_scale,
|
| 119 |
+
# ).images[0]
|
| 120 |
+
|
| 121 |
+
# output_image = output_image.resize((orig_width * image_scale, orig_height * image_scale))
|
| 122 |
+
# return output_image, seed
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def read_file(path: str) -> str:
|
| 126 |
+
with open(path, 'r', encoding='utf-8') as f:
|
| 127 |
+
content = f.read()
|
| 128 |
+
return content
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
css = """
|
| 132 |
+
#col-container {
|
| 133 |
+
margin: 0 auto;
|
| 134 |
+
max-width: 960px;
|
| 135 |
+
}
|
| 136 |
+
"""
|
| 137 |
+
|
| 138 |
+
with open('examples/0_examples.json', 'r') as file: examples = json.load(file)
|
| 139 |
+
|
| 140 |
+
with gr.Blocks() as demo:
|
| 141 |
+
with gr.Column(elem_id="col-container"):
|
| 142 |
+
with gr.Column():
|
| 143 |
+
gr.HTML(read_file("static/header.html"))
|
| 144 |
+
with gr.Row():
|
| 145 |
+
with gr.Column():
|
| 146 |
+
|
| 147 |
+
prompt = gr.Textbox(
|
| 148 |
+
label="Prompt",
|
| 149 |
+
show_label=False,
|
| 150 |
+
lines=2,
|
| 151 |
+
placeholder="Enter your prompt",
|
| 152 |
+
value="a man in a fishing boat. high quality, detailed"
|
| 153 |
+
# container=False,
|
| 154 |
+
)
|
| 155 |
+
# is_polish_prompt = gr.Checkbox(label="Polish prompt", value=True)
|
| 156 |
+
# control_mode = gr.Radio(
|
| 157 |
+
# choices=["Canny", "Depth", "HED", "MLSD", "Pose"],
|
| 158 |
+
# value="Canny",
|
| 159 |
+
# label="Control Mode"
|
| 160 |
+
# )
|
| 161 |
+
run_button = gr.Button("Generate", variant="primary")
|
| 162 |
+
with gr.Accordion("Advanced Settings", open=False):
|
| 163 |
+
|
| 164 |
+
negative_prompt = gr.Textbox(
|
| 165 |
+
label="Negative prompt",
|
| 166 |
+
lines=2,
|
| 167 |
+
container=False,
|
| 168 |
+
placeholder="Enter your negative prompt",
|
| 169 |
+
value="blurry, ugly, bad"
|
| 170 |
+
)
|
| 171 |
+
with gr.Row():
|
| 172 |
+
num_inference_steps = gr.Slider(
|
| 173 |
+
label="Steps",
|
| 174 |
+
minimum=1,
|
| 175 |
+
maximum=30,
|
| 176 |
+
step=1,
|
| 177 |
+
value=9,
|
| 178 |
+
)
|
| 179 |
+
control_context_scale = gr.Slider(
|
| 180 |
+
label="Context scale",
|
| 181 |
+
minimum=0.0,
|
| 182 |
+
maximum=1.0,
|
| 183 |
+
step=0.01,
|
| 184 |
+
value=0.75,
|
| 185 |
+
)
|
| 186 |
+
|
| 187 |
+
with gr.Row():
|
| 188 |
+
guidance_scale = gr.Slider(
|
| 189 |
+
label="Guidance scale",
|
| 190 |
+
minimum=0.0,
|
| 191 |
+
maximum=10.0,
|
| 192 |
+
step=0.1,
|
| 193 |
+
value=1.0,
|
| 194 |
+
)
|
| 195 |
+
|
| 196 |
+
seed = gr.Slider(
|
| 197 |
+
label="Seed",
|
| 198 |
+
minimum=0,
|
| 199 |
+
maximum=MAX_SEED,
|
| 200 |
+
step=1,
|
| 201 |
+
value=42,
|
| 202 |
+
)
|
| 203 |
+
randomize_seed = gr.Checkbox(label="Randomize seed", value=False)
|
| 204 |
+
|
| 205 |
+
with gr.Column():
|
| 206 |
+
output_image = gr.Image(label="Generated image", show_label=False)
|
| 207 |
+
# polished_prompt = gr.Textbox(label="Polished prompt", interactive=False)
|
| 208 |
+
|
| 209 |
+
# with gr.Accordion("Preprocessor output", open=False):
|
| 210 |
+
# control_image = gr.Image(label="Control image", show_label=False)
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
# gr.Examples(examples=examples, inputs=[input_image])
|
| 214 |
+
gr.Markdown(read_file("static/footer.md"))
|
| 215 |
+
|
| 216 |
+
run_button.click(
|
| 217 |
+
fn=inference,
|
| 218 |
+
inputs=[
|
| 219 |
+
prompt,
|
| 220 |
+
negative_prompt,
|
| 221 |
+
seed,
|
| 222 |
+
randomize_seed,
|
| 223 |
+
guidance_scale,
|
| 224 |
+
num_inference_steps,
|
| 225 |
+
],
|
| 226 |
+
outputs=[output_image, seed],
|
| 227 |
+
)
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
if __name__ == "__main__":
|
| 231 |
+
demo.launch(mcp_server=True, css=css)
|
examples/0_examples.json
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
["examples/pose2.jpg", "Woman wearing jeans and tanktop, from dubai", "HED"],
|
| 3 |
+
["examples/bottle.jpg", "A man holding a bottle", "HED"],
|
| 4 |
+
["examples/room.jpg", "modern architecture, living room", "Depth"],
|
| 5 |
+
["examples/pose1.jpg", "A female paladin. Mountain background.", "Pose"],
|
| 6 |
+
["examples/bird.jpg", "A bird sitting on a branch, cartoon.", "Canny"]
|
| 7 |
+
]
|
examples/bird.jpg
ADDED
|
examples/bottle.jpg
ADDED
|
examples/pose1.jpg
ADDED
|
examples/pose2.jpg
ADDED
|
examples/room.jpg
ADDED
|
requirements.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio
|
| 2 |
+
torch
|
| 3 |
+
transformers
|
| 4 |
+
accelerate
|
| 5 |
+
spaces
|
| 6 |
+
git+https://github.com/huggingface/diffusers.git
|
static/footer.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
## Usage
|
| 3 |
+
- **Polish Prompt**: ZIT needs a detailed prompt, which you can get by enabling polish prompt.
|
| 4 |
+
- **Context Scale**: Similar to strength, the higher the value, the more detail is preserved. The recommended control_context_scale range is 0.65 to 0.80.
|
| 5 |
+
- **Image Scale**: Upscale/downscale image resolution.
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
## References
|
| 9 |
+
- **alibaba-pai**: <https://huggingface.co/alibaba-pai/Z-Image-Turbo-Fun-Controlnet-Union>
|
| 10 |
+
- **Tongyi-MAI**: <https://huggingface.co/Tongyi-MAI/Z-Image-Turbo>
|
| 11 |
+
- **VideoX-Fun**: <https://github.com/aigc-apps/VideoX-Fun>
|
| 12 |
+
|
| 13 |
+
<!-- https://github.com/comfyanonymous/ComfyUI/pull/11062 -->
|
static/header.html
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<div style="text-align: center; max-width: 600px; margin: 0 auto;">
|
| 2 |
+
<h1>
|
| 3 |
+
Z-Image Turbo (ZIT) - Upscaler
|
| 4 |
+
</h1>
|
| 5 |
+
<div class="grid-container" >
|
| 6 |
+
<p>
|
| 7 |
+
This project is still in development, but it will be finished very soon!
|
| 8 |
+
<!-- This space still in development, please let me know if there are errors 🙏
|
| 9 |
+
<br>
|
| 10 |
+
Generate image from promt using LongCat-Image, a 6B parameters model designed for photorealism.
|
| 11 |
+
<br>
|
| 12 |
+
If you like my work, please support me by visiting <a href="https://aisudo.com/" target="_blank">AiSudo</a> 😊 -->
|
| 13 |
+
</div>
|
| 14 |
+
</div>
|
utils/image_utils.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from PIL import Image
|
| 3 |
+
import numpy as np
|
| 4 |
+
|
| 5 |
+
def rescale_image(img, scale, nearest=32, max_size=1280):
|
| 6 |
+
w, h = img.size
|
| 7 |
+
new_w = int(w * scale)
|
| 8 |
+
new_h = int(h * scale)
|
| 9 |
+
|
| 10 |
+
if new_w > max_size or new_h > max_size:
|
| 11 |
+
# Calculate new size keeping aspect ratio
|
| 12 |
+
scale = min(max_size / new_w, max_size / new_h)
|
| 13 |
+
new_w = int(new_w * scale)
|
| 14 |
+
new_h = int(new_h * scale)
|
| 15 |
+
|
| 16 |
+
# Adjust to nearest multiple
|
| 17 |
+
new_w = (new_w // nearest) * nearest
|
| 18 |
+
new_h = (new_h // nearest) * nearest
|
| 19 |
+
|
| 20 |
+
return img.resize((new_w, new_h), Image.LANCZOS), new_w, new_h
|
| 21 |
+
|
| 22 |
+
def padding_image(images, new_width, new_height):
|
| 23 |
+
new_image = Image.new('RGB', (new_width, new_height), (255, 255, 255))
|
| 24 |
+
|
| 25 |
+
aspect_ratio = images.width / images.height
|
| 26 |
+
if new_width / new_height > 1:
|
| 27 |
+
if aspect_ratio > new_width / new_height:
|
| 28 |
+
new_img_width = new_width
|
| 29 |
+
new_img_height = int(new_img_width / aspect_ratio)
|
| 30 |
+
else:
|
| 31 |
+
new_img_height = new_height
|
| 32 |
+
new_img_width = int(new_img_height * aspect_ratio)
|
| 33 |
+
else:
|
| 34 |
+
if aspect_ratio > new_width / new_height:
|
| 35 |
+
new_img_width = new_width
|
| 36 |
+
new_img_height = int(new_img_width / aspect_ratio)
|
| 37 |
+
else:
|
| 38 |
+
new_img_height = new_height
|
| 39 |
+
new_img_width = int(new_img_height * aspect_ratio)
|
| 40 |
+
|
| 41 |
+
resized_img = images.resize((new_img_width, new_img_height))
|
| 42 |
+
|
| 43 |
+
paste_x = (new_width - new_img_width) // 2
|
| 44 |
+
paste_y = (new_height - new_img_height) // 2
|
| 45 |
+
|
| 46 |
+
new_image.paste(resized_img, (paste_x, paste_y))
|
| 47 |
+
|
| 48 |
+
return new_image
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def get_image_latent(ref_image=None, sample_size=None, padding=False):
|
| 52 |
+
if ref_image is not None:
|
| 53 |
+
if isinstance(ref_image, str):
|
| 54 |
+
ref_image = Image.open(ref_image).convert("RGB")
|
| 55 |
+
if padding:
|
| 56 |
+
ref_image = padding_image(
|
| 57 |
+
ref_image, sample_size[1], sample_size[0])
|
| 58 |
+
ref_image = ref_image.resize((sample_size[1], sample_size[0]))
|
| 59 |
+
ref_image = torch.from_numpy(np.array(ref_image))
|
| 60 |
+
ref_image = ref_image.unsqueeze(0).permute(
|
| 61 |
+
[3, 0, 1, 2]).unsqueeze(0) / 255
|
| 62 |
+
elif isinstance(ref_image, Image.Image):
|
| 63 |
+
ref_image = ref_image.convert("RGB")
|
| 64 |
+
if padding:
|
| 65 |
+
ref_image = padding_image(
|
| 66 |
+
ref_image, sample_size[1], sample_size[0])
|
| 67 |
+
ref_image = ref_image.resize((sample_size[1], sample_size[0]))
|
| 68 |
+
ref_image = torch.from_numpy(np.array(ref_image))
|
| 69 |
+
ref_image = ref_image.unsqueeze(0).permute(
|
| 70 |
+
[3, 0, 1, 2]).unsqueeze(0) / 255
|
| 71 |
+
else:
|
| 72 |
+
ref_image = torch.from_numpy(np.array(ref_image))
|
| 73 |
+
ref_image = ref_image.unsqueeze(0).permute(
|
| 74 |
+
[3, 0, 1, 2]).unsqueeze(0) / 255
|
| 75 |
+
|
| 76 |
+
return ref_image
|
utils/prompt_utils.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from huggingface_hub import InferenceClient
|
| 3 |
+
|
| 4 |
+
# source: https://huggingface.co/spaces/InstantX/Qwen-Image-ControlNet/blob/main/app.py
|
| 5 |
+
def polish_prompt(original_prompt):
|
| 6 |
+
# """Rewrites the prompt using a Hugging Face InferenceClient."""
|
| 7 |
+
magic_prompt = "Ultra HD, 4K, cinematic composition"
|
| 8 |
+
system_prompt = """
|
| 9 |
+
You are a prompt engineering expert for text-to-image models. Since text-to-image models have limited capabilities in understanding user prompts, you need to identify the core theme and intent of the user's input and improve the model's understanding accuracy and generation quality through optimization and rewriting. The rewrite must strictly retain all information from the user's original prompt without deleting or distorting any details.
|
| 10 |
+
Specific requirements are as follows:
|
| 11 |
+
1. The rewrite must not affect any information expressed in the user's original prompt; the rewritten prompt should use coherent natural language, avoid low-information redundant descriptions, and keep the rewritten prompt length as concise as possible.
|
| 12 |
+
2. Ensure consistency between input and output languages: Chinese input yields Chinese output, and English input yields English output. The rewritten token count should not exceed 512.
|
| 13 |
+
3. The rewritten description should further refine subject characteristics and aesthetic techniques appearing in the original prompt, such as lighting and textures.
|
| 14 |
+
4. If the original prompt does not specify an image style, ensure the rewritten prompt uses a **realistic photography style**. If the user specifies a style, retain the user's style.
|
| 15 |
+
5. When the original prompt requires reasoning to clarify user intent, use logical reasoning based on world knowledge to convert vague abstract descriptions into specific tangible objects (e.g., convert "the tallest animal" to "a giraffe").
|
| 16 |
+
6. When the original prompt requires text generation, please use double quotes to enclose the text part (e.g., `"50% OFF"`).
|
| 17 |
+
7. When the original prompt requires generating text-heavy scenes like webpages, logos, UIs, or posters, and no specific text content is specified, you need to infer appropriate text content and enclose it in double quotes. For example, if the user inputs: "A tourism flyer with a grassland theme," it should be rewritten as: "A tourism flyer with the image title 'Grassland'."
|
| 18 |
+
8. When negative words exist in the original prompt, ensure the rewritten prompt does not contain negative words. For example, "a lakeside without boats" should be rewritten such that the word "boat" does not appear at all.
|
| 19 |
+
9. Except for text content explicitly requested by the user, **adding any extra text content is prohibited**.
|
| 20 |
+
Here are examples of rewrites for different types of prompts:
|
| 21 |
+
# Examples (Few-Shot Learning)
|
| 22 |
+
1. User Input: An animal with nine lives.
|
| 23 |
+
Rewrite Output: A cat bathed in soft sunlight, its fur soft and glossy. The background is a comfortable home environment with light from the window filtering through curtains, creating a warm light and shadow effect. The shot uses a medium distance perspective to highlight the cat's leisurely and stretched posture. Light cleverly hits the cat's face, emphasizing its spirited eyes and delicate whiskers, adding depth and affinity to the image.
|
| 24 |
+
2. User Input: Create an anime-style tourism flyer with a grassland theme.
|
| 25 |
+
Rewrite Output: In the lower right of the center, a short-haired girl sits sideways on a gray, irregularly shaped rock. She wears a white short-sleeved dress and brown flat shoes, holding a bunch of small white flowers in her left hand, smiling with her legs hanging naturally. The girl has dark brown shoulder-length hair with bangs covering her forehead, brown eyes, and a slightly open mouth. The rock surface has textures of varying depths. To the girl's left and front is lush grass, with long, yellow-green blades, some glowing golden in the sunlight. The grass extends into the distance, forming rolling green hills that fade in color as they recede. The sky occupies the upper half of the picture, pale blue dotted with a few fluffy white clouds. In the upper left corner, there is a line of text in italic, dark green font reading "Explore Nature's Peace". Colors are dominated by green, blue, and yellow, fluid lines, and distinct light and shadow contrast, creating a quiet and comfortable atmosphere.
|
| 26 |
+
3. User Input: A Christmas sale poster with a red background, promoting a Buy 1 Get 1 Free milk tea offer.
|
| 27 |
+
Rewrite Output: The poster features an overall red tone, embellished with white snowflake patterns on the top and left side. The upper right features a bunch of holly leaves with red berries and a pine cone. In the upper center, golden 3D text reads "Christmas Heartwarming Feedback" centered, along with red bold text "Buy 1 Get 1". Below, two transparent cups filled with bubble tea are placed side by side; the tea is light brown with dark brown pearls scattered at the bottom and middle. Below the cups, white snow piles up, decorated with pine branches, red berries, and pine cones. A blurry Christmas tree is faintly visible in the lower right corner. The image has high clarity, accurate text content, a unified design style, a prominent Christmas theme, and a reasonable layout, providing strong visual appeal.
|
| 28 |
+
4. User Input: A woman indoors shot in natural light, smiling with arms crossed, showing a relaxed and confident posture.
|
| 29 |
+
Rewrite Output: The image features a young Asian woman with long dark brown hair naturally falling over her shoulders, with some strands illuminated by light, showing a soft sheen. Her features are delicate, with long eyebrows, bright and spirited dark brown eyes looking directly at the camera, revealing peace and confidence. She has a high nose bridge, full lips with nude lipstick, and corners of the mouth slightly raised in a faint smile. Her skin is fair, with cheeks and collarbones illuminated by warm light, showing a healthy ruddiness. She wears a black spaghetti strap tank top revealing graceful collarbone lines, and a thin gold necklace with small beads and metal bars glinting in the light. Her outer layer is a beige knitted cardigan, soft in texture with visible knitting patterns on the sleeves. Her arms are crossed over her chest, hands covered by the cardigan sleeves, in a relaxed posture. The background is a pure dark brown without extra decoration, making the figure the absolute focus. The figure is located in the center of the frame. Light enters from the upper right, creating bright spots on her left cheek, neck, and collarbone, while the right side is slightly shadowed, creating a three-dimensional and soft tone. Image details are clear, showcasing skin texture, hair, and clothing materials well. Colors are dominated by warm tones, with the combination of beige and dark brown creating a warm and comfortable atmosphere. The overall style is natural, elegant, and artistic.
|
| 30 |
+
5. User Input: Create a series of images showing the growth process of an apple from seed to fruit. The series should include four stages: 1. Sowing, 2. Seedling growth, 3. Plant maturity, 4. Fruit harvesting.
|
| 31 |
+
Rewrite Output: A 4-panel exquisite illustration depicting the growth process of an apple, capturing each stage precisely and clearly. 1. "Sowing": A close-up shot of a hand gently placing a small apple seed into fertile dark soil, with visible soil texture and the seed's smooth surface. The background is a soft-focus garden dotted with green leaves and sunlight filtering through. 2. "Seedling Growth": A young apple sapling breaks through the soil, stretching tender green leaves toward the sky. The scene is set in a vibrant garden illuminated by warm golden light, highlighting the seedling's delicate structure. 3. "Plant Maturity": A mature apple tree, lush with branches and leaves, covered in tender green foliage and developing small apples. The background is a vibrant orchard under a clear blue sky, with dappled sunlight creating a peaceful atmosphere. 4. "Fruit Harvesting": A hand reaches into the tree to pick a ripe red apple, its smooth skin glistening in the sun. The scene shows the abundance of the orchard, with baskets of apples in the background, giving a sense of fulfillment. Each illustration uses a realistic style, focusing on details and harmonious colors to showcase the natural beauty and development of the apple's life cycle.
|
| 32 |
+
6. User Input: If 1 represents red, 2 represents green, 3 represents purple, and 4 represents yellow, please generate a four-color rainbow based on this rule. The color order from top to bottom is 3142.
|
| 33 |
+
Rewrite Output: The image consists of four horizontally arranged colored stripes, ordered from top to bottom as purple, red, yellow, and green. A white number is centered on each stripe. The top purple stripe features the number "3", the red stripe below it has the number "1", the yellow stripe further down has the number "4", and the bottom green stripe has the number "2". All numbers use a sans-serif font in pure white, forming a sharp contrast with the background colors to ensure good readability. The stripes have high color saturation and a slight texture. The overall layout is simple and clear, with distinct visual effects and no extra decorative elements, emphasizing the numerical information. The image is high definition, with accurate colors and a consistent style, offering strong visual appeal.
|
| 34 |
+
7. User Input: A stone tablet carved with "Guan Guan Ju Jiu, On the River Isle", natural light, background is a Chinese garden.
|
| 35 |
+
Rewrite Output: An ancient stone tablet carved with "Guan Guan Ju Jiu, On the River Isle", the surface covered with traces of time, the writing clear and deep. Natural light falls from above, softly illuminating every detail of the stone tablet and enhancing its sense of history. The background is an elegant Chinese garden featuring lush bamboo forests, winding paths, and quiet pools, creating a serene and distant atmosphere. The overall picture uses a realistic style with rich details and natural light and shadow effects, highlighting the cultural heritage of the stone tablet and the classical beauty of the garden.
|
| 36 |
+
# Output Format
|
| 37 |
+
Please directly output the rewritten and optimized Prompt content. Do not include any explanatory language or JSON formatting, and do not add opening or closing quotes yourself.
|
| 38 |
+
"""
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
api_key = os.environ.get("HF_TOKEN")
|
| 43 |
+
if not api_key:
|
| 44 |
+
print("Warning: HF_TOKEN is not set. Prompt enhancement is disabled.")
|
| 45 |
+
return original_prompt
|
| 46 |
+
|
| 47 |
+
if not original_prompt:
|
| 48 |
+
return magic_prompt
|
| 49 |
+
# client = InferenceClient(provider="cerebras", api_key=api_key)
|
| 50 |
+
# messages = []
|
| 51 |
+
client = InferenceClient(provider="nebius", api_key=api_key)
|
| 52 |
+
|
| 53 |
+
try:
|
| 54 |
+
completion = client.chat.completions.create(
|
| 55 |
+
model="Qwen/Qwen3-Coder-30B-A3B-Instruct",
|
| 56 |
+
max_tokens=256,
|
| 57 |
+
messages=[
|
| 58 |
+
{"role": "system", "content": system_prompt},
|
| 59 |
+
{"role": "user", "content": original_prompt}
|
| 60 |
+
],
|
| 61 |
+
)
|
| 62 |
+
# completion = client.chat.completions.create(
|
| 63 |
+
# model="Qwen/Qwen3-235B-A22B-Instruct-2507", messages=messages
|
| 64 |
+
# )
|
| 65 |
+
polished_prompt = completion.choices[0].message.content
|
| 66 |
+
# polished_prompt += f" {magic_prompt}"
|
| 67 |
+
return polished_prompt.strip().replace("\n", " ")
|
| 68 |
+
except Exception as e:
|
| 69 |
+
print(f"Error during prompt enhancement: {e}")
|
| 70 |
+
return original_prompt
|
| 71 |
+
|
utils/repo_utils.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os, subprocess
|
| 2 |
+
|
| 3 |
+
def clone_repo_if_not_exists(git_url, git_dir):
|
| 4 |
+
"""
|
| 5 |
+
Clones a Git repository if it doesn't already exist in the git_dir folder.
|
| 6 |
+
"""
|
| 7 |
+
home_dir = os.path.expanduser("~")
|
| 8 |
+
models_dir = os.path.join(home_dir, git_dir)
|
| 9 |
+
|
| 10 |
+
# Extract repository name from the Git URL
|
| 11 |
+
if git_url.endswith(".git"):
|
| 12 |
+
git_name = git_url.split('/')[-1][:-4]
|
| 13 |
+
else:
|
| 14 |
+
git_name = git_url.split('/')[-1]
|
| 15 |
+
|
| 16 |
+
repo_path = os.path.join(models_dir, git_name)
|
| 17 |
+
|
| 18 |
+
if not os.path.exists(models_dir):
|
| 19 |
+
os.makedirs(models_dir)
|
| 20 |
+
print(f"Created directory: {models_dir}")
|
| 21 |
+
|
| 22 |
+
if not os.path.exists(repo_path):
|
| 23 |
+
print(f"Repository '{git_name}' not found in '{models_dir}'. Cloning from {git_url}...")
|
| 24 |
+
try:
|
| 25 |
+
subprocess.run(["git", "clone", git_url, repo_path], check=True)
|
| 26 |
+
print(f"Successfully cloned '{git_name}' to '{repo_path}'.")
|
| 27 |
+
except subprocess.CalledProcessError as e:
|
| 28 |
+
print(f"Error cloning repository: {e}")
|
| 29 |
+
except FileNotFoundError:
|
| 30 |
+
print("Error: 'git' command not found. Please ensure Git is installed and in your PATH.")
|
| 31 |
+
else:
|
| 32 |
+
print(f"Repository '{git_name}' already exists at '{repo_path}'. Skipping clone.")
|
| 33 |
+
|