File size: 3,515 Bytes
2267636
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
"""Image resizing to FLUX-compatible resolutions (dimensions divisible by 32)."""

import cv2
import numpy as np
from typing import Tuple
from PIL import Image


class FluxResizer:
    """
    Resizer that ensures images are compatible with FLUX requirements.
    - FLUX: Dimensions divisible by 32 (due to 2x2 packing on top of 16-stride VAE)
    """
    
    # Predefined optimal resolutions (all divisible by 32)
    OPTIMAL_RESOLUTIONS = [
        # Square and near-square
        (1024, 1024),  # 1:1 (64×64, 64×64)
        (896, 1152),   # ~0.78:1 (56×72)
        (1152, 896),   # ~1.29:1 (72×56)
        (768, 1344),   # ~0.57:1 (48×84)
        (1344, 768),   # ~1.75:1 (84×48)
        
        # Additional common ratios
        (832, 1216),   # ~0.68:1 (52×76)
        (1216, 832),   # ~1.46:1 (76×52)
        (704, 1408),   # 0.5:1 (44×88)
        (1408, 704),   # 2:1 (88×44)
        (960, 1088),   # ~0.88:1 (60×68)
        (1088, 960),   # ~1.13:1 (68×60)
    ]
    
    def __init__(self):
        self.resolution_aspects = [
            (h, w, w / h) for h, w in self.OPTIMAL_RESOLUTIONS
        ]
    
    def select_best_resolution(self, original_h: int, original_w: int) -> Tuple[int, int]:
        """Pick the optimal resolution whose aspect ratio best matches the input."""
        original_aspect = original_w / original_h
        
        best_resolution = None
        min_aspect_diff = float('inf')
        
        for h, w, aspect in self.resolution_aspects:
            aspect_diff = abs(original_aspect - aspect)
            
            if aspect_diff < min_aspect_diff:
                min_aspect_diff = aspect_diff
                best_resolution = (h, w)
        
        return best_resolution
    
    def resize_image(self, image: np.ndarray) -> Tuple[np.ndarray, Tuple[int, int]]:
        """Resize an (H, W, C) numpy image to the optimal FLUX-compatible resolution."""
        original_h, original_w = image.shape[:2]
        target_h, target_w = self.select_best_resolution(original_h, original_w)
        
        resized_image = cv2.resize(image, (target_w, target_h), interpolation=cv2.INTER_LINEAR)
        
        return resized_image, (target_h, target_w)
    
    def resize_pil_image(self, image: Image.Image) -> Tuple[Image.Image, Tuple[int, int]]:
        """Resize a PIL image to the optimal FLUX-compatible resolution."""
        original_w, original_h = image.size  # PIL uses (W, H)
        target_h, target_w = self.select_best_resolution(original_h, original_w)
        
        resized_image = image.resize((target_w, target_h), Image.LANCZOS)
        
        return resized_image, (target_h, target_w)
    
    def resize_mask(self, mask: np.ndarray, target_size: Tuple[int, int]) -> np.ndarray:
        """Resize a mask to target_size=(H, W) using nearest-neighbor interpolation."""
        target_h, target_w = target_size
        
        if len(mask.shape) == 3 and mask.shape[2] == 1:
            mask = mask.squeeze(2)
        
        resized_mask = cv2.resize(mask, (target_w, target_h), interpolation=cv2.INTER_NEAREST)
        
        return resized_mask
    
    def get_compatible_resolutions(self) -> list:
        """Return list of all compatible resolutions."""
        return self.OPTIMAL_RESOLUTIONS.copy()
    
    @staticmethod
    def verify_compatibility(height: int, width: int) -> bool:
        """True if both dimensions are divisible by 32 (FLUX requirement)."""
        return (height % 32 == 0) and (width % 32 == 0)