Ultralytics YOLO27: Join the waitlist.
License YOLO
Guides

Small object detection with image tiling in Ultralytics YOLO, from Python to Rust

See when image tiling helps YOLO detect small objects, compare its costs, and run built-in tiled inference in Python or Rust without SAHI.

Onuralp SezerOnuralp Sezer14 min read

Listen to this article

Aerial view of a busy intersection with cars boxed by Ultralytics YOLO26s using 640-pixel tiles, dashed lines marking the tile seams

Why does a detector that works fine on ordinary photos miss most of the people in a drone frame? Often because it never sees them at full size. At the default imgsz=640, Ultralytics YOLO shrinks a 1920 x 1080 frame three times before the model looks at it, so a pedestrian 15 pixels tall ends up about 5 pixels tall.

Image tiling can preserve that small-object detail by running the model on overlapping windows of the full-resolution image and merging the results. On one VisDrone frame in our docs, it took Ultralytics YOLO26s from 35 detections to 161. The price is more work per frame: eight tile inputs instead of one image for that frame.

Tiling is now built in on both sides of a deployment. The Python package has had a tile argument since Ultralytics 8.4.204 on November 6, and the Rust crate added a --tile flag in Ultralytics Inference 0.0.57 on November 9. This guide covers when tiling recovers small objects, what it costs, and how to run it in each.

Resizing is what hides small objects#

Small object detection is hard for a simple reason: an object only has the pixels it has. An object detection model learns shapes from a handful of pixels per object, and when the whole frame is shrunk to 640 pixels on its longest side, distant people, vehicles and defects drop below what the network can separate from texture and noise.

You see this most in aerial imagery, satellite image analysis, wide-angle traffic and security cameras, and high-resolution inspection photos. VisDrone is a widely used public aerial benchmark: its docs describe objects that are often only a few dozen pixels in frame, in dense, heavily occluded groups.

Tiling keeps those pixels. With tile set to the model's input size, the model sees several windows at native resolution instead of one pass over a shrunken frame. The idea is the one the SAHI paper calls slicing-aided hyper inference, which its authors evaluated on the VisDrone and xView aerial datasets.

In the High Resolution Tiling guide, YOLO26s on a 1920 x 1080 VisDrone frame at the default confidence threshold returns 35 detections on the whole image at imgsz=640, and 161 with tile=640, including distant pedestrians, cyclists and cars. The guide shows both images side by side. Those are prediction counts, not a scored evaluation. The same weights produced both counts; tiling also changed the context each window saw.

The same aerial intersection twice: a few cars boxed after the whole frame is resized to 640 pixels, most cars boxed with 640-pixel tiles

A second 1920 x 1080 VisDrone validation frame, YOLO26s at the default confidence: 8 detections on the whole frame at imgsz=640, 128 with tile=640. Crop of the road shown, boxes without class labels.

Tiling adds one model input per window#

The guide defines the windowing precisely. Windows overlap by a base 20%, and the last row and column align to the image boundary, so edge windows can overlap more. An image smaller than the window gets a single full-image window.

That makes the cost easy to predict. A 1920 x 1080 frame at tile=640 runs eight windows, four across and two down. A 3840 x 2160 frame at tile=1280 also runs eight. Applying the same 20% rule to a 3840 x 2160 frame at tile=640 gives eight columns and four rows, 32 windows, so halving the window size on a 4K frame quadruples the tile inputs.

A 1920 x 1080 aerial frame divided into eight overlapping 640 x 640 windows, with the overlap bands shaded

The eight windows tile=640 uses on a 1920 x 1080 frame: columns start at x = 0, 512, 1024 and 1280, rows at y = 0 and 440. Shaded bands are seen by more than one window.

Batching changes how those inputs reach the model. In Python, batch sets how many windows run through the model together in one inference call, and the Rust crate does the same with --batch on a dynamic-batch export. Batching can improve throughput, but measure latency on your hardware; our guide to batch inference with Ultralytics YOLO explains why.

Here is how tiling compares with the other ways to give small objects more pixels:

ApproachModel inputs for a 1920 x 1080 frameWhat the model seesBest when
Whole image, imgsz=6401Frame shrunk 3xObjects stay clearly visible after resizing
Whole image, imgsz=12801, at roughly 4x the compute of 640Frame shrunk 1.5xMemory and latency allow a larger input
tile=True with imgsz=6408Native-resolution 640-pixel windowsImages several times larger than the model input
tile=400 with imgsz=640More than 8Windows enlarged to 640Objects only a few pixels wide even at native resolution

Each tile is a normal-sized model input, so tiling handles very large images without raising imgsz. Source-sized outputs such as masks and depth maps still need memory proportional to the original image, and max_det limits how many instance masks are kept.

