Spaces:
Running
Running
File size: 7,275 Bytes
e0336bc |
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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# Base configuration for scaling bucket options
_BASE_RESOLUTION = 640
_BASE_BUCKET_OPTIONS = [
(416, 960), (448, 864), (480, 832), (512, 768), (544, 704),
(576, 672), (608, 640), (640, 608), (672, 576), (704, 544),
(768, 512), (832, 480), (864, 448), (960, 416),
]
# Cache for generated bucket options to avoid redundant calculations
_generated_bucket_cache = {}
def _round_to_multiple(number, multiple):
"""Rounds a number to the nearest multiple of a given number."""
if multiple == 0:
# Default behavior: round to nearest int. Could also raise an error.
return int(round(number))
return int(multiple * round(float(number) / multiple))
def _adjust_resolution(resolution, divisor=32):
"""
Adjusts a given resolution to the nearest multiple of 'divisor'.
If the input resolution is positive but rounds to 0 (e.g., resolution=10, divisor=32),
it's adjusted to 'divisor'.
If the input resolution is non-positive (<=0), it defaults to 'divisor'.
"""
if resolution <= 0:
return divisor # Default to minimum valid resolution for non-positive inputs
adjusted = _round_to_multiple(resolution, divisor)
# If resolution was positive but _round_to_multiple resulted in 0
# (e.g. input 10 for divisor 32 rounds to 0), ensure it's at least the divisor.
if adjusted == 0:
return divisor
return adjusted
def generate_scaled_buckets(target_resolution_input,
base_resolution=_BASE_RESOLUTION,
base_options=_BASE_BUCKET_OPTIONS,
divisor=32):
"""
Generates scaled bucket options for a target resolution.
The target_resolution_input is first adjusted to the nearest multiple of 'divisor'.
Bucket dimensions are scaled from 'base_options' (which are for 'base_resolution')
to the adjusted target resolution. These scaled dimensions are then rounded to the
nearest multiple of 'divisor' and ensured to be at least 'divisor'.
Args:
target_resolution_input (int): The desired target resolution.
base_resolution (int): The resolution for which 'base_options' are defined.
base_options (list of tuples): A list of (height, width) tuples for 'base_resolution'.
divisor (int): The number that resolutions and bucket dimensions should be multiples of.
Returns:
list of tuples: Scaled and adjusted bucket options (height, width).
"""
# Adjust the target resolution for scaling
actual_target_resolution = _adjust_resolution(target_resolution_input, divisor)
if actual_target_resolution in _generated_bucket_cache:
return _generated_bucket_cache[actual_target_resolution]
# Optimization: If adjusted target resolution matches base resolution.
# This assumes base_options are already compliant with the divisor.
# (Our _BASE_BUCKET_OPTIONS are multiples of 32, so this is fine for divisor=32).
if actual_target_resolution == base_resolution:
options_to_return = list(base_options) # Return a copy
_generated_bucket_cache[actual_target_resolution] = options_to_return
return options_to_return
scaled_options = []
seen_options = set() # To handle potential duplicates after rounding
# Prevent division by zero if base_resolution is 0 (though _BASE_RESOLUTION is 640).
if base_resolution == 0:
# Fallback: return a single square bucket of the target resolution.
# This case should not be hit with current constants.
default_bucket = (actual_target_resolution, actual_target_resolution)
_generated_bucket_cache[actual_target_resolution] = [default_bucket]
return [default_bucket]
scale_factor = float(actual_target_resolution) / base_resolution
for base_h, base_w in base_options:
scaled_h_float = base_h * scale_factor
scaled_w_float = base_w * scale_factor
scaled_h = _round_to_multiple(scaled_h_float, divisor)
scaled_w = _round_to_multiple(scaled_w_float, divisor)
# Ensure minimum dimension is at least the divisor
scaled_h = max(scaled_h, divisor)
scaled_w = max(scaled_w, divisor)
bucket_tuple = (scaled_h, scaled_w)
if bucket_tuple not in seen_options:
scaled_options.append(bucket_tuple)
seen_options.add(bucket_tuple)
# If base_options was empty (not the case for internal use but could be if called externally),
# scaled_options would be empty. Provide a default bucket in such a scenario.
# actual_target_resolution is guaranteed to be >= divisor by _adjust_resolution.
if not scaled_options:
default_bucket = (actual_target_resolution, actual_target_resolution)
scaled_options.append(default_bucket)
_generated_bucket_cache[actual_target_resolution] = scaled_options
return scaled_options
def find_nearest_bucket(h, w, resolution=640):
"""
Finds the nearest bucket for a given height (h) and width (w)
at a specified target resolution.
The 'resolution' parameter is the user's intended target resolution.
This function will:
1. Adjust this resolution to the nearest multiple of 32 (minimum 32).
2. Generate a list of bucket options (height, width pairs) by scaling
predefined base options (for 640px) to this adjusted resolution.
All generated bucket dimensions will also be multiples of 32 and at least 32.
3. Find the bucket from this generated list that is "nearest" to the
aspect ratio of the input h, w. The nearness metric is
abs(input_h * bucket_w - input_w * bucket_h).
Args:
h (int): The height of the image/item.
w (int): The width of the image/item.
resolution (int): The target resolution for which to find buckets.
Defaults to 640.
Returns:
tuple: A (bucket_h, bucket_w) tuple representing the best bucket found.
"""
# generate_scaled_buckets handles the adjustment of 'resolution' internally
# and uses a divisor of 32 by default for its calculations.
# The problem statement implies a fixed divisor of 32 for this tool.
current_bucket_options = generate_scaled_buckets(resolution, divisor=32)
# Failsafe: If generate_scaled_buckets somehow returned an empty list (e.g., if _BASE_BUCKET_OPTIONS was empty),
# provide a default bucket based on the adjusted resolution.
if not current_bucket_options:
adjusted_res_for_fallback = _adjust_resolution(resolution, 32)
return (adjusted_res_for_fallback, adjusted_res_for_fallback)
min_metric = float('inf')
best_bucket = None
# Since current_bucket_options is guaranteed to be non-empty by the check above (or by generate_scaled_buckets's own logic
# when _BASE_BUCKET_OPTIONS is populated), best_bucket will be assigned in the loop.
for (bucket_h, bucket_w) in current_bucket_options:
metric = abs(h * bucket_w - w * bucket_h)
if metric <= min_metric: # Using "<=" preserves original behavior (last encountered wins on ties)
min_metric = metric
best_bucket = (bucket_h, bucket_w)
return best_bucket |