Detection
TransformsPresetDetection(max_size: int = 1333, min_size: int | None = None, size_divisible: int = 1, pad_position: str = 'center', pad_value: float | None = None, square: bool = True, min_area: float = 1.0, min_visibility: float = 0.0, mean: tuple[float, ...] | None = None, std: tuple[float, ...] | None = None, interpolation: str | Interpolation = Interpolation.BILINEAR)Object-detection preset — coordinates ride with the image.
Pipeline: LongestMaxSize(max_size) (ResizeShortestEdge(min_size, max_size) when min_size is given) → PadIfNeeded onto a
square of side canvas_size, the image placed per
pad_position → Normalize(mean, std) plus a
BboxParams(min_area, min_visibility) policy on the
enclosing Compose so out-of-frame boxes (and their
labels) drop automatically after the pipeline runs.
Parameters
max_sizeint= 1333800 / 1333.min_sizeint or None= Nonemax_size.size_divisibleint= 1max_size rounded up to a multiple of
this. The R-CNN weights pass 32, as the reference detection
transform rounds its batches: an R-CNN derives each pyramid
level's stride as canvas height over level height, and on a
canvas that does not divide by the strides those quotients fall
short (1333 gives 3, 7, 15, 31 and 63 instead of 4 to 64). The
default pads to exactly max_size, as a config saved before
this option existed did.pad_positionstr= "center"PadIfNeeded positions. The R-CNN and DETR references
leave it in the top-left corner ("top_left"), the frame a
detector's image_sizes is measured in; darknet letterboxes it
centred. The default is the placement a config saved before this
option existed had.pad_valuefloat or None= Nonepad_value * std + mean before normalising, so
the padding comes out at exactly pad_value. None pads the
raw pixels with 0 — about -2 after ImageNet normalisation — as a
config saved before this option existed did.squarebool= Truecanvas_size, the canvas every
image shares, so a batch of them stacks. False pads each axis
only up to the next multiple of size_divisible — the canvas the
reference detection transform gives a single image, with no
padding beyond what the strides need; images of different shapes
then no longer stack. canvas_size describes the square.min_areafloat= 1.0BboxParams.min_visibilityfloat= 0.0meantuple of float= Nonestdtuple of float= NoneExamples
>>> import lucid, lucid.utils.transforms as T
>>> tf = T.Detection()
>>> tuple(tf(T.Image(lucid.rand(3, 32, 32))).data.shape)
(3, 1333, 1333)
A preset, not a single transform: it resizes so the longest side
is at most max_size and pads to a square, which is what a
detector's backbone expects to batch. The R-CNN weights round the
square up to a multiple of 32 and keep the image in its top-left
corner, where image_size says it ends.
>>> rcnn = T.Detection(size_divisible=32, pad_position="top_left")
>>> rcnn.canvas_size, rcnn.image_size(300, 500)
(1344, (800, 1333))
With pad_value=0.0 the padding reaches the model at 0, as the R-CNN
references feed it, rather than at the normalised value of a black pixel:
>>> tf = T.Detection(max_size=64, size_divisible=32, pad_value=0.0,
... pad_position="top_left")
>>> padded = tf(T.Image(lucid.rand(3, 20, 30))).data
>>> float(padded[:, -1, :].abs().max().item()) < 1e-6
TrueUsed by 1
Constructors
1__init__
→None__init__(max_size: int = 1333, min_size: int | None = None, size_divisible: int = 1, pad_position: str = 'center', pad_value: float | None = None, square: bool = True, min_area: float = 1.0, min_visibility: float = 0.0, mean: tuple[float, ...] | None = None, std: tuple[float, ...] | None = None, interpolation: str | Interpolation = Interpolation.BILINEAR)Properties
1Instance methods
2(H, W) an image of this size has on the canvas, before padding.
With pad_position="top_left" the image fills rows [0, H) and
columns [0, W) of the canvas, so this is that image's entry in a
detector's postprocess(image_sizes=...).
Parameters
heightintwidthintReturns
tuple of intThe size the resize stage gives it — the same rule it applies to the image and its boxes.
Examples
>>> import lucid.utils.transforms as T
>>> T.Detection(min_size=800, max_size=1333).image_size(480, 640)
(800, 1067)Map boxes on the canvas back onto the image they came from.
A detector's postprocess answers in the coordinates of the canvas
it was fed. This undoes what the preset did to an image of
height x width — the pad offset, then the resize — and clips to
that image. A centred letterbox (YOLO's) shifts every box by the
margin above and left of the image; a top-left placement (the
R-CNNs', DETR's) leaves only the resize to undo.
Parameters
boxesTensor(N, 4) boxes as (x1, y1, x2, y2) on the canvas.heightintwidthintReturns
Tensor(N, 4) boxes in that image's pixels.
Examples
>>> import lucid, lucid.utils.transforms as T
>>> letterbox = T.Detection(max_size=64)
>>> letterbox.image_size(16, 32)
(32, 64)
>>> canvas_box = lucid.tensor([[0.0, 16.0, 64.0, 48.0]])
>>> letterbox.to_image_boxes(canvas_box, 16, 32).tolist()
[[0.0, 0.0, 32.0, 16.0]]