ML Reference
|
With MeVisLab 2.3 we decided to abandon the concept of the CarrierTypes.
MeVisLab allows to process images of non-scalar voxel types like complex numbers, vectors or matrices (called extended types by us).
Previously we had a mechanism in place that allowed to use all the common C++ operators on any of the registered extended types by wrapping them in so-called CarrierTypes of certain predefined sizes, which operated through tables of operations on the currently selected type. This allowed to write image algorithms on all registered voxel types by using templated methods which were instantiated for all scalar and all CarrierTypes - usually the instantiation was done through some multiplexing C macros provided by the ML. This was fairly easy, but had some drawbacks when you really needed to support extended types:
So we decided to abandon the CarrierTypes.
Some functions that had to do with handling of CarrierTypes have been removed or simply don't do anything anymore.
ML modules that only work on scalar voxel values probably won't need adaptation. The old multiplexing macros now default to only support the scalar types, but are deprecated in favor of macros with a name that better reflects the changed semantics. We also changed the arguments to ml::Module::setVoxelDataTypeSupport. The old enum values still work, but map to new values with sometimes different meaning. Please consult the documentation, especially if you used this method with the values FULLY_OPERATIONAL, MINIMUM_OPERATIONAL or even PARTIALLY_OPERATIONAL.
There are some new macros that additionally support some selected default extended types, but chances are that these don't fit your needs. We recommend that module authors that need to support selected extended types switch to the new typed output handlers, which have far more flexibility in what input and output types they support. There are some examples in the documentation for the class ml::TypedCalculateOutputImageHandler. Look also at the end of the file mlTypedHandlers.h for an example on how to define your own set of types. If you are in doubt on how to use this, don't hesitate to ask us at the MeVisLab forum; we acknowledge that this is slightly more complex than before.
Another challenge that arises from this change is when compiling code that supports scalar and extended types at the same time. The CarrierTypes had all the operators defined that the scalar types have, so compiling was no problem. With the real extended types the compiler will complain over each missing operator or conversion, so one needs to differentiate the code paths for scalar types and extended types. A simple "if" will not work for this, since the compiler will try to compile both code paths, even if the condition can be evaluated at compile-time. The solution for such problems can be found in mlTypeTraits.h, either in the specialized casts and the derived types provided by ml::TypeTraits or by using the OverloadSelector functions to switch to different function implementations depending on a template type.
This boils down to either using code like this:
typedef typename TypeTraits<DATATYPE>::IntermediateType IntermediateType; // IntermediateType is double for scalar types and // DATATYPE itself for all other types: IntermediateType retVal = ml_cast_from_scalar<IntermediateType>(0); for (size_t c=0; c < indexTabSize; c++){ retVal += static_cast<IntermediateType>(inCursor[indexTab[c]]) * ml_scalar_factor_cast<IntermediateType>(valTab[c]); } // this cast does rounding _and_ clamping for the built-in integer types: *outCursor = ml_cast_from_intermediate_type<DATATYPE>(retVal);
or like this:
// function for handling scalar voxel types template<DATATYPE> void doKernel(DATATYPE* inCursor, DATATYPE* outCursor, ..., OverloadSelector::OnTrue) { MLdouble retVal = 0.0; for (size_t c=0; c < indexTabSize; c++){ retVal += (inCursor[indexTab[c]] * valTab[c]); } *outCursor = static_cast<DATATYPE>(retVal); } // code for handling non-scalar voxel types template<DATATYPE> void doKernel(DATATYPE* inCursor, DATATYPE* outCursor, ..., OverloadSelector::OnFalse) { DATATYPE retVal = 0; for (size_t c=0; c < indexTabSize; c++){ retVal += static_cast<DATATYPE>(inCursor[indexTab[c]] * valTab[c]); } *outCursor = retVal; } ... // this call will call different functions, depending on what type DATATYPE is: doKernel(inCursor, outCursor, ..., OverloadSelector::isScalarType<DATATYPE>());
We also decided to not support the long double voxel type anymore. Under Windows long double only had normal double precision anyway, because we never had activated the correct compiler flags, so we assume that nobody really used that as voxel data type. This change makes the ML interface simpler at many places, but some old modules might need to be switched from long double to double variables at some places to avoid warnings or compile errors.