# Geometric transformations module#include "diplib/geometry.h"

Geometric image transformations.

## Interpolation methods

Many of the functions in this group resample the image using an interpolation method. They all perform interpolation separately (i.e. along one dimension at the time). These functions have an input argument called interpolationMethod, which determines how image data are interpolated. It can be set to one of the following strings:

• "3-cubic" (or ""): Third-order cubic spline interpolation (Keys, 1981), using 4 input samples to compute each output sample. This is the default method for most functions.

• "4-cubic": Fourth-order cubic spline interpolation (Keys, 1981), using 6 input samples to compute compute each output sample.

• "linear": Linear interpolation, using 2 input samples to compute each output sample.

• "nearest" (or "nn"): Nearest neighbor interpolation, samples are simply shifted and replicated.

• "inverse nearest" (or "nn2"): Nearest neighbor interpolation, but resolves the rounding of x.5 in the opposite direction that "nearest" does. This is useful when applying the inverse of an earlier transform, to be able to obtain the original geometry back.

• "bspline": A third-order cardinal B-spline is computed for all samples on an image line, which is sampled anew to obtain the interpolated sample values. All input samples are used to compute all output samples, but only about 10 input samples significantly influence each output value.

• "lanczos8": Lanczos interpolation with a = 8, using 16 input samples to compute each output sample. The Lanczos kernel is a sinc function windowed by a larger sinc function, where a is the width of the larger sinc function. The kernel is normalized.

• "lanczos6": Lanczos interpolation with a = 6, using 12 input samples to compute each output sample.

• "lanczos4": Lanczos interpolation with a = 4, using 8 input samples to compute each output sample.

• "lanczos3": Lanczos interpolation with a = 3, using 6 input samples to compute each output sample.

• "lanczos2": Lanczos interpolation with a = 2, using 4 input samples to compute each output sample.

• "ft": Interpolation through padding and cropping, and/or modifying the phase component of the Fourier transform of the image line. Padding with zeros increases the sampling density, cropping reduces the sampling density, and multiplying the phase component by shifts the image. Equivalent to interpolation with a sinc kernel. All input samples are used to compute all output samples. The boundary condition is ignored, as the Fourier transform imposes a periodic boundary condition.

Not all methods are available for all functions. If so, this is described in the function’s documentation. For operations on binary images, the interpolation method is ignored, and "nearest" is always used.

Interpolation methods require a boundary extension of half the number of input samples used to compute each output sample. For B-spline interpolation, a boundary extension of 5 is used. For the nearest neighbor and Fourier interpolation methods, no boundary extension is needed.

## Aliases

using dip::InterpolationFunctionPointer = void(*)(dip::Image, Image::Pixel, dip::FloatArray)
Pointer to an interpolation function. Only use pointers returned by dip::PrepareResampleAtUnchecked.

## Functions

