How BBOX_SCALE Controls Layout Detection and Chunk Parsing in Chandra

The BBOX_SCALE setting defines the resolution of the virtual coordinate grid used by Chandra's OCR model, directly determining how bounding box values in HTML attributes are converted to absolute pixel coordinates during layout analysis.

Chandra, an open-source document intelligence pipeline maintained by datalab-to, extracts visual structure by prompting language models to generate HTML markup with data-bbox attributes. The BBOX_SCALE constant defined in chandra/settings.py establishes the fixed canvas size that bridges the model's normalized output with the actual dimensions of source images.

The BBOX_SCALE Virtual Coordinate System

In chandra/settings.py at line 16, BBOX_SCALE is set to 1000 by default. This value represents the width and height of the abstract square canvas on which the model draws bounding boxes.

When the model annotates a document, it outputs HTML elements containing data-bbox attributes with four integers (x0, y0, x1, y1) representing coordinates within this 1000×1000 grid. Because the model operates on this fixed virtual canvas regardless of the input image size, changing BBOX_SCALE directly impacts the granularity of spatial detail the model must predict.

Converting Scaled Coordinates to Pixel Values

The transformation from model coordinates to image pixels occurs in the parse_layout function within chandra/output.py (lines 199-227). This function performs a three-step normalization process using the BBOX_SCALE value:

First, it computes per-pixel scalers by dividing the actual image dimensions by the scale factor:

width_scaler = width / bbox_scale
height_scaler = height / bbox_scale

Next, it multiplies the four scaled values from data-bbox by these scalers to recover absolute pixel positions (lines 221-227):

bbox = [
    max(0, int(bbox[0] * width_scaler)),   # left (x0)

    max(0, int(bbox[1] * height_scaler)), # top (y0)

    min(int(bbox[2] * width_scaler), width),   # right (x1)

    min(int(bbox[3] * height_scaler), height)  # bottom (y1)

]

Finally, the function clamps these values to the image bounds to prevent overflow.

Impact on Layout Detection Granularity

The choice of BBOX_SCALE creates a direct trade-off between coordinate precision and model complexity:

  • Larger values (e.g., 2000) produce smaller per-pixel scalers, allowing the model to specify finer spatial details with higher precision
  • Smaller values (e.g., 500) create coarser granularity that may be easier for the model to predict but result in less precise bounding boxes

According to the source code in chandra/output.py, the width / bbox_scale and height / bbox_scale calculations ensure that regardless of the scale setting, the final pixel coordinates remain accurate relative to the original image dimensions.

Propagation Through Chunk Parsing

The parse_chunks function in chandra/output.py (lines 42-45) acts as a thin wrapper around parse_layout, ensuring the same BBOX_SCALE value propagates through the entire pipeline:

def parse_chunks(html: str, image: Image.Image, bbox_scale=settings.BBOX_SCALE):
    layout = parse_layout(html, image, bbox_scale=bbox_scale)
    chunks = [asdict(block) for block in layout]
    return chunks

This guarantees that bounding boxes embedded in the final JSON chunks correspond exactly to the pixel coordinates of the source image, maintaining consistency between layout detection and downstream chunk consumption. The setting is also passed to the model backends in chandra/model/vllm.py and chandra/model/hf.py to ensure the language model generates coordinates in the correct scale.

Implementation Examples

Inspecting the Current Scale

To verify the active configuration:

from chandra import settings

print("Current BBOX_SCALE:", settings.BBOX_SCALE)  # Output: 1000

Manual Parsing with Custom Scale

Override the default scale when processing specific images:

from chandra.output import parse_chunks
from PIL import Image

img = Image.open("document.png")
html = "<div data-label='Header' data-bbox='0 0 1000 200'>Content</div>"

# Use a finer 2000-grid for higher precision

chunks = parse_chunks(html, img, bbox_scale=2000)
print(chunks[0]["bbox"])  # Pixel coordinates scaled appropriately

API Integration

When using the high-level API, the system respects settings.BBOX_SCALE automatically:

from chandra.scripts.run_app import run_ocr_on_image

result = run_ocr_on_image("invoice.pdf")
print(result["chunks"][0]["bbox"])  # Accurate pixel coordinates

Summary

  • BBOX_SCALE in chandra/settings.py defines the virtual 1000×1000 coordinate grid used by the OCR model
  • parse_layout in chandra/output.py converts scaled data-bbox values to pixels using width / BBOX_SCALE and height / BBOX_SCALE calculations at lines 199-227
  • parse_chunks propagates the same scale setting through to JSON output to ensure coordinate accuracy
  • The model backends in chandra/model/vllm.py and chandra/model/hf.py receive this setting to maintain consistent output formatting
  • Changing the scale requires coordinated updates to the model prompt or fine-tuning, as the conversion logic automatically adapts to the configured value

Frequently Asked Questions

What is the default BBOX_SCALE value in Chandra?

The default value is 1000, defined in chandra/settings.py at line 16. This creates a 1000×1000 virtual canvas for the model to specify bounding box coordinates.

How does increasing BBOX_SCALE improve layout detection?

Larger values reduce the per-pixel scaler (width/scale), meaning each integer increment in the model's output represents a smaller physical distance in the source image. This allows the model to define tighter, more precise bounding boxes around document elements.

Where does the coordinate conversion happen in the codebase?

The conversion from scaled to pixel coordinates occurs in the parse_layout function within chandra/output.py (lines 199-227), which computes scalers and transforms the four data-bbox integers into absolute pixel positions.

Can I use different BBOX_SCALE values for different documents?

Yes. While settings.BBOX_SCALE provides the global default, you can pass a custom bbox_scale parameter directly to parse_layout or parse_chunks for individual processing calls, as shown in the implementation examples above.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →