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
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
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
80 {
81 _useMask = useMask;
82 _dataType = type;
83 }
84
86 {
87 OrderedProcessAllPagesHandler::calculateOutputImageProperties(image);
88 image->setDataType(_dataType);
89 if (_useMask)
90 {
92 }
95 imageExt.c = 1;
96 imageExt.t = 1;
97 imageExt.u = 1;
100 pageExt.z = 1;
101 image->setPageExtent(pageExt);
102 _inImageExtent = imageExt;
103 }
104
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
178
181
184
186 void setResultFlag(bool result) { _hasResults = result; }
187
190
192 SubImage _currentSlices[_NR_OF_SUBIMAGES];
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
227
232
234 {
235 return static_cast<DerivedAlgorithm*>(this)->calcClustersForSlices();
236 }
237
238protected:
239
242
249 CLUSTERVOXELTYPE currentValue,
251 {
252 // Get predecessor voxel value.
253 CLUSTERVOXELTYPE neighborVoxel = DerivedAlgorithm::getVoxel(neighborVoxelPtr);
254 const bool isInToleranceFlag = static_cast<DerivedAlgorithm*>(this)->isInTolerance(neighborVoxel, currentValue);
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
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 {
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
343public:
344
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 {
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.
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 {
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
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
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
Calculates the (temporary and virtual) output image's properties, which by default has the properties...
SubImageBox calculateInputSubImageBox(int inputIndex, const SubImageBox &outputSubImageBox) override
Called by the host to determine which input image region (tile) of input inputIndex is required to ca...
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...
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...
#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:596
@ MLint8Type
Enumerator for the signed 8 bit ML integer type.
Definition mlTypeDefs.h:620
#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:788
MLint32 MLErrorCode
Type of an ML Error code.
Definition mlTypeDefs.h:716
#define ML_RESULT_OK
No error. Everything seems to be okay.
Definition mlTypeDefs.h:724
Target mlrange_cast(Source arg)
Generic version of checked ML casts.
void MLRequestProgressCB(void *usrData, double progress)
MLuint64 MLuint
An unsigned ML integer type with at least 64 bits used for index calculations on very large images ev...
Definition mlTypeDefs.h:506
double MLdouble
Definition mlTypeDefs.h:217
unsigned char MLTypeData
This is the pointer type used to point to the data of MLType data instances.
char MLint8
Definition mlTypeDefs.h:97
MLint64 MLint
A signed ML integer type with at least 64 bits used for index calculations on very large images even ...
Definition mlTypeDefs.h:490
Cluster user data parameters.
Structure to hold parameters for cluster computation.
MLRequestProgressCB * progressCB