| IOLink C# 1.11.0

Loading...
Searching...
No Matches
View concept

A `View` is the main entry point to use IOLink in order to access data. For this reason, its concept must be detailed.

# View

Basically, views point toward a data, whatever its origin, as long as it is accessible.

A view points toward one data

This data can be local or remote, on disk or in memory. Multiple views can point toward the same data without duplication.

Many views can point toward the same data

A View can also point toward a data contained in file. Data are accessible directly from the file without copy in memory, but low performance. It can be useful to read a small part of a huge file.

A view can point toward one file content

A view can point toward a remote data, identified by an Uniform Resource Identifier or URI for short. An HTTP connexion is setup with a remote server at view creation but no data will be exchanged before any operations on the view.

A view can point toward one remote file data

According to the data type, you can specialize your view to get type-specific informations. Let's take the case of images. Your `View` object can be casted into an `ImageView`. If the cast is not possible, it means your view is not an image.

An ImageView points toward an image data

A view is strongly linked to a data , wherever it is stored, and follows the RAII idiom (Resource Acquisition Is Initialization). Which means the memory allocated at view creation will be deallocated at view destruction, a file opened at view creation will be closed at destruction, etc.

As views are always exchanged through shared pointers, many users can share the same views, and the associated data will be released once nobody use it any longer.

View characteristics

# ImageView

This kind of view defines an image.

This image can have multiple dimensions:

- 2 dimensions for simple image (width and height)
- 3 dimensions for volume (width, height and depth), video (width, heigh, time series)
- 4 dimensions for animated volume (width, height, depth, time series)

You have access to basic informations like:

- number of dimensions
- shape of the image, its size in all its dimensions.
- pixel type, for example U8: unsigned-8-bit integer, used for grayscale images.

Definition or origin, pixel type, shape and dimensions

Through `ImageView` objects, you do not have always access to the image content. In order to do that, the instance must support a specific capacity: `READ`. Multiple capabilities are available for `ImageView` and it depends on the origin of the data.

For example, if the data is a read-only file content, the `READ` capability will be the only one available. On the other hand, an in-memory image will have all the capacities:

- `READ`: image data can be read.
- `WRITE` image data can be modified.
- `RESHAPE`: image can be resized, and number of dimensions can be changed. Its datatype can be changed too.
- `MEMORY_ACCESS`: you can get a direct pointer on the image data bytes.

## ImageView on disk

IOLink provides factories to handle an `ImageView` onto disk. The difference with the `ImageView` is that you don't have any RAW-access to data.

You can allocate an `ImageView` on disk or decide to create a copy of an existing `ImageView` on disk. Performance will decrease, but it will allow to work more easily in memory-limited environments.

## ImageView with generated data

IOLink provides a factory to create an `ImageView` without any allocated data.

You can allocate an uniform `ImageView`, where all pixels have the same value, defined at the creation.

## Read image content

When you have `READ` capability, you can now read value from a single pixel or pixel values from a complete region.

Pixels are read as follows:

Reading order of pixels

## Data Order

By default, IOLink method to read images is row-major order, which means that in IOLink, the first dimension of images is read first.

row and column major distinction

## Axes Interpretation

IOLink etablishes a convention of priority for dimension order:

- `COLUMN`: image width
- `ROW`: image height
- `SLICE`: image depth
- `CHANNEL`: number of channels
- `SEQUENCE`: just a sequence of image, usually the unit here is time.

`COLUMN` dimension has always priority over `ROW`, which has priority over `SLICE`, etc.

Dimensions cannot be inverted in your image, but according to your needs, some can be dropped:

- image :
  - Dimension 0: `COLUMN`
  - Dimension 1: `ROW`
- volume:
  - Dimension 0: `COLUMN`
  - Dimension 1: `ROW`
  - Dimension 2: `SLICE`
- images sequence:
  - Dimension 0: `COLUMN`
  - Dimension 1: `ROW`
  - Dimension 2: `SEQUENCE`
