1inkusFace commited on
Commit
67f9f1a
·
verified ·
1 Parent(s): d9d6072

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +133 -356
app.py CHANGED
@@ -2,429 +2,206 @@ import subprocess
2
  subprocess.run(['sh', './spaces.sh'])
3
 
4
  import os
5
-
6
  os.putenv('PYTORCH_NVML_BASED_CUDA_CHECK','1')
7
  os.putenv('TORCH_LINALG_PREFER_CUSOLVER','1')
8
- alloc_conf_parts = [
9
- 'expandable_segments:True',
10
- 'pinned_use_background_threads:True' # Specific to pinned memory.
11
- ]
12
- os.environ['PYTORCH_CUDA_ALLOC_CONF'] = ','.join(alloc_conf_parts)
13
  os.environ["SAFETENSORS_FAST_GPU"] = "1"
14
  os.putenv('HF_HUB_ENABLE_HF_TRANSFER','1')
15
 
16
  import spaces
17
-
18
  import gradio as gr
19
  import numpy as np
20
  import random
 
 
 
 
 
 
 
21
 
22
  import torch
 
23
  torch.backends.cuda.matmul.allow_tf32 = False
24
  torch.backends.cuda.matmul.allow_bf16_reduced_precision_reduction = False
25
  torch.backends.cuda.matmul.allow_fp16_reduced_precision_reduction = False
26
  torch.backends.cudnn.allow_tf32 = False
27
  torch.backends.cudnn.deterministic = False
28
  torch.backends.cudnn.benchmark = False
29
- torch.backends.cuda.preferred_blas_library="cublas"
30
- torch.backends.cuda.preferred_linalg_library="cusolver"
31
  torch.set_float32_matmul_precision("highest")
32
 
33
  from diffusers import StableDiffusion3Pipeline, SD3Transformer2DModel, AutoencoderKL
34
- from transformers import CLIPTextModelWithProjection, T5EncoderModel
35
- from transformers import CLIPTokenizer, T5TokenizerFast
36
-
37
- import re
38
- import paramiko
39
- import urllib
40
- import time
41
- from image_gen_aux import UpscaleWithModel
42
- from huggingface_hub import hf_hub_download
43
- import datetime
44
  from PIL import Image
 
45
 
46
- #from accelerate import Accelerator
47
-
48
- #accelerator = Accelerator(mixed_precision="bf16")
49
-
50
- hftoken = os.getenv("HF_AUTH_TOKEN")
51
-
52
- import paramiko
53
- import socket
54
- import threading # NEW IMPORT
55
- import queue # NEW IMPORT
56
-
57
- FTP_HOST = 'noahcohn.com'
58
- FTP_USER = 'ford442'
59
- FTP_PASS = os.getenv("FTP_PASS")
60
- FTP_DIR = 'img.noahcohn.com/stablediff/'
61
- FTP_HOST_FALLBACK = '1ink.us'
62
- FTP_DIR_FALLBACK = 'img.1ink.us/stablediff/'
63
 
64
- # --- WORKER FUNCTION FOR THREADING ---
65
- # This function contains the logic to connect to a single host.
66
- # It will be executed by each of our threads.
67
- def connect_worker(host, result_queue):
68
- """Tries to connect to a single host and puts the successful transport object into the queue."""
69
- transport = None
70
  try:
71
- transport = paramiko.Transport((host, 22))
72
- # We still use the 5-second timeout for the handshake
73
- transport.start_client(timeout=5)
74
- transport.auth_password(username=FTP_USER, password=FTP_PASS)
75
-
76
- # If we reach here, the connection was successful.
77
- # Put the result in the queue for the main thread to use.
78
- print(f"✅ Connection to {host} succeeded first.")
79
- result_queue.put(transport)
80
- except (paramiko.SSHException, socket.timeout, EOFError) as e:
81
- # This is an expected failure, just print a note.
82
- print(f"ℹ️ Connection to {host} failed or was too slow: {e}")
83
- if transport:
84
- transport.close()
85
  except Exception as e:
