Spaces:
Running
on
Zero
Running
on
Zero
added a new border plus default values for freeU
Browse files
app.py
CHANGED
|
@@ -195,20 +195,26 @@ def generate_qr_code_unified(prompt: str, text_input: str, input_type: str = "UR
|
|
| 195 |
else: # artistic
|
| 196 |
yield from _pipeline_artistic(prompt, qr_text, input_type, image_size, border_size, error_correction, module_size, module_drawer, actual_seed)
|
| 197 |
|
| 198 |
-
def add_noise_to_border_only(image_tensor, seed: int, border_size: int, image_size: int,
|
| 199 |
"""
|
| 200 |
-
Add
|
|
|
|
|
|
|
| 201 |
|
| 202 |
Args:
|
| 203 |
image_tensor: ComfyUI image tensor (batch, height, width, channels) with values 0-1
|
| 204 |
seed: Random seed for reproducible noise
|
| 205 |
border_size: Border size in QR modules (from QR generation settings)
|
| 206 |
image_size: Image size in pixels
|
| 207 |
-
|
| 208 |
|
| 209 |
Returns:
|
| 210 |
-
Modified tensor with
|
| 211 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
| 212 |
# Convert to numpy for manipulation
|
| 213 |
img_np = image_tensor.cpu().numpy()
|
| 214 |
|
|
@@ -219,10 +225,8 @@ def add_noise_to_border_only(image_tensor, seed: int, border_size: int, image_si
|
|
| 219 |
img = img_np[0] # (height, width, channels)
|
| 220 |
height, width, channels = img.shape
|
| 221 |
|
| 222 |
-
# Calculate border region in pixels
|
| 223 |
-
|
| 224 |
-
# We'll use a simple approach: outer X% of the image
|
| 225 |
-
border_thickness = max(int(height * 0.08), 20) # At least 20 pixels or 8% of image
|
| 226 |
|
| 227 |
# Create border mask (1 for border region, 0 for QR code interior)
|
| 228 |
border_mask = np.zeros((height, width), dtype=bool)
|
|
@@ -243,13 +247,34 @@ def add_noise_to_border_only(image_tensor, seed: int, border_size: int, image_si
|
|
| 243 |
# Combine: only border AND white areas
|
| 244 |
final_mask = border_mask & white_mask
|
| 245 |
|
| 246 |
-
#
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 253 |
|
| 254 |
# Put modified image back into batch array
|
| 255 |
img_np[0] = img
|
|
@@ -440,22 +465,23 @@ def _pipeline_artistic(prompt: str, qr_text: str, input_type: str, image_size: i
|
|
| 440 |
|
| 441 |
# Only add noise if there's a border (border_size > 0)
|
| 442 |
if border_size > 0:
|
| 443 |
-
yield base_qr_pil, "Generated base QR pattern... adding border
|
| 444 |
|
| 445 |
-
# Add
|
|
|
|
| 446 |
qr_with_border_noise = add_noise_to_border_only(
|
| 447 |
get_value_at_index(comfy_qr, 0),
|
| 448 |
seed=seed + 100,
|
| 449 |
border_size=border_size,
|
| 450 |
image_size=image_size,
|
| 451 |
-
|
| 452 |
)
|
| 453 |
|
| 454 |
-
# Show the noisy QR so you can see the border
|
| 455 |
noisy_qr_np = (qr_with_border_noise.cpu().numpy() * 255).astype(np.uint8)
|
| 456 |
noisy_qr_np = noisy_qr_np[0]
|
| 457 |
noisy_qr_pil = Image.fromarray(noisy_qr_np)
|
| 458 |
-
yield noisy_qr_pil, "Added
|
| 459 |
else:
|
| 460 |
# No border, skip noise
|
| 461 |
qr_with_border_noise = get_value_at_index(comfy_qr, 0)
|
|
@@ -486,7 +512,7 @@ def _pipeline_artistic(prompt: str, qr_text: str, input_type: str, image_size: i
|
|
| 486 |
control_net_name="control_v11f1e_sd15_tile_fp16.safetensors"
|
| 487 |
)
|
| 488 |
|
| 489 |
-
# First ControlNet pass (using
|
| 490 |
controlnet_apply = controlnetapplyadvanced.apply_controlnet(
|
| 491 |
strength=0.45,
|
| 492 |
start_percent=0,
|
|
@@ -498,14 +524,14 @@ def _pipeline_artistic(prompt: str, qr_text: str, input_type: str, image_size: i
|
|
| 498 |
vae=get_value_at_index(checkpointloadersimple_artistic, 2),
|
| 499 |
)
|
| 500 |
|
| 501 |
-
# Tile preprocessor (using
|
| 502 |
tile_processed = tilepreprocessor.execute(
|
| 503 |
pyrUp_iters=3,
|
| 504 |
resolution=image_size,
|
| 505 |
image=qr_with_border_noise,
|
| 506 |
)
|
| 507 |
|
| 508 |
-
# Second ControlNet pass (using tile processed from
|
| 509 |
controlnet_apply = controlnetapplyadvanced.apply_controlnet(
|
| 510 |
strength=0.45,
|
| 511 |
start_percent=0,
|
|
@@ -521,14 +547,24 @@ def _pipeline_artistic(prompt: str, qr_text: str, input_type: str, image_size: i
|
|
| 521 |
base_model = get_value_at_index(checkpointloadersimple_artistic, 0)
|
| 522 |
|
| 523 |
freeu = FreeU_V2()
|
| 524 |
-
|
| 525 |
model=base_model,
|
| 526 |
-
b1=1.3, # Backbone feature enhancement - improves fine details
|
| 527 |
-
b2=1.4, # Backbone feature enhancement (layer 2) - improves textures
|
| 528 |
-
s1=0.9, # Skip connection dampening - reduces
|
| 529 |
-
s2=0.2 # Skip connection dampening (layer 2) -
|
| 530 |
)[0]
|
| 531 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 532 |
# First sampling pass
|
| 533 |
samples = ksampler.sample(
|
| 534 |
seed=seed,
|
|
|
|
| 195 |
else: # artistic
|
| 196 |
yield from _pipeline_artistic(prompt, qr_text, input_type, image_size, border_size, error_correction, module_size, module_drawer, actual_seed)
|
| 197 |
|
| 198 |
+
def add_noise_to_border_only(image_tensor, seed: int, border_size: int, image_size: int, module_size: int = 12):
|
| 199 |
"""
|
| 200 |
+
Add QR-like cubic patterns ONLY to the border region of a QR code image.
|
| 201 |
+
Creates black squares that resemble QR modules for a smooth transition.
|
| 202 |
+
The density of border cubics automatically matches the QR code interior density.
|
| 203 |
|
| 204 |
Args:
|
| 205 |
image_tensor: ComfyUI image tensor (batch, height, width, channels) with values 0-1
|
| 206 |
seed: Random seed for reproducible noise
|
| 207 |
border_size: Border size in QR modules (from QR generation settings)
|
| 208 |
image_size: Image size in pixels
|
| 209 |
+
module_size: Size of QR modules in pixels (for cubic pattern)
|
| 210 |
|
| 211 |
Returns:
|
| 212 |
+
Modified tensor with QR-like cubic patterns in border region
|
| 213 |
"""
|
| 214 |
+
# Early return if no border
|
| 215 |
+
if border_size == 0:
|
| 216 |
+
return image_tensor
|
| 217 |
+
|
| 218 |
# Convert to numpy for manipulation
|
| 219 |
img_np = image_tensor.cpu().numpy()
|
| 220 |
|
|
|
|
| 225 |
img = img_np[0] # (height, width, channels)
|
| 226 |
height, width, channels = img.shape
|
| 227 |
|
| 228 |
+
# Calculate border region in pixels using exact QR parameters
|
| 229 |
+
border_thickness = border_size * module_size # Exact border size in pixels
|
|
|
|
|
|
|
| 230 |
|
| 231 |
# Create border mask (1 for border region, 0 for QR code interior)
|
| 232 |
border_mask = np.zeros((height, width), dtype=bool)
|
|
|
|
| 247 |
# Combine: only border AND white areas
|
| 248 |
final_mask = border_mask & white_mask
|
| 249 |
|
| 250 |
+
# Calculate QR code interior density to determine border cubic density
|
| 251 |
+
interior_mask = ~border_mask # Inverse of border = QR interior
|
| 252 |
+
interior_pixels = img_255[interior_mask][:, 0] # Get first channel (grayscale)
|
| 253 |
+
black_count = (interior_pixels < 128).sum() # Count black pixels (< 128)
|
| 254 |
+
total_count = len(interior_pixels)
|
| 255 |
+
qr_density = float(black_count) / float(total_count) if total_count > 0 else 0.5
|
| 256 |
+
|
| 257 |
+
# Use QR interior density as probability for placing border cubics
|
| 258 |
+
# This creates a natural transition matching the QR pattern density
|
| 259 |
+
|
| 260 |
+
# Generate QR-like cubic pattern noise
|
| 261 |
+
# Create a grid based on module_size
|
| 262 |
+
for y in range(0, height, module_size):
|
| 263 |
+
for x in range(0, width, module_size):
|
| 264 |
+
# Check if this module position is mostly in the border area
|
| 265 |
+
y_end = min(y + module_size, height)
|
| 266 |
+
x_end = min(x + module_size, width)
|
| 267 |
+
|
| 268 |
+
# Count how many pixels in this module are in the final_mask
|
| 269 |
+
module_region = final_mask[y:y_end, x:x_end]
|
| 270 |
+
|
| 271 |
+
# If at least 50% of the module is in the border, we can place a cubic here
|
| 272 |
+
if module_region.sum() > (module_size * module_size * 0.5):
|
| 273 |
+
# Randomly decide to place a black cubic based on QR interior density
|
| 274 |
+
if np.random.random() < qr_density:
|
| 275 |
+
# Place a black square (cubic) - set all channels to 0 (black)
|
| 276 |
+
for c in range(channels):
|
| 277 |
+
img[y:y_end, x:x_end, c] = 0
|
| 278 |
|
| 279 |
# Put modified image back into batch array
|
| 280 |
img_np[0] = img
|
|
|
|
| 465 |
|
| 466 |
# Only add noise if there's a border (border_size > 0)
|
| 467 |
if border_size > 0:
|
| 468 |
+
yield base_qr_pil, "Generated base QR pattern... adding QR-like cubics to border (step 1/5)"
|
| 469 |
|
| 470 |
+
# Add QR-like cubic patterns ONLY to border region (extends QR structure into border)
|
| 471 |
+
# Density automatically matches QR code interior density for natural transition
|
| 472 |
qr_with_border_noise = add_noise_to_border_only(
|
| 473 |
get_value_at_index(comfy_qr, 0),
|
| 474 |
seed=seed + 100,
|
| 475 |
border_size=border_size,
|
| 476 |
image_size=image_size,
|
| 477 |
+
module_size=module_size, # Use same module size as QR code
|
| 478 |
)
|
| 479 |
|
| 480 |
+
# Show the noisy QR so you can see the border cubic pattern effect
|
| 481 |
noisy_qr_np = (qr_with_border_noise.cpu().numpy() * 255).astype(np.uint8)
|
| 482 |
noisy_qr_np = noisy_qr_np[0]
|
| 483 |
noisy_qr_pil = Image.fromarray(noisy_qr_np)
|
| 484 |
+
yield noisy_qr_pil, "Added QR-like cubics to border... enhancing with AI (step 2/5)"
|
| 485 |
else:
|
| 486 |
# No border, skip noise
|
| 487 |
qr_with_border_noise = get_value_at_index(comfy_qr, 0)
|
|
|
|
| 512 |
control_net_name="control_v11f1e_sd15_tile_fp16.safetensors"
|
| 513 |
)
|
| 514 |
|
| 515 |
+
# First ControlNet pass (using QR with border cubics)
|
| 516 |
controlnet_apply = controlnetapplyadvanced.apply_controlnet(
|
| 517 |
strength=0.45,
|
| 518 |
start_percent=0,
|
|
|
|
| 524 |
vae=get_value_at_index(checkpointloadersimple_artistic, 2),
|
| 525 |
)
|
| 526 |
|
| 527 |
+
# Tile preprocessor (using QR with border cubics)
|
| 528 |
tile_processed = tilepreprocessor.execute(
|
| 529 |
pyrUp_iters=3,
|
| 530 |
resolution=image_size,
|
| 531 |
image=qr_with_border_noise,
|
| 532 |
)
|
| 533 |
|
| 534 |
+
# Second ControlNet pass (using tile processed from QR with border cubics)
|
| 535 |
controlnet_apply = controlnetapplyadvanced.apply_controlnet(
|
| 536 |
strength=0.45,
|
| 537 |
start_percent=0,
|
|
|
|
| 547 |
base_model = get_value_at_index(checkpointloadersimple_artistic, 0)
|
| 548 |
|
| 549 |
freeu = FreeU_V2()
|
| 550 |
+
freeu_model = freeu.patch(
|
| 551 |
model=base_model,
|
| 552 |
+
b1=1.3, # Backbone feature enhancement - improves fine details (reduced for more blending)
|
| 553 |
+
b2=1.4, # Backbone feature enhancement (layer 2) - improves textures (reduced)
|
| 554 |
+
s1=0.9, # Skip connection dampening - reduces QR structure visibility (increased)
|
| 555 |
+
s2=0.2 # Skip connection dampening (layer 2) - hides more cubics (increased)
|
| 556 |
)[0]
|
| 557 |
|
| 558 |
+
# Apply SEG (Self-Attention Guidance) for improved structural coherence
|
| 559 |
+
# DISABLED: SEG blurs attention maps which rounds position marker corners, affecting scannability
|
| 560 |
+
# smoothed_energy = NODE_CLASS_MAPPINGS["SelfAttentionGuidance"]()
|
| 561 |
+
# enhanced_model = smoothed_energy.patch(
|
| 562 |
+
# model=freeu_model,
|
| 563 |
+
# scale=1.5,
|
| 564 |
+
# blur_sigma=0.5,
|
| 565 |
+
# )[0]
|
| 566 |
+
enhanced_model = freeu_model # Use only FreeU, skip SEG
|
| 567 |
+
|
| 568 |
# First sampling pass
|
| 569 |
samples = ksampler.sample(
|
| 570 |
seed=seed,
|