pool¶
Downsample a volume by reducing each window of voxels to a single value, for example by averaging or taking the maximum. The orientation matrix of the output is updated so that it stays aligned with the input in world space.
Description¶
pool slides a window over the volume and replaces the voxels it covers by one
number, chosen by the pooling method. The window moves by
STRIDE voxels at a time, and by default the stride equals the window size.
The windows then tile the image without overlap, and the output has roughly
1 / WINDOW of the voxels along each axis. With -w 2 an image of
256 x 256 x 160 voxels becomes 128 x 128 x 80.
This is the same operation as a pooling layer in a convolutional network. It
differs from resize and
reslice in that it never interpolates: the output
voxels are statistics of exact groups of input voxels.
Each file is pooled independently, and the result is written next to the
input unless -o says otherwise. Only the first DIM dimensions of a file
(three by default) are pooled; further dimensions, such as the channels of a 4D
file, are kept and treated as independent channels. The output file uses
the header of the input as a template and has the same data type as the input:
values are converted back to that type without rescaling, so pooling an
integer-typed image with mean loses the fractional part of the result. Convert the image to
a floating-point type first (see convert) if you need to keep
fractional values.
Window and stride¶
WINDOW and STRIDE are given in voxels, one value per spatial dimension. If
you give fewer values than there are dimensions, the last value is repeated, so
-w 2 means -w 2 2 2. A window value of 0 (or less) means "the whole
axis": the dimension is reduced to a single voxel.
Without padding, a window is only placed where it fits entirely inside the image. The output size along an axis of length \(L\) is \(\lfloor (L - \text{window}) / \text{stride} \rfloor + 1\), so voxels that remain after the last complete window are dropped. For example, pooling 100 voxels with a window of 3 gives 33 voxels and drops the last one. You can avoid this with padding.
Orientation matrix¶
The output is not simply the input with fewer voxels: it is resampled on a coarser lattice, and the orientation matrix is updated to describe it. Output voxel \(i\) is centred on the centre of the window that produced it. In voxel coordinates of the input along one axis, with window size \(K\), stride \(S\) and padding \(P\) on the low side of the axis (the amount added before the first voxel), this is the position \(S\,i + (K-1)/2 - P\). As a consequence:
- the voxel size is multiplied by the stride (not by the window size) along each axis;
- the axes keep their direction;
- the origin is shifted by half a window, so that, say, a mean over a \(2 \times 2 \times 2\) window is placed between the four voxels it averages.
Because of this, the pooled image overlays the original correctly in a viewer, and can be used as a lower-resolution copy of it. When the window is larger than the stride, the windows overlap and the voxels are correlated, but the geometry follows the same rule.
Padding¶
-p adds voxels around the image before pooling. Each padding size may be
followed by the boundary condition to use along that axis, and an optional
boundary condition placed first applies to all the axes that do not specify
their own:
For example, -p 1 zero 2 dft pads the first axis by 1 voxel with zeros and
the remaining axes by 2 voxels with wrap-around, whereas -p zero 1 2 dft
pads the first axis by 1 voxel with zeros and the others by 2 voxels with
wrap-around (here zero is the default, which the first size inherits). A
boundary condition that does not follow a size is an error, unless it comes
first.
Each PAD is a number of voxels added on both sides of an axis, or the word
same. With same, the smallest padding is chosen that gives an output of
\(\lfloor L / \text{stride} \rfloor\) voxels along that axis. When the window
equals the stride, the unpadded output already has that size, so same adds
nothing; it matters for overlapping windows, for instance a window of 3 with a
stride of 1, which then keeps the size of the image. If the amount of padding
\(P\) needed is odd, it is split unevenly: \(\lfloor P / 2 \rfloor\) voxels are
added before the image and the remaining \(\lceil P / 2 \rceil\) after it, and
the orientation matrix accounts for the voxels added before. As for the window,
the last value is repeated if fewer than three are given, whether it is a size
or a boundary condition.
BOUND says what values the padding voxels take. The default is dct2, which
mirrors the image at its edge (the edge voxel is repeated, so a b c d is padded to b a a b c d d c).
The other values accepted are the boundary conditions listed in
Boundary conditions below.
Pooling methods¶
| Method | Value written for each window |
|---|---|
mean |
The arithmetic mean (the default). |
sum |
The sum. |
min |
The smallest value. |
max |
The largest value. |
median |
The median. |
rms |
The root mean square of the values (ssq is a deprecated alias). |
The method is not validated by the parser: an unknown name is reported as an error only once the computation starts.
Boundary conditions¶
The names below can be used with -p. Several names exist for most of them
(the Python/SciPy and PyTorch spellings are accepted too).
| Name | Pads with | Before a b c d |
After a b c d |
|---|---|---|---|
dct2 |
mirror about the edge (also reflect, neumann) |
b a |
d c |
dct1 |
mirror about the edge voxel (also mirror) |
c b |
c b |
replicate |
the edge value (also border, nearest, repeat) |
a a |
d d |
zero |
zeros (also zeros, constant) |
0 0 |
0 0 |
dft |
wrap-around (also wrap, circular) |
c d |
a b |
dst2 |
antisymmetric mirror (also antireflect, dirichlet) |
-b -a |
-d -c |
dst1 |
antisymmetric mirror (also antimirror) |
-a 0 |
0 -d |
Arguments¶
| Name | Description |
|---|---|
FILE |
One or more input volumes, in any format NITorch reads. They must come before the options. |
Options¶
| Option | Value | Description | Default |
|---|---|---|---|
-w, --window |
WINDOW... |
Window size, in voxels, per dimension. 0 pools a whole axis. See below. |
3 |
-s, --stride |
STRIDE... |
Step between windows, in voxels, per dimension. | same as the window |
-p, --padding |
[BOUND] PAD [BOUND] ... |
Padding added on both sides, in voxels, or same, each optionally followed by a boundary condition; a leading one applies to all axes. See below. |
0 |
-m, --method |
METHOD |
Pooling function: mean, sum, min, max, median or rms. See above. |
mean |
-d, --dim, --dimension |
DIM |
Number of spatial dimensions. It has to agree with the orientation matrix of the file, so leave it at 3 for ordinary volumes. 0 takes the value from the file. |
3 |
-o, --output |
FILE... |
Output file names or templates, one per input. A single template is reused for every input. | {dir}{sep}{base}.pool{ext} |
-cpu, --cpu |
Compute on the CPU. | CPU | |
-gpu, --gpu |
[ID] |
Compute on a GPU; ID selects the device (cuda:ID). |
Output names¶
Templates accept {dir} (directory of the input, . if none), {base} (name
without extension; .nii.gz counts as one extension), {ext} (extension with
its dot) and {sep} (the path separator). See
Output filenames. If you give fewer
names than inputs, the last name is repeated, which makes later outputs
overwrite earlier ones; give a template, or one name per input. Device options
are described in Hardware.
Examples¶
Halve the resolution of a volume by averaging \(2 \times 2 \times 2\) blocks. The
result is T1w.pool.nii.gz:
Average over a window of 4 voxels, but move by 2, so that windows overlap:
Downsample a binary mask so that a voxel is set if any voxel of its block was set:
Pool only in the first two directions, leaving the third at full resolution (a window of 1 along the last axis):
Collapse the whole third axis into one slice, taking the median along it:
Compute a local mean over a 3-voxel window that moves one voxel at a time, padding the edges so the image keeps its size and grid:
Pool several images at once. Each result is written next to its input, as
sub-01_T1w.pool.nii.gz and sub-02_T1w.pool.nii.gz: