System designExplainer

Non-square image size in YOLO training: rect vs square

Training YOLO on non-square machine vision images: why a two-value image size is ignored, what rect=True does to mosaic, and when to train square instead.

LumaSpec Vision10 min read Share on LinkedIn

On this page 10 sections
  1. 01What imgsz accepts during training
  2. 02How rect=True builds rectangular batches
  3. 03Why mosaic usually earns its padding
  4. 04Worked example: 1024 × 320 frames with 6-pixel defects
  5. 05Letterboxing at inference
  6. 06Choosing the setting
  7. 07The camera side of a 3.2:1 dataset
  8. 08When the usual answer is wrong
  9. 09Checklist before the first epoch
  10. 10Questions

Detector trainers assume square inputs, and machine-vision frames rarely are. A fixed ROI on an area scan camera hands you 1024 × 320 strips; a moving web hands you captures a few thousand pixels wide. Pass the trainer a two-value imgsz such as [1024, 320] and it trains at 1024 square anyway, so the rectangle looks ignored. The fix is a choice between three settings: square training with letterboxed inference, rect=True training on the native rectangle, or tiling wide strips into squares. Each has a pixel and memory cost you can compute before the first epoch, and the arithmetic below settles it for a 3.2:1 dataset.

What imgsz accepts during training

Training reads one integer: the long-side target. YOLOv8 and YOLO11 trainers keep 1024 from your [1024, 320] and letterbox every frame into a 1024 × 1024 tensor. The long side scales to target, the short side fills with a neutral gray band, and nothing stretches — content geometry survives, only the canvas is square.

Both sides of anything the network sees must be a multiple of the stride, 32. Your strip already qualifies: 1024 = 32 × 32 and 320 = 32 × 10. The rectangle is legal network geometry; the square canvas is simply the trainer's default packing, kept because the default augmentation needs that canvas.

The cost of the packing is pure padding:

aspect a = W / H square side S = W (the long side) gray pad rows = S - H wasted fraction = (S - H) / S tensor pixels = S x S ; content pixels = W x H

The waste depends only on the aspect ratio. At 3.2:1 you lose 704 / 1024 = 68.75% of every tensor to gray, whether you train at 1024 or at 640.

How rect=True builds rectangular batches

rect=True replaces the square loader with a batch builder that sorts frames by aspect ratio and pads each batch to one shared rectangle instead of a square. On a uniform dataset — every frame the same fixed ROI — every batch lands on exactly 1024 × 320: zero padding, native pixel scale, defects exactly the size you annotated them.

The price is fixed: rect training disables mosaic augmentation, because the two loaders are incompatible by construction. That is why the standard recommendation is to leave rect off unless memory forces it.

Why mosaic usually earns its padding

Mosaic stitches four frames onto one square canvas at the target size. In a single step the model sees objects at four scales and in neighbor combinations no real frame would show it. On sparse datasets — aerial imagery is the published case — it is reported as the strongest augmentation in the stack, lifting accuracy exactly where inspection data is thin.

The gray padding it forces is dead compute, not lost accuracy. The band carries no objects and the detector reads it as background. The square default therefore trades multiply-adds you do not need for an augmentation you do. When mosaic is off, put the diversity back with horizontal flips, scale jitter, and synthetic exposure augmentation for realistic camera images so brightness drift does not blind the model.

Worked example: 1024 × 320 frames with 6-pixel defects

Inputs:

  • Frame size: 1024 × 320 px (width × height), one fixed ROI; aspect 1024 / 320 = 3.2.
  • Smallest feature that must be found: 6 px across on native frames, measured on the annotated images.
  • Candidate square sides: 1024 px and 640 px, both multiples of 32.
  • Memory ceiling: whatever batch the GPU holds at 1024 square.

The 4 px detection floor below comes from the same pixel-count logic that sizes the camera itself; camera resolution for defect detection works it from the sensor side.

The frames can be a crop from a 12 MP USB3 area scan camera:

Area scan cameras12 MP USB3 area scan camera, IMX226, 1/1.7" rolling shutter, 30 fps, 4000 × 3000 resolution (LS-CMR-0166)LS-CMR-0166
Sensor format
1/1.7"
Interface
USB3
Resolution
12 MP
Shutter
Rolling
$560.00View partBuild a system with it

