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"
22
23#include <mlModuleIncludes.h>
25#include <mlHost.h>
26
27ML_START_NAMESPACE
28
59
60
61template <typename CLUSTERVOXELTYPE, typename DerivedAlgorithm> class ClusterAlgorithm;
62
64template <typename CLUSTERVOXELTYPE, typename DerivedAlgorithm>
66{
68
69public:
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 {
88 image->setDataType(_dataType);
89 if (_useMask)
90 {
92 }
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
134private:
136
137 ImageVector _inImageExtent;
138 MLDataType _dataType;
139 bool _useMask;
140 MLErrorCode _error;
141};
142
143//-----------------------------------------------------------------------------------------------
145//-----------------------------------------------------------------------------------------------
147{
148public:
149
152
153
156
157
160 bool hasResults() const { return _hasResults; }
161
162
163protected:
165 enum { _NR_OF_SUBIMAGES = 2 } NumInImagesEnum;
166
169
172
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
193
195
196private:
198 bool _hasResults;
199
202
203
204 template <typename CLUSTERVOXELTYPE, typename DerivedAlgorithm>
205 friend class ClusterHandler;
206};
207
208
209//-----------------------------------------------------------------------------------------------
219//-----------------------------------------------------------------------------------------------
220template <typename CLUSTERVOXELTYPE, typename DerivedAlgorithm>
222{
223public:
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
238protected:
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);
339
340 return errorCode;
341 }
342
343public:
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
507private:
508
510 ClusterAlgorithm& operator=(const ClusterAlgorithm&);
511};
512
513ML_END_NAMESPACE
514
515#endif
@ CLUSTER_MODE_IdenticalIntensities
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.
void setInSliceVoxelType(MLDataType dt)
Sets voxel type of input slices.
SubImage _currentSlices[_NR_OF_SUBIMAGES]
do not load the whole image into memory, only keep two slices at once
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)
MLErrorCode processSlice(MLint zi, const ImageVector &inputImageExtent, TSubImage< MLint8 > &inMaskSlice)
MLErrorCode calcClustersForSlices()
ClusterAlgorithm(const ComputeClusterParameters &parameters, Clusters *clusters)
Constructor to be used.
MLErrorCode processTiles(SubImage *images) override
void calculateOutputImageProperties(PagedImage *image) override
SubImageBox calculateInputSubImageBox(int inputIndex, const SubImageBox &outputSubImageBox) override
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
static Host & getDefaultHost()
Returns a reference to the Host singleton.
ImageVector getImageExtent() const
Returns the extent of the (sub)image.
void calculateOutputImageProperties(PagedImage *image) override
MLEXPORT void setInputSubImageDataType(int inputIndex, MLDataType dataType)
MLEXPORT void setPageExtent(const ImageVector &pageExtent)
Sets the extents of the pages to pageExtent.
MLEXPORT void setDataType(MLDataType dataType) override
Sets the type of data to dataType. Overridden 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)
const DATATYPE * getData() const
Returns the memory address of the image region. Overloads methods from SubImage.
ComponentType c
Color component of the vector.
ComponentType t
Time component of the vector.
ComponentType u
Unit/Modality/User component of the vector.
ComponentType z
Z component of the vector.
ComponentType x
X component of the vector.
ComponentType y
Y component of the vector.
#define ML_DISALLOW_COPY_AND_ASSIGN(className)
Defines basic macros.
Definition mlMacros.h:21
MLEXPORT size_t MLSizeOf(MLDataType dataType)
MLint32 MLDataType
Definition mlTypeDefs.h:595
@ MLint8Type
Enumerator for the signed 8-bit ML integer type.
Definition mlTypeDefs.h:619
#define ML_PROGRAMMING_ERROR
Definition mlTypeDefs.h:787
MLint32 MLErrorCode
Type of an ML Error code.
Definition mlTypeDefs.h:715
#define ML_RESULT_OK
No error. Everything seems to be okay.
Definition mlTypeDefs.h:723
void MLRequestProgressCB(void *usrData, double progress)
MLuint64 MLuint
Definition mlTypeDefs.h:505
double MLdouble
Definition mlTypeDefs.h:216
unsigned char MLTypeData
This is the pointer type used to point to the data of MLType data instances.
char MLint8
Definition mlTypeDefs.h:96
MLint64 MLint
Definition mlTypeDefs.h:489
TSubImageBox< MLint > SubImageBox
Defines the standard SubImageBox type used in the ML. Its size varies with the size of the MLint type...
TImageVector< MLint > ImageVector
Defines the standard ImageVector type that is used by the ML for indexing and coordinates.
Cluster user data parameters.
Structure to hold parameters for cluster computation.
MLRequestProgressCB * progressCB