v12.1: Fix QR alignment (496->512px resize), reduce feather to 16px, bump overlay to 60%
Browse files- handler.py +11 -10
handler.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
"""
|
| 2 |
-
QR-Verse AI Art Generator — HuggingFace Inference Endpoint Handler v12
|
| 3 |
|
| 4 |
Art + QR overlay pipeline: ControlNet art generation + post-processing QR composite.
|
| 5 |
|
|
@@ -66,10 +66,10 @@ P2_STRENGTH = 0.15
|
|
| 66 |
# ---------------------------------------------------------------------------
|
| 67 |
# QR overlay post-processing
|
| 68 |
# ---------------------------------------------------------------------------
|
| 69 |
-
OVERLAY_OPACITY = 0.
|
| 70 |
OVERLAY_BG_RATIO = 0.6 # Background alpha = opacity * ratio (lighter than modules)
|
| 71 |
OVERLAY_BLUR_SIGMA = 1.0 # Gaussian blur on overlay for softer edges
|
| 72 |
-
OVERLAY_FEATHER_PX =
|
| 73 |
|
| 74 |
# ---------------------------------------------------------------------------
|
| 75 |
# Quality tags — NO QR tags (QR structure from ControlNet only)
|
|
@@ -121,7 +121,7 @@ class EndpointHandler:
|
|
| 121 |
|
| 122 |
def __init__(self, path: str = ""):
|
| 123 |
"""Load models on endpoint startup."""
|
| 124 |
-
logger.info("Loading QR Art Generator pipeline v12 (Art+Overlay)...")
|
| 125 |
start = time.time()
|
| 126 |
|
| 127 |
device = "cuda" if torch.cuda.is_available() else "cpu"
|
|
@@ -182,7 +182,7 @@ class EndpointHandler:
|
|
| 182 |
self.device = device
|
| 183 |
self.dtype = dtype
|
| 184 |
elapsed = time.time() - start
|
| 185 |
-
logger.info(f"Pipeline v12 loaded in {elapsed:.1f}s on {device}")
|
| 186 |
|
| 187 |
def _generate_qr_images(self, data: str):
|
| 188 |
"""
|
|
@@ -212,8 +212,8 @@ class EndpointHandler:
|
|
| 212 |
|
| 213 |
qr_w, qr_h = qr_gray.size
|
| 214 |
|
| 215 |
-
#
|
| 216 |
-
if qr_w
|
| 217 |
qr_gray = qr_gray.resize(
|
| 218 |
(QR_TARGET_SIZE, QR_TARGET_SIZE), Image.NEAREST
|
| 219 |
)
|
|
@@ -223,6 +223,7 @@ class EndpointHandler:
|
|
| 223 |
logger.info(f"QR resized from {qr_w}x{qr_h} to {QR_TARGET_SIZE}x{QR_TARGET_SIZE}")
|
| 224 |
|
| 225 |
# Conditioning: center on gray canvas + pre-blur
|
|
|
|
| 226 |
conditioning = Image.new("RGB", (QR_CANVAS_SIZE, QR_CANVAS_SIZE), (128, 128, 128))
|
| 227 |
offset = (QR_CANVAS_SIZE - QR_TARGET_SIZE) // 2
|
| 228 |
conditioning.paste(qr_gray, (offset, offset))
|
|
@@ -288,9 +289,9 @@ class EndpointHandler:
|
|
| 288 |
ov_arr[:, :, 3] = (ov_arr[:, :, 3].astype(np.float32) * fade).astype(np.uint8)
|
| 289 |
overlay_img = Image.fromarray(ov_arr, "RGBA")
|
| 290 |
|
| 291 |
-
# Center overlay on full canvas
|
| 292 |
canvas = Image.new("RGBA", (QR_CANVAS_SIZE, QR_CANVAS_SIZE), (0, 0, 0, 0))
|
| 293 |
-
offset = (QR_CANVAS_SIZE -
|
| 294 |
canvas.paste(overlay_img, (offset, offset))
|
| 295 |
|
| 296 |
return canvas
|
|
@@ -489,7 +490,7 @@ class EndpointHandler:
|
|
| 489 |
"image": result_b64,
|
| 490 |
"seed": seed,
|
| 491 |
"parameters": {
|
| 492 |
-
"pipeline": f"{'two' if passes >= 2 else 'single'}-pass-v12-overlay",
|
| 493 |
"passes": passes,
|
| 494 |
"category": category,
|
| 495 |
"p1_monster": p1_monster,
|
|
|
|
| 1 |
"""
|
| 2 |
+
QR-Verse AI Art Generator — HuggingFace Inference Endpoint Handler v12.1
|
| 3 |
|
| 4 |
Art + QR overlay pipeline: ControlNet art generation + post-processing QR composite.
|
| 5 |
|
|
|
|
| 66 |
# ---------------------------------------------------------------------------
|
| 67 |
# QR overlay post-processing
|
| 68 |
# ---------------------------------------------------------------------------
|
| 69 |
+
OVERLAY_OPACITY = 0.60 # Alpha for QR modules (0=invisible, 1=solid black)
|
| 70 |
OVERLAY_BG_RATIO = 0.6 # Background alpha = opacity * ratio (lighter than modules)
|
| 71 |
OVERLAY_BLUR_SIGMA = 1.0 # Gaussian blur on overlay for softer edges
|
| 72 |
+
OVERLAY_FEATHER_PX = 16 # Fade-out at overlay borders (1 QR module width)
|
| 73 |
|
| 74 |
# ---------------------------------------------------------------------------
|
| 75 |
# Quality tags — NO QR tags (QR structure from ControlNet only)
|
|
|
|
| 121 |
|
| 122 |
def __init__(self, path: str = ""):
|
| 123 |
"""Load models on endpoint startup."""
|
| 124 |
+
logger.info("Loading QR Art Generator pipeline v12.1 (Art+Overlay)...")
|
| 125 |
start = time.time()
|
| 126 |
|
| 127 |
device = "cuda" if torch.cuda.is_available() else "cpu"
|
|
|
|
| 182 |
self.device = device
|
| 183 |
self.dtype = dtype
|
| 184 |
elapsed = time.time() - start
|
| 185 |
+
logger.info(f"Pipeline v12.1 loaded in {elapsed:.1f}s on {device}")
|
| 186 |
|
| 187 |
def _generate_qr_images(self, data: str):
|
| 188 |
"""
|
|
|
|
| 212 |
|
| 213 |
qr_w, qr_h = qr_gray.size
|
| 214 |
|
| 215 |
+
# Always resize to exact target size for consistent alignment
|
| 216 |
+
if qr_w != QR_TARGET_SIZE or qr_h != QR_TARGET_SIZE:
|
| 217 |
qr_gray = qr_gray.resize(
|
| 218 |
(QR_TARGET_SIZE, QR_TARGET_SIZE), Image.NEAREST
|
| 219 |
)
|
|
|
|
| 223 |
logger.info(f"QR resized from {qr_w}x{qr_h} to {QR_TARGET_SIZE}x{QR_TARGET_SIZE}")
|
| 224 |
|
| 225 |
# Conditioning: center on gray canvas + pre-blur
|
| 226 |
+
# Both conditioning and overlay MUST use the same offset for alignment
|
| 227 |
conditioning = Image.new("RGB", (QR_CANVAS_SIZE, QR_CANVAS_SIZE), (128, 128, 128))
|
| 228 |
offset = (QR_CANVAS_SIZE - QR_TARGET_SIZE) // 2
|
| 229 |
conditioning.paste(qr_gray, (offset, offset))
|
|
|
|
| 289 |
ov_arr[:, :, 3] = (ov_arr[:, :, 3].astype(np.float32) * fade).astype(np.uint8)
|
| 290 |
overlay_img = Image.fromarray(ov_arr, "RGBA")
|
| 291 |
|
| 292 |
+
# Center overlay on full canvas — MUST match conditioning offset
|
| 293 |
canvas = Image.new("RGBA", (QR_CANVAS_SIZE, QR_CANVAS_SIZE), (0, 0, 0, 0))
|
| 294 |
+
offset = (QR_CANVAS_SIZE - QR_TARGET_SIZE) // 2
|
| 295 |
canvas.paste(overlay_img, (offset, offset))
|
| 296 |
|
| 297 |
return canvas
|
|
|
|
| 490 |
"image": result_b64,
|
| 491 |
"seed": seed,
|
| 492 |
"parameters": {
|
| 493 |
+
"pipeline": f"{'two' if passes >= 2 else 'single'}-pass-v12.1-overlay",
|
| 494 |
"passes": passes,
|
| 495 |
"category": category,
|
| 496 |
"p1_monster": p1_monster,
|