void dip::Wrap(dip::Image const& in, dip::Image& out, dip::IntegerArray wrap)
Shifts the input image by an integer number of pixels, wrapping the pixels around.
void dip::Subsampling(dip::Image const& in, dip::Image& out, dip::UnsignedArray const& sample)
Subsamples the input image.
void dip::Resampling(dip::Image const& in, dip::Image& out, dip::FloatArray zoom, dip::FloatArray shift = {0.0}, dip::String const& interpolationMethod = "", dip::StringArray const& boundaryCondition = {})
Resamples an image with the given zoom factor and sub-pixel shift.
void dip::Shift(dip::Image const& in, dip::Image& out, dip::FloatArray const& shift, dip::String const& interpolationMethod = S::FOURIER, dip::StringArray const& boundaryCondition = {})
Shift an image. Calls dip::Resampling with zoom set to 1, and uses the “ft” method by default.
void dip::ShiftFT(dip::Image const& in, dip::Image& out, dip::FloatArray shift = {0.0})
Modulates the input Fourier spectrum to introduce a shift in the spatial domain
void dip::ResampleAt(dip::Image const& in, dip::Image& out, dip::FloatCoordinateArray const& coordinates, dip::String const& interpolationMethod = S::LINEAR, dip::Image::Pixel const& fill = {0})
Finds the values of the image at sub-pixel locations coordinates by interpolation.
auto dip::ResampleAt(dip::Image const& in, dip::FloatArray const& coordinates, dip::String const& interpolationMethod = S::LINEAR, dip::Image::Pixel const& fill = {0}) -> dip::Image::Pixel
Identical to the previous function with the same name, but for a single point.
dip::Image const& in, dip::String const& interpolationMethod) -> dip::InterpolationFunctionPointer
Prepare for repeated calls to dip::ResampleAtUnchecked. See dip::ResampleAt.
dip::Image const& in, dip::FloatArray const& coordinates, dip::InterpolationFunctionPointer function) -> dip::Image::Pixel
Similar to dip::ResampleAt, but optimized for repeated calls using the same parameters. function comes from PrepareResampleAtUnchecked. fill is always 0.
void dip::ResampleAt(dip::Image const& in, dip::Image const& map, dip::Image& out, dip::String const& interpolationMethod = S::LINEAR, dip::Image::Pixel const& fill = {0})
Resamples an image with sub-pixel locations specified by a coordinate map.
void dip::Skew(dip::Image const& in, dip::Image& out, dip::FloatArray const& shearArray, dip::uint axis, dip::String const& interpolationMethod = "", dip::StringArray const& boundaryCondition = {})
Skews (shears) an image
void dip::Skew(dip::Image const& in, dip::Image& out, dip::dfloat shear, dip::uint skew, dip::uint axis, dip::String const& interpolationMethod = "", dip::String const& boundaryCondition = {})
Skews (shears) an image
void dip::Rotation(dip::Image const& in, dip::Image& out, dip::dfloat angle, dip::uint dimension1, dip::uint dimension2, dip::String const& interpolationMethod = "", dip::String const& boundaryCondition = S::ADD_ZEROS)
Rotates an image in one orthogonal plane, over the center of the image.
void dip::Rotation2D(dip::Image const& in, dip::Image& out, dip::dfloat angle, dip::String const& interpolationMethod = "", dip::String const& boundaryCondition = S::ADD_ZEROS)
Rotates a 2D image
void dip::Rotation3D(dip::Image const& in, dip::Image& out, dip::dfloat angle, dip::uint axis = 2, dip::String const& interpolationMethod = "", dip::String const& boundaryCondition = S::ADD_ZEROS)
Rotates a 3D image in one orthogonal plane
void dip::Rotation3D(dip::Image const& in, dip::Image& out, dip::dfloat alpha, dip::dfloat beta, dip::dfloat gamma, dip::String const& interpolationMethod = "", dip::String const& boundaryCondition = S::ADD_ZEROS)
Applies an arbitrary 3D rotation to a 3D image
dip::Image& out, dip::dfloat angle)
Creates a 0D (one pixel) 2x2 matrix image containing a 2D rotation matrix.
dip::Image& out, dip::dfloat alpha, dip::dfloat beta, dip::dfloat gamma)
Creates a 0D (one pixel) 3x3 matrix image containing a 3D rotation matrix.
dip::Image& out, dip::FloatArray const& vector, dip::dfloat angle)
Creates a 0D (one pixel) 3x3 matrix image containing a 3D rotation matrix.
void dip::AffineTransform(dip::Image const& in, dip::Image& out, dip::FloatArray const& matrix, dip::String const& interpolationMethod = S::LINEAR)
Applies an arbitrary affine transformation to the 2D or 3D image.
dip::Image const& in, dip::Image& out, dip::FloatCoordinateArray const& inCoordinates, dip::FloatCoordinateArray const& outCoordinates, dip::dfloat lambda = 0, dip::String const& interpolationMethod = S::LINEAR)
Warps an image based on a set of control points using thin plate spline interpolation
dip::Image const& in, dip::Image& out, dip::String const& interpolationMethod = S::LINEAR)
Computes the log-polar transform of the 2D image.
void dip::Tile(dip::ImageConstRefArray const& in, dip::Image& out, dip::UnsignedArray tiling = {})
Tiles a set of images to form a single image.
dip::Image const& in, dip::Image& out)
Tiles the tensor elements of in to produce a scalar image
void dip::Concatenate(dip::ImageConstRefArray const& in, dip::Image& out, dip::uint dimension = 0)
Concatenates a set of images along one dimension.
void dip::Concatenate(dip::Image const& in1, dip::Image const& in2, dip::Image& out, dip::uint dimension = 0)
Concatenates two images.
void dip::JoinChannels(dip::ImageConstRefArray const& in, dip::Image& out)
Concatenates a set of scalar images along the tensor dimension.

## Function documentation

### void dip::Wrap(dip::Image const& in, dip::Image& out, dip::IntegerArray wrap)

Shifts the input image by an integer number of pixels, wrapping the pixels around.

dip::Wrap is equivalent to dip::Shift with nearest neighbor interpolation and periodic boundary condition, but faster.

### void dip::Subsampling(dip::Image const& in, dip::Image& out, dip::UnsignedArray const& sample)

Subsamples the input image.

The input image is subsampled by sample[ ii ] along dimension ii. The output image shares the data segment of the input image, meaning that no data are copied. If a data copy is required, calling dip::Image::ForceContiguousData after subsampling should trigger a data copy.

If out has an external interface different from that of in, the data will be copied.

### void dip::Resampling(dip::Image const& in, dip::Image& out, dip::FloatArray zoom, dip::FloatArray shift = {0.0}, dip::String const& interpolationMethod = "", dip::StringArray const& boundaryCondition = {})

Resamples an image with the given zoom factor and sub-pixel shift.

The shift is applied first, and causes part of the image to shift out of the field of view. Thus, shift is in input pixels. boundaryCondition determines how the new areas are filled in. See dip::BoundaryCondition. Note that the shift can be in fractional pixels. There is no largest possible shift, but applying very large shifts is not optimized for, and will use more computation and temporary memory than necessary. This is true even for the periodic boundary condition; use dip::Wrap to apply the integer shift with periodic boundary condition, then use Resampling for the remaining sub-pixel shift. The exception is the "ft" interpolation method, which uses the same memory and time for large as for small shifts.

The scaling is applied next. If zoom is larger than 1, the output image will be larger than the input, if it is smaller than 1, it will be smaller. The output image has size std::floor( in.Sizes( ii ) * zoom[ ii ] ) along dimension ii. For the "ft" method, the zoom factor is back-computed from this output image size, whereas for the other methods the zoom is used as given. This stems from the very different approach to interpolation.

The pixel at coordinates pos in the output image is interpolated from the position pos[ ii ] / zoom[ ii ] - shift[ ii ] along dimension ii. Thus, with zoom smaller than 1, no low-pass filtering is applied first. Again, the "ft" interpolation method is different: the output image is generated by inverse Fourier transform from the manipulated frequency spectrum of the input image. The "ft" method thus has low-pass filtering built-in when zooming out.

The output image has the same data type as the input image.

See Interpolation methods for information on the interpolationMethod parameter.

### void dip::ShiftFT(dip::Image const& in, dip::Image& out, dip::FloatArray shift = {0.0})

Modulates the input Fourier spectrum to introduce a shift in the spatial domain

in is the Fourier transform of an image img. It will be multiplied with a complex quantity to change its phase in such a way that the image img will be shifted. For example:

dip::Image img = dip::ImageReadTIFF( "erika" );
dip::Image ft = dip::FourierTransform( img );
dip::ShiftFT( ft, ft, { 10.3, -5.2 } );
dip::Image shifted = dip::FourierTransform( img, { "inverse", "real" } );


Produces the same output as:

dip::Image img = dip::ImageReadTIFF( "erika" );
dip::Image shifted = dip::Shift( img, { 10.3, -5.2 }, "fourier" );


### void dip::ResampleAt(dip::Image const& in, dip::Image& out, dip::FloatCoordinateArray const& coordinates, dip::String const& interpolationMethod = S::LINEAR, dip::Image::Pixel const& fill = {0})

Finds the values of the image at sub-pixel locations coordinates by interpolation.

The array coordinates must have all elements be arrays of the same length as the image dimensionality. For any coordinates outside of the image domain, the pixel value fill will be returned. That is, no extrapolation is performed. Coordinates match image indexing: the first pixel on a line has coordinate 0.

interpolationMethod has a restricted set of options: "linear", "3-cubic", or "nearest". See Interpolation methods for their definition. If in is binary, interpolationMethod will be ignored, nearest neighbor interpolation will be used.

out will be a 1D image with the same size as the coordinates array, and the same data type and tensor shape as in. To obtain results in a floating-point type, set the data type of out and protect it, see The “protect” flag.

### void dip::ResampleAt(dip::Image const& in, dip::Image const& map, dip::Image& out, dip::String const& interpolationMethod = S::LINEAR, dip::Image::Pixel const& fill = {0})

Resamples an image with sub-pixel locations specified by a coordinate map.

Returns an image of the same size as map with pixels taken from in at the subpixel coordinates specified by map. Each tensor element of map specifies the location for an input dimension. As such, it must have a number of tensor elements equal to in‘s dimensionality, and real-valued samples. For any coordinates outside of the image domain, the pixel value fill will be returned. That is, no extrapolation is performed. Coordinates match image indexing: the first pixel on a line has coordinate 0.

interpolationMethod has a restricted set of options: "linear", "3-cubic", or "nearest". See Interpolation methods for their definition. If in is binary, interpolationMethod will be ignored, nearest neighbor interpolation will be used.

out will have the same size as map, and the same data type and tensor shape as in. If out is protected, its data type will not change, but the computations will still be performed in the data type of in.

### void dip::Skew(dip::Image const& in, dip::Image& out, dip::FloatArray const& shearArray, dip::uint axis, dip::String const& interpolationMethod = "", dip::StringArray const& boundaryCondition = {})

Skews (shears) an image

The image is skewed such that a straight line along dimension axis is tilted by an angle of atan( shearArray[ ii ] ) radian in the direction of dimension ii. shearArray[ ii ] thus represents the sub-pixel shift of a line in the direction ii with respect to the previous line along axis. Each image sub-volume perpendicular to axis is shifted by a different amount. The output image has the same dimension as in in the axis direction, and larger dimensions in all other dimensions, such that no data are lost. The value of shearArray[ axis ] is ignored. The origin of the skew is the central pixel (see Coordinate system origin).

The output image has the same data type as the input image.

See Interpolation methods for information on the interpolationMethod parameter.

boundaryCondition determines how data outside of the input image domain are filled in. See dip::BoundaryCondition. If it is "periodic", a periodic skew is applied. This means that image lines are shifted using a periodic boundary condition, and wrap around. The output image does not grow along dimension skew.

### void dip::Skew(dip::Image const& in, dip::Image& out, dip::dfloat shear, dip::uint skew, dip::uint axis, dip::String const& interpolationMethod = "", dip::String const& boundaryCondition = {})

Skews (shears) an image

The image is skewed such that a straight line along dimension axis is tilted by an angle of shear radian in the direction of dimension skew. Each image line along dimension skew is shifted by a different amount. The output image has the same dimensions as in, except for dimension skew, which will be larger, such that no data are lost. The origin of the skew is the central pixel (see Coordinate system origin).

The output image has the same data type as the input image.

shear must have a magnitude smaller than π/2. Note that the definition of shear is different from that of shearArray in the other version of dip::Skew, documented above.

See Interpolation methods for information on the interpolationMethod parameter.

boundaryCondition determines how data outside of the input image domain are filled in. See dip::BoundaryCondition. If it is "periodic", a periodic skew is applied. This means that image lines are shifted using a periodic boundary condition, and wrap around. The output image does not grow along dimension skew.

### void dip::Rotation(dip::Image const& in, dip::Image& out, dip::dfloat angle, dip::uint dimension1, dip::uint dimension2, dip::String const& interpolationMethod = "", dip::String const& boundaryCondition = S::ADD_ZEROS)

Rotates an image in one orthogonal plane, over the center of the image.

Rotates an image in the plane defined by dimension1 and dimension2, over an angle angle, in radian. The origin of the rotation is the central pixel (see Coordinate system origin).

The output image has the same data type as the input image.

The rotation is computed by three consecutive calls to dip::Skew. See that function for the meaning of interpolationMethod and boundaryCondition.

### void dip::Rotation2D(dip::Image const& in, dip::Image& out, dip::dfloat angle, dip::String const& interpolationMethod = "", dip::String const& boundaryCondition = S::ADD_ZEROS)

Rotates a 2D image

Calls dip::Rotation, setting the dimension parameters to 0 and 1. Provides a simplified interface for 2D images.

### void dip::Rotation3D(dip::Image const& in, dip::Image& out, dip::dfloat angle, dip::uint axis = 2, dip::String const& interpolationMethod = "", dip::String const& boundaryCondition = S::ADD_ZEROS)

Rotates a 3D image in one orthogonal plane

Calls dip::Rotation, setting the dimension parameters according to axis. Provides a simplified interface for 3D images.

### void dip::Rotation3D(dip::Image const& in, dip::Image& out, dip::dfloat alpha, dip::dfloat beta, dip::dfloat gamma, dip::String const& interpolationMethod = "", dip::String const& boundaryCondition = S::ADD_ZEROS)

Applies an arbitrary 3D rotation to a 3D image

Rotates a 3D image over the Euler angles alpha, beta and gamma, by calling dip::Rotation three times (i.e. using nine skews). The first rotation is over alpha radian around the initial z-axis. The second rotation is over beta radian around the intermediate y-axis. The last rotation is over gamma radian around the final z-axis.

The rotation is over the center of the image, see Coordinate system origin.

### void dip::RotationMatrix2D(dip::Image& out, dip::dfloat angle)

Creates a 0D (one pixel) 2x2 matrix image containing a 2D rotation matrix.

Multiplying the output of dip::CreateCoordinates by this rotation matrix will produce an image with a rotated coordinate system. The rotation matrix must be on the left-hand-side of the multiplication operator.

The rotation is over angle radian. Note that when transforming a coordinate system (a passive transformation), then the transpose of the matrix must be used.

out is of type dip::DT_SFLOAT by default.

Example:

dip::Image coords = dip::CreateCoordinates( { 256, 256 }, { "frequency" } );
dip::Image rotatedCoords = dip::RotationMatrix2D( dip::pi/4 ) * coords;


### void dip::RotationMatrix3D(dip::Image& out, dip::dfloat alpha, dip::dfloat beta, dip::dfloat gamma)

Creates a 0D (one pixel) 3x3 matrix image containing a 3D rotation matrix.

Multiplying the output of dip::CreateCoordinates by this rotation matrix will produce an image with a rotated coordinate system. The rotation matrix must be on the left-hand-side of the multiplication operator.

The rotation is over the Euler angles alpha, beta and gamma: the first rotation is over alpha radian around the initial z-axis. The second rotation is over beta radian around the intermediate y-axis. The last rotation is over gamma radian around the final z-axis. Note that when transforming a coordinate system (a passive transformation), then the transpose of the matrix must be used.

out is of type dip::DT_SFLOAT by default.

Example:

dip::Image coords = dip::CreateCoordinates( { 50, 50, 50 } );
dip::Image rotatedCoords = dip::RotationMatrix3D( dip::pi/4, 0, dip::pi ) * coords;


### void dip::RotationMatrix3D(dip::Image& out, dip::FloatArray const& vector, dip::dfloat angle)

Creates a 0D (one pixel) 3x3 matrix image containing a 3D rotation matrix.

Multiplying the output of dip::CreateCoordinates by this rotation matrix will produce an image with a rotated coordinate system. The rotation matrix must be on the left-hand-side of the multiplication operator.

The rotation is over angle radian about the axis defined by vector. That is, vector will not be affected by the rotation. Note that when transforming a coordinate system (a passive transformation), then the transpose of the matrix must be used.

out is of type dip::DT_SFLOAT by default.

Example:

dip::Image coords = dip::CreateCoordinates( { 50, 50, 50 } );
dip::Image rotatedCoords = dip::RotationMatrix3D( { 1.0, 0.0, 0.0 }, dip::pi/4 ) * coords;


### void dip::AffineTransform(dip::Image const& in, dip::Image& out, dip::FloatArray const& matrix, dip::String const& interpolationMethod = S::LINEAR)

Applies an arbitrary affine transformation to the 2D or 3D image.

matrix contains 4 values (in is 2D) or 9 values (in is 3D) representing a linear transformation matrix. Optionally, a translation can be represented by 2 (2D) or 3 (3D) values added to the end of matrix. In this case, the matrix is an affine transform matrix using homogeneous coordinates, but with the bottom row removed (which is expected to be {0,0,1} in 2D or {0,0,0,1} in 3D). The values are stored in column-major order:

       ⎡ matrix  matrix  matrix ⎤
T_2D = ⎢ matrix  matrix  matrix ⎥
⎣    0          0          1      ⎦

⎡ matrix  matrix  matrix  matrix[ 9] ⎤
T_3D = ⎢ matrix  matrix  matrix  matrix ⎥
⎢ matrix  matrix  matrix  matrix ⎥
⎣    0          0          0          1       ⎦


The coordinates of each pixel in in (the origin of the coordinate system is the central pixel, see Coordinate system origin) is mapped through the transformation matrix to obtain its location in out. Although the algorithm actually uses the inverse of the matrix to transform each coordinate in out to obtain the location where to interpolate a value from. out is given the same size as in.

interpolationMethod has a restricted set of options: "linear", "3-cubic", or "nearest". See Interpolation methods for their definition. If in is binary, interpolationMethod will be ignored, nearest neighbor interpolation will be used.

### void dip::WarpControlPoints(dip::Image const& in, dip::Image& out, dip::FloatCoordinateArray const& inCoordinates, dip::FloatCoordinateArray const& outCoordinates, dip::dfloat lambda = 0, dip::String const& interpolationMethod = S::LINEAR)

Warps an image based on a set of control points using thin plate spline interpolation

inCoordinates and outCoordinates are two sets of points (they must be of equal size). The point inCoordinates[ ii ] will be moved to outCoordinates[ ii ], warping the image in accordingly.

lambda is the regularization parameter. By default it is zero, meaning that the control points will be matched exactly in the input and output images. Increasing lambda allows some error in the location of the control points, and will result in a smoother interpolation.

interpolationMethod has a restricted set of options: "linear", "3-cubic", or "nearest". See Interpolation methods for their definition. If in is binary, interpolationMethod will be ignored, nearest neighbor interpolation will be used.

### void dip::LogPolarTransform2D(dip::Image const& in, dip::Image& out, dip::String const& interpolationMethod = S::LINEAR)

Computes the log-polar transform of the 2D image.

By default, out will be a square image with side equal to the smaller of the two sides of in. However, if out is protected (see dip::Image::Protect), its sizes will be preserved, even if not forged.

The x-axis (horizontal) of out is the logarithm of the radius, and the y-axis is the angle.

interpolationMethod has a restricted set of options: "linear", "3-cubic", or "nearest". See Interpolation methods for their definition. If in is binary, interpolationMethod will be ignored, nearest neighbor interpolation will be used.

This function is an integral part of the Fourier-Mellin transform, see dip::FourierMellinMatch2D.

### void dip::Tile(dip::ImageConstRefArray const& in, dip::Image& out, dip::UnsignedArray tiling = {})

Tiles a set of images to form a single image.

Input images are arranged according to tiling. For example, tiling = { 3, 2 } will generate an output where three images are placed horizontally, and two vertically. In this case, up to 6 input images can be given in in. If in has fewer images, the corresponding locations in out will be zero. If in has 6 images, they will be placed as follows:

| in, in, in |
| in, in, in |


That is, images are tiled row-wise, from left to right and then top to bottom. tiling can have any number of elements, it is for example possible to tile 2D images along the 4th dimension.

If tiling is an empty array (the default), then ceil(sqrt(in.size())) images will be placed horizontally, in however many rows are necessary to fit all images.

The input images must all have the same sizes, with the exception for the case where all images are tiled along a single dimension, in which case their sizes can differ along that dimension (see dip::Concatenate)

The input images must all have the same number of tensor elements. If their tensor representations do not match, the output image will be a default column vector. If images are differing data types, the output will be of a type that best can represent the input (see dip::DataType::SuggestDyadicOperation).

### void dip::TileTensorElements(dip::Image const& in, dip::Image& out)

Tiles the tensor elements of in to produce a scalar image

The tensor elements of in are arranged according to its tensor representation, along the first two spatial dimensions. This produces a scalar image of size in.Size(0) * in.TensorColumns() along the horizontal dimension, and size in.Size(1) * in.TensorRows() along the vertical dimension. The output image has the same dimensions as in along the third and further dimensions.

### void dip::Concatenate(dip::ImageConstRefArray const& in, dip::Image& out, dip::uint dimension = 0)

Concatenates a set of images along one dimension.

Input images are concatenated along dimension dimension. They must all have the same sizes along all dimensions except dimension, where they can differ.

The input images must all have the same number of tensor elements. If their tensor representations do not match, the output image will be a default column vector. If images are differing data types, the output will be of a type that best can represent the input (see dip::DataType::SuggestDyadicOperation).

### void dip::JoinChannels(dip::ImageConstRefArray const& in, dip::Image& out)

Concatenates a set of scalar images along the tensor dimension.

Input images are individual tensor components in the output vector image. They must all have the same sizes and be scalar.

If images are differing data types, the output will be of a type that best can represent the input (see dip::DataType::SuggestDyadicOperation). out will be a vector image with in.size() samples per pixel.