When tiling helps, and when it hurts#

Tiling helps when objects are small relative to the frame and the frame is much larger than the model input. Drone and satellite imagery, overhead traffic, crowds, retail shelves photographed wide, and visual inspection images of whole boards or panels all fit.

It hurts when objects are large. A window only sees part of a truck that fills half the frame, and the guide is plain about it: objects larger than the overlap can still be split because no window sees them whole, and merging does not join separate fragments. It also removes global context, which matters most for classification and depth, where tile outputs may not agree across the image.

Smaller windows bring one more trade-off. Enlarging a 400-pixel window to 640 helps with objects only a few pixels wide, but it also enlarges background clutter, which can turn into false positives. Compare two or three tile sizes on your own validation set and watch precision as well as recall.

How tiles merge back into one result#

Each window produces its own predictions in window coordinates. Tiling maps them back to the source image and returns one Results object per original image, so downstream code that reads result.boxes works unchanged.

Overlap means an object near a seam can be detected twice. For detection, duplicates are suppressed with non-maximum suppression using your iou, agnostic_nms and max_det settings, so our guide to tuning the NMS IoU threshold applies directly to tiled output. A box cut by a window edge is dropped when another window sees the object clear of its edges and a kept box covers it, so the seam keeps the more complete prediction. A cut box with no better match is kept.

Boxes from neighboring windows at a seam: cut boxes from the top-row windows beside whole boxes from the bottom-row windows, then one box per car after merging

An illustration of seam handling at y = 640 in the same frame. Cyan boxes are cut by the top-row windows' bottom edge, magenta boxes come from the bottom-row windows, and the right panel shows the more complete box kept for each car after merging.

Every Ultralytics YOLO task merges in its own way:

  • Detect, segment, pose and OBB restore boxes, masks, keypoints or rotated boxes to source coordinates and apply box selection, with rotated NMS for OBB.
  • Semantic segmentation stitches class maps, choosing the most central tile where windows overlap.
  • Depth blends overlapping depth maps with weights that increase toward each tile's center.
  • Classify averages tile probabilities.

The Python pipeline also tiles YOLO-World, YOLOE, RT-DETR and FastSAM prediction. SAM keeps its own crop pipeline and doesn't accept tile.

Tiled inference in Python with the tile argument#

Tiling needs Ultralytics 8.4.204 or later and no extra package. In predict mode, pass tile=True to use imgsz as the window size, or an integer to set the window size separately:

from ultralytics import YOLO

model = YOLO("yolo26s.pt")

# Baseline: one pass over the frame shrunk to 640 pixels
full = model.predict("intersection.jpg", imgsz=640)

# Tiled: 640-pixel windows at native resolution, 8 windows per batch
tiled = model.predict("intersection.jpg", imgsz=640, tile=640, batch=8)

print(len(full[0].boxes), len(tiled[0].boxes))

The same argument works from the CLI with yolo predict model=yolo26s.pt source=large-image.tif tile=640 imgsz=640 batch=8. For video, pass stream=True to keep memory bounded. In track mode, the tracker runs once per frame on the merged predictions.

Tiling isn't only for prediction. When your images are much larger than imgsz, train and validate with the same setting:

  1. Train on tiles with model.train(data="aerial.yaml", tile=True, imgsz=640). Labels are clipped to each window, instances with less than 10% of their original box area left are removed, and empty windows stay in as negative examples.
  2. Validate on merged results with model.val(data="aerial.yaml", tile=True, imgsz=640). Predictions are merged before they are scored against the original annotations, so mAP reflects what you'll deploy.
  3. Cache large sources with cache="ram", since training shuffles tiles and would otherwise decode the same large image many times.
  4. Compare against a whole-image baseline on the same validation split before you keep the tiled model. For more ways to lift scores on tiny objects, see our small-object mAP checklist.

Training on tiles shows the model objects at the scale it will see during tiled prediction. Our drone detection dataset guide walks through this full train, tile and evaluate loop on 4K frames.

Exported models tile too. Tiling runs in the Python prediction pipeline, so a model exported to ONNX or TensorRT keeps its normal input graph, and YOLO("yolo26s.onnx").predict(source, tile=640) works as usual, including fixed export batch sizes and the last partial batch.

The VisDrone dataset page in Ultralytics Platform showing its aerial images

The VisDrone dataset in Ultralytics Platform, a ready-made benchmark for testing tile sizes before your own data is labeled.

To try tile sizes on public data first, start from the VisDrone dataset on Ultralytics Platform. To track a tiled run alongside your baseline, copy the Local Training command from the Platform training dialog, set project to your baseline's Platform project, and add tile=True to stream the tiled run's metrics there.

