見出し画像

Fixed ComfyUI-Custom-Scripts

D:\USERFILES\ComfyUI\ComfyUI\custom_nodes\ComfyUI-Custom-Scripts\py\constrain_image.py


---

## Errors That Occurred

### Error 1: `ZeroDivisionError: division by zero`
```python
if constrained_width / constrained_height > aspect_ratio:
   ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
   # constrained_height is 0, causing division by zero failure

Error 2: `ValueError: height and width must be > 0`

resized_image = img.resize((constrained_width, constrained_height), Image.LANCZOS)
# PIL does not accept dimensions of 0 or less

Root Cause Analysis

When a user sets `max_height=0` or `max_width=0`:

1. User sets max_height = 0
   ↓
2. constrained_height = min(max(current_height, min_height), max_height)
   = min(max(768, 0), 0)
   = min(768, 0)
   = 0  ← Becomes zero!
   ↓
3. aspect_ratio = current_width / current_height
   = 1024 / 0  ← Division by zero error!

And if this somehow passes:

4. resized_image = img.resize((1, 0))
   ← PIL raises "height must be > 0" error and fails

Complete Fixed Code

Fixed Code (Lines 28-86):

def constrain_image(self, images, max_width, max_height, min_width, min_height, crop_if_required):
    crop_if_required = crop_if_required == "yes"
    results = []
    for image in images:
        i = 255. * image.cpu().numpy()
        img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)).convert("RGB")

        current_width, current_height = img.size
        
        # ========== Check Point 1: Image Size Zero Check ==========
        # Zero value check (set minimum value to 1)
        if current_height <= 0:
            current_height = 1
        if current_width <= 0:
            current_width = 1
        
        aspect_ratio = current_width / current_height  # ✓ Safe

        # ========== Check Point 2: Parameter Zero Check ==========
        # If max_width or max_height is 0, use the original size
        if max_width <= 0:
            max_width = current_width
        if max_height <= 0:
            max_height = current_height

        # ========== Check Point 3: Initial Calculation ==========
        constrained_width = min(max(current_width, min_width), max_width)
        constrained_height = min(max(current_height, min_height), max_height)

        # ========== Check Point 4: Zero Prevention After Calculation ==========
        # Prevent zero after calculation
        if constrained_width <= 0:
            constrained_width = 1
        if constrained_height <= 0:
            constrained_height = 1

        # ========== Check Point 5: Aspect Ratio Adjustment ==========
        if constrained_width / constrained_height > aspect_ratio:
            constrained_width = max(int(constrained_height * aspect_ratio), 1)
            if crop_if_required and current_width > 0:
                constrained_height = max(int(current_height / (current_width / max(constrained_width, 1))), 1)
        else:
            constrained_height = max(int(constrained_width / aspect_ratio), 1)
            if crop_if_required and current_height > 0:
                constrained_width = max(int(current_width / (current_height / max(constrained_height, 1))), 1)

        # ========== Check Point 6: Final Verification ==========
        # Final check: ensure size is positive
        constrained_width = max(int(constrained_width), 1)
        constrained_height = max(int(constrained_height), 1)

        # ========== Execute PIL Resize ==========
        resized_image = img.resize((constrained_width, constrained_height), Image.LANCZOS)

        if crop_if_required and (constrained_width > max_width or constrained_height > max_height):
            left = max((constrained_width - max_width) // 2, 0)
            top = max((constrained_height - max_height) // 2, 0)
            right = min(constrained_width, max_width) + left
            bottom = min(constrained_height, max_height) + top
            resized_image = resized_image.crop((left, top, right, bottom))

        resized_image = np.array(resized_image).astype(np.float32) / 255.0
        resized_image = torch.from_numpy(resized_image)[None,]
        results.append(resized_image)
            
    return (results,)

Detailed Explanation of 6 Check Points

1️⃣ Check Point 1: Image Size Zero Check (Lines 37-41)

if current_height <= 0:
    current_height = 1
if current_width <= 0:
    current_width = 1

What it does:

  • If the image height retrieved from PIL is 0 or negative, force it to 1

Why it is needed:

  • The image file might be corrupted

  • Prevents division by zero

  • Used in `aspect_ratio = current_width / current_height`

Example:

Original image: current_height = 0
After fix: current_height = 1 ← Avoids zero division

2️⃣ Check Point 2: Parameter Zero Check (Lines 45-49)

if max_width <= 0:
    max_width = current_width
if max_height <= 0:
    max_height = current_height

What it does:

  • If a user sets `max_width=0` or `max_height=0`, set the upper limit to the original image size

Why it is needed:

  • Users might accidentally set zero values

  • Prevents `min(..., max_width)` from becoming 0

Concrete Example:

User sets: max_height = 0

Before fix (calculation):
  constrained_height = min(max(768, 0), 0)
                    = min(768, 0)
                    = 0  ← Fails!

After fix:
  max_height = 768 (original size)
  constrained_height = min(max(768, 0), 768)
                    = min(768, 768)
                    = 768  ✓ Success

3️⃣ Check Point 3: Initial Calculation (Lines 51-52)

constrained_width = min(max(current_width, min_width), max_width)
constrained_height = min(max(current_height, min_height), max_height)

What it does:

  • Apply both minimum and maximum value constraints simultaneously

  • `max(...)` ensures at least the minimum value

  • `min(...)` ensures at most the maximum value

Calculation Logic:

constrained_height = min(max(current_height, min_height), max_height)
                    = min(   apply minimum constraint,      apply maximum constraint )

Example: current_height=768, min_height=0, max_height=1024
         = min(max(768, 0), 1024)
         = min(768, 1024)
         = 768 ✓

Note: At this point, values might still become 0


4️⃣ Check Point 4: Zero Prevention After Calculation (Lines 54-58)

if constrained_width <= 0:
    constrained_width = 1
if constrained_height <= 0:
    constrained_height = 1

What it does:

  • After Check Point 3 calculation, if a value becomes 0 or negative, force it to 1

Why it is needed:

  • Even after Check Point 2's fix, Check Point 3's result might not be safe

  • Double-check strategy

Example:

Check Point 2 fix: max_height = 768
Check Point 3: constrained_height = min(768, 768) = 768
Check Point 4: 768 > 0, so no change ✓

However, other calculation paths might yield 0, so we need this safeguard

5️⃣ Check Point 5: Aspect Ratio Adjustment (Lines 60-67)

if constrained_width / constrained_height > aspect_ratio:
    constrained_width = max(int(constrained_height * aspect_ratio), 1)
    if crop_if_required and current_width > 0:
        constrained_height = max(int(current_height / (current_width / max(constrained_width, 1))), 1)
else:
    constrained_height = max(int(constrained_width / aspect_ratio), 1)
    if crop_if_required and current_height > 0:
        constrained_width = max(int(current_width / (current_height / max(constrained_height, 1))), 1)

What it does:

  • Adjust width or height while maintaining the aspect ratio (width-to-height ratio)

  • Ensure calculated results are at least 1 by using `max(..., 1)`

Logic:

  • If width is too large compared to height → Adjust width based on height

  • If height is too large compared to width → Adjust height based on width

Important: No division by zero occurs at this point (guaranteed by Check Point 4)


6️⃣ Check Point 6: Final Verification (Lines 69-71)

constrained_width = max(int(constrained_width), 1)
constrained_height = max(int(constrained_height), 1)

resized_image = img.resize((constrained_width, constrained_height), Image.LANCZOS)

What it does:

  • Final verification after all calculation steps

  • Convert floating-point numbers to integers using `int()`

  • Ensure values are at least 1 by using `max(..., 1)`

  • Triple-check strategy

Example:

Calculation result: constrained_width = 0.5
Step 1: int(0.5) = 0
Step 2: max(0, 1) = 1 ✓

Calculation result: constrained_height = 0.0
Step 1: int(0.0) = 0
Step 2: max(0, 1) = 1 ✓

This is where `img.resize()` is called:

  • PIL accepts only positive integers

  • Only values of 1 or higher pass through all checks


Fix Summary Flow Diagram

Input: max_height=0
  ↓
[Check Point 1] Verify image size (if 0, set to 1)
  ↓
[Check Point 2] max_height=0 → Fix to max_height = current_height ✅
  ↓
[Check Point 3] Calculate constraints
  ↓
[Check Point 4] Result is 0? → Fix to 1
  ↓
[Check Point 5] Adjust aspect ratio (guarantee minimum 1)
  ↓
[Check Point 6] Final check (int conversion + max(..., 1))
  ↓
PIL.resize((1 or more, 1 or more)) → Success! ✅

Before and After Comparison

| Item | Before Fix | After Fix |
|------|-----------|-----------|
| Zero Division Prevention | None | 6 check points |
| max_width=0 Handling | None | Check Point 2 fix |
| Post-Calculation Check | None | Check Point 4 rechecks |
| PIL Pre-Call Check | None | Check Point 6 final verification |
| Error: ZeroDivisionError | Occurs | None (safe) |
| Error: ValueError height/width | Occurs | None (safe) |


Mathematical Guarantee

After all 6 check points, the following is absolutely guaranteed:

1 ≤ constrained_width < ∞  (positive integer)
1 ≤ constrained_height < ∞  (positive integer)

Therefore:

  • `constrained_width / constrained_height` is always valid (never divides by zero)

  • `PIL.Image.resize((constrained_width, constrained_height))` will always succeed

  • No exceptions will be raised


Why Multiple Check Points Are Necessary

Each check point handles a different failure scenario:

  1. Check Point 1: Handles corrupted image files

  2. Check Point 2: Handles user input errors (0 values)

  3. Check Point 3: Calculates constrained dimensions

  4. Check Point 4: Catches unexpected zero values from Check Point 3

  5. Check Point 5: Maintains aspect ratio with guaranteed minimums

  6. Check Point 6: Final guarantee before passing to PIL

Redundancy is safety. Each check point is independent and acts as a fallback for the others.


Code Execution Example with max_height=0

Input parameters:
  current_width = 1024
  current_height = 768
  max_width = 1024
  max_height = 0
  min_width = 0
  min_height = 0

Step 1: Check image size
  current_height = 768 > 0 → No change
  current_width = 1024 > 0 → No change
  aspect_ratio = 1024 / 768 = 1.333

Step 2: Check parameters
  max_height = 0 ≤ 0 → Fix to current_height = 768 ✅

Step 3: Calculate constraints
  constrained_width = min(max(1024, 0), 1024) = 1024
  constrained_height = min(max(768, 0), 768) = 768

Step 4: Check after calculation
  constrained_width = 1024 > 0 → No change
  constrained_height = 768 > 0 → No change

Step 5: Aspect ratio adjustment
  1024 / 768 = 1.333 vs 1.333 → Equal, use else branch
  constrained_height = max(int(1024 / 1.333), 1) = max(768, 1) = 768

Step 6: Final check
  constrained_width = max(int(1024), 1) = 1024 ✓
  constrained_height = max(int(768), 1) = 768 ✓

PIL.resize((1024, 768)) → SUCCESS! ✓

This fix ensures absolute safety at every calculation step!

\py\constrain_image.py

---

## Errors That Occurred

### Error 1: `ZeroDivisionError: division by zero`
```python
if constrained_width / constrained_height > aspect_ratio:
   ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
   # constrained_height is 0, causing division by zero failure