86
- # Handle any other unexpected errors.
87
- print(f"❌ Unexpected error connecting to {host}: {e}")
88
- if transport:
89
- transport.close()
90
-
91
- def upload_to_ftp(filename):
92
- """
93
- Attempts to connect to two FTP hosts simultaneously and uses the first one that responds.
94
- It now uses a corresponding directory for the primary and fallback hosts.
95
- """
96
- hosts = [FTP_HOST]
97
- if FTP_HOST_FALLBACK:
98
- hosts.append(FTP_HOST_FALLBACK)
99
-
100
- result_queue = queue.Queue()
101
- threads = []
102
-
103
- print(f"--> Racing connections to {hosts} for uploading {filename}...")
104
- for host in hosts:
105
- thread = threading.Thread(target=connect_worker, args=(host, result_queue))
106
- thread.daemon = True
107
- thread.start()
108
- threads.append(thread)
109
  try:
110
- winning_transport = result_queue.get(timeout=7)
 
 
 
 
 
 
 
111
 
112
- # --- THIS IS THE NEW LOGIC ---
113
- # 1. Determine which host won the race.
114
- winning_host = winning_transport.getpeername()[0]
115
-
116
- # 2. Select the correct destination directory based on the winning host.
117
- # If the fallback directory isn't specified, it safely defaults to the primary directory.
118
- if winning_host == FTP_HOST:
119
- destination_directory = FTP_DIR
120
- else:
121
- destination_directory = FTP_DIR_FALLBACK if FTP_DIR_FALLBACK else FTP_DIR
122
- print(f"--> Proceeding with upload to {winning_host} in directory {destination_directory}...")
123
- # 3. Construct the full destination path using the selected directory.
124
- sftp = paramiko.SFTPClient.from_transport(winning_transport)
125
- destination_path = os.path.join(destination_directory, os.path.basename(filename))
126
- sftp.put(filename, destination_path)
127
-
128
- print(f"✅ Successfully uploaded {filename}.")
129
-
130
- sftp.close()
131
- winning_transport.close()
132
- except queue.Empty:
133
- print("❌ Critical Error: Neither FTP host responded in time.")
134
  except Exception as e:
135
- print(f"❌ An unexpected error occurred during SFTP operation: {e}")
136
 
 
137
  device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
 
138
 
139
- #vae=AutoencoderKL.from_pretrained("ford442/sdxl-vae-bf16", use_safetensors=True, subfolder='vae',token=True)
140
- vaeX=AutoencoderKL.from_pretrained("ford442/stable-diffusion-3.5-large-fp32", safety_checker=None, use_safetensors=True, subfolder='vae', low_cpu_mem_usage=False, torch_dtype=torch.float32)
141
  pipe = StableDiffusion3Pipeline.from_pretrained(
142
- #"stabilityai # stable-diffusion-3.5-large",
143
  "ford442/stable-diffusion-3.5-large-bf16",
144
  trust_remote_code=True,
145
- #vae=None,
146
- #vae=AutoencoderKL.from_pretrained("ford442/stable-diffusion-3.5-large-fp32", use_safetensors=True, subfolder='vae',token=True),
147
- #scheduler = FlowMatchHeunDiscreteScheduler.from_pretrained('ford442/stable-diffusion-3.5-large-bf16', subfolder='scheduler',token=True),
148
- #text_encoder=None, #CLIPTextModelWithProjection.from_pretrained("ford442/stable-diffusion-3.5-large-bf16", subfolder='text_encoder', token=True),
149
- # text_encoder=CLIPTextModelWithProjection.from_pretrained("ford442/stable-diffusion-3.5-large-bf16", subfolder='text_encoder', token=True),
150
- #text_encoder_2=None, #CLIPTextModelWithProjection.from_pretrained("ford442/stable-diffusion-3.5-large-bf16", subfolder='text_encoder_2',token=True),
151
- # text_encoder_2=CLIPTextModelWithProjection.from_pretrained("ford442/stable-diffusion-3.5-large-bf16", subfolder='text_encoder_2',token=True),
152
- #text_encoder_3=None, #T5EncoderModel.from_pretrained("ford442/stable-diffusion-3.5-large-bf16", subfolder='text_encoder_3',token=True),
153
- # text_encoder_3=T5EncoderModel.from_pretrained("ford442/stable-diffusion-3.5-large-bf16", subfolder='text_encoder_3',token=True),
154
- #tokenizer=CLIPTokenizer.from_pretrained("ford442/stable-diffusion-3.5-large-bf16", add_prefix_space=True, subfolder="tokenizer", token=True),
155
- #tokenizer_2=CLIPTokenizer.from_pretrained("ford442/stable-diffusion-3.5-large-bf16", add_prefix_space=True, subfolder="tokenizer_2", token=True),
156
- transformer=None,
157
- #tokenizer_3=T5TokenizerFast.from_pretrained("ford442/stable-diffusion-3.5-large-bf16", add_prefix_space=False, use_fast=True, subfolder="tokenizer_3", token=True),
158
- #torch_dtype=torch.bfloat16,
159
  use_safetensors=True,
 
160
  )