That sensor sends 4000 × 3000 frames over USB3 at 30 fps. A fixed inspection ROI then crops each one down to the 1024 × 320 strip you annotate, and the crop is what the dataset stores.

Square at 1024:

  • pad rows = 1024 − 320 = 704, split into 352 rows top and bottom
  • waste = 704 / 1024 = 68.75%
  • tensor pixels = 1024 × 1024 = 1,048,576 against 327,680 of content
  • the 6 px defect stays 6 px — comfortable against the 4 px floor

Square at 640:

  • scale = 640 / 1024 = 0.625
  • the strip becomes 640 × 200 (320 × 0.625 = 200); pad = 640 − 200 = 440 rows; waste = 440 / 640 = 68.75%, unchanged
  • the defect becomes 6 × 0.625 = 3.75 px — below the floor, so 640 is out

rect at 1024:

  • every batch is exactly 1024 × 320 = 327,680 px, no padding, defect 6 px, mosaic off
  • pixels per frame drop by 1,048,576 / 327,680 = 3.2×, so activation memory and step time fall by roughly 3× — a first-order estimate, since memory tracks pixel count. A card that held batch 8 at the square holds about 25 at the rectangle (8 × 3.2 = 25.6)

Decision, by threshold: the 6 px feature forbids downscaling, so 640 is rejected on pixels, not preference. If the GPU holds 1024-square batches, train square with mosaic and letterbox at inference — the default and the best-accuracy path. If it does not, set imgsz=1024 with rect=True and replace mosaic with flips, scale jitter and exposure augmentation. Had the smallest feature been 10 px or larger, the arithmetic flips: 10 × 0.625 = 6.25 px survives 640, and the smaller square with mosaic on is usually the better accuracy per training hour.

If the ROI itself is still an open decision, size the camera first:

Camera resolution for the smallest feature
Units
Pixels needed
–
Sensor size
– MP
Smallest sensor that fits
–
Pixel on the part
– mm
Pixels across the feature, that sensor
–

Pixels needed = field / feature × pixels per feature on each axis (field incl. margin). The lens must resolve the pixels too: Nyquist = 1000 / (2 × pixel µm) lp/mm.

Find cameras with enough pixels Open the full calculator

Letterboxing at inference

Prediction letterboxes whatever you feed it: scale to the target, pad the short side with the same gray, never stretch. Run the model at imgsz=1024 and your strip reaches the network at native scale inside a 1024 × 1024 tensor — exactly the packing the square-trained model learned, gray bands included. Validation needs no help: it batches frames by aspect ratio on its own, so metrics come from letterboxed rectangles however you trained.

Two rules hold the pipeline together. First, identical letterbox logic on both sides — same scaling, same fill, no stretching anywhere. Second, feed the network the original pixels: resize inside your pipeline from the raw frame, never from an already-scaled preview, and start from the raw camera buffer rather than a rendered copy.

The head is also fully convolutional, so a deployment can feed the unpadded 1024 × 320 tensor directly — both sides divide by 32 — and skip the padding compute, provided the scaling and fill match training. That saving is the same two-thirds of the tensor the square trainer wasted.

Choosing the setting

Path Setting Mosaic Tensor waste Pick it when
Square, letterbox at inference imgsz=1024 on 69% Default: memory holds the square batch
Rectangular batches imgsz=1024, rect=True off none Memory-bound and small features need native scale
Smaller square imgsz=640 on 69% Smallest feature 10 px native or larger (10 px becomes 6.25 px)
Tiled squares crop with overlap at least the largest object on none Strips around 3000 × 350 and wider

For a uniform 1024 × 320 ROI the order of preference is: square 1024 with mosaic; rect at 1024 when memory says no; 640 square only when the pixel budget allows it; tiling when the strip is a web capture thousands of pixels wide.

The camera side of a 3.2:1 dataset

Wide training sets have three usual sources: a fixed ROI cropped from an area scan camera (the case above), an area scan camera run in line-scan mode over a moving web, and true line scan cameras whose frames are long strips — grain sorting with a line scan camera is a worked instance of the last. Training geometry does not care which source; the pixel budget does, because a web strip's height is the line height while its width grows with the capture.

For wider webs and for models that must run on the device, two parts fit:

