1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
|
/*
//
// Copyright 1997-2009 Torsten Rohlfing
//
// Copyright 2004-2012 SRI International
//
// This file is part of the Computational Morphometry Toolkit.
//
// http://www.nitrc.org/projects/cmtk/
//
// The Computational Morphometry Toolkit is free software: you can
// redistribute it and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// The Computational Morphometry Toolkit is distributed in the hope that it
// will be useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with the Computational Morphometry Toolkit. If not, see
// <http://www.gnu.org/licenses/>.
//
// $Revision: 5436 $
//
// $LastChangedDate: 2018-12-10 19:01:20 -0800 (Mon, 10 Dec 2018) $
//
// $LastChangedBy: torstenrohlfing $
//
*/
#ifndef __cmtkGroupwiseRegistrationFunctionalBase_h_included_
#define __cmtkGroupwiseRegistrationFunctionalBase_h_included_
#include <cmtkconfig.h>
#include <Base/cmtkFunctional.h>
#include <Base/cmtkUniformVolume.h>
#include <Base/cmtkXform.h>
#include <System/cmtkSmartPtr.h>
#include <System/cmtkThreads.h>
#include <vector>
namespace
cmtk
{
/** \addtogroup Registration */
//@{
/** Base class for groupwise registration functionals.
* This class provides the lowest-level components of groupwise image registration, such as
* image IO, preprocessing, generic access to transformation and image vectors, and parameters
* common to all algorithms such as zero-sum optimization.
*/
class GroupwiseRegistrationFunctionalBase :
/** Inherit from abstract functional base class. */
public Functional
{
public:
/// Type of parent class.
typedef Functional Superclass;
/// Type of this class.
typedef GroupwiseRegistrationFunctionalBase Self;
/// Smart pointer.
typedef SmartPointer<Self> SmartPtr;
/// Exception for "bad transformation".
class BadXform {};
/// Constructor.
GroupwiseRegistrationFunctionalBase();
/// Destructor.
virtual ~GroupwiseRegistrationFunctionalBase();
/** Set flag for freeing and rereading images.
* If registration uses smoothed images, the original data can be freed after smoothing
* and reread from the file system if needed again. This saves roughly 1/2 of memory
* allocation.
*/
virtual void SetFreeAndRereadImages( const bool flag = true )
{
this->m_FreeAndRereadImages = flag;
}
/// Set flag for repeated histogram-based intensity matching.
virtual void SetRepeatIntensityHistogramMatching( const bool flag = true )
{
this->m_RepeatIntensityHistogramMatching = flag;
if ( flag )
{
this->SetFreeAndRereadImages( false );
}
}
/** Create template grid based on target images.
* The template image is constructed so that is has the maximum number of
* pixels of all targets in each dimension, and the minimum isotropic
* pixel size.
*
*\param targets The vector of target images for this groupwise registration.
*\param downsample Downsampling factor. The voxel size in the template
* image is increased by this factor.
*/
virtual void CreateTemplateGridFromTargets( const std::vector<UniformVolume::SmartPtr>& targets, const int downsample = 0 );
/** Create template grid based on geometry.
*/
virtual void CreateTemplateGrid( const DataGrid::IndexType& dims /*!< Template grid dimensions */, const UniformVolume::CoordinateVectorType& deltas /*!< Template grid deltas (i.e., pixel size). */ );
/** Set template grid.
*/
virtual void SetTemplateGrid( UniformVolume::SmartPtr& templateGrid /*!< The template grid that defines size and resolution for the implicit registration template. */,
const int downsample = 1 /*!< Downsampling factor */, const bool useTemplateData = false /*!< Flag to use template data, not just grid */ );
/** Retrieve the template grid.
*/
virtual UniformVolume::SmartPtr& GetTemplateGrid()
{
return this->m_TemplateGrid;
}
/** Retrieve the template grid.
*/
virtual const UniformVolume* GetTemplateGrid() const
{
return this->m_TemplateGrid;
}
/** Set target images.
*\param tImages Vector of all images to be registered.
*/
virtual void SetTargetImages( std::vector<UniformVolume::SmartPtr>& tImages );
/** Get the original target images.
*/
virtual void GetOriginalTargetImages( std::vector<UniformVolume::SmartPtr>& tImages )
{
tImages = this->m_OriginalImageVector;
}
/** Get the original target images.
*/
virtual std::vector<UniformVolume::SmartPtr>& GetOriginalTargetImages()
{
return this->m_OriginalImageVector;
}
/** Get number of target images.
*/
virtual size_t GetNumberOfTargetImages() const
{
return this->m_OriginalImageVector.size();
}
/** Get a smart pointer to one original target image.
*/
virtual UniformVolume::SmartPtr GetOriginalTargetImage( const size_t imageIdx )
{
return this->m_OriginalImageVector[imageIdx];
}
/** Get a constant pointer to one original target image.
*/
virtual const UniformVolume* GetOriginalTargetImage( const size_t imageIdx ) const
{
return this->m_OriginalImageVector[imageIdx];
}
/** Set Gaussian smoothing kernel width for target images.
* Non-positive values turn off smoothing.
*/
virtual void SetGaussianSmoothImagesSigma( const Types::Coordinate gaussianSmoothImagesSigma )
{
this->m_GaussianSmoothImagesSigma = gaussianSmoothImagesSigma;
}
/** Set user-defined image background value to be substituted outside the field of view. */
virtual void SetUserBackgroundValue( const byte value = 0 )
{
this->m_UserBackgroundValue = value;
this->m_UserBackgroundFlag = true;
}
/** Unset user-defined image background value. */
virtual void UnsetUserBackgroundValue()
{
this->m_UserBackgroundFlag = false;
}
/// Set flag for zero-sum updates.
virtual void SetForceZeroSum( const bool forceZeroSum = true )
{
this->m_ForceZeroSum = forceZeroSum;
}
/// Set count for restricted zero-sum updates.
virtual void SetForceZeroSumFirstN( const size_t forceZeroSumFirstN )
{
this->m_ForceZeroSumFirstN = forceZeroSumFirstN;
this->m_ForceZeroSum = this->m_ForceZeroSum || (this->m_ForceZeroSumFirstN>0);
}
/** Set range of currently active images.
* The "to" parameter is the index of the last active image plus one, so
* it can be used directly as the upper bound in a "for" loop.
*/
virtual void SetActiveImagesFromTo( const size_t from, const size_t to )
{
this->m_ActiveImagesFrom = from;
this->m_ActiveImagesTo = to;
}
/** Set range of currently active transformations.
* The "to" parameter is the index of the last active transformation plus one, so
* it can be used directly as the upper bound in a "for" loop.
*/
virtual void SetActiveXformsFromTo( const size_t from, const size_t to )
{
this->m_ActiveXformsFrom = from;
this->m_ActiveXformsTo = to;
}
/** Set probabilistic sampling density.
*/
virtual void SetProbabilisticSampleDensity( const float density )
{
this->m_ProbabilisticSampleDensity = density;
}
/** Set number of iterations after which probabilistic samples are updated.
*/
virtual void SetProbabilisticSampleUpdatesAfter( const int iterations )
{
this->m_ProbabilisticSampleUpdatesAfter = iterations;
this->m_ProbabilisticSampleUpdatesSince = 0;
}
/** Set transformations.
*/
void SetXforms( const std::vector<Xform::SmartPtr>& xformVector )
{
this->m_XformVector.resize( xformVector.size() );
for ( size_t i = 0; i < this->m_XformVector.size(); ++i )
{
this->m_XformVector[i] = xformVector[i];
}
}
/** Get coordinate transformation for one image in the group.
*\param idx Index of the volume/transformation.
*\return Transformation for the selected volume.
*/
virtual const Xform* GetGenericXformByIndex( const size_t idx ) const
{
return this->m_XformVector[idx];
}
/** Get coordinate transformation for one image in the group.
*\param idx Index of the volume/transformation.
*\return Transformation for the selected volume.
*/
virtual Xform::SmartPtr GetGenericXformByIndex( const size_t idx )
{
return this->m_XformVector[idx];
}
/** Get coordinate transformation for one active image in the group.
*\param idx Index of the volume/transformation.
*\return Transformation for the selected volume.
*/
virtual const Xform* GetGenericActiveXformByIndex( const size_t idx ) const
{
return this->m_XformVector[idx + this->m_ActiveXformsFrom];
}
/** Get coordinate transformation for one active image in the group.
*\param idx Index of the volume/transformation.
*\return Transformation for the selected volume.
*/
virtual Xform::SmartPtr GetGenericActiveXformByIndex( const size_t idx )
{
return this->m_XformVector[idx + this->m_ActiveXformsFrom];
}
/** Get parameter stepping in milimeters.
*\param idx Parameter index.
*\param mmStep Desired step length. This is typically used as a scalar factor for the default (1mm) step size.
*\return Step of given parameter that corresponds to 1 mm effective motion.
*/
virtual Types::Coordinate GetParamStep( const size_t idx, const Types::Coordinate mmStep = 1 ) const
{
const size_t xformIdx = idx / this->m_ParametersPerXform;
if ( (xformIdx >= this->m_ActiveXformsFrom) && (xformIdx < this->m_ActiveXformsTo) )
{
return this->m_XformVector[xformIdx]->GetParamStep( idx % this->m_ParametersPerXform, this->m_ImageVector[xformIdx]->m_Size, mmStep );
}
else
{
return 0.0;
}
}
/** Return the functional's parameter vector dimension.
* We assume that all transformations have the same number of parameters.
* This is true for affine transformations.
*/
virtual size_t ParamVectorDim() const
{
return this->m_ParametersPerXform * this->m_XformVector.size();
}
/** Return the number of variable parameters of the transformation.
*\return This function returns the same value as ParamVectorDim().
* Non-varying parameters (e.g., rotation centers) are handled via
* parameter step values.
*/
virtual size_t VariableParamVectorDim() const
{
return this->ParamVectorDim();
}
/** Get parameter vector.
*/
virtual void GetParamVector( CoordinateVector& v );
/** Set parameter vector.
*/
virtual void SetParamVector( CoordinateVector& v );
/** Set parameter vector for a given transformation.
*/
virtual void SetParamVector( CoordinateVector& v, const size_t xformIdx );
/** Set single parameter value.
*/
virtual void SetParameter( const size_t param, const Types::Coordinate value );
/** Set single parameter value with separate xform and parameter index.
*/
virtual void SetParameter( const size_t xform, const size_t param, const Types::Coordinate value );
/** Evaluate functional with given parameter vector.
* This function sets the current parameter vector, reformats all image data
* into template space according to the current transformations, and calls
* Evaluate() to compute the entropy-based functional value.
*\param v Parameter vector.
*\return Const function value for given parameters.
*/
virtual Self::ReturnType EvaluateAt ( CoordinateVector& v );
/** Compute functional value and gradient.
*\param v Parameter vector.
*\param g The extimated gradient of the functional is stored in this vector.
*\param step Step size for finite difference gradient approximation. Default
* is 1 mm.
*\return Const function value for given parameters.
*/
virtual Self::ReturnType EvaluateWithGradient( CoordinateVector& v, CoordinateVector& g, const Types::Coordinate step = 1 );
/** Allocate storage for reformatted images etc.
* This function must be called AFTER setting template grid and target images, but BEFORE
* any calls to Evaluate, EvaluateAt, or EvaluateWithGradient.
*/
virtual void AllocateStorage();
/// Write all images for debug purposes.
void DebugWriteImages();
protected:
/// Number of threads in thread pool (for allocation of temporary thread memory).
size_t m_NumberOfThreads;
/// Number of tasks for thread pool (for allocation of task arguments and results).
size_t m_NumberOfTasks;
/// Flag for freeing and re-reading original images if using smoothed data.
bool m_FreeAndRereadImages;
/// Flag for enforcing zero-sum parameter changes.
bool m_ForceZeroSum;
/// Restrict zero-sum computation to first N images.
size_t m_ForceZeroSumFirstN;
/// Currently active images from index.
size_t m_ActiveImagesFrom;
/// Currently active images to index (plus 1).
size_t m_ActiveImagesTo;
/// Currently active transformations from index.
size_t m_ActiveXformsFrom;
/// Currently active transformations to index (plus 1).
size_t m_ActiveXformsTo;
/// Enforce gradient to be zero-sum over all images.
virtual void ForceZeroSumGradient( CoordinateVector& g ) const;
/// Number of pixels in template.
size_t m_TemplateNumberOfPixels;
/// Number of samples drawn from the pixels in template.
size_t m_TemplateNumberOfSamples;
/// Template grid (not pixel data).
UniformVolume::SmartPtr m_TemplateGrid;
/// Flag for use of template pixel data in registration.
bool m_UseTemplateData;
/// Prepared (smoothed, scaled etc.) data of the template image if used in registration.
std::vector<byte> m_TemplateData;
/// Vector of image volumes with pre-scaled pixel values.
std::vector<UniformVolume::SmartPtr> m_ImageVector;
/// Vector of original image volumes.
std::vector<UniformVolume::SmartPtr> m_OriginalImageVector;
/// Vector of transformations
std::vector<Xform::SmartPtr> m_XformVector;
/// Probabilistic sample count.
float m_ProbabilisticSampleDensity;
/// Pixel indices of probabilistic samples.
std::vector<size_t> m_ProbabilisticSamples;
/** Number of iterations (calls to Evaluate()) after which probabilistic
* samples are updated.
*/
int m_ProbabilisticSampleUpdatesAfter;
/** Current number of iterations since last update of probabilistic samples.
*/
int m_ProbabilisticSampleUpdatesSince;
/** Update probablistic samples.
* This function generates a new list of random pixel indices for sampling
* the images. It is called every m_ProbabilisticSampleUpdatesAfter calls to
* Evaluate().
*/
virtual void UpdateProbabilisticSamples();
/** Interpolate all moving images.
* By default, this only calls InterpolateImage() for each image.
*/
virtual void InterpolateAllImages();
/** Interpolate given moving image to template.
*\param idx Index of of to reformat to template. This also determines which
* transformation is used.
*\param destination The reformatted pixel data is stored in this array.
* Sufficient memory (for as many pixels as there are in the template grid)
* must be allocated there.
*/
virtual void InterpolateImage( const size_t idx, byte* const destination ) { UNUSED(idx); UNUSED(destination); } // cannot make this pure virtual because we need to instantiate for affine initialization
/// Vector of reformatted and rescaled image data.
std::vector<byte*> m_Data;
/// Temporary data allocated at correct size of template grid.
std::vector<byte> m_TempData;
/// Kernel width in [mm] for Gaussian smoothing of target images.
Types::Coordinate m_GaussianSmoothImagesSigma;
/// Value used to mark regions outside the FOV.
static const byte m_PaddingValue = 255;
/// User-defined value to fill regions outside FOV.
byte m_UserBackgroundValue;
/// Flag for user-defined background value.
bool m_UserBackgroundFlag;
/// Number of parameters per transformation..
size_t m_ParametersPerXform;
/// Repeat histogram-based intensity matching after each stage.
bool m_RepeatIntensityHistogramMatching;
/// Update probabilistic sample table..
virtual bool Wiggle();
/// Prepare data for one image.
virtual UniformVolume::SmartPtr PrepareSingleImage( UniformVolume::SmartPtr& image );
/// Smooth and pre-scale target images.
virtual void PrepareTargetImages();
/// Reformat one image to a given target grid.
virtual const UniformVolume::SmartPtr GetReformattedImage( const UniformVolume::SmartPtr& targetGrid, const size_t idx ) const;
private:
/// Copy template data from TypedArray to byte vector.
void CopyTemplateData();
/// Initializer class shall be our friend.
friend class GroupwiseRegistrationFunctionalAffineInitializer;
};
//@}
} // namespace cmtk
#endif // #ifndef __cmtkGroupwiseRegistrationFunctionalBase_h_included_
|