fix: anti-clamp via avail_w/avail_h cap on k — guarantees 0% ratio drift at image borders
This commit is contained in:
+32
-15
@@ -15,13 +15,13 @@ class BBoxMultipleFix:
|
||||
S'insere apres 'Mask Bounding Box' (ComfyUI Essentials).
|
||||
|
||||
force_square (bool) :
|
||||
False — comportement par defaut, ratio libre du bbox.
|
||||
True — force le crop a etre carre (cote = max(width, height)),
|
||||
False - comportement par defaut, ratio libre du bbox.
|
||||
True - force le crop a etre carre (cote = max(width, height)),
|
||||
en restant aligne sur le multiple choisi.
|
||||
|
||||
force_target_downscale (bool) :
|
||||
False — comportement par defaut : si bbox > target, fallback cap MAX_SIDE.
|
||||
True — si bbox > target, downscale GCD vers le target (meme algo que l'upscale).
|
||||
False - comportement par defaut : si bbox > target, fallback cap MAX_SIDE.
|
||||
True - si bbox > target, downscale GCD vers le target (meme algo que l'upscale).
|
||||
Ignore si target="none".
|
||||
|
||||
Mode none (target="none") :
|
||||
@@ -33,6 +33,10 @@ class BBoxMultipleFix:
|
||||
Si force_target_downscale=True et bbox > target : downscale GCD vers target.
|
||||
Si force_target_downscale=False et bbox > target : cap MAX_SIDE (comportement historique).
|
||||
|
||||
Anti-clamp : dans tous les cas, k est plafonne a l'espace disponible
|
||||
autour du centre du bbox -> le crop ne depasse jamais l'image source
|
||||
-> pas de clamp post-GCD -> ratio pixel-perfect garanti (0% de drift).
|
||||
|
||||
Sorties :
|
||||
image_cropped / mask_cropped -> VAE Encode (Inpaint)
|
||||
x / y -> ImageCompositeMasked
|
||||
@@ -80,7 +84,18 @@ class BBoxMultipleFix:
|
||||
else:
|
||||
base_w, base_h = width, height
|
||||
|
||||
# ── Step 2 : calcul new_w / new_h / up_w / up_h ──────────────────────
|
||||
# ── Step 2 : espace disponible autour du centre du bbox ───────────────
|
||||
# Garantit que le crop ne depasse jamais l'image -> pas de clamp post-GCD
|
||||
# -> ratio pixel-perfect dans tous les cas, y compris bords d'image.
|
||||
cx = x + width // 2
|
||||
cy = y + height // 2
|
||||
avail_w = min(cx, W_src - cx) * 2
|
||||
avail_h = min(cy, H_src - cy) * 2
|
||||
# Plancher de securite : au minimum le bbox lui-meme
|
||||
avail_w = max(avail_w, base_w)
|
||||
avail_h = max(avail_h, base_h)
|
||||
|
||||
# ── Step 3 : calcul new_w / new_h / up_w / up_h ──────────────────────
|
||||
need_resize = False
|
||||
resize_label = ""
|
||||
up_w = up_h = 0
|
||||
@@ -97,29 +112,29 @@ class BBoxMultipleFix:
|
||||
t_w = math.ceil((base_w * t / base_h) / mult) * mult
|
||||
|
||||
if t_w > base_w and t_h > base_h:
|
||||
# Upscale vers target (comportement historique)
|
||||
# Upscale vers target
|
||||
g = math.gcd(t_w, t_h)
|
||||
a, b = t_w // g, t_h // g
|
||||
k = round((base_w / a + base_h / b) / 2)
|
||||
k = max(1, k)
|
||||
k = max(1, min(k, avail_w // a, avail_h // b)) # anti-clamp
|
||||
new_w, new_h = a * k, b * k
|
||||
up_w, up_h = t_w, t_h
|
||||
need_resize = True
|
||||
resize_label = "upscale "
|
||||
|
||||
elif force_target_downscale:
|
||||
# Downscale GCD vers target (nouveau comportement optionnel)
|
||||
# Downscale GCD vers target
|
||||
g = math.gcd(t_w, t_h)
|
||||
a, b = t_w // g, t_h // g
|
||||
k = round((base_w / a + base_h / b) / 2)
|
||||
k = max(1, k)
|
||||
k = max(1, min(k, avail_w // a, avail_h // b)) # anti-clamp
|
||||
new_w, new_h = a * k, b * k
|
||||
up_w, up_h = t_w, t_h
|
||||
need_resize = True
|
||||
resize_label = "downscale"
|
||||
|
||||
else:
|
||||
# Fallback historique : arrondi au multiple + cap MAX_SIDE
|
||||
# Fallback : arrondi au multiple + cap MAX_SIDE
|
||||
print(f"[BBoxMultipleFix] target={t} <= bbox -> fallback cap {MAX_SIDE}px")
|
||||
new_w = math.ceil(base_w / mult) * mult
|
||||
new_h = math.ceil(base_h / mult) * mult
|
||||
@@ -135,7 +150,7 @@ class BBoxMultipleFix:
|
||||
g = math.gcd(down_w, down_h)
|
||||
a, b = down_w // g, down_h // g
|
||||
k = round((new_w / a + new_h / b) / 2)
|
||||
k = max(1, k)
|
||||
k = max(1, min(k, avail_w // a, avail_h // b)) # anti-clamp
|
||||
new_w, new_h = a * k, b * k
|
||||
up_w, up_h = down_w, down_h
|
||||
need_resize = True
|
||||
@@ -159,7 +174,7 @@ class BBoxMultipleFix:
|
||||
g = math.gcd(down_w, down_h)
|
||||
a, b = down_w // g, down_h // g
|
||||
k = round((new_w / a + new_h / b) / 2)
|
||||
k = max(1, k)
|
||||
k = max(1, min(k, avail_w // a, avail_h // b)) # anti-clamp
|
||||
new_w, new_h = a * k, b * k
|
||||
up_w, up_h = down_w, down_h
|
||||
need_resize = True
|
||||
@@ -167,7 +182,9 @@ class BBoxMultipleFix:
|
||||
else:
|
||||
up_w, up_h = new_w, new_h
|
||||
|
||||
# ── Step 3 : expansion symetrique autour du bbox original ─────────────
|
||||
# ── Step 4 : expansion symetrique autour du bbox original ─────────────
|
||||
# Grace a l'anti-clamp, new_w/new_h rentrent toujours dans l'image —
|
||||
# le clamp ci-dessous ne modifie donc rien dans les cas normaux.
|
||||
new_x = x - (new_w - width) // 2
|
||||
new_y = y - (new_h - height) // 2
|
||||
|
||||
@@ -189,13 +206,13 @@ class BBoxMultipleFix:
|
||||
print(f"[BBoxMultipleFix] bbox : {width}x{height} @({x},{y}){flag_info}")
|
||||
print(f"[BBoxMultipleFix] crop : {orig_w}x{orig_h} @({new_x},{new_y})")
|
||||
|
||||
# ── Step 4 : crop ─────────────────────────────────────────────────────
|
||||
# ── Step 5 : crop ─────────────────────────────────────────────────────
|
||||
img_cropped = image[:, new_y:new_y + new_h, new_x:new_x + new_w, :]
|
||||
if mask.dim() == 2:
|
||||
mask = mask.unsqueeze(0)
|
||||
mask_cropped = mask[:, new_y:new_y + new_h, new_x:new_x + new_w]
|
||||
|
||||
# ── Step 5 : resize Lanczos si necessaire ─────────────────────────────
|
||||
# ── Step 6 : resize Lanczos si necessaire ─────────────────────────────
|
||||
if need_resize and (up_w != orig_w or up_h != orig_h):
|
||||
print(f"[BBoxMultipleFix] {resize_label} : {orig_w}x{orig_h} -> {up_w}x{up_h}")
|
||||
img_cropped = self._resize(img_cropped, up_w, up_h, "image")
|
||||
|
||||
Reference in New Issue
Block a user