- animated volume:
  - Dimension 0: `COLUMN`
  - Dimension 1: `ROW`
  - Dimension 2: `SLICE`
  - Dimension 3: `SEQUENCE`
- multi-channel image
  - Dimension 0: `COLUMN`
  - Dimension 1: `ROW`
  - Dimension 2: `CHANNEL`

Some pre-defined image interpretations are available in `AxesInterpretationId`.

Axes interpretations available in IOLink

# Adapting an ImageView

Anytime a new view is created from another one for adaptation or transformation. No duplication of data is done. An implicit adapter is applied on the original view to make the new view fit your needs.

## Region ImageView

A view can point toward a part of the another image. A new view must be created from the original one using the `ImageViewFactory`. By handling this new ImageView, it will be like pointing toward a smaller image.

For example, in the following figure, ImageView 2 is created from the original ImageView, with a region defined by its origin and its shape.

Example of regions in a 2 dimensional image

It can also allow interesting data discrimination.

Example of regions in a 3 dimensional image

In this last image, you have the example of the ImageView 3 and the ImageView 4 which points toward the same region, but the first `ImageView` remains a three-dimensional image, while the second one is considered as a two-dimensional image, as the flat dimension has been removed.

Just to make it clear, and as said before, creating a region view from another one does not imply any memory duplication.

## Change ImageView pixel type

From an original view with a certain pixel type, it is possible to create another view with a different pixel type.

For example, from a view onto a grayscale image which pixel type is unsigned char, you can create a new view onto a true color RGB image which will point toward the same data.

Or the opposite, from a RGB image view, you can create a grayscale view.

When reading, the original pixel samples are transformed to match the new type, and when writing, the given pixels samples are transformed to match the original type. Transformations can imply some local data duplication.

In the following table are given details of some typical transformations, not all of them are listed:

Original Pixel Type | Destination Pixel Type | Sample transformation | Comment
--- | --- | --- | ---
UINT8 | INT8 | (X) --> (X') | X is remapped from [0,255] into [-127, 127]
UINT8 | VEC3_UINT8 | (X) --> (X,X,X) | X is duplicated 3 times
INT16 | FLOAT | (X) --> (X') | X is remapped from [-32767, 32767] into [0.0, 1.0]
DOUBLE | UINT8 | (X) --> (X') | X is remapped from [0.0, 1.0] into [0, 255]
VEC3_UINT8 | VEC2_UINT16 | (X, Y, Z) --> (X', Y') | Z is skipped, X and Y are remapped from [0, 255] into [0, 65536]

## Isolate a channel

From an original `ImageView` with a complex pixel type, at least two channels: `GRAYSCALE` and `ALPHA` for example., You can ask to isolate one particular channel in another view. The result would be a new `ImageView`, with the same shape than the original, but with the data type reduced to its scalar value.

For example:

- `ImageView` VEC3_UINT8 => `ImageView` UINT8
- `ImageView` VEC4_FLOAT => `ImageView` FLOAT
- `ImageView` UINT16 => same `ImageView` UINT16

## Data densification

Each adaptation applied on a view to create a new one can imply some performance reduction, or capacity restriction. E.g. a region view created from an in-memory view won't have the `MEMORY_ACCESS` capability, since data may not be contiguous in memory anymore. You can also have a view toward a remote file with low-performances access.

To solve this problem, you can decide to require data densification. In order to get the previious performance back, to fully work in-memory, and keep the `MEMORY_ACCESS` capacity.

A method in `ImageViewFactory` allows you to load your view in CPU memory. Any adaptation applied on the original view will be definitely applied on the final view.

E.g. if you decide to densify a region view, a small part of a larger image, only the region will be copied into local memory.

Applying this densification onto an already in-memory view won't have any effect.

## Thread safety

A specific adapter can be applied on an ImageView to add locks on every method, so as to make it compatible with multithreaded environments.

By default, views are not threadsafe.

## Disassemble and Assemble an ImageView

It is possible to deinterlace a N-dimensional `ImageView` with a vectorial-typed sample, to create a N+1 dimensional `ImageView`. A new `CHANNEL` dimension is created.

E.g.: an `IMAGE` view will become a ``MULTISPECTRAL_IMAGE` view.

It is also possible to interlace a N-dimension `ImageView` with a `CHANNEL` dimension, so as to create a N-1 dimension `ImageView`. The `CHANNEL` dimension is removed.

E.g.: a `MULTISPECTRAL_IMAGE_SEQUENCE` view will become an `IMAGE_SEQUENCE` view.

Disassembling and assembling a MultiImageView

Remark: disassembling could also be done using the deinterlacing then the stacking methods to create the `CHANNEL` dimension, but it can hinder performances.

## Sub-Sample an ImageView

IOLink provides a way to generate a sub-sampled version of an `ImageView`. It is a simple method to reduce the `ImageView` shape by picking only one sample every N samples, with N configurable, in every dimensions.

# Multi-ImageView

A view can point toward multiple `ImageView` instances. This is a simple way to handle a stack of images. `MultiImageView` object aggregates `ImageView` instances with heterogenous properties.

MultiImageView principle

## Stacking

A method in `ImageViewFactory` can be used to stack N-dimensional images into a N+1-dimensional image. The shape and pixel type of all `ImageView` instances must be equal.

Stacking a multiImageView

As already explained, IOLink follows a convention for dimensional order. This can have an impact on the stack operation in the case where the added dimension is named by the user, and is not in the last position in the created object.

For example: a user handles a `MultiImageView` containing a set of 1000 `ImageView` instances which are known as `IMAGE_SEQUENCE`. These images use the `COLUMN`, `ROW` and `SEQUENCE` dimensions, in this order.

// shape of each ImageView contained in the MultiImageView
shape = { 640, 480, 700 }
640 -> COLUMN dimension size
480 -> ROW dimension size
700 -> SEQUENCE dimension size

All these `IMAGE_SEQUENCE` images could represent a volumetric sequence. Consequently, the user wants to stack all these ImageViews to obtain an unique `VOLUME_SEQUENCE` object. The added `SLICE` dimension is added before the `SEQUENCE` dimension, according to the IOLink convention.

// final ImageView after stacking
// new SLICE dimension is added before the SEQUENCE dimension
shape = { 640, 480, 1000, 700 }

## Un-Stacking

It is also possible to unstack a N-dimension image into N-1 images.

Unstacking into a multiImageView

## Deinterlace an ImageView

As previously seen, it is possible to isolate a channel from an `ImageView` to get a new `ImageView`. Thus it is also possible to entirely deinterlace an `ImageView` to obtain a `MultiImageView` which will contain one `ImageView` for each original channel.

Deinterlacing an ImageView

A deinterlace method from `MultiImageViewFactory` creates this `MultiImageView` from any `ImageView` with at least 2 channels.

## Interlace a MultiImageView

It is also possible to do the opposite. Starting from a MultiImageView containing many frames of teh same shape and datatype, one for each channel, and interlacing these frames to create an `ImageView`.

Interlacing a MultiImageView

An interlace method from `MultiImageViewFactory` creates this `ImageView` from a `MultiImageView` whose frames only contain one channel.

# LodImageView

`LodImageView` ("Lod" for "Level Of Definition") is a container of ImageViews, specialized to handle different resolutions of the same image. This allows to have a version of your dataset for any situation:

- many low-level resolutions versions for a quick load and limited ressources.
- many medium-level resolutions versions for intermediate work.
- a high-level resolution for accurate work, longer to load but with full original details

Different Levels of Definition

Methods provided in the `LodImageView` interface allow to handle these differents LOD images as a pool of `ImageView`.

Regions of Interest