161
- #text_encoder=CLIPTextModelWithProjection.from_pretrained("ford442/stable-diffusion-3.5-large-bf16", subfolder='text_encoder', token=True).to(torch.device("cuda:0"), dtype=torch.bfloat16)
162
- #text_encoder_2=CLIPTextModelWithProjection.from_pretrained("ford442/stable-diffusion-3.5-large-bf16", subfolder='text_encoder_2',token=True).to(torch.device("cuda:0"), dtype=torch.bfloat16)
163
- #text_encoder_3=T5EncoderModel.from_pretrained("ford442/stable-diffusion-3.5-large-bf16", subfolder='text_encoder_3',token=True).to(torch.device("cuda:0"), dtype=torch.bfloat16)
164
- ll_transformer=SD3Transformer2DModel.from_pretrained("ford442/stable-diffusion-3.5-large-bf16", subfolder='transformer').to(torch.device("cuda:0"), dtype=torch.bfloat16)
165
  pipe.transformer=ll_transformer
166
  pipe.load_lora_weights("ford442/sdxl-vae-bf16", weight_name="LoRA/UltraReal.safetensors")
167
-
168
- #pipe.to(accelerator.device)
169
  pipe.to(device=device, dtype=torch.bfloat16)
170
 
171
- upscaler_2 = UpscaleWithModel.from_pretrained("Kim2091/ClearRealityV1").to(torch.device('cuda'))
172
 
173
  MAX_SEED = np.iinfo(np.int32).max
174
-
175
  MAX_IMAGE_SIZE = 4096
176
 
