eLynx SDK v3.3.0
C++ image processing API reference

What are resolution templates?



Why using resolution templates?

To minimize code and have generic algorithms.
To be concrete lets implement Min function without using macro:

ubyte  Min(ubyte iA,  ubyte iB)  { return (iA<iB) ? iA : iB; }
ushort Min(ushort iA, ushort iB) { return (iA<iB) ? iA : iB; }
int    Min(int iA,    int iB)    { return (iA<iB) ? iA : iB; }
float  Min(float iA,  float iB)  { return (iA<iB) ? iA : iB; }
double Min(double iA, double iB) { return (iA<iB) ? iA : iB; }

Now using template we can factorize code writing:

template<typename T>
T Min(T iA, T iB) { return (iA<iB) ? iA : iB; }

What are available resolutions in sdk?

In sdk we define at low level in CoreTypes.h:

An enum eLynx::EResolution has been created.


What are needed resolutions in image processing?

We need:

To have common operators for all of these types we define template eLynx::ResolutionTypeTraits:

template<typename T>
struct ResolutionTypeTraits
{
  static const EResolution _Resolution; // The resolution implementation.
  static const uint   _Bits;            // Number of bits used in resolution
  static const bool   _Signed;          // Does resolution is signed or not
  static const bool   _Floated;         // Does resolution support floating point
  static const T      _Min;             // Min value in resolution range
  static const T      _Max;             // Max value in resolution range
  static const double _MinInDouble;     // Min value in resolution range cast in double resolution
  static const double _MaxInDouble;     // Max value in resolution range cast in double resolution
  static const double _NormScale;       // Scaling coefficient to have normalized double
};

What are the common problems with template resolutions math?

Lets see what's going with simple math first. We implement mean of two values function:

template <typename T>
T elxTestMean1(T iV1, T iV2) 
{ 
  const T sum = iV1 + iV2;
  return (sum / T(2));
}

ubyte a=100, b=200, c=elxTestMean1(a,b);
::printf("Mean(%d, %d) = %d with ubyte\n", a,b,c);

ushort a=100, b=200, c=elxTestMean1(a,b);
::printf("Mean(%d, %d) = %d with ushort\n", a,b,c);

int a=100, b=200, c=elxTestMean1(a,b);
::printf("Mean(%d, %d) = %d with int\n", a,b,c);

float a=100, b=200, c=elxTestMean1(a,b);
::printf("Mean(%f, %f) = %f with float\n", a,b,c);

double a=100, b=200, c=elxTestMean1(a,b);
::printf("Mean(%f, %f) = %f with double\n", a,b,c);

Results are:

With ubyte we expected to have 150 but we get 22. We are facing to overflow error.
ubyte range is [0,255]. 100+200 = 300, but 300 can't be stored with only 8 bits.
300 = 1 0010 1100 in binary it need 9 bits. If we restrict to 8 bits loosing the most significant bit we get 0010 1100 = 44. So with ubyte 100+200=44 and 44/2=22.

How to bypass little sum overflow problem?

We must compute whole operation with more bits and clamp result to original resolution.
We are leading to the same problem with ushort, int and float type.
Lets define a typedef named SumOverflow_type for ResolutionTypeTraits<T> implementations.

We also define _MinS, _MaxS and ClampS:

static const SumOverflow_type _MinS; // min cast into SumOverflow_type
static const SumOverflow_type _MaxS; // max cast into SumOverflow_type

static ubyte ClampS(SumOverflow_type iValue)
{
  if (iValue < _MinS) return _Min;
  if (iValue > _MaxS) return _Max;
  return ubyte(iValue);
}

Now we can write:

template <typename T>
T elxTestMean2(T iV1, T iV2)
{
  typedef typename ResolutionTypeTraits<T>::SumOverflow_type S_type;
  S_type sum = iV1 + iV2;
  sum /= 2;
  return ResolutionTypeTraits<T>::ClampS(sum);
}

And replacing elxTestMean1 by elxTestMean2 the results are:

We can now write simple additions without overflow problem using template resolution. Try now writing a function with more additions. Lets use multiplications:

template <typename T>
T elxFoo1(T iV)
{
  typedef typename ResolutionTypeTraits<T>::SumOverflow_type S_type;
  S_type f = S_type(iV);
  S_type n = (f+1)*(f+2)*(f+3);
  S_type d = f*f;
  f = n / d;
  return ResolutionTypeTraits<T>::ClampS(f);
}

ubyte a=255;
::printf("Foo1(%d) = %d with ubyte\n", a, elxFoo1(a));

ushort a=255;
::printf("Foo1(%d) = %d with ushort\n", a, elxFoo1(a));

int a=255;
::printf("Foo1(%d) = %d with int\n", a, elxFoo1(a));

float a=255;
::printf("Foo1(%f) = %f with float\n", a, elxFoo1(a));

double a=255;
::printf("Foo1(%f) = %f with double\n", a, elxFoo1(a));

Results are:

We get overflow problem because ResolutionTypeTraits<T>::SumOverflow_type has no enough bits for temporary computation. So we must work in higher bit definition. Lets say MulOverflow_type:

template <typename T>
T elxFoo2(T iV)
{
  typedef typename ResolutionTypeTraits<T>::MulOverflow_type M_type;
  M_type f = M_type(iV);
  M_type n = (f+1)*(f+2)*(f+3);
  M_type d = f*f;
  f = n / d;
  return ResolutionTypeTraits<T>::ClampM(f);
}

using elxFoo2 instead of elxFoo1 results are:

With ubyte we wont get 261 but 255 which is the max we can store with 8 bits, result is 'valid'.


Generated on Thu Dec 9 2010 by doxygen 1.7.2