Spaces:
Running
Running
| # 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 |