177
- @spaces.GPU(duration=70)
178
- def infer_60(
179
- prompt,
180
- negative_prompt_1,
181
- negative_prompt_2,
182
- negative_prompt_3,
183
- width,
184
- height,
185
- guidance_scale,
186
- num_inference_steps,
187
- progress=gr.Progress(track_tqdm=True),
188
- ):
189
- seed = random.randint(0, MAX_SEED)
190
- generator = torch.Generator(device='cuda').manual_seed(seed)
191
- print('-- generating image --')
192
- sd_image = pipe(
193
- prompt=prompt,
194
- prompt_2=prompt,
195
- prompt_3=prompt,
196
- negative_prompt=negative_prompt_1,
197
- negative_prompt_2=negative_prompt_2,
198
- negative_prompt_3=negative_prompt_3,
199
- guidance_scale=guidance_scale,
200
- num_inference_steps=num_inference_steps,
201
- width=width,
202
- height=height,
203
- generator=generator,
204
- max_sequence_length=512
205
- ).images[0]
206
- print('-- got image --')
207
- timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
208
- sd35_path = f"sd35ll_{timestamp}.png"
209
- sd_image.save(sd35_path,optimize=False,compress_level=0)
210
- upload_to_ftp(sd35_path)
211
- with torch.no_grad():
212
- upscale = upscaler_2(sd_image, tiling=True, tile_width=256, tile_height=256)
213
- upscale2 = upscaler_2(upscale, tiling=True, tile_width=256, tile_height=256)
214
- print('-- got upscaled image --')
215
- downscale = upscale2.resize((upscale2.width // 4, upscale2.height // 4),Image.LANCZOS)
216
- upscale_path = f"sd35ll_upscale_{timestamp}.png"
217
- downscale.save(upscale_path,optimize=False,compress_level=0)
218
- upload_to_ftp(upscale_path)
219
- return sd_image, prompt
220
-
221
- @spaces.GPU(duration=100)
222
- def infer_90(
223
- prompt,
224
- negative_prompt_1,
225
- negative_prompt_2,
226
- negative_prompt_3,
227
- width,
228
- height,
229
- guidance_scale,
230
- num_inference_steps,
231
- progress=gr.Progress(track_tqdm=True),
232
- ):
233
- seed = random.randint(0, MAX_SEED)
234
- generator = torch.Generator(device='cuda').manual_seed(seed)
235
- print('-- generating image --')
236
- sd_image = pipe(
237
- prompt=prompt,
238
- prompt_2=prompt,
239
- prompt_3=prompt,
240
- negative_prompt=negative_prompt_1,
241
- negative_prompt_2=negative_prompt_2,
242
- negative_prompt_3=negative_prompt_3,
243
- guidance_scale=guidance_scale,
244
- num_inference_steps=num_inference_steps,
245
- width=width,
246
- height=height,
247
- generator=generator,
248
- max_sequence_length=512
249
- ).images[0]
250
- print('-- got image --')
251
- timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
252
- sd35_path = f"sd35ll_{timestamp}.png"
253
- sd_image.save(sd35_path,optimize=False,compress_level=0)
254
- upload_to_ftp(sd35_path)
255
- with torch.no_grad():
256
- upscale = upscaler_2(sd_image, tiling=True, tile_width=256, tile_height=256)
257
- upscale2 = upscaler_2(upscale, tiling=True, tile_width=256, tile_height=256)
258
- print('-- got upscaled image --')
259
- downscale = upscale2.resize((upscale2.width // 4, upscale2.height // 4),Image.LANCZOS)
260
- upscale_path = f"sd35ll_upscale_{timestamp}.png"
261
- downscale.save(upscale_path,optimize=False,compress_level=0)
262
- upload_to_ftp(upscale_path)
263
- return sd_image, prompt
264
-
265
  @spaces.GPU(duration=120)
266
- def infer_110(
267
- prompt,
268
- negative_prompt_1,
269
- negative_prompt_2,
270
- negative_prompt_3,
271
- width,
272
- height,
273
- guidance_scale,
274
- num_inference_steps,
275
- progress=gr.Progress(track_tqdm=True),
276
- ):
277
  seed = random.randint(0, MAX_SEED)
278
- generator = torch.Generator(device='cuda').manual_seed(seed)
 
279
  print('-- generating image --')
280
  sd_image = pipe(
281
- prompt=prompt,
282
- prompt_2=prompt,
283
- prompt_3=prompt,
284
- negative_prompt=negative_prompt_1,
285
- negative_prompt_2=negative_prompt_2,
286
- negative_prompt_3=negative_prompt_3,
287
- guidance_scale=guidance_scale,
288
- num_inference_steps=num_inference_steps,
289
- width=width,
290
- height=height,
291
- generator=generator,
292
- max_sequence_length=512
293
  ).images[0]
294
  print('-- got image --')
295
- timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
296
- sd35_path = f"sd35ll_{timestamp}.png"
297
- sd_image.save(sd35_path,optimize=False,compress_level=0)
298
- upload_to_ftp(sd35_path)
299
  with torch.no_grad():
300
  upscale = upscaler_2(sd_image, tiling=True, tile_width=256, tile_height=256)
301
  upscale2 = upscaler_2(upscale, tiling=True, tile_width=256, tile_height=256)
302
  print('-- got upscaled image --')
303
- downscale = upscale2.resize((upscale2.width // 4, upscale2.height // 4),Image.LANCZOS)
304
- upscale_path = f"sd35ll_upscale_{timestamp}.png"
305
- downscale.save(upscale_path,optimize=False,compress_level=0)
306
- upload_to_ftp(upscale_path)
307
- return sd_image, prompt
308
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
309
  css = """
310
  #col-container {margin: 0 auto;max-width: 640px;}
311
  body{background-color: blue;}
312
  """
313
 
314
- with gr.Blocks(theme=gr.themes.Origin(),css=css) as demo:
315
  with gr.Column(elem_id="col-container"):
316
  gr.Markdown(" # StableDiffusion 3.5 Large with UltraReal lora test")
317
- expanded_prompt_output = gr.Textbox(label="Prompt", lines=1) # Add this line
318
  with gr.Row():
319
  prompt = gr.Text(
320
- label="Prompt",
321
- show_label=False,
322
- max_lines=1,
323
- placeholder="Enter your prompt",
324
- container=False,
325
  )
326
- run_button_60 = gr.Button("Run 60", scale=0, variant="primary")
327
- run_button_90 = gr.Button("Run 90", scale=0, variant="primary")
328
- run_button_110 = gr.Button("Run 110", scale=0, variant="primary")
329
- result = gr.Image(label="Result", show_label=False)
 
 
 
 
 
 
 
 
330
  with gr.Accordion("Advanced Settings", open=True):
331
- negative_prompt_1 = gr.Text(
332
- label="Negative prompt 1",
333
- max_lines=1,
334
- placeholder="Enter a negative prompt",
335
- visible=True,
336
- value="bad anatomy, poorly drawn hands, distorted face, blurry, out of frame, low resolution, grainy, pixelated, disfigured, mutated, extra limbs, bad composition"
337
- )
338
- negative_prompt_2 = gr.Text(
339
- label="Negative prompt 2",
340
- max_lines=1,
341
- placeholder="Enter a second negative prompt",
342
- visible=True,
343
- value="unrealistic, cartoon, anime, sketch, painting, drawing, illustration, graphic, digital art, render, 3d, blurry, deformed, disfigured, poorly drawn, bad anatomy, mutated, extra limbs, ugly, out of frame, bad composition, low resolution, grainy, pixelated, noisy, oversaturated, undersaturated, (worst quality, low quality:1.3), (bad hands, missing fingers:1.2)"
344
- )
345
- negative_prompt_3 = gr.Text(
346
- label="Negative prompt 3",
347
- max_lines=1,
348
- placeholder="Enter a third negative prompt",
349
- visible=True,
350
- value="(worst quality, low quality:1.3), (bad anatomy, bad hands, missing fingers, extra digit, fewer digits:1.2), (blurry:1.1), cropped, watermark, text, signature, logo, jpeg artifacts, (ugly, deformed, disfigured:1.2), (poorly drawn:1.2), mutated, extra limbs, (bad proportions, gross proportions:1.2), (malformed limbs, missing arms, missing legs, extra arms, extra legs:1.2), (fused fingers, too many fingers, long neck:1.2), (unnatural body, unnatural pose:1.1), out of frame, (bad composition, poorly composed:1.1), (oversaturated, undersaturated:1.1), (grainy, pixelated:1.1), (low resolution, noisy:1.1), (unrealistic, distorted:1.1), (extra fingers, mutated hands, poorly drawn hands, bad hands:1.3), (missing fingers:1.3)"
351
- )
352
- num_iterations = gr.Number(
353
- value=1000,
354
- label="Number of Iterations")
355
  with gr.Row():
356
- width = gr.Slider(
357
- label="Width",
358
- minimum=256,
359
- maximum=MAX_IMAGE_SIZE,
360
- step=32,
361
- value=768,
362
- )
363
- height = gr.Slider(
364
- label="Height",
365
- minimum=256,
366
- maximum=MAX_IMAGE_SIZE,
367
- step=32,
368
- value=768,
369
- )
370
- guidance_scale = gr.Slider(
371
- label="Guidance scale",
372
- minimum=0.0,
373
- maximum=30.0,
374
- step=0.1,
375
- value=4.2,
376
- )
377
- num_inference_steps = gr.Slider(
378
- label="Number of inference steps",
379
- minimum=1,
380
- maximum=500,
381
- step=1,
382
- value=100,
383
- )
384
- gr.on(
385
- triggers=[run_button_60.click, prompt.submit],
386
- fn=infer_60,
387
- inputs=[
388
- prompt,
389
- negative_prompt_1,
390
- negative_prompt_2,
391
- negative_prompt_3,
392
- width,
393
- height,
394
- guidance_scale,
395
- num_inference_steps,
396
- ],
397
- outputs=[result, expanded_prompt_output],
398
- )
399
- gr.on(
400
- triggers=[run_button_90.click, prompt.submit],
401
- fn=infer_90,
402
- inputs=[
403
- prompt,
404
- negative_prompt_1,
405
- negative_prompt_2,
406
- negative_prompt_3,
407
- width,
408
- height,
409
- guidance_scale,
410
- num_inference_steps,
411
- ],
412
- outputs=[result, expanded_prompt_output],
413
- )
414
- gr.on(
415
- triggers=[run_button_110.click, prompt.submit],
416
- fn=infer_110,
417
- inputs=[
418
- prompt,
419
- negative_prompt_1,
420
- negative_prompt_2,
421
- negative_prompt_3,
422
- width,
423
- height,
424
- guidance_scale,
425
- num_inference_steps,
426
- ],
427
- outputs=[result, expanded_prompt_output],
428
  )
429
 
430
  if __name__ == "__main__":
 
2
  subprocess.run(['sh', './spaces.sh'])
3
 
4
  import os
5
+ # Environment variable setup
6
  os.putenv('PYTORCH_NVML_BASED_CUDA_CHECK','1')
7
  os.putenv('TORCH_LINALG_PREFER_CUSOLVER','1')
8
+ os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'expandable_segments:True,pinned_use_background_threads:True'
 
 
 
 
9
  os.environ["SAFETENSORS_FAST_GPU"] = "1"
10
  os.putenv('HF_HUB_ENABLE_HF_TRANSFER','1')
11
 
12
  import spaces
 
13
  import gradio as gr
14
  import numpy as np
15
  import random
16
+ import datetime
17
+ import threading
18
+ import io
19
+
20
+ # --- New GCS Imports ---
21
+ from google.oauth2 import service_account
22
+ from google.cloud import storage
23
 
24
  import torch
25
+ # Torch performance settings
26
  torch.backends.cuda.matmul.allow_tf32 = False
27
  torch.backends.cuda.matmul.allow_bf16_reduced_precision_reduction = False
28
  torch.backends.cuda.matmul.allow_fp16_reduced_precision_reduction = False
29
  torch.backends.cudnn.allow_tf32 = False
30
  torch.backends.cudnn.deterministic = False
31
  torch.backends.cudnn.benchmark = False
 
 
32
  torch.set_float32_matmul_precision("highest")
33
 
34
  from diffusers import StableDiffusion3Pipeline, SD3Transformer2DModel, AutoencoderKL
 
 
 
 
 
 
 
 
 
 
35
  from PIL import Image
36
+ from image_gen_aux import UpscaleWithModel
37
 
38
+ # --- GCS Configuration ---
39
+ # Make sure to set these secrets in your Hugging Face Space settings
40
+ GCS_BUCKET_NAME = os.getenv("GCS_BUCKET_NAME")
41
+ GCS_SA_KEY = os.getenv("GCS_SA_KEY") # The full JSON key content as a string
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
+ # Initialize GCS client if credentials are available
44
+ gcs_client = None
45
+ if GCS_SA_KEY and GCS_BUCKET_NAME:
 
 
 
46
  try:
47
+ credentials_info = eval(GCS_SA_KEY) # Using eval is safe here if you trust the secret source
48
+ credentials = service_account.Credentials.from_service_account_info(credentials_info)
49
+ gcs_client = storage.Client(credentials=credentials)
50
+ print("✅ GCS Client initialized successfully.")
 
 
 
 
 
 
 
 
 
 
51
  except Exception as e:
52
+ print(f"❌ Failed to initialize GCS client: {e}")
53
+
54
+ # --- New GCS Upload Function (runs on CPU) ---
55
+ def upload_to_gcs(image_object, filename):
56
+ """Uploads a PIL Image object to GCS from memory."""
57
+ if not gcs_client:
58
+ print("⚠️ GCS client not initialized. Skipping upload.")
59
+ return
60
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  try:
62
+ print(f"--> Starting GCS upload for {filename}...")
63
+ bucket = gcs_client.bucket(GCS_BUCKET_NAME)
64
+ blob = bucket.blob(f"stablediff/{filename}")
65
+
66
+ # Convert PIL image to bytes stream
67
+ img_byte_arr = io.BytesIO()
68
+ image_object.save(img_byte_arr, format='PNG', optimize=False, compress_level=0)
69
+ img_byte_arr = img_byte_arr.getvalue()
70
 
71
+ # Upload from the in-memory string
72
+ blob.upload_from_string(img_byte_arr, content_type='image/png')
73
+ print(f"✅ Successfully uploaded {filename} to GCS.")
74
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  except Exception as e:
76
+ print(f"❌ An error occurred during GCS upload: {e}")
77
 
78
+ # --- Model Loading ---
79
  device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
80
+ hftoken = os.getenv("HF_AUTH_TOKEN")
81
 
 
 
82
  pipe = StableDiffusion3Pipeline.from_pretrained(
 
83
  "ford442/stable-diffusion-3.5-large-bf16",
84
  trust_remote_code=True,
85
+ transformer=None, # Load transformer separately
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  use_safetensors=True,
87
+ # token=hftoken
88
  )
89
+ # Load transformer separately and move to device with specified dtype
90
+ ll_transformer=SD3Transformer2DModel.from_pretrained("ford442/stable-diffusion-3.5-large-bf16", subfolder='transformer', token=hftoken).to(device, dtype=torch.bfloat16)
 
 
91
  pipe.transformer=ll_transformer
92
  pipe.load_lora_weights("ford442/sdxl-vae-bf16", weight_name="LoRA/UltraReal.safetensors")
 
 
93
  pipe.to(device=device, dtype=torch.bfloat16)
94
 
95
+ upscaler_2 = UpscaleWithModel.from_pretrained("Kim2091/ClearRealityV1").to(device)
96
 
97
  MAX_SEED = np.iinfo(np.int32).max
 
98
  MAX_IMAGE_SIZE = 4096
99
 
100
+ # --- Refactored GPU Inference Function ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  @spaces.GPU(duration=120)
102
+ def generate_images(prompt, neg_prompt_1, neg_prompt_2, neg_prompt_3, width, height, guidance, steps, progress=gr.Progress(track_tqdm=True)):
103
+ """Generates the main image and its upscaled version on the GPU."""
 
 
 
 
 
 
 
 
 
104
  seed = random.randint(0, MAX_SEED)
105
+ generator = torch.Generator(device=device).manual_seed(seed)
106
+
107
  print('-- generating image --')
108
  sd_image = pipe(
109
+ prompt=prompt, prompt_2=prompt, prompt_3=prompt,
110
+ negative_prompt=neg_prompt_1, negative_prompt_2=neg_prompt_2, negative_prompt_3=neg_prompt_3,
111
+ guidance_scale=guidance, num_inference_steps=steps,
112
+ width=width, height=height, generator=generator,
113
+ max_sequence_length=512
 
 
 
 
 
 
 
114
  ).images[0]
115
  print('-- got image --')
116
+
 
 
 
117
  with torch.no_grad():
118
  upscale = upscaler_2(sd_image, tiling=True, tile_width=256, tile_height=256)
119
  upscale2 = upscaler_2(upscale, tiling=True, tile_width=256, tile_height=256)
120
  print('-- got upscaled image --')
121
+ downscaled_upscale = upscale2.resize((upscale2.width // 4, upscale2.height // 4), Image.LANCZOS)
 
 
 
 
122
 
123
+ return sd_image, downscaled_upscale, prompt
124
+
125
+ # --- Main Gradio Handler (runs on CPU) ---
126
+ def run_inference_and_upload(prompt, neg_prompt_1, neg_prompt_2, neg_prompt_3, width, height, guidance, steps, save_consent, progress=gr.Progress(track_tqdm=True)):
127
+ """
128
+ Orchestrates the process: calls the GPU function, then handles the upload if consented.
129
+ """
130
+ # 1. Call the GPU-bound function to get the images
131
+ sd_image, upscaled_image, expanded_prompt = generate_images(prompt, neg_prompt_1, neg_prompt_2, neg_prompt_3, width, height, guidance, steps, progress)
132
+
133
+ # 2. If user consented, start uploads in background threads
134
+ if save_consent:
135
+ print("✅ User consented to save. Preparing uploads...")
136
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
137
+ sd_filename = f"sd35ll_{timestamp}.png"
138
+ upscale_filename = f"sd35ll_upscale_{timestamp}.png"
139
+
140
+ # Create and start threads for each upload
141
+ sd_thread = threading.Thread(target=upload_to_gcs, args=(sd_image, sd_filename))
142
+ upscale_thread = threading.Thread(target=upload_to_gcs, args=(upscaled_image, upscale_filename))
143
+
144
+ sd_thread.start()
145
+ upscale_thread.start()
146
+ else:
147
+ print("ℹ️ User did not consent to save. Skipping upload.")
148
+
149
+ # 3. Return the primary image to the UI immediately
150
+ return sd_image, expanded_prompt
151
+
152
+ # --- Gradio UI Definition ---
153
  css = """
154
  #col-container {margin: 0 auto;max-width: 640px;}
155
  body{background-color: blue;}
156
  """
157
 
158
+ with gr.Blocks(theme=gr.themes.Origin(), css=css) as demo:
159
  with gr.Column(elem_id="col-container"):
160
  gr.Markdown(" # StableDiffusion 3.5 Large with UltraReal lora test")
161
+ expanded_prompt_output = gr.Textbox(label="Prompt", lines=1)
162
  with gr.Row():
163
  prompt = gr.Text(
164
+ label="Prompt", show_label=False, max_lines=1,
165
+ placeholder="Enter your prompt", container=False,
 
 
 
166
  )
167
+ # Use a single run button for simplicity or keep multiple if durations are critical
168
+ run_button = gr.Button("Run", scale=0, variant="primary")
169
+
170
+ result = gr.Image(label="Result", show_label=False, type="pil")
171
+
172
+ # --- New Consent Checkbox ---
173
+ save_consent_checkbox = gr.Checkbox(
174
+ label="✅ Anonymously upload result to a public gallery",
175
+ value=False, # Default to not uploading
176
+ info="Check this box to help us by contributing your image."
177
+ )
178
+
179
  with gr.Accordion("Advanced Settings", open=True):
180
+ negative_prompt_1 = gr.Text(label="Negative prompt 1", max_lines=1, placeholder="Enter a negative prompt", value="bad anatomy, poorly drawn hands, distorted face, blurry, out of frame, low resolution, grainy, pixelated, disfigured, mutated, extra limbs, bad composition")
181
+ negative_prompt_2 = gr.Text(label="Negative prompt 2", max_lines=1, placeholder="Enter a second negative prompt", value="unrealistic, cartoon, anime, sketch, painting, drawing, illustration, graphic, digital art, render, 3d, blurry, deformed, disfigured, poorly drawn, bad anatomy, mutated, extra limbs, ugly, out of frame, bad composition, low resolution, grainy, pixelated, noisy, oversaturated, undersaturated, (worst quality, low quality:1.3), (bad hands, missing fingers:1.2)")
182
+ negative_prompt_3 = gr.Text(label="Negative prompt 3", max_lines=1, placeholder="Enter a third negative prompt", value="(worst quality, low quality:1.3), (bad anatomy, bad hands, missing fingers, extra digit, fewer digits:1.2), (blurry:1.1), cropped, watermark, text, signature, logo, jpeg artifacts, (ugly, deformed, disfigured:1.2), (poorly drawn:1.2), mutated, extra limbs, (bad proportions, gross proportions:1.2), (malformed limbs, missing arms, missing legs, extra arms, extra legs:1.2), (fused fingers, too many fingers, long neck:1.2), (unnatural body, unnatural pose:1.1), out of frame, (bad composition, poorly composed:1.1), (oversaturated, undersaturated:1.1), (grainy, pixelated:1.1), (low resolution, noisy:1.1), (unrealistic, distorted:1.1), (extra fingers, mutated hands, poorly drawn hands, bad hands:1.3), (missing fingers:1.3)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  with gr.Row():
184
+ width = gr.Slider(label="Width", minimum=256, maximum=MAX_IMAGE_SIZE, step=32, value=768)
185
+ height = gr.Slider(label="Height", minimum=256, maximum=MAX_IMAGE_SIZE, step=32, value=768)
186
+ with gr.Row():
187
+ guidance_scale = gr.Slider(label="Guidance scale", minimum=0.0, maximum=30.0, step=0.1, value=4.2)
188
+ num_inference_steps = gr.Slider(label="Inference steps", minimum=1, maximum=150, step=1, value=60)
189
+
190
+ # Connect the button to the main handler function
191
+ run_button.click(
192
+ fn=run_inference_and_upload,
193
+ inputs=[
194
+ prompt,
195
+ negative_prompt_1,
196
+ negative_prompt_2,
197
+ negative_prompt_3,
198
+ width,
199
+ height,
200
+ guidance_scale,
201
+ num_inference_steps,
202
+ save_consent_checkbox # Pass the checkbox value
203
+ ],
204
+ outputs=[result, expanded_prompt_output],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  )
206
 
207
  if __name__ == "__main__":