sayshara commited on
Commit
6bd4fea
Β·
1 Parent(s): 6895797
Files changed (3) hide show
  1. .gitignore +10 -2
  2. app.py +117 -18
  3. diffqrcoder_wrapper.py +25 -19
.gitignore CHANGED
@@ -1,2 +1,10 @@
1
- __pycache__/*
2
- losses/__pycache__/*
 
 
 
 
 
 
 
 
 
1
+ app.py
2
+ __pycache__/diffqrcoder_wrapper.cpython-310.pyc
3
+ diffqrcoder/__pycache__/__init__.cpython-310.pyc
4
+ diffqrcoder/__pycache__/image_processor.cpython-310.pyc
5
+ diffqrcoder/__pycache__/pipeline_diffqrcoder.cpython-310.pyc
6
+ diffqrcoder/__pycache__/srpg.cpython-310.pyc
7
+ diffqrcoder/losses/__pycache__/__init__.cpython-310.pyc
8
+ diffqrcoder/losses/__pycache__/perceptual_loss.cpython-310.pyc
9
+ diffqrcoder/losses/__pycache__/personalized_code_loss.cpython-310.pyc
10
+ diffqrcoder/losses/__pycache__/scanning_robust_loss.cpython-310.pyc
app.py CHANGED
@@ -4,6 +4,7 @@ import spaces
4
  from diffqrcoder_wrapper import generate_qr_art, load_pipeline
5
  import torch
6
 
 
7
  @spaces.GPU
8
  def infer(
9
  url_or_text: str,
@@ -14,22 +15,120 @@ def infer(
14
  perceptual_guidance_scale: float,
15
  srmpgd_iters: int,
16
  ):
17
- # πŸ”Ή Make sure pipeline is loaded *once* and then just moved to GPU
18
- pipe = load_pipeline()
19
-
20
- # move to GPU *here*, with an explicit non-blocking call
21
- pipe = pipe.to("cuda")
22
-
23
- srmpgd_num_iteration = None if srmpgd_iters == 0 else srmpgd_iters
24
-
25
- img = generate_qr_art(
26
- pipe,
27
- url_or_text=url_or_text,
28
- prompt=prompt,
29
- num_inference_steps=num_inference_steps,
30
- controlnet_conditioning_scale=controlnet_scale,
31
- scanning_robust_guidance_scale=scanning_robust_guidance_scale,
32
- perceptual_guidance_scale=perceptual_guidance_scale,
33
- srmpgd_num_iteration=srmpgd_num_iteration,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  )
35
- return img
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  from diffqrcoder_wrapper import generate_qr_art, load_pipeline
5
  import torch
6
 
7
+
8
  @spaces.GPU
9
  def infer(
10
  url_or_text: str,
 
15
  perceptual_guidance_scale: float,
16
  srmpgd_iters: int,
17
  ):
18
+ try:
19
+ print("πŸ”§ infer() starting")
20
+ print("CUDA available?", torch.cuda.is_available())
21
+ if torch.cuda.is_available():
22
+ print("CUDA device count:", torch.cuda.device_count())
23
+ print("Current device:", torch.cuda.current_device())
24
+ print("Device name:", torch.cuda.get_device_name(0))
25
+
26
+ pipe = load_pipeline()
27
+ print("βœ… pipeline loaded on CPU")
28
+
29
+ # Attach to GPU in ZeroGPU context
30
+ pipe = pipe.to("cuda")
31
+ print("βœ… pipeline moved to CUDA")
32
+
33
+ srmpgd_num_iteration = None if srmpgd_iters == 0 else srmpgd_iters
34
+ print(
35
+ f"Params β†’ steps={num_inference_steps}, "
36
+ f"ctrl={controlnet_scale}, srg={scanning_robust_guidance_scale}, "
37
+ f"pg={perceptual_guidance_scale}, iters={srmpgd_num_iteration}"
38
+ )
39
+
40
+ img = generate_qr_art(
41
+ pipe,
42
+ url_or_text=url_or_text,
43
+ prompt=prompt,
44
+ num_inference_steps=num_inference_steps,
45
+ controlnet_conditioning_scale=controlnet_scale,
46
+ scanning_robust_guidance_scale=scanning_robust_guidance_scale,
47
+ perceptual_guidance_scale=perceptual_guidance_scale,
48
+ srmpgd_num_iteration=srmpgd_num_iteration,
49
+ )
50
+
51
+ print("βœ… generation complete")
52
+ return img
53
+
54
+ except Exception as e:
55
+ print("❌ Error in infer():", repr(e))
56
+ raise
57
+
58
+ with gr.Blocks() as demo:
59
+ gr.Markdown(
60
+ r"""
61
+ # DiffQRCoder – ZeroGPU demo
62
+
63
+ Generate aesthetic, scanning-robust QR codes using the **DiffQRCoder** pipeline
64
+ ([WACV 2025](https://openaccess.thecvf.com/content/WACV2025/html/Liao_DiffQRCoder_Diffusion-Based_Aesthetic_QR_Code_Generation_with_Scanning_Robustness_Guided_WACV_2025_paper.html)) πŸš€
65
+ """
66
  )
67
+
68
+ with gr.Row():
69
+ url = gr.Textbox(
70
+ label="QR contents (URL or text)",
71
+ value="https://example.com",
72
+ )
73
+
74
+ prompt = gr.Textbox(
75
+ label="Style prompt",
76
+ value=DEFAULT_PROMPT,
77
+ lines=3,
78
+ )
79
+
80
+ with gr.Accordion("Advanced parameters", open=False):
81
+ steps = gr.Slider(
82
+ minimum=10,
83
+ maximum=60,
84
+ value=40,
85
+ step=1,
86
+ label="Diffusion steps (num_inference_steps)",
87
+ )
88
+ control_scale = gr.Slider(
89
+ minimum=0.5,
90
+ maximum=2.0,
91
+ value=1.35,
92
+ step=0.05,
93
+ label="ControlNet conditioning scale",
94
+ )
95
+ srg_scale = gr.Slider(
96
+ minimum=0,
97
+ maximum=800,
98
+ value=500,
99
+ step=10,
100
+ label="Scanning-robust guidance scale (srg)",
101
+ )
102
+ pg_scale = gr.Slider(
103
+ minimum=0,
104
+ maximum=10,
105
+ value=2,
106
+ step=0.5,
107
+ label="Perceptual guidance scale (pg)",
108
+ )
109
+ srmpgd_iters = gr.Slider(
110
+ minimum=0,
111
+ maximum=64,
112
+ value=0,
113
+ step=1,
114
+ label="SR-MPGD iterations (0 = disabled)",
115
+ )
116
+
117
+ btn = gr.Button("Generate QR Art ✨", variant="primary")
118
+ out = gr.Image(label="Output QR art", type="pil")
119
+
120
+ btn.click(
121
+ fn=infer,
122
+ inputs=[
123
+ url,
124
+ prompt,
125
+ steps,
126
+ control_scale,
127
+ srg_scale,
128
+ pg_scale,
129
+ srmpgd_iters,
130
+ ],
131
+ outputs=[out],
132
+ )
133
+
134
+ demo.launch()
diffqrcoder_wrapper.py CHANGED
@@ -9,10 +9,8 @@ from diffqrcoder import DiffQRCoderPipeline
9
 
10
  # ---- Defaults taken from run_diffqrcoder.py ---- #
11
 
12
- # ControlNet is already a proper HF repo id:
13
  CONTROLNET_CKPT = "monster-labs/control_v1p_sd15_qrcode_monster"
14
 
15
- # For the base SD model (Cetus-Mix), use repo + filename rather than raw URL
16
  PIPE_REPO_ID = "fp16-guy/Cetus-Mix_Whalefall_fp16_cleaned"
17
  PIPE_FILENAME = "cetusMix_Whalefall2_fp16.safetensors"
18
 
@@ -42,30 +40,30 @@ def _make_qr_image(
42
  def load_pipeline():
43
  """
44
  Lazily load ControlNet + DiffQRCoderPipeline.
45
-
46
- This now:
47
- - pulls the ControlNet weights from HF by repo id
48
- - downloads the Cetus-Mix safetensors file via hf_hub_download
49
  """
50
  global _controlnet, _pipe
51
 
52
  if _pipe is not None:
53
  return _pipe
54
 
55
- # 1. Load ControlNet from its HF repo
56
  if _controlnet is None:
57
  _controlnet = ControlNetModel.from_pretrained(
58
  CONTROLNET_CKPT,
59
  torch_dtype=torch.float16,
60
  )
 
61
 
62
- # 2. Download the base model safetensors from Hugging Face Hub
63
  ckpt_path = hf_hub_download(
64
  repo_id=PIPE_REPO_ID,
65
  filename=PIPE_FILENAME,
 
 
66
  )
 
67
 
68
- # 3. Build DiffQRCoder pipeline from the local safetensors file
69
  pipe = DiffQRCoderPipeline.from_single_file(
70
  ckpt_path,
71
  controlnet=_controlnet,
@@ -73,40 +71,47 @@ def load_pipeline():
73
  use_auth_token=True, # uses the Space's HF token
74
  )
75
 
76
- # 4. Same scheduler as in run_diffqrcoder.py
77
  pipe.scheduler = DDIMScheduler.from_config(pipe.scheduler.config)
78
 
79
- # NOTE: we call .to("cuda") inside the @spaces.GPU function so that
80
- # it only happens when a GPU is actually attached.
 
 
 
 
 
 
81
  _pipe = pipe
82
  return _pipe
83
 
84
 
85
  def generate_qr_art(
86
- pipe: DiffQRCoderPipeline,
87
  url_or_text: str,
88
  prompt: str,
89
  neg_prompt: str = "easynegative",
90
- num_inference_steps: int = 40,
91
  qrcode_module_size: int = 20,
92
  qrcode_padding: int = 78,
93
  controlnet_conditioning_scale: float = 1.35,
94
- scanning_robust_guidance_scale: float = 500.0,
95
  perceptual_guidance_scale: float = 2.0,
96
- srmpgd_num_iteration: int | None = None,
97
  srmpgd_lr: float = 0.1,
98
  seed: int = 1,
99
  ) -> Image.Image:
 
 
 
 
100
 
101
- generator = torch.Generator(device="cuda").manual_seed(seed)
102
  qrcode_img = _make_qr_image(
103
  data=url_or_text,
104
  box_size=qrcode_module_size,
105
  border=4,
106
  )
107
 
108
- # pipe = pipe.to(DEVICE)
109
-
110
  result = pipe(
111
  prompt=prompt,
112
  qrcode=qrcode_img,
@@ -121,4 +126,5 @@ def generate_qr_art(
121
  srmpgd_num_iteration=srmpgd_num_iteration,
122
  srmpgd_lr=srmpgd_lr,
123
  )
 
124
  return result.images[0]
 
9
 
10
  # ---- Defaults taken from run_diffqrcoder.py ---- #
11
 
 
12
  CONTROLNET_CKPT = "monster-labs/control_v1p_sd15_qrcode_monster"
13
 
 
14
  PIPE_REPO_ID = "fp16-guy/Cetus-Mix_Whalefall_fp16_cleaned"
15
  PIPE_FILENAME = "cetusMix_Whalefall2_fp16.safetensors"
16
 
 
40
  def load_pipeline():
41
  """
42
  Lazily load ControlNet + DiffQRCoderPipeline.
 
 
 
 
43
  """
44
  global _controlnet, _pipe
45
 
46
  if _pipe is not None:
47
  return _pipe
48
 
49
+ print("πŸ”§ Loading ControlNet...")
50
  if _controlnet is None:
51
  _controlnet = ControlNetModel.from_pretrained(
52
  CONTROLNET_CKPT,
53
  torch_dtype=torch.float16,
54
  )
55
+ print("βœ… ControlNet loaded.")
56
 
57
+ print("πŸ”§ Downloading base model checkpoint from Hub...")
58
  ckpt_path = hf_hub_download(
59
  repo_id=PIPE_REPO_ID,
60
  filename=PIPE_FILENAME,
61
+ local_dir="models",
62
+ local_dir_use_symlinks=False,
63
  )
64
+ print("βœ… Base model checkpoint at:", ckpt_path)
65
 
66
+ print("πŸ”§ Building DiffQRCoderPipeline from checkpoint...")
67
  pipe = DiffQRCoderPipeline.from_single_file(
68
  ckpt_path,
69
  controlnet=_controlnet,
 
71
  use_auth_token=True, # uses the Space's HF token
72
  )
73
 
 
74
  pipe.scheduler = DDIMScheduler.from_config(pipe.scheduler.config)
75
 
76
+ # Memory helpers – cheaper attention
77
+ try:
78
+ pipe.enable_attention_slicing()
79
+ # Optional: pipe.enable_xformers_memory_efficient_attention()
80
+ except Exception as e:
81
+ print("⚠️ Could not enable attention optimizations:", repr(e))
82
+
83
+ print("βœ… Pipeline constructed on CPU.")
84
  _pipe = pipe
85
  return _pipe
86
 
87
 
88
  def generate_qr_art(
89
+ pipe: DiffQRCoderPipeline,
90
  url_or_text: str,
91
  prompt: str,
92
  neg_prompt: str = "easynegative",
93
+ num_inference_steps: int = 20, # gentler default
94
  qrcode_module_size: int = 20,
95
  qrcode_padding: int = 78,
96
  controlnet_conditioning_scale: float = 1.35,
97
+ scanning_robust_guidance_scale: float = 300.0, # softer default
98
  perceptual_guidance_scale: float = 2.0,
99
+ srmpgd_num_iteration: int | None = 0, # 0 = disable SR-MPGD by default
100
  srmpgd_lr: float = 0.1,
101
  seed: int = 1,
102
  ) -> Image.Image:
103
+ assert pipe is not None, "Pipeline must be loaded before calling generate_qr_art"
104
+
105
+ print("✨ generate_qr_art() starting...")
106
+ generator = torch.Generator(device=DEVICE).manual_seed(seed)
107
 
 
108
  qrcode_img = _make_qr_image(
109
  data=url_or_text,
110
  box_size=qrcode_module_size,
111
  border=4,
112
  )
113
 
114
+ print("✨ Starting DiffQRCoder forward pass...")
 
115
  result = pipe(
116
  prompt=prompt,
117
  qrcode=qrcode_img,
 
126
  srmpgd_num_iteration=srmpgd_num_iteration,
127
  srmpgd_lr=srmpgd_lr,
128
  )
129
+ print("βœ… DiffQRCoder forward pass finished.")
130
  return result.images[0]