MeVisLab Toolbox Reference
mlClusterAlgorithm.h
Go to the documentation of this file.
1 /*************************************************************************************
2 **
3 ** Copyright 2007, MeVis Medical Solutions AG
4 **
5 ** The user may use this file in accordance with the license agreement provided with
6 ** the Software or, alternatively, in accordance with the terms contained in a
7 ** written agreement between the user and MeVis Medical Solutions AG.
8 **
9 ** For further information use the contact form at https://www.mevislab.de/contact
10 **
11 **************************************************************************************/
12 
13 #ifndef ML_CLUSTER_ALGORITHM_H
14 #define ML_CLUSTER_ALGORITHM_H
15 
17 
18 #include "mlClusters.h"
19 #include "mlClusterInfo.h"
20 #include "mlClusterRefVolume.h"
21 #include "mlClusterRefCollection.h"
22 
23 #include <mlModuleIncludes.h>
25 #include <mlHost.h>
26 
27 ML_START_NAMESPACE
28 
31 {
33  {
35  similarityToleranceIntensities = 0;
36  similarityToleranceVectors = 0.98;
37  neighborhoodRelation = NBH_3D_26_XYZ;
38  contentImage = nullptr;
39  maskImage = nullptr;
40  useBackgroundValue = true;
41  backgroundValue = 0;
42  useImageValueAsUserData = false;
43  progressCB = nullptr;
44  progressCBUserData = nullptr;
45  }
46 
58 };
59 
60 
61 template <typename CLUSTERVOXELTYPE, typename DerivedAlgorithm> class ClusterAlgorithm;
62 
64 template <typename CLUSTERVOXELTYPE, typename DerivedAlgorithm>
66 {
68 
69 public:
71  {
72  _algorithm = algorithm;
73  _error = ML_RESULT_OK;
74  _useMask = false;
75  _dataType = -1;
76  }
77 
79  void setParameters(bool useMask, MLDataType type)
80  {
81  _useMask = useMask;
82  _dataType = type;
83  }
84 
86  {
87  OrderedProcessAllPagesHandler::calculateOutputImageProperties(image);
88  image->setDataType(_dataType);
89  if (_useMask)
90  {
92  }
93  image->setInputSubImagesAreReadOnly(true);
94  ImageVector imageExt = image->getImageExtent();
95  imageExt.c = 1;
96  imageExt.t = 1;
97  imageExt.u = 1;
98  image->setImageExtent(imageExt);
99  ImageVector pageExt = imageExt;
100  pageExt.z = 1;
101  image->setPageExtent(pageExt);
102  _inImageExtent = imageExt;
103  }
104 
105  SubImageBox calculateInputSubImageBox(int inputIndex, const SubImageBox& outputSubImageBox) override
106  {
107  if ((inputIndex == 0) || (inputIndex == 1 && _useMask))
108  {
109  return outputSubImageBox;
110  }
111  else
112  {
113  return SubImageBox();
114  }
115  }
116 
118  {
119  TSubImage<MLint8> maskImage;
120  if (_useMask)
121  {
122  maskImage = TSubImage<MLint8>(images[1]);
123  }
124  _algorithm->storeNextSlice(images[0]);
125  _error = _algorithm->processSlice(images[0].getOrigin().z, _inImageExtent, maskImage);
126  return _error;
127  }
128 
130  {
131  return _error;
132  }
133 
134 private:
136 
137  ImageVector _inImageExtent;
138  MLDataType _dataType;
139  bool _useMask;
140  MLErrorCode _error;
141 };
142 
143 //-----------------------------------------------------------------------------------------------
145 //-----------------------------------------------------------------------------------------------
147 {
148 public:
149 
152 
153 
156 
157 
160  bool hasResults() const { return _hasResults; }
161 
162 
163 protected:
165  enum { _NR_OF_SUBIMAGES = 2 } NumInImagesEnum;
166 
169 
172 
174  void storeNextSlice(SubImage& slice);
175 
177  MLErrorCode loadNextSlice(MLint sliceNr, const ImageVector& inImgExt);
178 
180  MLErrorCode loadMaskSlice(TSubImage<MLint8>& slice, MLint sliceNr, const ImageVector& inImgExt) const;
181 
184 
186  void setResultFlag(bool result) { _hasResults = result; }
187 
190 
192  SubImage _currentSlices[_NR_OF_SUBIMAGES];
193 
195 
196 private:
198  bool _hasResults;
199 
201  ClusterAlgorithmBase& operator=(const ClusterAlgorithmBase&);
202 
203 
204  template <typename CLUSTERVOXELTYPE, typename DerivedAlgorithm>
205  friend class ClusterHandler;
206 };
207 
208 
209 //-----------------------------------------------------------------------------------------------
219 //-----------------------------------------------------------------------------------------------
220 template <typename CLUSTERVOXELTYPE, typename DerivedAlgorithm>
222 {
223 public:
224 
226  typedef CLUSTERVOXELTYPE ClusterVoxelType;
227 
229  ClusterAlgorithm(const ComputeClusterParameters& parameters, Clusters* clusters) : ClusterAlgorithmBase(parameters, clusters)
230  {
231  }
232 
234  {
235  return static_cast<DerivedAlgorithm*>(this)->calcClustersForSlices();
236  }
237 
238 protected:
239 
242 
248  inline MLuint updateClusterRefForNeighbor(void* neighborVoxelPtr, MLint x, MLint y, MLint z,
249  CLUSTERVOXELTYPE currentValue,
250  MLuint currentClusterRef)
251  {
252  // Get predecessor voxel value.
253  CLUSTERVOXELTYPE neighborVoxel = DerivedAlgorithm::getVoxel(neighborVoxelPtr);
254  const bool isInToleranceFlag = static_cast<DerivedAlgorithm*>(this)->isInTolerance(neighborVoxel, currentValue);
255  if (isInToleranceFlag)
256  {
257  currentClusterRef = mergeClusterReference(x, y, z, currentClusterRef);
258  }
259  return currentClusterRef;
260  }
261 
262  inline static CLUSTERVOXELTYPE convertBackgroundValue(MLdouble backgroundValue)
263  {
264  return static_cast<CLUSTERVOXELTYPE>(backgroundValue);
265  }
266 
270  {
271  // Allocate memory with subimages of the requested voxel type.
272  setInSliceVoxelType(_parameters.contentImage->getDataType());
273 
274  ImageVector inputImageExtent = _parameters.contentImage->getImageExtent();
275 
276  TSubImage<MLint8> inMaskSlice;
277 
278  // Initialize data structures for clustering.
279  MLErrorCode errorCode = _clusters->init(inputImageExtent.x, inputImageExtent.y, inputImageExtent.z);
280 
281  if (ML_RESULT_OK == errorCode)
282  {
283  if ((_parameters.neighborhoodRelation == NBH_2D_4_XY) || (_parameters.neighborhoodRelation == NBH_2D_8_XY))
284  {
285  _clusters->setUse2DNeighborhood();
286  }
287 
288  const bool useMask = _parameters.maskImage != nullptr;
289 
290  if (!Host::getDefaultHost().getUseClassicHost())
291  {
293  handler.setParameters(useMask, _currentSlices[0].getDataType());
294 
295  std::vector<PagedImage*> images;
296  images.push_back(_parameters.contentImage);
297  if (useMask)
298  {
299  images.push_back(_parameters.maskImage);
300  }
301 
302  errorCode = Host::getDefaultHost().processAllPagesWithInputImages(images, handler, SubImageBox(), _parameters.progressCB, _parameters.progressCBUserData);
303  if (errorCode != ML_RESULT_OK)
304  {
305  errorCode = handler.getErrorCode();
306  }
307  }
308  else
309  {
310  // Main loop, proceed slice-wise only on success.
311  for (MLint zi = 0; (ML_RESULT_OK == errorCode) && (zi < inputImageExtent.z); zi++)
312  {
313  errorCode = loadNextSlice(zi, inputImageExtent);
314  if (ML_RESULT_OK == errorCode && useMask)
315  {
316  errorCode = loadMaskSlice(inMaskSlice, zi, inputImageExtent);
317  }
318 
319  if (ML_RESULT_OK == errorCode)
320  {
321  errorCode = processSlice(zi, inputImageExtent, inMaskSlice);
322  }
323  }
324  }
325 
326  if (ML_RESULT_OK == errorCode)
327  {
328  ClusterUserDataParameters userDataParameters;
329  userDataParameters.useImageValueAsUserData = _parameters.useImageValueAsUserData;
330  userDataParameters.voxelSizeInMilliliters = _clusters->getVoxelVolumeInMilliliters();
331  userDataParameters.backgroundValue = _parameters.backgroundValue;
332 
333  _clusters->calcClusterSizes(userDataParameters);
334  }
335  }
336 
337  setResultFlag(ML_RESULT_OK == errorCode);
338  freeInSliceData();
339 
340  return errorCode;
341  }
342 
343 public:
344 
345  MLErrorCode processSlice(MLint zi, const ImageVector& inputImageExtent, TSubImage<MLint8>& inMaskSlice)
346  {
347  MLErrorCode errorCode = ML_RESULT_OK;
348 
349  const MLint xres = inputImageExtent.x;
350  const MLint yres = inputImageExtent.y;
351 
352  const ClusterVoxelType backgroundValue = DerivedAlgorithm::convertBackgroundValue(_parameters.backgroundValue);
353 
354  MLTypeData* sliceInternalPtr = reinterpret_cast<MLTypeData*>(_currentSlices[1].getData());
355  MLTypeData* previousSliceInternalPtr = reinterpret_cast<MLTypeData*>(_currentSlices[0].getData());
356  const MLDataType dataType = _currentSlices[1].getDataType();
357  size_t dataTypeSize = MLSizeOf(dataType);
358 
359  MLint8* maskPtr = inMaskSlice.getData();
360 
361  size_t xoffset = dataTypeSize;
362  size_t yoffset = dataTypeSize * xres;
363 
364  const bool useMask = _parameters.maskImage != nullptr;
365  const bool useBackgroundValue = _parameters.useBackgroundValue;
366 
367  // iterate over slice voxels
368  for (MLint yi = 0; yi < yres; yi++)
369  {
370  for (MLint xi = 0; xi < xres; xi++)
371  {
372  MLTypeData* slicePtr = sliceInternalPtr;
373  MLTypeData* previousSlicePtr = previousSliceInternalPtr;
374  // advance the internal pointers, we can't advance slicePtr/previousSlicePtr,
375  // because they are accessed below and we don't want to place the advancing into each
376  // continue statement.
377  sliceInternalPtr += dataTypeSize;
378  previousSliceInternalPtr += dataTypeSize;
379 
380  // get current voxel value from untyped image.
381  CLUSTERVOXELTYPE currentValue = DerivedAlgorithm::getVoxel(slicePtr);
382 
383  // get corresponding mask value
384  if (useMask)
385  {
386  MLint8 maskValue = *maskPtr++;
387  if (maskValue == 0)
388  {
389  continue;
390  }
391  }
392 
393  // if background skip voxel
394  if (useBackgroundValue && (currentValue == backgroundValue))
395  {
396  continue;
397  }
398 
399  // reset cluster reference
400  MLuint currentClusterRef = 0;
401 
402  // left and top neighbor are always evaluated
403  if (xi > 0)
404  {
405  currentClusterRef = updateClusterRefForNeighbor(slicePtr - xoffset, xi - 1, yi, zi, currentValue, currentClusterRef);
406  }
407  if (yi > 0)
408  {
409  currentClusterRef = updateClusterRefForNeighbor(slicePtr - yoffset, xi, yi - 1, zi, currentValue, currentClusterRef);
410  }
411 
412  switch (_parameters.neighborhoodRelation)
413  {
414  case NBH_2D_8_XY:
415  if ((xi > 0) && (yi > 0))
416  {
417  currentClusterRef = updateClusterRefForNeighbor(slicePtr - xoffset - yoffset, xi - 1, yi - 1, zi, currentValue, currentClusterRef);
418  }
419  if ((xi<xres - 1) && (yi>0))
420  {
421  currentClusterRef = updateClusterRefForNeighbor(slicePtr + xoffset - yoffset, xi + 1, yi - 1, zi, currentValue, currentClusterRef);
422  }
423  break;
424  // The missing breaks in the following 3d cases have been left out intentionally to be able to deal with
425  // the different 3d neighborhoods without doubling code lines (so complete 26-3d-neighborhood calculation is
426  // done by case NBH_3D_26_XYZ + NBH_3D_18_XYZ + NBH_3D_6_XYZ)
427  case NBH_3D_26_XYZ:
428  if ((xi > 0) && (yi > 0) && (zi > 0))
429  {
430  currentClusterRef = updateClusterRefForNeighbor(previousSlicePtr - xoffset - yoffset, xi - 1, yi - 1, zi - 1, currentValue, currentClusterRef);
431  }
432  if ((xi<xres - 1) && (yi>0) && (zi > 0))
433  {
434  currentClusterRef = updateClusterRefForNeighbor(previousSlicePtr + xoffset - yoffset, xi + 1, yi - 1, zi - 1, currentValue, currentClusterRef);
435  }
436  if ((xi > 0) && (yi<yres - 1) && (zi>0))
437  {
438  currentClusterRef = updateClusterRefForNeighbor(previousSlicePtr - xoffset + yoffset, xi - 1, yi + 1, zi - 1, currentValue, currentClusterRef);
439  }
440  if ((xi<xres - 1) && (yi<yres - 1) && (zi>0))
441  {
442  currentClusterRef = updateClusterRefForNeighbor(previousSlicePtr + xoffset + yoffset, xi + 1, yi + 1, zi - 1, currentValue, currentClusterRef);
443  }
444  [[fallthrough]];
445  case NBH_3D_18_XYZ:
446  if ((xi>0) && (yi > 0))
447  {
448  currentClusterRef = updateClusterRefForNeighbor(slicePtr - xoffset - yoffset, xi - 1, yi - 1, zi, currentValue, currentClusterRef);
449  }
450  if ((xi<xres - 1) && (yi>0))
451  {
452  currentClusterRef = updateClusterRefForNeighbor(slicePtr + xoffset - yoffset, xi + 1, yi - 1, zi, currentValue, currentClusterRef);
453  }
454  if ((zi > 0) && (xi > 0))
455  {
456  currentClusterRef = updateClusterRefForNeighbor(previousSlicePtr - xoffset, xi - 1, yi, zi - 1, currentValue, currentClusterRef);
457  }
458  if ((zi > 0) && (xi<xres - 1))
459  {
460  currentClusterRef = updateClusterRefForNeighbor(previousSlicePtr + xoffset, xi + 1, yi, zi - 1, currentValue, currentClusterRef);
461  }
462  if ((zi>0) && (yi > 0))
463  {
464  currentClusterRef = updateClusterRefForNeighbor(previousSlicePtr - yoffset, xi, yi - 1, zi - 1, currentValue, currentClusterRef);
465  }
466  if ((zi > 0) && (yi<yres - 1))
467  {
468  currentClusterRef = updateClusterRefForNeighbor(previousSlicePtr + yoffset, xi, yi + 1, zi - 1, currentValue, currentClusterRef);
469  }
470  [[fallthrough]];
471  case NBH_3D_6_XYZ:
472  if (zi>0)
473  {
474  currentClusterRef = updateClusterRefForNeighbor(previousSlicePtr, xi, yi, zi - 1, currentValue, currentClusterRef);
475  }
476  break;
477  case NBH_2D_4_XY:
478  // nothing to do, because 2 neighbors are already evaluated before the switch
479  break;
480  case NBH_COUNT:
481  errorCode = ML_PROGRAMMING_ERROR;
482  break;
483  default:
484  errorCode = ML_PROGRAMMING_ERROR;
485  break;
486  }
487 
488  // if no matching neighbor found create new clusterRef
489  if (currentClusterRef == 0)
490  {
491  MLdouble imageValue = 0;
492 
493  if (_parameters.useImageValueAsUserData)
494  {
495  imageValue = DerivedAlgorithm::getVoxelAsDouble(slicePtr);
496  }
497 
498  currentClusterRef = _clusters->getNewReference(imageValue);
499  }
500  _clusters->setClusterRef(xi, yi, zi, currentClusterRef);
501  }
502  }
503  return errorCode;
504  }
505 
506 
507 private:
508 
510  ClusterAlgorithm& operator=(const ClusterAlgorithm&);
511 };
512 
513 ML_END_NAMESPACE
514 
515 #endif
@ CLUSTER_MODE_IdenticalIntensities
Interface class for clustering algorithms.
Clusters * _clusters
Result clusters.
ClusterAlgorithmBase(const ComputeClusterParameters &parameters, Clusters *clusters)
Constructor to be used.
MLErrorCode loadMaskSlice(TSubImage< MLint8 > &slice, MLint sliceNr, const ImageVector &inImgExt) const
Gets current slice of the mask image.
virtual ~ClusterAlgorithmBase()
Default virtual destructor.
void freeInSliceData()
Releases memory of input image slices.
MLErrorCode loadNextSlice(MLint sliceNr, const ImageVector &inImgExt)
Gets next image data.
void setResultFlag(bool result)
Sets internal result flag to result.
MLuint mergeClusterReference(MLint x, MLint y, MLint z, MLuint currClusterRef)
Merges a cluster reference currClusterRef for position (x,y,z).
ComputeClusterParameters _parameters
void storeNextSlice(SubImage &slice)
Sets next image data, expects the data to be managed by memory manager.
bool hasResults() const
Returns whether getCluster() and getClusterOfVoxel() contains cluster data.
void setInSliceVoxelType(MLDataType dt)
Sets voxel type of input slices.
Type specific implementations of the interface class for clustering algorithms.
static CLUSTERVOXELTYPE convertBackgroundValue(MLdouble backgroundValue)
CLUSTERVOXELTYPE ClusterVoxelType
The used type of the voxels, needed by derived classes.
MLuint updateClusterRefForNeighbor(void *neighborVoxelPtr, MLint x, MLint y, MLint z, CLUSTERVOXELTYPE currentValue, MLuint currentClusterRef)
Checks if neighbor at position x,y,z belongs to the same cluster given by currentClusterRef.
MLErrorCode processSlice(MLint zi, const ImageVector &inputImageExtent, TSubImage< MLint8 > &inMaskSlice)
MLErrorCode calcClustersForSlices()
Template applying cluster calculations for one slice.
ClusterAlgorithm(const ComputeClusterParameters &parameters, Clusters *clusters)
Constructor to be used.
ClusterHandler that is used to provide slice-by-slice data to the cluster algorithm.
MLErrorCode processTiles(SubImage *images) override
Re-implement this method to do the processing, return ML_RESULT_OK if processing should go on,...
void calculateOutputImageProperties(PagedImage *image) override
Re-implement this method to specify the virtual output image's datatype/size/page extent.
SubImageBox calculateInputSubImageBox(int inputIndex, const SubImageBox &outputSubImageBox) override
Re-implement this method to specify the input image box for the input images By default,...
void setParameters(bool useMask, MLDataType type)
Stores all parameters that are needed to call the algorithm.
MLErrorCode getErrorCode() const
ClusterHandler(ClusterAlgorithm< CLUSTERVOXELTYPE, DerivedAlgorithm > *algorithm)
Structure computes and holds all cluster information. For internal use.
Definition: mlClusters.h:29
ImageVector getImageExtent() const
Returns the extent of the (sub)image.
A ProcessAllPages handler that offers ordered tile delivery on any number of input images.
Class which represents an image, which manages properties of an image and image data which is located...
Definition: mlPagedImage.h:70
MLEXPORT void setInputSubImageDataType(int inputIndex, MLDataType dataType)
Sets the dataType for the given inputIndex.
MLEXPORT void setPageExtent(const ImageVector &pageExtent)
Sets the extents of the pages to pageExtent.
MLEXPORT void setDataType(MLDataType dataType) override
Sets type of data to dataType. Overriden to perform access check.
virtual MLEXPORT void setImageExtent(const ImageVector &extent)
Sets the extents of the image to extent. The list of valid pages and its content are cleared.
MLEXPORT void setInputSubImagesAreReadOnly(bool readOnly=true)
Sets the read-only flag for all input images (see setInputSubImageIsReadOnly() for details)
This class manages/represents a rectangular 6d image region which is organized linearly in memory.
Definition: mlSubImage.h:75
This template class manages/represents a rectangular 6d image region in memory which is organized lin...
Definition: mlTSubImage.h:110
const DATATYPE * getData() const
Returns memory address of image region (Overloads method from SubImage)
Definition: mlTSubImage.h:217
ComponentType c
Color component of the vector.
Definition: mlImageVector.h:65
ComponentType t
Time component of the vector.
Definition: mlImageVector.h:67
ComponentType u
Unit/Modality/User component of the vector.
Definition: mlImageVector.h:69
ComponentType z
Z component of the vector.
Definition: mlImageVector.h:63
ComponentType x
X component of the vector.
Definition: mlImageVector.h:59
ComponentType y
Y component of the vector.
Definition: mlImageVector.h:61
#define ML_DISALLOW_COPY_AND_ASSIGN(className)
Defines basic macros.
Definition: mlMacros.h:23
MLEXPORT size_t MLSizeOf(MLDataType dataType)
Returns the size of the data type dataType in bytes.
MLint32 MLDataType
MLDataType.
Definition: mlTypeDefs.h:684
@ MLint8Type
Enumerator for the signed 8 bit ML integer type.
Definition: mlTypeDefs.h:722
#define ML_PROGRAMMING_ERROR
A case occurred which should not appear and here are a variety of reasons, typically it is a programm...
Definition: mlTypeDefs.h:890
MLint32 MLErrorCode
Type of an ML Error code.
Definition: mlTypeDefs.h:818
#define ML_RESULT_OK
No error. Everything seems to be okay.
Definition: mlTypeDefs.h:826
void MLRequestProgressCB(void *usrData, double progress)
Definition: mlTypeDefs.h:1318
MLuint64 MLuint
An unsigned ML integer type with at least 64 bits used for index calculations on very large images ev...
Definition: mlTypeDefs.h:594
double MLdouble
Definition: mlTypeDefs.h:223
unsigned char MLTypeData
This is the pointer type used to point to the data of MLType data instances.
Definition: mlTypeDefs.h:1436
char MLint8
Definition: mlTypeDefs.h:103
MLint64 MLint
A signed ML integer type with at least 64 bits used for index calculations on very large images even ...
Definition: mlTypeDefs.h:578
TSubImageBox< MLint > SubImageBox
Define the standard SubImageBox type used in the ML. Its size varies with the size of the MLint type.
Cluster user data parameters.
Definition: mlClusterInfo.h:24
Structure to hold parameters for cluster computation.
MLRequestProgressCB * progressCB