Tiled inference in Rust with the new --tile flag#

Ultralytics Inference is our Rust crate and CLI for running exported ONNX models through ONNX Runtime and OpenVINO IR models natively, with no Python runtime at inference time. It targets servers, containers and edge devices on CPU, CUDA, TensorRT, CoreML, OpenVINO and other execution providers. Version 0.0.57 brings the same tiling approach as Python: a base 20% window overlap, results mapped back to the source image, duplicates removed per class.

A traffic camera and a compact edge computer mounted on a pole above a highway interchange at golden hour

Illustrative. A roadside camera with an edge computer, the kind of Python-free deployment the Rust crate targets.

The team checked tiled output against Ultralytics Python on ONNX Runtime CPU, OpenVINO CPU, GPU and NPU, CUDA and TensorRT, for images, video and webcam. Detection, segmentation, semantic segmentation, depth, classification, pose and OBB models are all supported.

From the CLI, add --tile with a window size, or pass it bare to use --imgsz. Exporting the model is a one-time Python step; inference itself needs only the Rust binary:

# One-time export in Python: a dynamic-batch ONNX model so several windows run per call
pip install -U "ultralytics[export-base]>=8.4.204"
yolo export model=yolo26s.pt format=onnx dynamic=True

# Install the Rust CLI
cargo install ultralytics-inference

# Tiled inference on a large image: 640-pixel windows, 8 per batch
ultralytics-inference predict --model yolo26s.onnx --source aerial.jpg --tile 640 --batch 8

As a library, set tiling once in InferenceConfig and every predict call is tiled. with_tile(0) uses the inference size:

use ultralytics_inference::{InferenceConfig, YOLOModel};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = InferenceConfig::new().with_tile(640);
    let mut model = YOLOModel::load_with_config("yolo26s.onnx", config)?;
    let results = model.predict("aerial.jpg")?;
    if let Some(ref boxes) = results[0].boxes {
        println!("{} detections", boxes.len());
    }
    Ok(())
}

Duplicate boxes across windows are removed with per-class NMS at the configured --iou, which defaults to 0.5 as of the same release, matching Ultralytics prediction defaults. Tiling stays off by default, because every window is another model input. Enable it where detail matters, measure inference latency on the target device, and pick the execution provider that fits it; our ONNX vs TensorRT comparison helps on NVIDIA hardware, and the NVIDIA Jetson guide covers on-device setup.

SAHI users can switch to the built-in tile argument#

If you already slice images with the SAHI library, the built-in argument does the same job inside Ultralytics, without a second package or a separate results format. Our SAHI integration guide is now deprecated in favor of tile.

Moving a SAHI script over is mostly a change of arguments:

SAHIBuilt-in tiling
AutoDetectionModel.from_pretrained(model_type="ultralytics", ...)YOLO("yolo26s.pt")
get_sliced_prediction(image, model, slice_height=256, slice_width=256, ...)model.predict(image, tile=256)
overlap_height_ratio, overlap_width_ratioFixed base 20% overlap, edge windows aligned to the image boundary
SAHI PredictionResultStandard Ultralytics Results

Windows are square, and overlap and merging follow the rules described above rather than SAHI's configurable postprocessing, so expect similar rather than identical boxes. Re-check your thresholds on a validation set after switching.

The practical difference is integration. tile plugs into Ultralytics training, validation, prediction and tracking with the same argument, returns standard Results objects, and has a matching option in the Rust crate, so a tile size you validated in a notebook carries over to a Rust binary on the edge.

Who should turn tiling on#

Turn tiling on if you run Ultralytics YOLO on images several times larger than the model input and your validation shows missed small objects: drone and satellite teams, traffic and security integrators with wide-angle cameras, and inspection teams photographing whole boards, panels or shelves. Rust users get the same tiling approach on edge hardware without a Python runtime.

Leave it off when objects are large relative to the frame, when a larger imgsz already recovers them in one pass, or when the latency budget can't absorb several tile inputs per frame. In every case, compare tiled and whole-image results on the same validation split at the same confidence threshold, looking at recall, false positives and latency, and keep tiling only where its gain is worth the extra work.

Ultralytics Platform keeps that comparison in one place: label your own high-resolution images or start from VisDrone, train a whole-image baseline on cloud GPUs, train another baseline at a larger imgsz, and log a tiled run from your own machine into the same project. The High Resolution Tiling guide details the tiling options, and the Ultralytics Inference docs cover the Rust deployment.

Both the Python package and the Rust crate are available under AGPL-3.0. If tiled detection is going into a closed-source product or an edge device you ship to customers, an Ultralytics Enterprise License permits commercial use of the covered Ultralytics software without AGPL-3.0 source-disclosure requirements, subject to its terms.

Frequently asked questions

  • Image tiling splits a large image into overlapping windows, runs the detector on each window, and merges the boxes back onto the original image. When the window matches the model input size, small objects keep pixels they would lose if the whole image were resized. In Ultralytics YOLO, the tile argument does this with a base 20% overlap and returns one result per image.

  • First check whether resizing is the problem: if objects shrink to a few pixels at imgsz=640, test a larger imgsz or tile=True. Train with the same setting you'll predict with, so the model learns objects at their deployed scale, and validate per size and class. Tiling suits images several times larger than the model input.

  • It adds one model input per window: a 1920 x 1080 frame at tile=640 produces eight tile inputs instead of one image. Running several tiles in one batched call, with batch in Python or --batch with a dynamic-batch export in Rust, can improve throughput. Measure latency on your target hardware.

  • No. Since Ultralytics 8.4.204, the built-in tile argument handles sliced inference without an extra package, and it also tiles training, validation and tracking. The SAHI guide in our docs is deprecated in favor of tile.

  • Yes. In Python, exported models keep their normal input graph and you pass tile to predict as usual. In Rust, Ultralytics Inference 0.0.57 adds --tile to the CLI and with_tile to InferenceConfig, checked against Python on ONNX Runtime CPU, OpenVINO, CUDA and TensorRT.

  • Start with tile=True, which uses imgsz as the window size. Use a smaller window, such as tile=400 with imgsz=640, when objects are only a few pixels wide even at native resolution, and a larger one when objects risk being cut at seams. Compare a few sizes on your validation set, watching both recall and false positives.

Written by

Onuralp Sezer
Onuralp Sezer

Senior Machine Learning Engineer · Ultralytics

Onuralp Sezer is a Senior Machine Learning Engineer at Ultralytics, where he works on advancing computer vision technologies such as YOLO and contributes to open-source development. A long-time Google Developer Group (GDG) organizer and open-source contributor, he is active in the developer community as a speaker focused on machine learning and computer vision, and has previously contributed to the Fedora Project.

Comments

Ask a question, share your results, or help someone else.

Explore solutions

Computer vision in sports

Computer vision in sports

Discover how Ultralytics YOLO powers sports vision with player and ball tracking, performance analytics, coaching insights, and athlete rehabilitation.
Learn more
Computer vision for aerial imagery

Computer vision for aerial imagery

Turn drone and aerial imagery into real-time insights for agriculture, aquaculture, conservation, and geospatial analysis with Ultralytics YOLO.
Learn more
Computer vision in aerospace

Computer vision in aerospace

Apply computer vision in aerospace with Ultralytics YOLO for aircraft inspection, runway FOD detection, airport ground operations, and composite defect detection.
Learn more
Computer vision in security

Computer vision in security

Protect every site with Ultralytics YOLO models. Vision AI powers perimeter monitoring, threat detection, license-plate recognition, and workplace safety.
Learn more
Computer vision in robotics

Computer vision in robotics

Power smarter machines with Ultralytics YOLO models. Vision AI in robotics drives autonomous navigation, perception, object tracking, and real-time control.
Learn more
Computer vision in logistics

Computer vision in logistics

Streamline logistics with Ultralytics YOLO models. Vision AI enables package inspection, sorting, vehicle tracking, and real-time warehouse safety monitoring.
Learn more
Computer vision in retail

Computer vision in retail

Reimagine retail with Ultralytics YOLO models. Vision AI powers inventory tracking, shelf monitoring, queue management, and smarter customer insights.
Learn more
Computer vision in healthcare

Computer vision in healthcare

Build healthcare solutions with Ultralytics YOLO models. Vision AI in healthcare powers faster medical imaging, smarter diagnostics, and patient monitoring.
Learn more
Computer vision in manufacturing

Computer vision in manufacturing

Optimize manufacturing with Ultralytics YOLO models. Vision AI drives quality control, defect detection, PPE compliance, and assembly line automation.
Learn more
Computer vision in automotive

Computer vision in automotive

Apply computer vision in automotive with Ultralytics YOLO models. Vision AI elevates road safety, driver assistance, and vehicle automation for smarter roads.
Learn more
Computer vision in agriculture

Computer vision in agriculture

Bring vision AI to smart agriculture with Ultralytics YOLO models. Power crop monitoring, livestock tracking, and precision farming for higher, smarter yields.
Learn more

Let's build the future of AI together!

Begin your journey with the future of machine learning