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
|
/*
//
// Copyright 2016 Google, Inc.
//
// Copyright 2004-2013 SRI International
//
// Copyright 1997-2010 Torsten Rohlfing
//
// 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 __cmtkDataGrid_h_included_
#define __cmtkDataGrid_h_included_
#include <cmtkconfig.h>
#include <Base/cmtkMacros.h>
#include <Base/cmtkTypes.h>
#include <Base/cmtkTypedArray.h>
#include <Base/cmtkFixedVector.h>
#include <Base/cmtkScalarImage.h>
#include <Base/cmtkRegion.h>
#include <Base/cmtkMetaInformationObject.h>
#include <Base/cmtkAnatomicalOrientation.h>
#include <System/cmtkSmartPtr.h>
#include <System/cmtkThreads.h>
#include <vector>
namespace
cmtk
{
/** \addtogroup Base */
//@{
/** Grid topology of data arranged in a 3D lattice.
* This class extends the plain data handling functions of TypedArray
* with a 3D topology. Real world coordinates, however, are not considered and
* need to be handled by derived classes. Thus, this class provides the coordinate
* independent services such as median filtering and, to a certain extent,
* interpolation.
*/
class DataGrid :
/// Inherit class that handles meta information.
public MetaInformationObject
{
public:
/// This class.
typedef DataGrid Self;
/// Smart pointer to DataGrid.
typedef SmartPointer<Self> SmartPtr;
/// Smart pointer-to-const to DataGrid.
typedef SmartConstPointer<Self> SmartConstPtr;
/// Region type.
typedef Region<3,Types::GridIndexType> RegionType;
/// Index type.
typedef RegionType::IndexType IndexType;
/// Space vector type.
typedef FixedVector<3,Types::Coordinate> SpaceVectorType;
/// Number of grid samples in the three spatial dimensions
Self::IndexType m_Dims;
/// Offset increments when moving to the next pixel in each of the three grid dimensions.
Self::IndexType m_GridIncrements;
/// Data array (element type is variable)
cmtkGetSetMacro(TypedArray::SmartPtr,Data);
public:
/// Copy constructor.
DataGrid( const Self& other );
/// Constructor.
DataGrid( const Self::IndexType& dims, TypedArray::SmartPtr& data = TypedArray::SmartPtr::Null() )
: m_Dims( dims ),
m_Data( data )
{
this->ComputeGridIncrements();
this->m_CropRegion = this->GetWholeImageRegion();
}
/// Virtual destructor.
virtual ~DataGrid() {}
/** Create a physical copy of this object.
*\param copyData If true, the associated data array is also copied.
*/
Self::SmartPtr Clone( const bool copyData )
{
return Self::SmartPtr( this->CloneVirtual( copyData ) );
}
/** Create a physical copy of this object.
*/
Self::SmartPtr Clone() const
{
return Self::SmartPtr( this->CloneVirtual() );
}
/// Test whether this grid matches another one, i.e., has the same dimensions.
bool GridMatches( const Self& other ) const
{
return (this->m_Dims == other.m_Dims);
}
/// Downsampling and pixel-averaging constructor function.
virtual DataGrid* GetDownsampledAndAveraged( const Types::GridIndexType (&downsample)[3] ) const;
/// Downsampling without averaging constructor function.
virtual DataGrid* GetDownsampled( const Types::GridIndexType (&downsample)[3] ) const;
/** Reorientation constructor function.
*\param newOrientation Three letter orientation code that specifies the anatomically-based
* orientation of the reoriented volume. Each letter can be one of the following: R, L, A,
* P, I, S. These stand for Right, Left, Anterior, Posterior, Inferior, Superior.
*
* The three letters in the orientation string define the directions of the positive x, y,
* and z axes, in this order. For example, "RAS", the standard orientation for this software,
* means that the pixels along the x axis are arranged from the subject's Left to the Right
* side, along the y axis from the subject's Posterior (back) to Anterior (front), and along
* the z axis from Inferior (feet) to Superior (head).
*
* The current orientation of this volume is to be taken from its meta information,
* as this->m_MetaInformation[CMTK_META_IMAGE_ORIENTATION]. This is also a three-letter string of the
* same form as the one given to this function as a parameter.
*
* If the current orientation is not set, a warning message should be printed to StdErr, and
* a NULL pointer returned.
*
*\return Reoriented data grid with permuted pixels in this->Data and permuted grid dimensions
* in this->Dims. The returned pointers points to a newly allocated object, which can be
* wrapped in an SmartPointer.
*/
const DataGrid::SmartPtr GetReoriented( const char* newOrientation = AnatomicalOrientation::ORIENTATION_STANDARD ) const;
/// Get dimensions array.
const Self::IndexType GetDims() const
{
return this->m_Dims;
}
/** Create data array.
*\param dataType ID of scalar data type for the array. This is the image pixel type.
*\param setToZero If this flag is set, all values in the newly created array will be initialized to zero.
*/
virtual TypedArray::SmartPtr CreateDataArray( const ScalarDataType dataType, const bool setToZero = false );
/// Get number of data items in the volume.
size_t GetNumberOfPixels () const { return this->m_Dims[0]*this->m_Dims[1]*this->m_Dims[2]; }
/// Check whether given pixel index is inside grid.
bool IndexIsInRange( const Types::GridIndexType x, const Types::GridIndexType y, const Types::GridIndexType z ) const
{
return (x>=0) && (x<this->m_Dims[0]) && (y>=0) && (y<this->m_Dims[1]) && (z>=0) && (z<this->m_Dims[2]);
}
/// Get offset of a pixel.
Types::GridIndexType GetOffsetFromIndex( const Types::GridIndexType x, const Types::GridIndexType y, const Types::GridIndexType z ) const
{
return x + nextJ * y + nextK * z;
}
/// Get offset of a pixel.
Types::GridIndexType GetOffsetFromIndex( const Self::IndexType& index ) const
{
return index[0] + this->nextJ * index[1] + this->nextK * index[2];
}
/// Get index of a pixel identified by its offset.
void GetIndexFromOffset( const size_t offset, Types::GridIndexType& x, Types::GridIndexType& y, Types::GridIndexType& z ) const
{
z = offset / nextK;
y = (offset % nextK) / nextJ;
x = (offset % nextK) % nextJ;
}
/// Get index of a pixel identified by its offset.
Self::IndexType GetIndexFromOffset( const Types::GridIndexType offset ) const
{
Self::IndexType index;
index[2] = offset / nextK;
index[1] = (offset % nextK) / nextJ;
index[0] = (offset % nextK) % nextJ;
return index;
}
/// Return data at specified offset
bool GetDataAt ( Types::DataItem& data, const size_t offset ) const
{
return this->m_Data->Get( data, offset );
}
/// Return data at specified grid point.
bool GetDataAt ( Types::DataItem& data, const Types::GridIndexType x, const Types::GridIndexType y, const Types::GridIndexType z ) const
{
return this->m_Data->Get( data, this->GetOffsetFromIndex( x, y, z ) );
}
/// Set data at specified offset
void SetDataAt ( const Types::DataItem data, const size_t offset )
{
this->m_Data->Set( data, offset );
}
/// Set data at specified grid point.
void SetDataAt ( const Types::DataItem data, const Types::GridIndexType x, const Types::GridIndexType y, const Types::GridIndexType z )
{
this->SetDataAt( data, this->GetOffsetFromIndex( x, y, z ) );
}
/// Return data at specified grid point, or a given default value if no data exists there.
Types::DataItem GetDataAt ( const Types::GridIndexType x, const Types::GridIndexType y, const Types::GridIndexType z, const Types::DataItem defaultValue = 0.0 ) const
{
Types::DataItem value;
if ( this->GetDataAt( value, this->GetOffsetFromIndex( x, y, z ) ) )
return value;
else
return defaultValue;
}
/// Return data at specified grid offset, or a given default value if no data exists there.
Types::DataItem GetDataAt ( const size_t offset, const Types::DataItem defaultValue = 0.0 ) const
{
return this->m_Data->ValueAt( offset, defaultValue );
}
/** Return data after mirroring.
*\param axis Coordinate axis normal to mirror plane. Default is AXIS_X
* (mirror about mid-sagittal plane).
*/
TypedArray::SmartPtr GetDataMirrorPlane( const int axis = AXIS_X ) const;
/// Replace data with mirrored version.
void ApplyMirrorPlane( const int axis = AXIS_X );
public:
/** Get cropped region reference.
*/
Self::RegionType& CropRegion()
{
return this->m_CropRegion;
}
/** Set cropped region.
* This function can deal with negative crop region values, which refer to the upper grid
* boundary and are automatically converted.
*/
virtual void SetCropRegion( const Self::RegionType& region );
/** Get cropped region reference.
*/
const Self::RegionType& CropRegion() const
{
return this->m_CropRegion;
}
/// Get whole image region.
const Self::RegionType GetWholeImageRegion() const;
/// Get region covering one slice of the image.
const Self::RegionType GetSliceRegion( const int axis /*!< Coordinate axis perpendicular to the slice, i.e., 0 for x, 1 for y, 2 for z.*/, const Types::GridIndexType slice /*!< Index of selected slice. */ ) const;
/// Get index increments for crop region.
const Self::IndexType GetCropRegionIncrements() const;
/** Automatically crop to minimal region above given threshold.
*\param threshold The cropping threshold.
*\param recrop If this flag is true, then the cropping will be performed
* inside an already existing cropping region. If this flag is false
* (default), then any pre-set crop region is ignored.
*\param margin Width of additional margin added around the threshold-cropped region.
*\return The crop region that was applied.
*/
Self::RegionType AutoCrop( const Types::DataItem threshold, const bool recrop = false, const Types::GridIndexType margin = 0 );
/// Fill volume outside current crop region with constant value.
void FillCropBackground( const Types::DataItem value );
/// Return data for a region of the volume.
TypedArray::SmartPtr GetRegionData( const Self::RegionType& region ) const;
/// Accessor functions for protected member variables
Types::GridIndexType GetNextI() const { return nextI; }
Types::GridIndexType GetNextJ() const { return nextJ; }
Types::GridIndexType GetNextK() const { return nextK; }
Types::GridIndexType GetNextIJ() const { return nextIJ; }
Types::GridIndexType GetNextIK() const { return nextIK; }
Types::GridIndexType GetNextJK() const { return nextJK; }
Types::GridIndexType GetNextIJK() const { return nextIJK; }
/// Get center of mass of pixel data.
virtual FixedVector<3,Types::Coordinate> GetCenterOfMassGrid() const;
/// Get center of mass and first-order moments of pixel data.
virtual FixedVector<3,Types::Coordinate> GetCenterOfMassGrid( FixedVector<3,Types::Coordinate>& firstOrderMoment ) const;
/** Return orthogonal slice as a 2D image.
*/
virtual ScalarImage::SmartPtr GetOrthoSlice( const int axis, const Types::GridIndexType plane ) const;
/** Extract orthogonal slice as a data grid object.
*/
Self::SmartPtr ExtractSlice( const int axis /*!< Coordinate axis perpendicular to extracted plane*/, const Types::GridIndexType plane /*!< Index of extracted plane */ ) const;
/** Set orthogonal slice from a 2D image.
*/
virtual void SetOrthoSlice( const int axis, const Types::GridIndexType idx, const ScalarImage* slice );
/// Print object.
void Print() const;
protected:
/** Create a physical copy of this object.
*\param copyData If true, the associated data array is also copied.
*/
virtual Self* CloneVirtual( const bool copyData );
virtual Self* CloneVirtual() const;
/** Utility function for trilinear interpolation.
*\param data This reference is set to the interpolated data value. It is
* valid if and only if this function returns 1.
*\param location 3D coordinate to interpolate data at.
*\param x Grid index x.
*\param y Grid index y.
*\param z Grid index z.
*\param location Location within grid cell.
*\param cellFrom 3D coordinate of the lower-left-front voxel of the cell
* enclosing the given location.
*\param cellTo 3D coordinate of the upper-right-rear voxel of the cell
* enclosing the given location.
*\return True if there is valid data for all eight voxels enclosing the
* given location, so that the interpolation could be completed successfully,
* False otherwise.
*/
bool TrilinearInterpolation( Types::DataItem& data, const Types::GridIndexType x, const Types::GridIndexType y, const Types::GridIndexType z, const Self::SpaceVectorType& location, const Types::Coordinate* cellFrom, const Types::Coordinate* cellTo ) const;
/** Utility function for trilinear interpolation from a primitive data array.
* This function is provided for computational efficiency when a large number
* of interpolations from a given data volume of known pixel type are required.
*
*\param dataPtr Pointer to the primitive data array.
*\param x Grid index x.
*\param y Grid index y.
*\param z Grid index z.
*\param gridPosition (x,y,z) indices of the voxel containing the given
* location.
*\param cellFrom 3D coordinate of the lower-left-front voxel of the cell
* enclosing the given location.
*\param cellTo 3D coordinate of the upper-right-rear voxel of the cell
* enclosing the given location.
*\return The interpolated data value..
*/
template<class TData>
inline TData TrilinearInterpolation
( const TData* dataPtr, const Types::GridIndexType x, const Types::GridIndexType y, const Types::GridIndexType z, const Self::SpaceVectorType& gridPosition, const Types::Coordinate* cellFrom, const Types::Coordinate* cellTo ) const;
/** Utility function for trilinear interpolation from multiple primitive data arrays of identical grid structure.
* This function is provided for computational efficiency when a large number
* of interpolations from a given data volume of known pixel type are required.
*/
template<class TData,class TOutputIterator>
inline void TrilinearInterpolation
( TOutputIterator result /*!< Output iterator to store interpolated values.*/,
const std::vector<TData*>& dataPtr /*!< Vector of data arrays to interpolate from */,
const Types::GridIndexType x /*!< Grid position x */,
const Types::GridIndexType y /*!< Grid position y */,
const Types::GridIndexType z /*!< Grid position z */,
const Types::Coordinate fracX /*!< Fractional coordinate X within pixel */,
const Types::Coordinate fracY /*!< Fractional coordinate Y within pixel */,
const Types::Coordinate fracZ /*!< Fractional coordinate Z within pixel */ ) const;
/// Offset to next voxel column.
Types::GridIndexType nextI;
/// Offset to next voxel row.
Types::GridIndexType nextJ;
/// Offset to next voxel plane.
Types::GridIndexType nextK;
/// Offset to next column and row.
Types::GridIndexType nextIJ;
/// Offset to next column and plane.
Types::GridIndexType nextIK;
/// Offset to next row and plane.
Types::GridIndexType nextJK;
/// Offset to next column, row, and plane.
Types::GridIndexType nextIJK;
private:
/** Crop region.
*/
Self::RegionType m_CropRegion;
/** Compute grid increments for fast n-dimensional offset computations.
*/
virtual void ComputeGridIncrements();
/// Mirror about plane without allocating additional memory.
static void MirrorPlaneInPlace( TypedArray& data, const Self::IndexType& dims, const int axis = AXIS_X );
};
//@}
} // namespace cmtk
#include "cmtkDataGrid.txx"
#endif // #ifndef __cmtkDataGrid_h_included_
|