multimodalart HF Staff commited on
Commit
0657309
·
verified ·
1 Parent(s): 85a0112

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ examples/input_0.png filter=lfs diff=lfs merge=lfs -text
37
+ examples/input_1.png filter=lfs diff=lfs merge=lfs -text
38
+ examples/output.png filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,13 +1,28 @@
1
  ---
2
- title: Joyai Image Edit Plus
3
- emoji: 🌍
4
- colorFrom: purple
5
- colorTo: indigo
6
  sdk: gradio
7
- sdk_version: 6.19.0
8
- python_version: '3.12'
9
  app_file: app.py
10
- pinned: false
 
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: JoyAI Image Edit Plus
3
+ emoji: 🎨
4
+ colorFrom: indigo
5
+ colorTo: green
6
  sdk: gradio
7
+ sdk_version: 6.15.1
 
8
  app_file: app.py
9
+ short_description: Multi-image instruction-guided image editing
10
+ python_version: "3.12"
11
+ startup_duration_timeout: 1h
12
  ---
13
 
14
+ JoyAI-Image Edit Plus is a multi-image instruction-guided editing model from the JoyAI-Image family.
15
+ Provide one or more reference images and a text instruction to generate a new image that combines
16
+ elements from the references according to the instruction.
17
+
18
+ ## Model
19
+
20
+ - **Model**: [jdopensource/JoyAI-Image-Edit-Plus-Diffusers](https://huggingface.co/jdopensource/JoyAI-Image-Edit-Plus-Diffusers)
21
+ - **Architecture**: Qwen3-VL-8B text encoder + JoyImageEditPlusTransformer3D (16B) + AutoencoderKLWan VAE
22
+ - **License**: Apache-2.0
23
+
24
+ ## How it works
25
+
26
+ 1. Upload 1–6 reference images in the gallery.
27
+ 2. Write an instruction describing how to combine or edit the images.
28
+ 3. The model generates a new image that follows your instruction using the references as context.
app.py ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """JoyAI-Image Edit Plus — multi-image instruction-guided editing demo.
2
+
3
+ Loads the jdopensource/JoyAI-Image-Edit-Plus-Diffusers model and exposes a
4
+ Gradio interface where visitors provide one or more reference images and a
5
+ text instruction; the model generates a new image that combines elements
6
+ from the references according to the instruction.
7
+ """
8
+
9
+ import os
10
+
11
+ os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")
12
+
13
+ import spaces # noqa: E402 -- must precede torch import
14
+
15
+ import random # noqa: E402
16
+
17
+ import gradio as gr # noqa: E402
18
+ import torch # noqa: E402
19
+ from diffusers import JoyImageEditPlusPipeline # noqa: E402
20
+
21
+ MODEL_ID = "jdopensource/JoyAI-Image-Edit-Plus-Diffusers"
22
+
23
+ pipe = JoyImageEditPlusPipeline.from_pretrained(
24
+ MODEL_ID,
25
+ torch_dtype=torch.bfloat16,
26
+ )
27
+ pipe.enable_model_cpu_offload()
28
+
29
+
30
+ def _estimate_duration(images, prompt, num_inference_steps, guidance_scale, seed, negative_prompt, progress):
31
+ """Estimate the GPU duration needed based on step count and image count."""
32
+ n_imgs = len(images) if images else 0
33
+ # ~1s per step for 16B transformer + 8B text encoder + VAE, plus overhead
34
+ return min(300, 60 + int(num_inference_steps * 3.5) + n_imgs * 10)
35
+
36
+
37
+ @spaces.GPU(duration=_estimate_duration)
38
+ def generate(
39
+ images,
40
+ prompt: str,
41
+ negative_prompt: str,
42
+ num_inference_steps: int,
43
+ guidance_scale: float,
44
+ seed: int,
45
+ randomize_seed: bool,
46
+ progress=gr.Progress(track_tqdm=True),
47
+ ):
48
+ """Edit/generate an image from multiple reference images and a text instruction.
49
+
50
+ Args:
51
+ images: One or more reference images (1-6 supported).
52
+ prompt: Text instruction describing the desired edit or composition.
53
+ negative_prompt: Negative prompt to guide what to avoid.
54
+ num_inference_steps: Number of denoising steps (30 recommended).
55
+ guidance_scale: Classifier-free guidance scale (4.0 recommended).
56
+ seed: RNG seed for reproducibility.
57
+ randomize_seed: If True, pick a random seed each run.
58
+ """
59
+ if images is None or (isinstance(images, list) and len(images) == 0):
60
+ raise gr.Error("Please provide at least one reference image.")
61
+ if not prompt or not prompt.strip():
62
+ raise gr.Error("Please provide a text instruction.")
63
+
64
+ # Gradio Gallery returns list of file paths or dicts; normalize to PIL list
65
+ from PIL import Image
66
+
67
+ if isinstance(images, list):
68
+ pil_images = []
69
+ for img in images:
70
+ if isinstance(img, dict) and "path" in img:
71
+ pil_images.append(Image.open(img["path"]).convert("RGB"))
72
+ elif isinstance(img, str):
73
+ pil_images.append(Image.open(img).convert("RGB"))
74
+ elif isinstance(img, Image.Image):
75
+ pil_images.append(img.convert("RGB"))
76
+ else:
77
+ pil_images.append(Image.open(img).convert("RGB"))
78
+ elif isinstance(images, str):
79
+ pil_images = [Image.open(images).convert("RGB")]
80
+ else:
81
+ pil_images = [images.convert("RGB")]
82
+
83
+ if len(pil_images) > 6:
84
+ pil_images = pil_images[:6]
85
+
86
+ if randomize_seed:
87
+ seed = random.randint(0, 2**31 - 1)
88
+
89
+ # Determine output resolution from the last reference image
90
+ target_h, target_w = pipe.vae_image_processor.get_default_height_width(pil_images[-1])
91
+
92
+ generator = torch.Generator(device="cpu").manual_seed(int(seed))
93
+
94
+ result = pipe(
95
+ images=pil_images,
96
+ prompt=prompt,
97
+ negative_prompt=negative_prompt,
98
+ height=target_h,
99
+ width=target_w,
100
+ num_inference_steps=int(num_inference_steps),
101
+ guidance_scale=float(guidance_scale),
102
+ generator=generator,
103
+ )
104
+
105
+ output_image = result.images[0]
106
+ return output_image, gr.update(value=int(seed))
107
+
108
+
109
+ CSS = """
110
+ #col-container { max-width: 1100px; margin: 0 auto; }
111
+ .dark .gradio-container { color: var(--body-text-color); }
112
+ """
113
+
114
+ with gr.Blocks(theme=gr.themes.Citrus(), css=CSS) as demo:
115
+ gr.Markdown(
116
+ """
117
+ # JoyAI-Image Edit Plus
118
+ Multi-image instruction-guided editing — provide reference images and a text instruction
119
+ to generate a new image combining elements from the references.
120
+ [Model card](https://huggingface.co/jdopensource/JoyAI-Image-Edit-Plus-Diffusers)
121
+ """
122
+ )
123
+
124
+ with gr.Row(elem_id="col-container"):
125
+ with gr.Column(scale=1):
126
+ input_gallery = gr.Gallery(
127
+ label="Reference images",
128
+ show_label=True,
129
+ columns=3,
130
+ height=240,
131
+ object_fit="contain",
132
+ file_types=["image"],
133
+ type="filepath",
134
+ )
135
+ prompt = gr.Textbox(
136
+ label="Edit instruction",
137
+ placeholder="e.g. The woman is lovingly holding the cute puppy in her arms",
138
+ lines=2,
139
+ )
140
+ run_btn = gr.Button("Generate", variant="primary")
141
+
142
+ with gr.Accordion("Advanced settings", open=False):
143
+ negative_prompt = gr.Textbox(
144
+ label="Negative prompt",
145
+ value="low quality, blurry, deformed",
146
+ lines=2,
147
+ )
148
+ num_inference_steps = gr.Slider(
149
+ label="Inference steps",
150
+ minimum=1,
151
+ maximum=100,
152
+ step=1,
153
+ value=30,
154
+ )
155
+ guidance_scale = gr.Slider(
156
+ label="Guidance scale",
157
+ minimum=1.0,
158
+ maximum=20.0,
159
+ step=0.1,
160
+ value=4.0,
161
+ )
162
+ randomize_seed = gr.Checkbox(label="Randomize seed", value=True)
163
+ seed = gr.Number(label="Seed", value=42, precision=0)
164
+
165
+ with gr.Column(scale=1):
166
+ output_image = gr.Image(label="Result", show_label=True, height=420)
167
+
168
+ run_btn.click(
169
+ fn=generate,
170
+ inputs=[
171
+ input_gallery,
172
+ prompt,
173
+ negative_prompt,
174
+ num_inference_steps,
175
+ guidance_scale,
176
+ seed,
177
+ randomize_seed,
178
+ ],
179
+ outputs=[output_image, seed],
180
+ api_name="generate",
181
+ )
182
+
183
+ gr.Examples(
184
+ examples=[
185
+ {
186
+ "images": ["examples/input_0.png", "examples/input_1.png"],
187
+ "prompt": "The woman is lovingly holding the cute puppy in her arms",
188
+ },
189
+ ],
190
+ inputs=[input_gallery, prompt],
191
+ outputs=[output_image, seed],
192
+ fn=generate,
193
+ cache_examples=True,
194
+ cache_mode="lazy",
195
+ )
196
+
197
+ demo.launch(mcp_server=True)
examples/input_0.png ADDED

Git LFS Details

  • SHA256: 24248026e21927428ea239ce9c5361c958e6c81d900240e5d71c47af735b3cf8
  • Pointer size: 132 Bytes
  • Size of remote file: 1.27 MB
examples/input_1.png ADDED

Git LFS Details

  • SHA256: b0e6672c0645482a3397a2622659a155f3838bb4728710892ce1297c5a3b65e1
  • Pointer size: 132 Bytes
  • Size of remote file: 1.25 MB
examples/output.png ADDED

Git LFS Details

  • SHA256: dd774f471a6e66e0a4f1b57d03603b853139a8f7c761788806bdec4a44a73975
  • Pointer size: 132 Bytes
  • Size of remote file: 1.37 MB
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ git+https://github.com/tangyanf/diffusers.git@add-joyimage-edit-plus
2
+ transformers>=4.57.0
3
+ accelerate
4
+ sentencepiece
5
+ pillow
6
+ numpy