Area scan camerasHikrobot MV-CH250-25TC-F-NF 25 MP 10GigE area scan camera, PYTHON25K, Color, F-mount, With fanMV-CH250-25TC-F-NF
Sensor format
APS-H
Lens mount
F-mount
$7,349.00 · quotedView and quote
Smart cameras & vision sensorsSC / SC smart camera (X86 platform), 8.8 MP, USB3, Color/Mono, global shutter (LS-SMC-0017)LS-SMC-0017
Interface
USB 2.0
Resolution
6-10 MP
$1,359.00View part

The MV-CH250-25TC-F-NF is a 25 MP 10GigE camera on an F-mount, suited to wide fields at high frame counts. The LS-SMC-0017 is an 8.8 MP smart camera with a C-mount and a global shutter that runs inference beside the sensor; its letterbox must match training exactly, because there is no second chance to resize on the device. Wide sensors also raise the lens question — the image circle has to cover the format — so machine vision lenses belong in the same decision. The camera finder filters by sensor format and mount, and the configurator walks the chain with the lens attached.

CamerasArea scan cameras

GigE, USB3, 10GigE and CoaXPress

1,287 models · from $149.00

When the usual answer is wrong

  • Aspect ratios beyond about 4:1. A 3000 × 350 web capture (aspect ≈ 8.6) pads about 88% of every square tensor, and mosaic stitches four nearly empty strips that teach little. Tile, or use rect.
  • Thin datasets. A few hundred frames is exactly where mosaic's four-image stitching adds scene diversity you cannot annotate; switching it off hurts most here.
  • Mixed ROIs. If the dataset mixes aspect ratios — the ROI changed mid-project — rect batches go ragged and per-batch padding returns. Re-crop everything to one ROI, or train square.
  • Pre-scaled images. Frames saved already resized bake in interpolation and can alias small features away before training sees them; antialiasing when you downsample or warp images covers what that costs. Store native frames and resize in the loader.

Checklist before the first epoch

  • Both frame sides are multiples of the stride, 32.
  • One ROI for the entire dataset; mixed ratios defeat rect batching.
  • Smallest feature at least 4 px after scaling — at a 640 square that needs 6.4 px native (4 / 0.625).
  • Letterbox in both training and inference: same scale, same gray fill, never a stretch.
  • Mosaic on unless you chose rect; if rect, add flips, scale jitter and exposure augmentation.
  • Read the mAP from the aspect-batched validation logs, not from square preview renders.
  • Store native frames; resize only inside the pipeline.

If camera, ROI and lens are still open decisions, start from the guides hub and lock the pixel budget before the first annotation. Questions about a specific strip geometry go to our team through the contact page.

Questions

Does the trainer stretch 1024 × 320 frames to fill the square?

No. The loader scales the long side to imgsz and pads the short side with a neutral gray band — at 1024 that is 352 rows above and 352 below the content. Circles stay circles and object sizes stay true. If your frames look stretched, your own preprocessing is doing it; the trainer never distorts content to fill the canvas.

Why does rect=True switch mosaic off?

Mosaic builds one square canvas at imgsz and stitches four random frames onto it, then trains on the composite. The rect loader instead packs each batch into a shared rectangle near native aspect, so the square canvas mosaic needs does not exist and the loader disables it. You keep native scale and lose the four-scale scene diversity — that is the whole trade.

Is tiling wide frames into squares a real alternative?

Yes, and for strips around 3000 × 350 it is often the best one. Crop overlapping squares, keep every crop that contains an object, and set the overlap to at least your largest object so nothing is cut in half at a tile edge; merge overlapping detections at inference. You keep square training, mosaic augmentation and native pixel scale all at once.

The 1024 square will not fit in memory — what now?

First try imgsz=1024 with rect=True: batches train at 1024 × 320, 327,680 pixels per frame instead of 1,048,576, and memory falls by roughly the same 3.2 factor. Drop to a 640 square only if the smallest feature survives: everything scales by 0.625 there, so a 6 px defect becomes 3.75 px (too small) and a 10 px defect becomes 6.25 px (fine).

Do validation and prediction need square inputs too?

No. Validation groups frames by aspect ratio and scores letterboxed rectangles on its own. Prediction letterboxes what you feed it — scale to target, gray fill, no stretch — so a 1024 × 320 frame runs inside a 1024 square, or as the bare rectangle in a deployment that feeds tensors directly, since both sides divide by 32. Keep the letterbox identical on both sides of training.