cmtkInverseInterpolationVolumeReconstruction.txx

Go to the documentation of this file.
00001 /*
00002 //
00003 //  Copyright 1997-2010 Torsten Rohlfing
00004 //
00005 //  Copyright 2004-2010 SRI International
00006 //
00007 //  This file is part of the Computational Morphometry Toolkit.
00008 //
00009 //  http://www.nitrc.org/projects/cmtk/
00010 //
00011 //  The Computational Morphometry Toolkit is free software: you can
00012 //  redistribute it and/or modify it under the terms of the GNU General Public
00013 //  License as published by the Free Software Foundation, either version 3 of
00014 //  the License, or (at your option) any later version.
00015 //
00016 //  The Computational Morphometry Toolkit is distributed in the hope that it
00017 //  will be useful, but WITHOUT ANY WARRANTY; without even the implied
00018 //  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019 //  GNU General Public License for more details.
00020 //
00021 //  You should have received a copy of the GNU General Public License along
00022 //  with the Computational Morphometry Toolkit.  If not, see
00023 //  <http://www.gnu.org/licenses/>.
00024 //
00025 //  $Revision: 2398 $
00026 //
00027 //  $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $
00028 //
00029 //  $LastChangedBy: torstenrohlfing $
00030 //
00031 */
00032 
00033 #include <algorithm>
00034 
00035 #include <Base/cmtkHistogramBase.h>
00036 
00037 namespace
00038 cmtk 
00039 {
00040 
00041 template <class TInterpolator>
00042 void
00043 InverseInterpolationVolumeReconstruction<TInterpolator>
00044 ::Interpolation( const ap::real_1d_array& reconstructedPixelArray )
00045 {
00046   this->m_InterpolatedPassImages.clear();
00047   for ( int pass = 0; pass < this->m_NumberOfPasses; ++pass )
00048     {
00049     const UniformVolume* passImage = this->m_OriginalPassImages[pass];
00050     UniformVolume::SmartPtr result( passImage->CloneGrid() );
00051     result->CreateDataArray( TYPE_FLOAT, true/*setToZero*/ );
00052     
00053     const DataGrid::IndexType& passImageDims = passImage->GetDims();
00054     const int passImageDimsX = passImageDims[0], passImageDimsY = passImageDims[1], passImageDimsZ = passImageDims[2];
00055     
00056     const DataGrid::IndexType& correctedImageDims = this->m_CorrectedImage->GetDims();
00057     const int correctedImageDimsX = correctedImageDims[0], correctedImageDimsY = correctedImageDims[1], correctedImageDimsZ = correctedImageDims[2];    
00058 
00059     const AffineXform* passXform = dynamic_cast<const AffineXform*>( this->m_TransformationsToPassImages[pass].GetPtr() )->GetInverse();
00060     
00061 #pragma omp parallel for
00062     for ( int x = 0; x < passImageDimsX; ++x )
00063       {
00064       UniformVolume::CoordinateVectorType v;
00065       int correctedImageGridPoint[3];
00066       Types::Coordinate from[3], to[3];
00067     
00068       for ( int y = 0; y < passImageDimsY; ++y )
00069         {
00070         for ( int z = 0; z < passImageDimsZ; ++z )
00071           {
00072           Types::DataItem interpolatedData = 0;
00073           Types::Coordinate totalWeight = 0;
00074           v = passImage->GetGridLocation( x, y, z );
00075           passXform->ApplyInPlace( v );
00076 
00077           if ( this->m_CorrectedImage->FindVoxel( v, correctedImageGridPoint, from, to ) )
00078             {
00079             const int xx = correctedImageGridPoint[0] + 1 - TInterpolator::RegionSizeLeftRight;
00080             const int yy = correctedImageGridPoint[1] + 1 - TInterpolator::RegionSizeLeftRight;
00081             const int zz = correctedImageGridPoint[2] + 1 - TInterpolator::RegionSizeLeftRight;
00082 
00083             Types::DataItem data;
00084             Types::Coordinate difference[3];
00085             Types::Coordinate interpolationWeights[3][2 * TInterpolator::RegionSizeLeftRight];
00086             for ( int n = 0; n < 3; ++n )
00087               {
00088               difference[n] = (v[n] - from[n]) / (to[n] - from[n]);
00089               for ( int m = 1-TInterpolator::RegionSizeLeftRight; m <= TInterpolator::RegionSizeLeftRight; ++m )
00090                 {
00091                 interpolationWeights[n][m+TInterpolator::RegionSizeLeftRight-1] = TInterpolator::GetWeight(m, difference[n]);
00092                 }
00093               }
00094             
00095             const int iMin = std::max( 0, -xx );
00096             const int iMax = std::min( 2 * TInterpolator::RegionSizeLeftRight, correctedImageDimsX - xx );
00097 
00098             const int jMin = std::max( 0, -yy );
00099             const int jMax = std::min( 2 * TInterpolator::RegionSizeLeftRight, correctedImageDimsY - yy );
00100 
00101             const int kMin = std::max( 0, -zz );
00102             const int kMax = std::min( 2 * TInterpolator::RegionSizeLeftRight, correctedImageDimsZ - zz );
00103 
00104             for ( int k = kMin; k < kMax; ++k )
00105               {
00106               for ( int j = jMin; j < jMax; ++j )
00107                 {
00108                 const Types::Coordinate weightJK = interpolationWeights[1][j] * interpolationWeights[2][k];
00109                 for ( int i = iMin; i < iMax; ++i )
00110                   {
00111                   const Types::Coordinate weightIJK = interpolationWeights[0][i] * weightJK;
00112                   data = reconstructedPixelArray( 1+this->m_CorrectedImage->GetOffsetFromIndex( xx + i, yy + j, zz + k ) );
00113 
00114                   interpolatedData = interpolatedData + data * weightIJK;
00115                   totalWeight += weightIJK;
00116                   }
00117                 }
00118               }
00119             }
00120           
00121           if ( totalWeight == 0 )
00122             result->GetData()->SetPaddingAt( result->GetOffsetFromIndex( x, y, z ) );
00123           else
00124             result->SetDataAt( interpolatedData / totalWeight, x, y, z );
00125           }
00126         }
00127       }
00128     this->m_InterpolatedPassImages.push_back( result );
00129     }
00130 }
00131 
00132 template <class TInterpolator>
00133 void
00134 InverseInterpolationVolumeReconstruction<TInterpolator>
00135 ::ComputeErrorGradientImage( ap::real_1d_array& g )
00136 {
00137   const UniformVolume* correctedImage = this->m_CorrectedImage;
00138   const size_t numberOfPixels = correctedImage->GetNumberOfPixels();
00139   for ( size_t i = 1; i <= numberOfPixels; ++i )
00140     g(i) = 0;
00141 
00142   const DataGrid::IndexType& correctedImageDims = correctedImage->GetDims();
00143   const int correctedImageDimsX = correctedImageDims[0], correctedImageDimsY = correctedImageDims[1];
00144   const int correctedImageDimsXY = correctedImageDimsX*correctedImageDimsY;
00145 
00146   for ( int pass = 0; pass < this->m_NumberOfPasses; ++pass )
00147     {
00148     const UniformVolume* differencePassImage = this->m_DifferencePassImages[pass];
00149     const UniformVolume* interpolatedPassImage = this->m_InterpolatedPassImages[pass];
00150 
00151     const AffineXform* transformationToPassImage = dynamic_cast<const AffineXform*>( this->m_TransformationsToPassImages[pass].GetPtr() );
00152     const AffineXform* inverseTransformationToPassImage = transformationToPassImage->GetInverse();
00153     const DataGrid::IndexType& passImageDims = this->m_InterpolatedPassImages[pass]->GetDims();
00154 
00155     const Types::Coordinate passImageWeight = this->m_PassWeights[pass];
00156 
00157     if ( passImageWeight > 0 )
00158       {
00159 #pragma omp parallel for
00160       for ( size_t offset = 0; offset < numberOfPixels; ++offset )
00161         {
00162         const int correctedImageCurrentGridPoint[3] = 
00163           { (offset % correctedImageDimsXY) % correctedImageDimsX, (offset % correctedImageDimsXY) / correctedImageDimsX, (offset / correctedImageDimsXY) };
00164         
00165         int passImageDependentRegion[6];
00166         this->GetPassImageDependentPixelRegion( passImageDependentRegion, correctedImage, correctedImageCurrentGridPoint, interpolatedPassImage, transformationToPassImage, passImageDims );
00167 
00168         Types::DataItem gradientData = 0;
00169         Types::Coordinate from[3], to[3];
00170         for (int k = passImageDependentRegion[2]; k <= passImageDependentRegion[5]; ++k)
00171           {
00172           for (int j = passImageDependentRegion[1]; j <= passImageDependentRegion[4]; ++j)
00173             {
00174             for (int i = passImageDependentRegion[0]; i <= passImageDependentRegion[3]; ++i)
00175               {
00176               Types::DataItem differenceData;
00177               if ( differencePassImage->GetDataAt( differenceData, i, j, k ) && (differenceData !=0) ) // if differenceData==0, we can skip this, too
00178                 {
00179                 Types::DataItem weight = 0;
00180               
00181                 UniformVolume::CoordinateVectorType v = interpolatedPassImage->GetGridLocation( i, j, k );
00182                 inverseTransformationToPassImage->ApplyInPlace( v );
00183               
00184                 int correctedImageGridPoint[3];
00185                 if ( correctedImage->FindVoxel( v, correctedImageGridPoint, from, to ) )
00186                   {
00187                   const int correctedImageDifference[3] = 
00188                     {
00189                       correctedImageCurrentGridPoint[0] - correctedImageGridPoint[0],
00190                       correctedImageCurrentGridPoint[1] - correctedImageGridPoint[1],
00191                       correctedImageCurrentGridPoint[2] - correctedImageGridPoint[2],
00192                     };
00193                 
00194                   if ( correctedImageDifference[0] > -TInterpolator::RegionSizeLeftRight && correctedImageDifference[1] > -TInterpolator::RegionSizeLeftRight && 
00195                        correctedImageDifference[2] > -TInterpolator::RegionSizeLeftRight && correctedImageDifference[0] <= TInterpolator::RegionSizeLeftRight &&
00196                        correctedImageDifference[1] <= TInterpolator::RegionSizeLeftRight && correctedImageDifference[2] <= TInterpolator::RegionSizeLeftRight )
00197                     {
00198                     weight = passImageWeight;
00199                     for ( int n = 0; n < 3; ++n )
00200                       {
00201                       const Types::Coordinate relative = (v[n] - from[n]) / (to[n] - from[n]);
00202                       weight *= TInterpolator::GetWeight( correctedImageDifference[n], relative );
00203                       }
00204                     }
00205                   }
00206               
00207                 gradientData += weight * differenceData;
00208                 }
00209               }
00210             }
00211           }
00212         
00213         if ( this->m_FourthOrderError )
00214           g(offset+1) += 4 * (gradientData*gradientData*gradientData) / numberOfPixels;
00215         else
00216           g(offset+1) += 2 * gradientData / numberOfPixels;
00217         }
00218       }
00219     }
00220 }
00221 
00222 template<class TInterpolator>
00223 void
00224 InverseInterpolationVolumeReconstruction<TInterpolator>
00225 ::GetPassImageDependentPixelRegion
00226 ( int* region, const UniformVolume* correctedImage, const int* currentCorrectedGridPoint, 
00227   const UniformVolume* passImage, const AffineXform* transformationToPassImage, const DataGrid::IndexType& passImageDims )
00228 {
00229   // compute neighborhood in corrected image from which interpolated pixels are affected by current corrected image pixel
00230   int corners[2][3];
00231   for ( int dim = 0; dim < 3; ++dim )
00232     {
00233     corners[0][dim] = std::max( currentCorrectedGridPoint[dim] - TInterpolator::RegionSizeLeftRight, 0 );
00234     corners[1][dim] = std::min( currentCorrectedGridPoint[dim] + TInterpolator::RegionSizeLeftRight, correctedImage->GetDims()[dim]-1 );
00235     }
00236 
00237   UniformVolume::CoordinateVectorType corners3D[8];
00238   int neighborIdx = 0;
00239   for ( int a = 0; a < 2 ; ++a )
00240     {
00241     for ( int b = 0; b < 2 ; ++b )
00242       {
00243       for ( int c = 0; c < 2; ++c, ++neighborIdx )
00244         {
00245         corners3D[neighborIdx] = correctedImage->GetGridLocation( corners[a][0], corners[b][1], corners[c][2] );
00246         transformationToPassImage->ApplyInPlace( corners3D[neighborIdx] );
00247         }
00248       }
00249     }
00250   
00251   // now get bounding box of transformed region that is aligned with pass image
00252   Vector3D bboxMin = corners3D[0];
00253   Vector3D bboxMax = corners3D[0];
00254   for ( int m = 1; m < 8; ++m )
00255     {
00256     for ( int o = 0; o < 3; ++o )
00257       {
00258       if ( corners3D[m][o] < bboxMin[o] )
00259         {
00260         bboxMin[o] = corners3D[m][o];
00261         }
00262       else
00263         if ( corners3D[m][o] > bboxMax[o] )
00264           {
00265           bboxMax[o] = corners3D[m][o];
00266           }
00267       }
00268     }
00269 
00270   // get pass image pixels indexes corresponding to bounding box
00271   passImage->GetVoxelIndexNoBounds( bboxMin, region+0 );
00272   passImage->GetVoxelIndexNoBounds( bboxMax, region+3 );
00273   
00274   // clip bounding box against pass image boundaries
00275   for ( int dim = 0; dim < 3; ++dim )
00276     {
00277     region[dim] = std::max( region[dim], 0 );
00278     // increment upper indexes by one to compensate for floating point truncation in pixel index lookup.
00279     region[dim+3] = std::min( region[dim+3]+1, passImageDims[dim]-1 );
00280     }
00281 }
00282 
00283 template<class TInterpolator>
00284 void
00285 InverseInterpolationVolumeReconstruction<TInterpolator>
00286 ::FunctionAndGradient
00287 ::Evaluate( const ap::real_1d_array& x, ap::real_value_type& f, ap::real_1d_array& g )
00288 {
00289   this->m_Function->Interpolation( x );
00290   this->m_Function->ComputeApproximationError();
00291   this->m_Function->ComputeErrorGradientImage( g );
00292   const ap::real_value_type msd = f = this->m_Function->GetMeanSquaredError();
00293 
00294   ap::real_value_type lnorm = 0;
00295   if ( this->m_Function->m_ConstraintWeightLNorm > 0 )
00296     {
00297     f += this->m_Function->m_ConstraintWeightLNorm * (lnorm = this->m_Function->ComputeCorrectedImageLaplacianNorm( x ));
00298     this->m_Function->AddLaplacianGradientImage( g, x, this->m_Function->m_ConstraintWeightLNorm );
00299     }
00300 
00301   if ( this->m_Function->GetMaximumError() <= this->m_Function->m_LowestMaxError )
00302     {
00303     this->m_Function->m_LowestMaxError = this->m_Function->GetMaximumError();
00304     const int numberOfPixels = this->m_Function->m_CorrectedImage->GetNumberOfPixels();
00305     for ( int i = 1; i <= numberOfPixels; ++i )
00306       this->m_Function->m_CorrectedImage->SetDataAt( x(i), i-1 );
00307     this->m_Function->m_LowestMaxErrorImage = UniformVolume::SmartPtr( this->m_Function->m_CorrectedImage->Clone( true /*copyData*/ ) );
00308     }
00309   
00310   std::cerr << "f " << f << " MSD " << msd
00311             << " MAX " << this->m_Function->GetMaximumError() 
00312             << " KLD " << this->m_Function->GetOriginalToCorrectedImageKLD( x )
00313             << " LNORM " << lnorm << std::endl;
00314 }
00315 
00316 } // namespace cmtk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines