Go to the documentation of this file.00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 #ifndef __cmtkJointHistogram_h_included_
00034 #define __cmtkJointHistogram_h_included_
00035 
00036 #include <cmtkconfig.h>
00037 
00038 #include <Base/cmtkJointHistogramBase.h>
00039 #include <Base/cmtkUniformVolume.h>
00040 #include <Base/cmtkTypedArray.h>
00041 #include <Base/cmtkMathUtil.h>
00042 #include <Base/cmtkTypes.h>
00043 #include <Base/cmtkHistogram.h>
00044 
00045 #include <System/cmtkSmartPtr.h>
00046 
00047 #include <vector>
00048 #include <algorithm>
00049 
00050 namespace
00051 cmtk
00052 {
00053 
00056 
00061 template<class T>
00062 class JointHistogram : 
00064   public JointHistogramBase 
00065 {
00066 protected:
00068   size_t NumBinsX;
00069 
00071   Types::DataItem BinWidthX;
00072 
00074   Types::DataItem BinOffsetX;
00075 
00077   size_t NumBinsY;
00078 
00080   Types::DataItem BinWidthY;
00081 
00083   Types::DataItem BinOffsetY;
00084 
00086   std::vector<T> JointBins;
00087 
00089   size_t m_TotalNumberOfBins;
00090   
00091 public:
00093   typedef JointHistogram<T> Self;
00094 
00096   typedef SmartPointer<Self> SmartPtr;
00097 
00100   JointHistogram () 
00101   {
00102     this->m_TotalNumberOfBins = NumBinsX = NumBinsY = 0; 
00103     BinWidthX = BinWidthY = 1.0;
00104     BinOffsetX = BinOffsetY = 0.0;
00105   }
00106 
00109   JointHistogram( const size_t numBinsX, const size_t numBinsY, const bool reset = true ) 
00110   {
00111     this->m_TotalNumberOfBins = NumBinsX = NumBinsY = 0; 
00112     BinWidthX = BinWidthY = 1.0;
00113     BinOffsetX = BinOffsetY = 0.0;
00114     this->Resize( numBinsX, numBinsY, reset );
00115   }
00116 
00120   Self* Clone () const 
00121   {
00122     return new Self( *this );
00123   }
00124 
00126   void Resize( const size_t numberOfBinsX, const size_t numberOfBinsY, const bool reset = true )
00127   {
00128     this->NumBinsX = numberOfBinsX;
00129     this->NumBinsY = numberOfBinsY;
00130     this->m_TotalNumberOfBins = this->NumBinsX * this->NumBinsY;
00131 
00132     this->JointBins.resize( this->m_TotalNumberOfBins );
00133     
00134     if ( reset ) 
00135       this->Reset();
00136   }
00137 
00139   size_t GetNumBinsX() const 
00140   { 
00141     return this->NumBinsX;
00142   }
00143 
00145   size_t GetNumBinsY() const
00146   { 
00147     return this->NumBinsY;
00148   }
00149 
00152   void SetRangeX( const Types::DataItemRange& range ) 
00153   {
00154     this->BinOffsetX = range.m_LowerBound;
00155     this->BinWidthX = range.Width() / ( this->NumBinsX - 1 );
00156   }
00157 
00160   void SetRangeY( const Types::DataItemRange& range ) 
00161   {
00162     this->BinOffsetY = range.m_LowerBound;
00163     this->BinWidthY = range.Width() / ( this->NumBinsY - 1 );
00164   }
00165   
00168   void SetRangeCenteredX( const Types::DataItemRange& range ) 
00169   {
00170     this->BinWidthX = range.Width() / (this->NumBinsX - 1);
00171     this->BinOffsetX = - this->BinWidthX / 2;
00172   }
00173 
00176   void SetRangeCenteredY( const Types::DataItemRange& range ) 
00177   {
00178     this->BinWidthY = range.Width() / (this->NumBinsY - 1);
00179     this->BinOffsetY = - this->BinWidthY / 2;
00180   }
00181   
00184   const Types::DataItemRange GetRangeX() const 
00185   {
00186     return Types::DataItemRange( this->BinOffsetX, this->BinOffsetX + this->BinWidthX * ( this->NumBinsX - 1) );
00187   }
00188   
00191   const Types::DataItemRange GetRangeY() const 
00192   {
00193     return Types::DataItemRange( this->BinOffsetY, this->BinOffsetY + this->BinWidthY * ( this->NumBinsY - 1) );
00194   }
00195   
00202   void Reset () 
00203   {
00204     std::fill( this->JointBins.begin(), this->JointBins.end(), 0 );
00205   }
00206   
00211   size_t ValueToBinX ( const Types::DataItem value ) const 
00212   {
00213     const size_t binIndex = static_cast<size_t>( (value - BinOffsetX) / BinWidthX );
00214     return std::max<size_t>( 0, std::min<size_t>( NumBinsX-1, binIndex ) );
00215   }
00216   
00221   size_t ValueToBinY ( const Types::DataItem value ) const 
00222   {
00223     const size_t binIndex = static_cast<int>( (value - BinOffsetY) / BinWidthY );
00224     return std::max<size_t>( 0, std::min<size_t>( NumBinsY-1, binIndex ) );
00225   }
00226 
00231   Types::DataItem BinToValueX ( const size_t bin ) const 
00232   {
00233     return BinOffsetX + (bin+0.5) * BinWidthX;
00234   }
00235 
00240   Types::DataItem BinToValueY ( const size_t bin ) const 
00241   {
00242     return BinOffsetY + (bin+0.5) * BinWidthY;
00243   }
00244 
00252   T ProjectToX ( const size_t indexX ) const 
00253   {
00254     T project = 0;
00255     for ( size_t j=0; j < NumBinsY; ++j )
00256       project += this->JointBins[indexX + j * NumBinsX];
00257     
00258     return project;
00259   }
00260   
00268   T ProjectToY ( const size_t indexY ) const 
00269   {
00270     T project = 0;
00271     size_t offset = indexY * NumBinsX;
00272     for ( size_t i = 0; i < NumBinsX; ++i )
00273       project += this->JointBins[i + offset];
00274     
00275     return project;
00276   }
00277 
00279   Histogram<T>* GetMarginalX() const;
00280 
00282   Histogram<T>* GetMarginalY() const;
00283 
00286   T GetBin( const size_t indexX, const size_t indexY ) const 
00287   {
00288     return this->JointBins[ indexX + NumBinsX * indexY ];
00289   }
00290   
00293   T SampleCount () const 
00294   {
00295     T sampleCount = 0;
00296     
00297     for ( size_t idx = 0; idx < this->m_TotalNumberOfBins; ++idx )
00298       {
00299       sampleCount += this->JointBins[idx];
00300       }
00301     
00302     return sampleCount;
00303   }
00304 
00307   T GetMaximumBinValue () const 
00308   {
00309     T maximum = 0;
00310     
00311     size_t idx = 0;
00312     for ( size_t i=0; i<NumBinsY; ++i ) 
00313       {
00314       for ( size_t j=0; j<NumBinsX; ++j, ++idx )
00315         maximum = std::max( maximum, this->JointBins[idx] );
00316       }
00317     
00318     return maximum;
00319   }
00320   
00329   void GetMarginalEntropies ( double& HX, double& HY ) const;
00330 
00336   double GetJointEntropy() const;
00337   
00344   void Increment ( const size_t sampleX, const size_t sampleY ) 
00345   {
00346     ++this->JointBins[sampleX+sampleY*NumBinsX];
00347   }
00348 
00356   void Increment 
00357   ( const size_t sampleX, const size_t sampleY, const T weight ) 
00358   {
00359     this->JointBins[ sampleX + sampleY * NumBinsX ] += weight;
00360   }
00361 
00370   void Decrement ( const size_t sampleX, const size_t sampleY ) 
00371   {
00372     --this->JointBins[sampleX+sampleY*NumBinsX];
00373   }
00374   
00375   void Decrement
00376   ( const size_t sampleX, const size_t sampleY, const Types::DataItem weight ) 
00377   {
00378     this->JointBins[ sampleX + sampleY * NumBinsX ] -= static_cast<T>( weight );
00379   }
00380   
00390   void AddJointHistogram ( const Self& other ) 
00391   {
00392     for ( size_t idx = 0; idx < this->m_TotalNumberOfBins; ++idx )
00393       this->JointBins[idx] += other.JointBins[idx];
00394   }
00395   
00407   void AddHistogramRow( const Histogram<T>& other, const size_t sampleY, const float weight = 1 ) 
00408   {
00409     size_t idx = this->NumBinsX * sampleY;
00410     for ( size_t i = 0; i<NumBinsX; ++i, ++idx )
00411       {
00412       this->JointBins[idx] += static_cast<T>( weight * other[i] );
00413       }
00414   }
00415   
00427   void AddHistogramColumn( const size_t sampleX, const Histogram<T>& other, const float weight = 1 ) 
00428   {
00429     size_t idx = sampleX;
00430     for ( size_t j = 0; j<NumBinsY; ++j, idx += NumBinsX )
00431       this->JointBins[idx] += static_cast<T>( weight * other[j] );
00432   }
00433   
00443   void RemoveJointHistogram ( const Self& other ) 
00444   {
00445     for ( size_t idx = 0; idx < this->m_TotalNumberOfBins; ++idx )
00446       {
00447       this->JointBins[idx] -= other.JointBins[idx];
00448       }
00449   }
00450   
00456   void NormalizeOverX( const double normalizeTo = 1.0 ) 
00457   {
00458     for ( size_t j = 0; j < NumBinsY; ++j ) 
00459       {
00460       const T project = this->ProjectToY( j );
00461       if ( project > 0 )
00462         {
00463         const double factor = normalizeTo / project;
00464         for ( size_t i = 0; i < NumBinsX; ++i )
00465           this->JointBins[ i + NumBinsX * j ] = static_cast<T>( this->JointBins[ i + NumBinsX * j ] * factor );
00466         }
00467       }
00468   }
00469   
00475   void NormalizeOverY( const double normalizeTo = 1.0 ) 
00476   {
00477     for ( size_t i = 0; i < NumBinsX; ++i ) 
00478       {
00479       const T project = this->ProjectToX( i );
00480       if ( project > 0 ) 
00481         {
00482         const double factor = normalizeTo / project;
00483         for ( size_t j = 0; j < NumBinsY; ++j )
00484           this->JointBins[ i + NumBinsX * j ] = static_cast<T>( this->JointBins[ i + NumBinsX * j ] * factor );
00485         }
00486       }
00487   }
00488   
00489   
00490 
00491 
00492 
00493   size_t GetMaximumBinIndexOverX( const size_t j ) const 
00494   {
00495     size_t offset = j * NumBinsX;
00496     
00497     size_t maxIndex = 0;
00498     T maxValue = this->JointBins[ offset ];
00499     
00500     for ( size_t i = 1; i < NumBinsX; ++i ) 
00501       {
00502       offset++;
00503       if ( this->JointBins[ offset ] > maxValue ) 
00504         {
00505         maxValue = this->JointBins[ offset ];
00506         maxIndex = i;
00507         }
00508       }
00509     return maxIndex;
00510   }
00511   
00512   
00513 
00514 
00515 
00516   size_t GetMaximumBinIndexOverY( const size_t i ) const 
00517   {
00518     size_t offset = i;
00519     
00520     size_t maxIndex = 0;
00521     T maxValue = this->JointBins[ offset ];
00522     
00523     for ( size_t j = 1; j < NumBinsY; ++j ) 
00524       {
00525       offset += NumBinsX;
00526       if ( this->JointBins[ offset ] > maxValue ) 
00527         {
00528         maxValue = this->JointBins[ offset ];
00529         maxIndex = j;
00530         }
00531       }
00532     return maxIndex;
00533   }
00534 
00541   double GetMutualInformation( const bool normalized = false ) const 
00542   {
00543     double hX, hY, hXY;
00544     this->GetMarginalEntropies( hX, hY );
00545     hXY = this->GetJointEntropy();
00546     if ( hXY > 0 )
00547       if ( normalized ) 
00548         return (hX + hY) / hXY;
00549       else
00550         return (hX + hY) - hXY;
00551     else
00552       return 0;
00553   }
00554 
00556   double GetCorrelationRatio() const 
00557   {
00558     T sampleCount = this->SampleCount();
00559     if ( ! sampleCount ) return 0;
00560     
00561     double sigSquare = 0, m = 0;
00562     for ( size_t j = 0; j < NumBinsY; ++j ) 
00563       {
00564       sigSquare += ( MathUtil::Square(j) * ProjectToY(j) );
00565       m += (j * ProjectToY(j));
00566       }
00567     
00568     double invSampleCount = 1.0 / sampleCount;
00569     
00570     m *= invSampleCount;
00571     (sigSquare *= invSampleCount) -= MathUtil::Square(m);
00572     
00573     double eta = 0;
00574     for ( size_t i = 0; i < NumBinsX; ++i ) 
00575       {
00576       if ( ProjectToX(i) > 0 ) 
00577         {
00578         double sigSquare_i = 0, m_i = 0;
00579         for ( size_t j = 0; j < NumBinsY; ++j ) 
00580           {
00581           sigSquare_i += (MathUtil::Square(j) * this->JointBins[ i + j * NumBinsX ]);
00582           m_i += (j * this->JointBins[ i + j * NumBinsX ]);
00583           }
00584         m_i *= (1.0 / ProjectToX(i) );
00585         (sigSquare_i *= (1.0 / ProjectToX(i))) -= MathUtil::Square(m_i);
00586         eta += (sigSquare_i * ProjectToX(i));
00587         }
00588       }
00589     
00590     return eta / (sigSquare * sampleCount); 
00591   }
00592 };
00593 
00595 
00596 } 
00597 
00598 #endif // #ifndef __cmtkJointHistogram_h_included_