Error 2: `ValueError: height and width must be > 0`

resized_image = img.resize((constrained_width, constrained_height), Image.LANCZOS)
# PIL does not accept dimensions of 0 or less

Root Cause Analysis

When a user sets `max_height=0` or `max_width=0`:

1. User sets max_height = 0
   ↓
2. constrained_height = min(max(current_height, min_height), max_height)
   = min(max(768, 0), 0)
   = min(768, 0)
   = 0  ← Becomes zero!
   ↓
3. aspect_ratio = current_width / current_height
   = 1024 / 0  ← Division by zero error!

And if this somehow passes:

4. resized_image = img.resize((1, 0))
   ← PIL raises "height must be > 0" error and fails

Complete Fixed Code

Fixed Code (Lines 28-86):

def constrain_image(self, images, max_width, max_height, min_width, min_height, crop_if_required):
    crop_if_required = crop_if_required == "yes"
    results = []
    for image in images:
        i = 255. * image.cpu().numpy()
        img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)).convert("RGB")

        current_width, current_height = img.size
        
        # ========== Check Point 1: Image Size Zero Check ==========
        # Zero value check (set minimum value to 1)
        if current_height <= 0:
            current_height = 1
        if current_width <= 0:
            current_width = 1
        
        aspect_ratio = current_width / current_height  # ✓ Safe

        # ========== Check Point 2: Parameter Zero Check ==========
        # If max_width or max_height is 0, use the original size
        if max_width <= 0:
            max_width = current_width
        if max_height <= 0:
            max_height = current_height

        # ========== Check Point 3: Initial Calculation ==========
        constrained_width = min(max(current_width, min_width), max_width)
        constrained_height = min(max(current_height, min_height), max_height)

        # ========== Check Point 4: Zero Prevention After Calculation ==========
        # Prevent zero after calculation
        if constrained_width <= 0:
            constrained_width = 1
        if constrained_height <= 0:
            constrained_height = 1

        # ========== Check Point 5: Aspect Ratio Adjustment ==========
        if constrained_width / constrained_height > aspect_ratio:
            constrained_width = max(int(constrained_height * aspect_ratio), 1)
            if crop_if_required and current_width > 0:
                constrained_height = max(int(current_height / (current_width / max(constrained_width, 1))), 1)
        else:
            constrained_height = max(int(constrained_width / aspect_ratio), 1)
            if crop_if_required and current_height > 0:
                constrained_width = max(int(current_width / (current_height / max(constrained_height, 1))), 1)

        # ========== Check Point 6: Final Verification ==========
        # Final check: ensure size is positive
        constrained_width = max(int(constrained_width), 1)
        constrained_height = max(int(constrained_height), 1)

        # ========== Execute PIL Resize ==========
        resized_image = img.resize((constrained_width, constrained_height), Image.LANCZOS)

        if crop_if_required and (constrained_width > max_width or constrained_height > max_height):
            left = max((constrained_width - max_width) // 2, 0)
            top = max((constrained_height - max_height) // 2, 0)
            right = min(constrained_width, max_width) + left
            bottom = min(constrained_height, max_height) + top
            resized_image = resized_image.crop((left, top, right, bottom))

        resized_image = np.array(resized_image).astype(np.float32) / 255.0
        resized_image = torch.from_numpy(resized_image)[None,]
        results.append(resized_image)
            
    return (results,)

Detailed Explanation of 6 Check Points

1️⃣ Check Point 1: Image Size Zero Check (Lines 37-41)

if current_height <= 0:
    current_height = 1
if current_width <= 0:
    current_width = 1

What it does:

  • If the image height retrieved from PIL is 0 or negative, force it to 1

Why it is needed:

  • The image file might be corrupted

  • Prevents division by zero

  • Used in `aspect_ratio = current_width / current_height`

Example:

Original image: current_height = 0
After fix: current_height = 1 ← Avoids zero division

2️⃣ Check Point 2: Parameter Zero Check (Lines 45-49)

if max_width <= 0:
    max_width = current_width
if max_height <= 0:
    max_height = current_height

What it does:

  • If a user sets `max_width=0` or `max_height=0`, set the upper limit to the original image size

Why it is needed:

  • Users might accidentally set zero values

  • Prevents `min(..., max_width)` from becoming 0

Concrete Example:

User sets: max_height = 0

Before fix (calculation):
  constrained_height = min(max(768, 0), 0)
                    = min(768, 0)
                    = 0  ← Fails!

After fix:
  max_height = 768 (original size)
  constrained_height = min(max(768, 0), 768)
                    = min(768, 768)
                    = 768  ✓ Success

3️⃣ Check Point 3: Initial Calculation (Lines 51-52)

constrained_width = min(max(current_width, min_width), max_width)
constrained_height = min(max(current_height, min_height), max_height)

What it does:

  • Apply both minimum and maximum value constraints simultaneously

  • `max(...)` ensures at least the minimum value

  • `min(...)` ensures at most the maximum value

Calculation Logic:

constrained_height = min(max(current_height, min_height), max_height)
                    = min(   apply minimum constraint,      apply maximum constraint )

Example: current_height=768, min_height=0, max_height=1024
         = min(max(768, 0), 1024)
         = min(768, 1024)
         = 768 ✓

Note: At this point, values might still become 0


4️⃣ Check Point 4: Zero Prevention After Calculation (Lines 54-58)

if constrained_width <= 0:
    constrained_width = 1
if constrained_height <= 0:
    constrained_height = 1

What it does:

  • After Check Point 3 calculation, if a value becomes 0 or negative, force it to 1

Why it is needed:

  • Even after Check Point 2's fix, Check Point 3's result might not be safe

  • Double-check strategy

Example:

Check Point 2 fix: max_height = 768
Check Point 3: constrained_height = min(768, 768) = 768
Check Point 4: 768 > 0, so no change ✓

However, other calculation paths might yield 0, so we need this safeguard

5️⃣ Check Point 5: Aspect Ratio Adjustment (Lines 60-67)

if constrained_width / constrained_height > aspect_ratio:
    constrained_width = max(int(constrained_height * aspect_ratio), 1)
    if crop_if_required and current_width > 0:
        constrained_height = max(int(current_height / (current_width / max(constrained_width, 1))), 1)
else:
    constrained_height = max(int(constrained_width / aspect_ratio), 1)
    if crop_if_required and current_height > 0:
        constrained_width = max(int(current_width / (current_height / max(constrained_height, 1))), 1)

What it does:

  • Adjust width or height while maintaining the aspect ratio (width-to-height ratio)

  • Ensure calculated results are at least 1 by using `max(..., 1)`

Logic:

  • If width is too large compared to height → Adjust width based on height

  • If height is too large compared to width → Adjust height based on width

Important: No division by zero occurs at this point (guaranteed by Check Point 4)


6️⃣ Check Point 6: Final Verification (Lines 69-71)

constrained_width = max(int(constrained_width), 1)
constrained_height = max(int(constrained_height), 1)

resized_image = img.resize((constrained_width, constrained_height), Image.LANCZOS)

What it does:

  • Final verification after all calculation steps

  • Convert floating-point numbers to integers using `int()`

  • Ensure values are at least 1 by using `max(..., 1)`

  • Triple-check strategy

Example:

Calculation result: constrained_width = 0.5
Step 1: int(0.5) = 0
Step 2: max(0, 1) = 1 ✓

Calculation result: constrained_height = 0.0
Step 1: int(0.0) = 0
Step 2: max(0, 1) = 1 ✓

This is where `img.resize()` is called:

  • PIL accepts only positive integers

  • Only values of 1 or higher pass through all checks


Fix Summary Flow Diagram

Input: max_height=0
  ↓
[Check Point 1] Verify image size (if 0, set to 1)
  ↓
[Check Point 2] max_height=0 → Fix to max_height = current_height ✅
  ↓
[Check Point 3] Calculate constraints
  ↓
[Check Point 4] Result is 0? → Fix to 1
  ↓
[Check Point 5] Adjust aspect ratio (guarantee minimum 1)
  ↓
[Check Point 6] Final check (int conversion + max(..., 1))
  ↓
PIL.resize((1 or more, 1 or more)) → Success! ✅

Before and After Comparison

| Item | Before Fix | After Fix |
|------|-----------|-----------|
| Zero Division Prevention | None | 6 check points |
| max_width=0 Handling | None | Check Point 2 fix |
| Post-Calculation Check | None | Check Point 4 rechecks |
| PIL Pre-Call Check | None | Check Point 6 final verification |
| Error: ZeroDivisionError | Occurs | None (safe) |
| Error: ValueError height/width | Occurs | None (safe) |


Mathematical Guarantee

After all 6 check points, the following is absolutely guaranteed:

1 ≤ constrained_width < ∞  (positive integer)
1 ≤ constrained_height < ∞  (positive integer)

Therefore:

  • `constrained_width / constrained_height` is always valid (never divides by zero)

  • `PIL.Image.resize((constrained_width, constrained_height))` will always succeed

  • No exceptions will be raised


Why Multiple Check Points Are Necessary

Each check point handles a different failure scenario:

  1. Check Point 1: Handles corrupted image files

  2. Check Point 2: Handles user input errors (0 values)

  3. Check Point 3: Calculates constrained dimensions

  4. Check Point 4: Catches unexpected zero values from Check Point 3

  5. Check Point 5: Maintains aspect ratio with guaranteed minimums

  6. Check Point 6: Final guarantee before passing to PIL

Redundancy is safety. Each check point is independent and acts as a fallback for the others.


Code Execution Example with max_height=0

Input parameters:
  current_width = 1024
  current_height = 768
  max_width = 1024
  max_height = 0
  min_width = 0
  min_height = 0

Step 1: Check image size
  current_height = 768 > 0 → No change
  current_width = 1024 > 0 → No change
  aspect_ratio = 1024 / 768 = 1.333

Step 2: Check parameters
  max_height = 0 ≤ 0 → Fix to current_height = 768 ✅

Step 3: Calculate constraints
  constrained_width = min(max(1024, 0), 1024) = 1024
  constrained_height = min(max(768, 0), 768) = 768

Step 4: Check after calculation
  constrained_width = 1024 > 0 → No change
  constrained_height = 768 > 0 → No change

Step 5: Aspect ratio adjustment
  1024 / 768 = 1.333 vs 1.333 → Equal, use else branch
  constrained_height = max(int(1024 / 1.333), 1) = max(768, 1) = 768

Step 6: Final check
  constrained_width = max(int(1024), 1) = 1024 ✓
  constrained_height = max(int(768), 1) = 768 ✓

PIL.resize((1024, 768)) → SUCCESS! ✓

This fix ensures absolute safety at every calculation step!

いいなと思ったら応援しよう!