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.pydefines the virtual 1000×1000 coordinate grid used by the OCR model - parse_layout in
chandra/output.pyconverts scaleddata-bboxvalues to pixels usingwidth / BBOX_SCALEandheight / BBOX_SCALEcalculations 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.pyandchandra/model/hf.pyreceive 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →