# Geometric Transformations
Several geometric image transformations are implemented in Lima:
- pixel binning,
- crop (also known as Region-of-Interest: RoI),
- vertical (top-down) / horizontal (left-right) flip and
- rotation by 90 degrees, both clockwise (CW) and counter-clockwise (CCW), and 180 degrees.
These operations are normally applied on the generated images on user demand. Several detectors can implement pixel binning and or RoIs, typically increasing the effective frame rate or reducing the data bandwidth. Other operations like flip do not affect the detector timing but avoids the need of extra software processing, so hardware-acceleration is preferred.
Isometric transformations like flip and rotation are also necessary to reconstruct the raw images sent by tiled detectors, in which constrains in the detector head design impose different readout sequences for different modules. Lima follows the default image convention where column index X increases towards the right direction and row index Y increases towards the bottom, so <0,0> is the top-left corner. The corresponding flip and/or rotation must be performed before assembling each elemental region in order to follow this rule.
The `lima::processing::geom` namespace provides helpers to manipulate the coordinates during the application of geometric transformations.
## Affine Transformation Matrix
Each geometric transformation can be described by an affine Boost.Gil 3x2 matrix (actually a 3x3 matrix whose last column is always <0,0,1>). The matrix converts the coordinates of a pixel at the input of the transformation to its corresponding pixel in the output:
```math
\begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} = \begin{bmatrix} a & b & 0 \\ c & d & 0 \\ d & e & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix}
```
The third dimension allows translations in the coordinate system.
For scaling transformations, such as binning, the arithmetic precision can be guarantied with rational number arithmetic (e.g. Boost.Rational) or arbitrary precision arithmetic (e.g. Boost.Multiprecision):
```c++
using integral_t = std::ptrdiff_t;
using point_t = boost::gil::point;
using rational_t = boost::rational;
using affine_t = boost::gil::matrix3x2;
```
The affine matrix associated to a sequence of transformation is the multiplication of the individual matrices:
```math
A = A_1 . A_2 ... A_n
```
The `boost::gil::inverse` function returns a transformation matrix to input coordinates from output ones:
```c++
point_t output = input * xform_affine;
BOOST_CHECK(input == output * boost::gil::inverse(xform_affine));
```
To get the coordinate of a pixel after a given transformation, a number of step
- Transform the pixel coordinate to geometric coordinate (e.g. offset to the center of the pixel p' = p + {0.5, 0.5})
- Move the image to the center of the coordinate system
- Apply the transformation matrix m (p' = m * p)
- Move the image back to the first quadrant of the coordinate system
- Transform the geometric coordinate to pixel coordinate (e.g. offset to the center of the pixel p' = p - {0.5, 0.5})
## Available basic transformations
* `bin`
* `crop`
* `flip_up_down`
* `flip_left_right`
* `rotate_90ccw`
* `rotate_180`
* `rotate_90cw`
## Isometric transformations
Eight isometric transformations are identified in Lima:
* isometric_xform_none
* isometric_xform_vflip
* isometric_xform_hflip
* isometric_xform_rot90ccw
* isometric_xform_rot180
* isometric_xform_rot90cw
* isometric_xform_vflip_rot90ccw
* isometric_xform_hflip_rot90ccw
The *any_isometric_xform_t* is a *std::variant* of these transformations.
The implementation represents an isometric transformation as a combination of
three basic operations: vertical flip, horizontal flip and rotation 90 CCW. In
particular, it is a Boost.Hana tuple of three booleans:
```c++
#define LIMA_ISO_XFORM_TUPLE(v, h, r) \
boost::hana::tuple, \
boost::hana::bool_, \
boost::hana::bool_>
```
The result of the successive application of two transformations, which is
also an isometric transformation, is given by the *operator +*. It is implemented
to provide a constexpr, as well as its inverse *operator -*. For any given
transformation, its *inverse* can always obtained:
```c++
template
constexpr auto inverse(const isometric_xform_base_t& t)
{
return isometric_xform_none() - t;
}
```
## Helpers
The following helpers are also defined in `lima::processing::geometry`:
* `rectangle`: represents a selection in a image, providing inclusion and
overlapping calculations
* get_affine: function template returning the affine matrix of an arbitrary
transformation
* get_output_dimensions: function template returning the output dimensions
after an arbitrary transformation
* apply_isometric_xform: result of an rectangle after a transformation