Chapter 8. Registered Voxel Data Types

Table of Contents

8.1. Overview of Registered Voxel Data Types
8.1.1. Registered Voxel Data Types
8.1.2. About the Difference Between Standard, Carrier and Registered Voxel Types
8.2. Implementing Image Processing on Registered Voxel Data Types
8.2.1. First Steps or How to Configure Your Module to Work Fine With Registered Voxel Types
8.2.2. Important Functions For Voxel Types
8.2.3. Examples with Registered Voxel Types
8.2.4. Compile and Runtime Decisions on Standard and Registered Voxel Types
8.2.5. Handling Generalized Registered Voxel Types as Module Parameters
8.3. Limitations of Registered Data Types
8.4. Traps and Pitfalls When Using Registered Voxel Types
8.5. Advanced Issues on Registered Voxel Types
8.5.1. About the Difference Between Standard, Carrier and Registered Voxel Types
8.5.2. Getting and Managing Metadata About Registered Voxel Types
8.5.3. Reducing Generated Code and Compile Times
8.5.4. Configuration of Supported Voxel Types
8.5.5. Working with Templates on Registered Voxel Types Outside the Template Function calculateOutputSubImage
8.5.6. Implementing a New Voxel Data Type by Deriving from MLTypeInfos

Chapter Objectives

This chapter gives an introduction to programming, implementing and registering user-defined data types for voxels:

See Section 8.2.3, “Examples with Registered Voxel Types” for code examples.

8.1. Overview of Registered Voxel Data Types

ML modules normally implement algorithms on integer or floating point typed voxels, such as MLint8, MLuint16 or MLfloat. To support all these types, the image processing parts of ML modules algorithms normally use templates. However, it is not very efficient to integrate new basic data types because each new data type requires the recompilation of all modules and of the ML itself and the size of the generated code grows for each new voxel type. Moreover, precompiled modules from other sites cannot be used on the new voxel types.

To solve this problem the ML provides some specialized data types for voxels, the so-called Carrier Types. They do not implement specific functionality, but provide just an interface to the operations of any other voxel type. A carrier type can be configured to use any user-defined or predefined voxel data type.

This means:

  • All modules which compile the carrier types can use registered voxel types, even if they were created or registered after module compilation.

  • The number of registered voxel types is potentially unlimited.

  • A programmer can add his own voxel types.

  • A module can use carrier types to handle arbitrary voxel types or it can suppress the use of carrier types.

  • An image processing algorithm can use operations such as +, -, *, /, etc., however, it is not obvious what the registered operations really do on the data. It also might be a "no operation" or e.g., a lexicographical comparison operator or "" on vector components.

  • An image processing algorithm can also use explicit voxel types without carrier types which is less universal, but usually faster, because the carrier types are not used as interfaces.

  • Only a limited number of carrier types is implemented/compiled, and their sizes cannot be changed. Hence a registered voxel type uses the smallest fitting carrier type that can store the information of a registered type. Therefore, voxels with a carrier type might need more memory than their registered type imply which requires some precautions (see Section 8.4, “Traps and Pitfalls When Using Registered Voxel Types”).

Application areas for new voxel types could be vector field processing, color voxel filters, voxels with segmentation information (like bit fields, object indices, etc.), matrix/tensor images, complex numbers, quaternions, strings as voxels and many more.

8.1.1. Registered Voxel Data Types

The following voxel types are already registered in the ML:

    • complexf,

    • complexd, and

    • complexld.

    Complex numbers use float, double and long double floating point arithmetics. They make the standard C++ complex data type available and are implemented in the ML as a template class MLTComplexTypeInfos in project MLTypeExtensions.

    • quaternionf,

    • quaterniond, and

    • quaternionld.

    Quaternions use float, double and long double floating point arithmetics. They make the quaternion data type (from project MLLinearAlgebra ) available and are implemented in the ML as a template class MLTQuaternionTypeInfos in project MLTypeExtensions.

    • vecf2 and vec2,

    • vecf3 and vec3,

    • vecf4 and vec4,

    • vecf5 and vec5,

    • vecf6 and vec6,

    • vecf7 and vec7,

    • vecf8 and vec8,

    • vecf9 and vec9,

    • vecf10 and vec10,

    • vecf16 and vec16,

    • vecf32 and vec32, and

    • vecf64 and vec64.

    These voxel types as well as some other specializations of the ScalarVectorTemplate (from project MLLinearAlgebra ) for higher vector dimensions and for float and double data types. They are implemented in the ML as specializations of the template class MLTDoubleVectorTypeInfos in project MLTypeExtensions.

    • matf2 and mat2,

    • matf3 and mat3,

    • matf4 and mat4,

    • matf5 and mat5, and

    • matf6 and mat6.

    These matrix voxel types are implemented in the ML as a template class MLTMatrixTypeInfos in project MLTypeExtensions. The used base types can be found in the project MLLinearAlgebra..

For the registration of these classes, the class TypeInfosBase has been implemented in the project MLTypeExtensions.

[Note]Note

The standard data types MLuint8, MLint8, MLuint16, MLint16, MLuint32, MLint32, MLint64, MLfloat, MLdouble and MLldouble are also registered for the sake of completeness. Thus it is possible to request their type properties as with all the other registered data types. However, the operations (add, subtract, multiply, etc.) on these data types are not redirected by a function table, but the built-in operations of the primitive data types are used because they are faster.

The type information for the standard types are implemented in the ML as specializations of the template class MLTStdTypeInfos.

8.1.2. About the Difference Between Standard, Carrier and Registered Voxel Types

There are three different voxel types that need to be distinguished if you want to understand how the ML works in detail.

  • Standard Voxel Types

    Standard voxel types are the "normal" data types. They are available in many programming languages, such as signed and unsigned 8, 16, 32 and/or 64 bit sized integers, float, double and long double types. They are also the most typical types used for image voxels.

  • Carrier Voxel Types

    Carrier voxel types are interface types that most ML modules handle and compile in the same way as they compile/handle standard voxel types. To the compiler, the behavior of standard voxel types and carrier voxel types is almost identical. That means: carrier voxel types can be added, subtracted, multiplied, cast, shifted and so on as integer or double types can. Carrier voxel types "carry" the data for a registered voxel type and use the functions they provide to operate on that data. Only a limited number of carrier types exists, so the size of a carrier type is always greater than or equal to the size of the data of its carried registered voxel type.

  • Registered Voxel Types

    Registered voxel types are loaded to the application code at runtime. Each registered type provides a function table with basic functions for data addition, subtraction, multiplication, shift and so on. A precompiled carrier type uses these functions to manipulate data such as the voxels of an image. Registered voxel types are indirectly used by a carrier type; their size is always smaller than or equal to the size of the carrier types that use them.

See Section 8.5.1, “About the Difference Between Standard, Carrier and Registered Voxel Types” for a detailed voxel type discussion.

Figure 8.1. Gaps between registered data voxels managed by precompiled carrier voxels

Gaps between registered data voxels managed by precompiled carrier voxels