cmtkVolumeInjectionReconstruction.cxx

Go to the documentation of this file.
00001 /*
00002 //
00003 //  Copyright 1997-2009 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 "cmtkVolumeInjectionReconstruction.h"
00034 
00035 #include <Base/cmtkHistogramBase.h>
00036 #include <Base/cmtkTypedArrayNoiseEstimatorNaiveGaussian.h>
00037 #include <IO/cmtkVolumeIO.h>
00038 #include <System/cmtkProgress.h>
00039 
00040 #include <algorithm>
00041 
00042 namespace
00043 cmtk
00044 {
00045 
00048 
00049 VolumeInjectionReconstruction
00050 ::VolumeInjectionReconstruction( const UniformVolume* originalImage, const int interleaveFactor, const int interleaveAxis )
00051   : m_NumberOfPasses( interleaveFactor ),
00052     m_PassWeights( interleaveFactor ),
00053     m_OriginalImageHistogram(),
00054     m_CorrectedImageHistogram()
00055 {
00056   this->m_OriginalImageHistogram = Histogram<double>::SmartPtr( new Histogram<double>( Self::NumberOfHistogramBins ) );
00057   this->m_CorrectedImageHistogram = Histogram<double>::SmartPtr( new Histogram<double>( Self::NumberOfHistogramBins ) );
00058 
00059   const TypedArray* originalData = originalImage->GetData();
00060   this->SetupHistogramKernels( originalData );
00061 
00062   this->m_CorrectedImage = UniformVolume::SmartPtr( originalImage->CloneGrid() );
00063   this->m_CorrectedImage->CreateDataArray( TYPE_FLOAT );
00064   
00065   // split original image into subvolumes and store these.
00066   this->m_OriginalPassImages.clear();
00067   for ( int pass = 0; pass < this->m_NumberOfPasses; ++pass )
00068     {
00069     UniformVolume::SmartPtr passImage( originalImage->GetInterleavedSubVolume( interleaveAxis, this->m_NumberOfPasses, pass ) );
00070     this->m_OriginalPassImages.push_back( passImage );
00071     }
00072 
00073   std::fill( m_PassWeights.begin(), m_PassWeights.end(), 1.0 );
00074 
00075   this->m_TransformationsToPassImages.clear();
00076   for ( int pass = 0; pass < this->m_NumberOfPasses; ++pass )
00077     {
00078     this->m_TransformationsToPassImages.push_back( AffineXform::SmartPtr( new AffineXform ) );
00079     }
00080 }
00081 
00082 VolumeInjectionReconstruction
00083 ::VolumeInjectionReconstruction( const UniformVolume* reconstructionGrid, std::vector<UniformVolume::SmartPtr>& images )
00084   : m_NumberOfPasses( images.size() ),
00085     m_PassWeights( images.size() ),
00086     m_OriginalImageHistogram( new Histogram<double>( Self::NumberOfHistogramBins ) ),
00087     m_CorrectedImageHistogram( new Histogram<double>( Self::NumberOfHistogramBins ) )
00088 {
00089   const TypedArray* originalData = reconstructionGrid->GetData();
00090   if ( !originalData )
00091     originalData = images[0]->GetData();
00092   this->SetupHistogramKernels( originalData );
00093 
00094   this->m_CorrectedImage = UniformVolume::SmartPtr( reconstructionGrid->CloneGrid() );
00095   this->m_CorrectedImage->CreateDataArray( TYPE_FLOAT );
00096 
00097   this->m_OriginalPassImages = images;
00098   std::fill( m_PassWeights.begin(), m_PassWeights.end(), 1.0 );
00099 
00100   this->m_TransformationsToPassImages.clear();
00101   for ( int pass = 0; pass < this->m_NumberOfPasses; ++pass )
00102     {
00103     this->m_TransformationsToPassImages.push_back( AffineXform::SmartPtr( new AffineXform ) );
00104     }
00105 }
00106 
00107 int
00108 VolumeInjectionReconstruction
00109 ::GuessInterleaveAxis
00110 ( const UniformVolume* image, const int defaultAxis )
00111 {
00112   if ( (image->m_Dims[0] == image->m_Dims[1]) && (image->m_Dims[1] != image->m_Dims[2]) )
00113     return 2;
00114   if ( (image->m_Dims[0] == image->m_Dims[2]) && (image->m_Dims[1] != image->m_Dims[2]) )
00115     return 1;
00116   if ( (image->m_Dims[1] == image->m_Dims[2]) && (image->m_Dims[1] != image->m_Dims[0]) )
00117     return 0;
00118 
00119   if ( (image->m_Delta[0] == image->m_Delta[1]) && (image->m_Delta[1] != image->m_Delta[2]) )
00120     return 2;
00121   if ( (image->m_Delta[0] == image->m_Delta[2]) && (image->m_Delta[1] != image->m_Delta[2]) )
00122     return 1;
00123   if ( (image->m_Delta[1] == image->m_Delta[2]) && (image->m_Delta[1] != image->m_Delta[0]) )
00124     return 0;
00125 
00126   return defaultAxis;
00127 }
00128 
00129 void
00130 VolumeInjectionReconstruction
00131 ::SetupHistogramKernels( const TypedArray* originalData )
00132 {
00133   this->m_OriginalImageRange = originalData->GetRange();
00134   this->m_CorrectedImageHistogram->SetRange( this->m_OriginalImageRange );
00135   this->m_OriginalImageHistogram->SetRange( this->m_OriginalImageRange );
00136   originalData->GetEntropy( *this->m_OriginalImageHistogram, true /*fractional*/ );
00137   
00138   const HistogramType::BinType noiseSigma = TypedArrayNoiseEstimatorNaiveGaussian( originalData, Self::NumberOfHistogramBins ).GetNoiseLevelSigma();
00139   const HistogramType::BinType kernelSigma = Self::NumberOfHistogramBins * noiseSigma / this->m_OriginalImageRange.Width();
00140   size_t kernelRadius = static_cast<size_t>( 1 + 2 * kernelSigma );
00141 
00142   // We now make sure kernel radius is large enough to cover any gaps in the original image histogram.
00143   // This is to avoid infinite KL divergence values
00144   size_t runLengthZeroes = 1;
00145   for ( size_t i = 0; i < Self::NumberOfHistogramBins; ++i )
00146     {
00147     if ( (*this->m_OriginalImageHistogram)[i] == 0 )
00148       {
00149       ++runLengthZeroes;
00150       kernelRadius = std::max( kernelRadius, runLengthZeroes );
00151       }
00152     else
00153       {
00154       runLengthZeroes = 0;
00155       }
00156     }
00157 
00158   // Create Gaussian kernel using the previously determined sigma and cutoff radius.
00159   this->m_OriginalImageIntensityNoiseKernel.resize( kernelRadius );
00160   if ( kernelRadius > 1 )
00161     {
00162     const HistogramType::BinType normFactor = 1.0/(sqrt(2*M_PI) * kernelSigma);
00163     for ( size_t i = 0; i < kernelRadius; ++i )
00164       {
00165       this->m_OriginalImageIntensityNoiseKernel[i] = static_cast<HistogramType::BinType>( normFactor * exp( -MathUtil::Square( 1.0 * i / kernelSigma ) / 2 ) );
00166       }
00167     }
00168   else
00169     {
00170     this->m_OriginalImageIntensityNoiseKernel[0] = static_cast<HistogramType::BinType>( 1 );
00171     }
00172 
00173   // Finally, get original image histogram again, this time using the previously determined kernel.
00174   originalData->GetEntropy( *this->m_OriginalImageHistogram, &this->m_OriginalImageIntensityNoiseKernel[0], this->m_OriginalImageIntensityNoiseKernel.size() );
00175 }
00176 
00177 void
00178 VolumeInjectionReconstruction
00179 ::ComputeTransformationsToPassImages( const int registrationMetric )
00180 {
00181   this->m_TransformationsToPassImages.clear();
00182   
00183   UniformVolume::SmartPtr registerToImage = this->m_ReferenceImage ? this->m_ReferenceImage : this->m_OriginalPassImages[0];
00184   for ( int pass = 0; pass < this->m_NumberOfPasses; ++pass )
00185     {
00186     if ( registerToImage == this->m_OriginalPassImages[pass] )
00187       {
00188       // set identity transformation if current subvolume is used as the reference.
00189       this->m_TransformationsToPassImages.push_back( AffineXform::SmartPtr( new AffineXform ) );
00190       }
00191     else
00192       {
00193       // run the registration
00194       AffineRegistration ar;
00195       ar.SetVolume_1( registerToImage );
00196       ar.SetVolume_2( this->m_OriginalPassImages[pass] );
00197       
00198       ar.AddNumberDOFs( 6 );
00199       
00200       ar.SetInitialAlignCenters( false );
00201       ar.SetNoSwitch( true );
00202       
00203       ar.SetMetric( registrationMetric );
00204       ar.SetExploration( 4 * this->m_CorrectedImage->GetMaxDelta() );
00205       ar.SetAccuracy( .1 * this->m_CorrectedImage->GetMinDelta() );
00206       ar.SetSampling( 2 * this->m_CorrectedImage->GetMaxDelta() ); 
00207       
00208       ar.Register();
00209       
00210       this->m_TransformationsToPassImages.push_back( ar.GetTransformation() );
00211       }
00212     }
00213 }
00214 
00215 void
00216 VolumeInjectionReconstruction
00217 ::VolumeInjectionAnisotropic( const Types::Coordinate kernelSigmaFactor, const Types::Coordinate kernelRadiusFactor )
00218 {
00219   const Types::Coordinate minusOneOverTwoSigmaSquare = -1 / (2 * kernelSigmaFactor*kernelSigmaFactor);
00220                     
00221   UniformVolume::SmartPtr& correctedImage = this->m_CorrectedImage;
00222   TypedArray::SmartPtr correctedImageData = correctedImage->GetData();
00223   const size_t correctedImageNumPixels = correctedImage->GetNumberOfPixels();
00224   const Types::Coordinate correctedDelta[3] = { correctedImage->m_Delta[0], correctedImage->m_Delta[1], correctedImage->m_Delta[2] };
00225 
00226   this->m_NeighorhoodMaxPixelValues.setbounds( 1, correctedImageNumPixels );
00227   this->m_NeighorhoodMinPixelValues.setbounds( 1, correctedImageNumPixels );
00228   for ( size_t i = 1; i <= correctedImageNumPixels; ++i )
00229     {
00230     this->m_NeighorhoodMaxPixelValues(i) = this->m_OriginalImageRange.m_LowerBound;
00231     this->m_NeighorhoodMinPixelValues(i) = this->m_OriginalImageRange.m_UpperBound;
00232     }
00233 
00234   Progress::Begin( 0, correctedImageNumPixels, 1e5, "Anisotropic Volume Injection" );
00235 
00236 #pragma omp parallel for schedule(dynamic)
00237   for ( size_t correctedPx = 0; correctedPx < correctedImageNumPixels; ++correctedPx )
00238     {
00239     if ( (correctedPx % ((size_t)1e5)) == 0 )
00240       Progress::SetProgress( correctedPx );
00241 
00242     ap::real_value_type sum = 0;
00243     ap::real_value_type weight = 0;
00244 
00245     const UniformVolume::CoordinateVectorType vCorrected = correctedImage->GetGridLocation( correctedPx );
00246     
00247     for ( int pass = 0; pass < this->m_NumberOfPasses; ++pass )
00248       {
00249       const ap::real_value_type passImageWeight = this->m_PassWeights[pass];
00250       
00251       if ( passImageWeight > 0 )
00252         {
00253         const UniformVolume* passImage = this->m_OriginalPassImages[pass];
00254         const DataGrid::IndexType& passImageDims = passImage->GetDims();
00255         const Xform* passImageXform = this->m_TransformationsToPassImages[pass];
00256 
00257         const  UniformVolume::CoordinateVectorType vPass = passImageXform->Apply( vCorrected );
00258 
00259         int passGridPosition[3];
00260         passImage->GetVoxelIndexNoBounds( vPass, passGridPosition );
00261 
00262         int passGridFrom[3], passGridTo[3];
00263         for ( int n = 0; n < 3; ++n )
00264           {
00265           passGridFrom[n] = std::max( passGridPosition[n] - static_cast<int>( kernelRadiusFactor ), 0 );
00266           passGridTo[n] = std::min( passGridPosition[n] + static_cast<int>( kernelRadiusFactor ) + 1, passImageDims[n] );
00267           }
00268 
00269         UniformVolume::CoordinateVectorType u, delta;
00270         for ( int k = passGridFrom[2]; k < passGridTo[2]; ++k )
00271           {
00272           u[2] = passImage->GetPlaneCoord( AXIS_Z, k );
00273           delta[2] = (u[2] - vPass[2]) / correctedDelta[2];
00274               
00275           for ( int j = passGridFrom[1]; j < passGridTo[1]; ++j )
00276             {
00277             u[1] = passImage->GetPlaneCoord( AXIS_Y, j );
00278             delta[1] = (u[1] - vPass[1]) / correctedDelta[1];
00279             
00280             for ( int i = passGridFrom[0]; i < passGridTo[0]; ++i )
00281               {                
00282               u[0] = passImage->GetPlaneCoord( AXIS_X, i );
00283               delta[0] = (u[0] - vPass[0]) / correctedDelta[0];
00284               
00285               Types::DataItem passImageData;
00286               if ( passImage->GetDataAt( passImageData, i, j, k ) ) 
00287                 {
00288                 const Types::Coordinate mahalanobis = delta.RootSumOfSquares();
00289                 if ( mahalanobis <= kernelRadiusFactor )
00290                   {
00291                   const ap::real_value_type kernelWeightPixel = passImageWeight * exp( mahalanobis*mahalanobis * minusOneOverTwoSigmaSquare );
00292                   
00293                   sum += passImageData * kernelWeightPixel;
00294                   weight += kernelWeightPixel;
00295                   
00296                   if ( passImageData < this->m_NeighorhoodMinPixelValues(correctedPx+1) )
00297                     this->m_NeighorhoodMinPixelValues(correctedPx+1) = passImageData;
00298                   if ( passImageData > this->m_NeighorhoodMaxPixelValues(correctedPx+1) )
00299                     this->m_NeighorhoodMaxPixelValues(correctedPx+1) = passImageData;
00300                   }
00301                 }
00302               }
00303             }
00304           }
00305         }
00306       }
00307     if ( weight > 0 )
00308       correctedImageData->Set( static_cast<Types::DataItem>( sum / weight ), correctedPx );
00309     else
00310       correctedImageData->SetPaddingAt( correctedPx );
00311     }
00312 
00313   Progress::Done();
00314 }
00315 
00316 void
00317 VolumeInjectionReconstruction
00318 ::VolumeInjectionIsotropic( const Types::Coordinate kernelSigma, const Types::Coordinate kernelRadius )
00319 {
00320   const size_t correctedImageNumPixels = this->m_CorrectedImage->GetNumberOfPixels();
00321   const DataGrid::IndexType& splattedImageDims = this->m_CorrectedImage->GetDims();
00322 
00323   this->m_CorrectedImage->GetData()->ClearArray();
00324   
00325   this->m_NeighorhoodMaxPixelValues.setbounds( 1, correctedImageNumPixels );
00326   this->m_NeighorhoodMinPixelValues.setbounds( 1, correctedImageNumPixels );
00327   for ( size_t i = 1; i <= correctedImageNumPixels; ++i )
00328     {
00329     this->m_NeighorhoodMaxPixelValues(i) = this->m_OriginalImageRange.m_LowerBound;
00330     this->m_NeighorhoodMinPixelValues(i) = this->m_OriginalImageRange.m_UpperBound;
00331     }
00332 
00333   const int kernelRadiusIndex[3] = 
00334   {
00335     1 + static_cast<int>( kernelRadius / this->m_CorrectedImage->m_Delta[0] ),
00336     1 + static_cast<int>( kernelRadius / this->m_CorrectedImage->m_Delta[1] ),
00337     1 + static_cast<int>( kernelRadius / this->m_CorrectedImage->m_Delta[2] )
00338   };
00339 
00340   const Types::Coordinate kernelRadiusSquare = kernelRadius * kernelRadius;
00341   const Types::Coordinate minusOneOverTwoSigmaSquare = -1 / (2 * kernelSigma*kernelSigma);
00342 
00343   std::vector<ap::real_value_type> kernelWeights( correctedImageNumPixels );
00344   std::fill( kernelWeights.begin(), kernelWeights.end(), 0 );
00345       
00346   std::vector<ap::real_value_type> splattedImage( correctedImageNumPixels );
00347   std::fill( splattedImage.begin(), splattedImage.end(), 0 );  
00348   
00349   Progress::Begin( 0, this->m_NumberOfPasses, 1, "Isotropic Volume Injection" );
00350 
00351   for ( int pass = 0; pass < this->m_NumberOfPasses; ++pass )
00352     {
00353     Progress::SetProgress( pass );
00354 
00355     const ap::real_value_type passImageWeight = this->m_PassWeights[pass];
00356 
00357     if ( passImageWeight > 0 )
00358       {
00359       const UniformVolume* passImage = this->m_OriginalPassImages[pass];
00360       AffineXform::SmartPtr affinePassImageXform = AffineXform::SmartPtr::DynamicCastFrom( this->m_TransformationsToPassImages[pass] );
00361       const AffineXform* passImageXformInverse = (affinePassImageXform) ? affinePassImageXform->GetInverse() : static_cast<const AffineXform*>( NULL );
00362       
00363       const int passImageNumPixels = passImage->GetNumberOfPixels();
00364       for ( int offset = 0; offset < passImageNumPixels; ++offset )
00365         {      
00366         Types::DataItem passImageData;
00367         if ( passImage->GetDataAt( passImageData, offset ) ) 
00368           {
00369           int x, y, z;
00370           passImage->GetIndexFromOffset( offset, x, y, z );
00371           
00372           UniformVolume::CoordinateVectorType v = passImage->GetGridLocation( x, y, z );
00373           if ( passImageXformInverse )
00374             {
00375             passImageXformInverse->ApplyInPlace( v );
00376             }
00377           else
00378             {
00379             this->m_TransformationsToPassImages[pass]->ApplyInverseInPlace( v );
00380             }
00381           
00382           int targetGridPosition[3];
00383           if ( this->m_CorrectedImage->FindVoxel( v, targetGridPosition ) )
00384             {
00385             // check if neighbours are outside - if yes, set new neighbour ranges
00386             int targetGridFrom[3], targetGridTo[3];
00387             for ( int n = 0; n < 3; ++n )
00388               {
00389               targetGridFrom[n] = std::max( targetGridPosition[n] - kernelRadiusIndex[n], 0 );
00390               targetGridTo[n] = std::min( targetGridPosition[n] + kernelRadiusIndex[n] + 1, splattedImageDims[n] );
00391               }
00392             
00393             UniformVolume::CoordinateVectorType u;
00394             for ( int k = targetGridFrom[2]; k < targetGridTo[2]; ++k )
00395               {
00396               u[2] = this->m_CorrectedImage->GetPlaneCoord( AXIS_Z, k );
00397               
00398               for ( int j = targetGridFrom[1]; j < targetGridTo[1]; ++j )
00399                 {
00400                 u[1] = this->m_CorrectedImage->GetPlaneCoord( AXIS_Y, j );
00401                 
00402                 size_t splattedImageOffset = this->m_CorrectedImage->GetOffsetFromIndex( targetGridFrom[0], j, k );
00403                 for ( int i = targetGridFrom[0]; i < targetGridTo[0]; ++i, ++splattedImageOffset )
00404                   {                
00405                   u[0] = this->m_CorrectedImage->GetPlaneCoord( AXIS_X, i );
00406                   
00407                   const Types::Coordinate distanceSquare = ( u-v ).SumOfSquares();
00408                   if ( distanceSquare <= kernelRadiusSquare )
00409                     {
00410                     const ap::real_value_type kernelWeightPixel = passImageWeight * exp( distanceSquare * minusOneOverTwoSigmaSquare );
00411                     
00412                     splattedImage[splattedImageOffset] += passImageData * kernelWeightPixel;
00413                     kernelWeights[splattedImageOffset] += kernelWeightPixel;
00414                     
00415                     if ( passImageData < this->m_NeighorhoodMinPixelValues(splattedImageOffset+1) )
00416                       this->m_NeighorhoodMinPixelValues(splattedImageOffset+1) = passImageData;
00417                     if ( passImageData > this->m_NeighorhoodMaxPixelValues(splattedImageOffset+1) )
00418                       this->m_NeighorhoodMaxPixelValues(splattedImageOffset+1) = passImageData;
00419                     }
00420                   }
00421                 }
00422               }
00423             }
00424           }
00425         }      
00426       }
00427     }
00428   
00429 #pragma omp parallel for
00430   for ( size_t idx = 0; idx < correctedImageNumPixels; ++idx )
00431     {
00432     const Types::DataItem kernelWeightPixel = static_cast<Types::DataItem>( kernelWeights[idx] );
00433     if ( kernelWeightPixel > 0 ) // check if pixel is a neighbour
00434       {
00435       this->m_CorrectedImage->SetDataAt( static_cast<Types::DataItem>( splattedImage[idx] / kernelWeightPixel ), idx ); // Set normalized data on grid
00436       }
00437     }
00438   
00439   Progress::Done();
00440 }
00441 
00442 UniformVolume::SmartPtr&
00443 VolumeInjectionReconstruction
00444 ::GetCorrectedImage()
00445 {
00446   return this->m_CorrectedImage;
00447 }
00448 
00449 void
00450 VolumeInjectionReconstruction
00451 ::SetReferenceImage( UniformVolume::SmartPtr& referenceImage )
00452 {
00453   this->m_ReferenceImage = referenceImage;
00454 }
00455 
00456 ap::real_value_type
00457 VolumeInjectionReconstruction
00458 ::GetOriginalToCorrectedImageKLD( const ap::real_1d_array& x )
00459 {
00460   this->m_CorrectedImageHistogram->Reset();
00461   for ( int i = x.getlowbound(); i <= x.gethighbound(); ++i )
00462     this->m_CorrectedImageHistogram->AddWeightedSymmetricKernel
00463         ( this->m_CorrectedImageHistogram->ValueToBin( static_cast<Types::DataItem>( x(i) ) ), this->m_OriginalImageIntensityNoiseKernel.size(), &this->m_OriginalImageIntensityNoiseKernel[0] );
00464   const ap::real_value_type kld = this->m_CorrectedImageHistogram->GetKullbackLeiblerDivergence( *this->m_OriginalImageHistogram );
00465 
00466   return kld;
00467 }
00468 
00469 ap::real_value_type
00470 VolumeInjectionReconstruction
00471 ::ComputeCorrectedImageLaplacianNorm( const ap::real_1d_array& correctedImagePixels )
00472 {
00473   const UniformVolume* correctedImage = this->m_CorrectedImage;
00474   const size_t correctedImageNumPixels = correctedImage->GetNumberOfPixels();
00475   this->m_CorrectedImageLaplacians.resize( correctedImageNumPixels );
00476 
00477   const DataGrid::IndexType& correctedImageDims = correctedImage->GetDims();
00478   const int nextI = 1;
00479   const int nextJ = nextI * correctedImageDims[0];
00480   const int nextK = nextJ * correctedImageDims[1];
00481 
00482   ap::real_value_type lnorm = 0;
00483 #pragma omp parallel for reduction(+:lnorm)
00484   for ( size_t idx = 1; idx <= correctedImageNumPixels; ++idx )
00485     {
00486     int x, y, z;
00487     correctedImage->GetIndexFromOffset( idx-1, x, y, z );
00488     
00489     const int xm = (x>0) ? idx-nextI : idx+nextI;
00490     const int ym = (y>0) ? idx-nextJ : idx+nextJ;
00491     const int zm = (z>0) ? idx-nextK : idx+nextK;
00492     
00493     const int xp = (x+1 < correctedImageDims[0]) ? idx+nextI : idx-nextI;
00494     const int yp = (y+1 < correctedImageDims[1]) ? idx+nextJ : idx-nextJ;
00495     const int zp = (z+1 < correctedImageDims[2]) ? idx+nextK : idx-nextK;
00496     
00497     const ap::real_value_type l = 
00498       correctedImagePixels( xm ) + correctedImagePixels( xp ) +
00499       correctedImagePixels( ym ) + correctedImagePixels( yp ) +
00500       correctedImagePixels( zm ) + correctedImagePixels( zp ) - 
00501       6 * correctedImagePixels( idx );
00502     
00503     this->m_CorrectedImageLaplacians[idx-1] = l;
00504     lnorm += l*l;
00505     }
00506   return lnorm / correctedImageNumPixels;
00507 }
00508 
00509 void
00510 VolumeInjectionReconstruction
00511 ::AddLaplacianGradientImage( ap::real_1d_array& g, const ap::real_1d_array&, const ap::real_value_type weight ) const
00512 {
00513   const UniformVolume* correctedImage = this->m_CorrectedImage;
00514   const size_t correctedImageNumPixels = correctedImage->GetNumberOfPixels();
00515   const DataGrid::IndexType& correctedImageDims = correctedImage->GetDims();
00516 
00517   const int nextI = 1;
00518   const int nextJ = nextI * correctedImageDims[0];
00519   const int nextK = nextJ * correctedImageDims[1];
00520   
00521 #pragma omp parallel for
00522   for ( size_t idx = 0; idx < correctedImageNumPixels; ++idx )
00523     {
00524     int x, y, z;
00525     correctedImage->GetIndexFromOffset( idx, x, y, z );
00526     
00527     const int xm = (x>0) ? idx-nextI : idx+nextI;
00528     const int ym = (y>0) ? idx-nextJ : idx+nextJ;
00529     const int zm = (z>0) ? idx-nextK : idx+nextK;
00530 
00531     const int xp = (x+1 < correctedImageDims[0]) ? idx+nextI : idx-nextI;
00532     const int yp = (y+1 < correctedImageDims[1]) ? idx+nextJ : idx-nextJ;
00533     const int zp = (z+1 < correctedImageDims[2]) ? idx+nextK : idx-nextK;
00534 
00535     g(idx+1) += (2 * weight / correctedImageNumPixels) *
00536       ( this->m_CorrectedImageLaplacians[xm] + this->m_CorrectedImageLaplacians[xp] +
00537         this->m_CorrectedImageLaplacians[ym] + this->m_CorrectedImageLaplacians[yp] +
00538         this->m_CorrectedImageLaplacians[zm] + this->m_CorrectedImageLaplacians[zp] - 
00539         6 * this->m_CorrectedImageLaplacians[idx] );
00540     }
00541 }
00542 
00543 } // namespace cmtk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines