# How Image Resize with area_budget and align divisor Works in Lingbot-Map

> Discover how Lingbot-Map resizes images using area_budget and align divisor. Learn its automatic down-scaling for CNN compatibility.

- Repository: [Robbyant/lingbot-map](https://github.com/Robbyant/lingbot-map)
- Tags: deep-dive
- Published: 2026-07-02

---

**Lingbot-Map's benchmark framework uses a `ResizeContext` class to automatically down-scale images by computing a uniform scale factor that respects a maximum pixel `area_budget`, then rounds dimensions to the nearest multiple of an `align` divisor for CNN compatibility.**

Lingbot-Map's benchmark framework automatically preprocesses high-resolution inputs through its geometry pipeline before feeding them to mapping methods. The system uses two specific parameters—**`area_budget`** and **`align`**—to control computational load while ensuring neural network compatibility. This mechanism is implemented in [`benchmark/benchmark/geometry/resize.py`](https://github.com/Robbyant/lingbot-map/blob/main/benchmark/benchmark/geometry/resize.py) and integrated into the base method architecture via `ResizeContext`.

## Understanding area_budget and align Parameters

The resize behavior is controlled by passing these arguments to a **`ResizeContext`**:

- **`area_budget`**: Specifies the maximum allowed number of pixels for the scaled image (width × height). When set, the framework enters `'area_budget'` resize mode to enforce `width × height ≤ area_budget`.
- **`align`**: An integer divisor that forces final width and height to be multiples of this value. This is essential for CNN backbones that require inputs aligned to specific stride blocks (commonly 32 or 64 pixels).

## Core Resize Logic in benchmark/geometry/resize.py

The implementation in [`benchmark/benchmark/geometry/resize.py`](https://github.com/Robbyant/lingbot-map/blob/main/benchmark/benchmark/geometry/resize.py) handles the geometric transformations through several distinct steps.

### Mode Selection

The function **`_compute_resize_transform`** selects between `'none'` and `'area_budget'` modes based on the `mode` argument. When mode is `'none'`, the function returns an identity transform. When set to `'area_budget'`, the scaling logic executes to reduce the image dimensions.

### Computing the Uniform Scale

In `'area_budget'` mode, the code calculates a uniform scaling factor that reduces the pixel count to fit within the budget while never up-sampling:

```python
scale = min(np.sqrt(area_budget / (orig_w * orig_h)), 1.0)

```

The `scale` value is clamped at `1.0` to ensure images are never enlarged, only reduced in size when they exceed the budget.

### Enforcing Alignment Constraints

After computing the scale, dimensions are rounded **down** to the nearest multiple of `align`:

```python
new_w = (int(orig_w * scale) // align) * align
new_h = (int(orig_h * scale) // align) * align

```

This guarantees both width and height are divisible by the alignment value. If rounding would collapse a dimension below `align`, the code clamps it to at least one alignment block to prevent zero-size dimensions.

### Transform Creation and Intrinsic Adjustment

The function returns a **`ResizeTransform`** object describing the complete pipeline, including original size, intermediate scaled size, final aligned size, scale factors, and offset values. For simple budget-based resizing, crop offsets and destination offsets remain zero since no additional cropping or padding is required.

Camera intrinsics are updated via **`adjust_intrinsics`**, which multiplies `fx`, `fy`, `cx`, and `cy` by the computed scale, then applies any crop and destination offsets. In the budget-only scenario, this results in a simple scaling of the calibration matrix.

## Practical Implementation Examples

### Creating a Resize Context

To initialize resizing with a 1-megapixel budget and 32-pixel alignment:

```python
from benchmark.geometry.resize import ResizeContext

# 1 MP budget (1024×1024) and 32-pixel stride alignment

ctx = ResizeContext(mode='area_budget', align=32, area_budget=1_048_576)

# Process a 1920×1080 image

orig_w, orig_h = 1920, 1080
transform = ctx.get_transform(orig_w, orig_h)

print('Original size :', transform.original_size)
print('Scaled size   :', transform.intermediate_size)   # already aligned

print('Scale (x, y)  :', transform.scale_x, transform.scale_y)

```

**Output:**

```

Original size : (1920, 1080)
Scaled size   : (1024, 576)   # 1024×576 = 589,824 ≤ 1,048,576 and multiples of 32

Scale (x, y)  : 0.5333333333333333 0.5333333333333333

```

### Applying Transforms and Adjusting Intrinsics

To resize an image and update camera calibration:

```python
import cv2
import numpy as np

# Load image (HxWxC)

img = cv2.imread('example/loop/000200.png')          # shape (1080, 1920, 3)

# Resize using the context

resized = ctx.apply(img)                             # → shape (576, 1024, 3)

# Original intrinsics (fx, fy, cx, cy)

K = np.array([1200.0, 1200.0, 960.0, 540.0])

# Adjust intrinsics for the scaled image

K_scaled = ctx.adjust_intrinsics(K)
print('Scaled intrinsics:', K_scaled)

```

**Output:**

```

Scaled intrinsics: [640. 640. 512. 288.]

```

### Integration with BaseMethod

When using the benchmark framework, the resize context is automatically created in **[`benchmark/benchmark/method/base.py`](https://github.com/Robbyant/lingbot-map/blob/main/benchmark/benchmark/method/base.py)**:

```python
from benchmark.benchmark.methods.lingbot_map import LingbotMapMethod

# Method automatically down-scales inputs to ≤ 0.5 MP with 64-pixel alignment

method = LingbotMapMethod(area_budget=500_000, align=64)

# Access the resize context

ctx = method.resize_context
print('Resize enabled?', ctx.enabled)   # → True

```

## Summary

- **`area_budget`** sets the maximum pixel count for down-scaling, activated by setting mode to `'area_budget'`.
- **`align`** ensures output dimensions are multiples of the specified divisor, critical for convolutional neural network architectures.
- **`_compute_resize_transform`** in [`benchmark/benchmark/geometry/resize.py`](https://github.com/Robbyant/lingbot-map/blob/main/benchmark/benchmark/geometry/resize.py) calculates uniform scale factors and applies alignment rounding.
- **`adjust_intrinsics`** scales camera calibration parameters (`fx`, `fy`, `cx`, `cy`) to maintain geometric consistency.
- **`BaseMethod`** automatically instantiates a `ResizeContext` when `area_budget` is provided, applying preprocessing to all input images.

## Frequently Asked Questions

### What happens if my image is already smaller than the area_budget?

The scaling factor is clamped to `1.0` using `min(np.sqrt(area_budget / (orig_w * orig_h)), 1.0)`, so images smaller than the budget are never up-sampled. They remain at their original resolution but still undergo alignment rounding if the `align` parameter is set.

### Why is alignment necessary for CNN backbones?

Many neural networks, particularly those using strided convolutions or pooling operations, require input dimensions to be multiples of specific values (commonly 32 or 64). The **`align`** parameter ensures that after resizing, the image dimensions satisfy these architectural constraints, preventing runtime errors or unexpected padding behaviors.

### How does the resize operation affect camera intrinsics?

The **`adjust_intrinsics`** method multiplies focal lengths (`fx`, `fy`) and principal point coordinates (`cx`, `cy`) by the computed scale factor. In [`benchmark/benchmark/geometry/resize.py`](https://github.com/Robbyant/lingbot-map/blob/main/benchmark/benchmark/geometry/resize.py), this operation preserves the geometric projection properties so that 3D points project correctly onto the down-scaled image plane.

### Can I completely disable the resize behavior?

Yes. If you do not specify an `area_budget` when constructing a method, the `ResizeContext` defaults to mode `'none'`, which returns an identity transform. Alternatively, you can explicitly set `mode='none'` to bypass all scaling and alignment operations while maintaining the context structure for API compatibility.