/*========================================================================= Program: Visualization Toolkit Module: vtkImageSlab.cxx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkImageSlab.h" #include "vtkImageData.h" #include "vtkStreamingDemandDrivenPipeline.h" #include "vtkObjectFactory.h" #include "vtkInformationVector.h" #include "vtkInformation.h" #include "vtkMath.h" #include "vtkTypeTraits.h" #include "vtkTemplateAliasMacro.h" // turn off 64-bit ints when templating over all types, since // they cannot be stored in "double" without loss of precision # undef VTK_USE_INT64 # define VTK_USE_INT64 0 # undef VTK_USE_UINT64 # define VTK_USE_UINT64 0 #include vtkStandardNewMacro(vtkImageSlab); //---------------------------------------------------------------------------- vtkImageSlab::vtkImageSlab() { this->Operation = VTK_IMAGE_SLAB_MEAN; this->TrapezoidIntegration = 0; this->Orientation = 2; this->SliceRange[0] = VTK_INT_MIN; this->SliceRange[1] = VTK_INT_MAX; this->OutputScalarType = 0; this->MultiSliceOutput = 0; } //---------------------------------------------------------------------------- vtkImageSlab::~vtkImageSlab() { } //---------------------------------------------------------------------------- int vtkImageSlab::RequestInformation( vtkInformation *, vtkInformationVector **inputVector, vtkInformationVector *outputVector) { int extent[6]; int range[2]; double origin[3]; double spacing[3]; double sliceSpacing; int dimIndex; int scalarType; vtkInformation *outInfo = outputVector->GetInformationObject(0); vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), extent); inInfo->Get(vtkDataObject::SPACING(), spacing); inInfo->Get(vtkDataObject::ORIGIN(), origin); // get the direction along which to sum slices dimIndex = this->GetOrientation(); // clamp the range to the whole extent this->GetSliceRange(range); if (range[0] < extent[2*dimIndex]) { range[0] = extent[2*dimIndex]; } if (range[1] > extent[2*dimIndex+1]) { range[1] = extent[2*dimIndex+1]; } // set new origin to be in the center of the stack of slices sliceSpacing = spacing[dimIndex]; origin[dimIndex] = (origin[dimIndex] + 0.5*sliceSpacing*(range[0] + range[1])); if (this->GetMultiSliceOutput()) { // output extent is input extent, decreased by the slice range extent[2*dimIndex] -= range[0]; extent[2*dimIndex+1] -= range[1]; } else { // set new extent to single-slice extent[2*dimIndex] = 0; extent[2*dimIndex+1] = 0; } // set the output scalar type scalarType = this->GetOutputScalarType(); // set the output information outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), extent, 6); outInfo->Set(vtkDataObject::SPACING(), spacing, 3); outInfo->Set(vtkDataObject::ORIGIN(), origin, 3); // if requested, change the type to float or double if (scalarType == VTK_FLOAT || scalarType == VTK_DOUBLE) { vtkDataObject::SetPointDataActiveScalarInfo(outInfo, scalarType, -1); } return 1; } //---------------------------------------------------------------------------- int vtkImageSlab::RequestUpdateExtent( vtkInformation *, vtkInformationVector **inputVector, vtkInformationVector *outputVector) { int outExt[6]; int inExt[6]; int extent[6]; int range[2]; int dimIndex; vtkInformation *outInfo = outputVector->GetInformationObject(0); vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), outExt); inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), extent); // initialize input extent to output extent inExt[0] = outExt[0]; inExt[1] = outExt[1]; inExt[2] = outExt[2]; inExt[3] = outExt[3]; inExt[4] = outExt[4]; inExt[5] = outExt[5]; // get the direction along which to sum slices dimIndex = this->GetOrientation(); // clamp the range to the whole extent this->GetSliceRange(range); if (range[0] < extent[2*dimIndex]) { range[0] = extent[2*dimIndex]; } if (range[1] > extent[2*dimIndex+1]) { range[1] = extent[2*dimIndex+1]; } // input range is the output range plus the specified slice range inExt[2*dimIndex] += range[0]; inExt[2*dimIndex+1] += range[1]; inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), inExt, 6); return 1; } // anonymous namespace to limit visibility namespace { //---------------------------------------------------------------------------- // rounding functions for each type template void vtkSlabRound(double val, T& rnd) { rnd = static_cast(vtkMath::Floor(val + 0.5)); } template<> void vtkSlabRound(double val, vtkTypeUInt32& rnd) { rnd = static_cast(val + 0.5); } template<> void vtkSlabRound(double val, vtkTypeFloat32& rnd) { rnd = val; } template<> void vtkSlabRound(double val, vtkTypeFloat64& rnd) { rnd = val; } //---------------------------------------------------------------------------- // clamping functions for each type template void vtkSlabClamp(double val, T& clamp) { double minval = static_cast(vtkTypeTraits::Min()); double maxval = static_cast(vtkTypeTraits::Max()); val = (val > minval ? val : minval); val = (val < maxval ? val : maxval); vtkSlabRound(val, clamp); } template<> void vtkSlabClamp(double val, float& clamp) { clamp = val; } template<> void vtkSlabClamp(double val, double& clamp) { clamp = val; } //---------------------------------------------------------------------------- template void vtkImageSlabExecute(vtkImageSlab *self, vtkImageData *inData, T1 *inPtr, vtkImageData *outData, T2 *outPtr, int outExt[6], int id) { vtkIdType outIncX, outIncY, outIncZ; vtkIdType inInc[3]; int inExt[6]; // get increments to march through data inData->GetExtent(inExt); inData->GetIncrements(inInc); outData->GetContinuousIncrements(outExt, outIncX, outIncY, outIncZ); int numscalars = inData->GetNumberOfScalarComponents(); int rowlen = (outExt[1] - outExt[0] + 1)*numscalars; // get the operation int operation = self->GetOperation(); int trapezoid = self->GetTrapezoidIntegration(); // get the dimension along which to do the projection int dimIndex = self->GetOrientation(); if (dimIndex < 0) { dimIndex = 0; } else if (dimIndex > 2) { dimIndex = 2; } // clamp the range to the whole extent int range[2]; self->GetSliceRange(range); if (range[0] < inExt[2*dimIndex]) { range[0] = inExt[2*dimIndex]; } if (range[1] > inExt[2*dimIndex+1]) { range[1] = inExt[2*dimIndex+1]; } int numSlices = range[1] - range[0] + 1; // trapezoid integration is impossible if only one slice if (numSlices <= 1) { trapezoid = 0; } // averaging requires double precision summation double *rowBuffer = nullptr; if (operation == VTK_IMAGE_SLAB_MEAN || operation == VTK_IMAGE_SLAB_SUM) { rowBuffer = new double[rowlen]; } unsigned long count = 0; unsigned long target = ((unsigned long)(outExt[3]-outExt[2]+1) *(outExt[5]-outExt[4]+1)); target++; // Loop through output pixels for (int idZ = outExt[4]; idZ <= outExt[5]; idZ++) { T1 *inPtrY = inPtr; for (int idY = outExt[2]; idY <= outExt[3]; idY++) { if (!id) { if (!(count%target)) { self->UpdateProgress(count/(1.0*target)); } count++; } // ====== code for handling average and sum ====== if (operation == VTK_IMAGE_SLAB_MEAN || operation == VTK_IMAGE_SLAB_SUM) { T1 *inSlicePtr = inPtrY; double *rowPtr = rowBuffer; // initialize using first row T1 *inPtrX = inSlicePtr; if (trapezoid) { double f = 0.5; for (int j = 0; j < rowlen; j++) { *rowPtr++ = f*(*inPtrX++); } } else { for (int j = 0; j < rowlen; j++) { *rowPtr++ = *inPtrX++; } } inSlicePtr += inInc[dimIndex]; // perform the summation int sumSlices = (trapezoid ? (numSlices-1) : numSlices); for (int sliceIdx = 1; sliceIdx < sumSlices; sliceIdx++) { inPtrX = inSlicePtr; rowPtr = rowBuffer; for (int i = 0; i < rowlen; i++) { *rowPtr++ += *inPtrX++; } inSlicePtr += inInc[dimIndex]; } if (trapezoid) { inPtrX = inSlicePtr; rowPtr = rowBuffer; double f = 0.5; for (int i = 0; i < rowlen; i++) { *rowPtr++ += f*(*inPtrX++); } } rowPtr = rowBuffer; if (operation == VTK_IMAGE_SLAB_MEAN) { // do the division via multiplication double factor = 1.0/sumSlices; for (int k = 0; k < rowlen; k++) { vtkSlabRound((*rowPtr++)*factor, *outPtr++); } } else // VTK_IMAGE_SLAB_SUM { // clamp to limits of numeric type for (int k = 0; k < rowlen; k++) { vtkSlabClamp(*rowPtr++, *outPtr++); } } } // ====== code for handling max and min ====== else { T1 *inSlicePtr = inPtrY; T2 *outPtrX = outPtr; // initialize using first row T1 *inPtrX = inSlicePtr; for (int j = 0; j < rowlen; j++) { *outPtrX++ = *inPtrX++; } inSlicePtr += inInc[dimIndex]; if (operation == VTK_IMAGE_SLAB_MIN) { for (int sliceIdx = 1; sliceIdx < numSlices; sliceIdx++) { inPtrX = inSlicePtr; outPtrX = outPtr; for (int i = 0; i < rowlen; i++) { *outPtrX = ((*outPtrX < *inPtrX) ? *outPtrX : *inPtrX); inPtrX++; outPtrX++; } inSlicePtr += inInc[dimIndex]; } } else // VTK_IMAGE_SLAB_MAX { for (int sliceIdx = 1; sliceIdx < numSlices; sliceIdx++) { inPtrX = inSlicePtr; outPtrX = outPtr; for (int i = 0; i < rowlen; i++) { *outPtrX = ((*outPtrX > *inPtrX) ? *outPtrX : *inPtrX); inPtrX++; outPtrX++; } inSlicePtr += inInc[dimIndex]; } } outPtr += rowlen; } // ====== end of operation-specific code ====== outPtr += outIncY; inPtrY += inInc[1]; } outPtr += outIncZ; inPtr += inInc[2]; } if (operation == VTK_IMAGE_SLAB_MEAN || operation == VTK_IMAGE_SLAB_SUM) { delete [] rowBuffer; } } } // end of anonymous namespace //---------------------------------------------------------------------------- void vtkImageSlab::ThreadedRequestData(vtkInformation *, vtkInformationVector **inVector, vtkInformationVector *, vtkImageData ***inData, vtkImageData **outData, int outExt[6], int id) { void *inPtr; void *outPtr; int inExt[6]; int extent[6]; int dimIndex; int range[2]; vtkDebugMacro("Execute: inData = " << inData << ", outData = " << outData); // get the direction along which to sum slices dimIndex = this->GetOrientation(); // clamp the range to the whole extent vtkInformation *inInfo = inVector[0]->GetInformationObject(0); inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), extent); this->GetSliceRange(range); if (range[0] < extent[2*dimIndex]) { range[0] = extent[2*dimIndex]; } if (range[1] > extent[2*dimIndex+1]) { range[1] = extent[2*dimIndex+1]; } // initialize input extent to output extent inExt[0] = outExt[0]; inExt[1] = outExt[1]; inExt[2] = outExt[2]; inExt[3] = outExt[3]; inExt[4] = outExt[4]; inExt[5] = outExt[5]; // the adjust for the slice range inExt[2*dimIndex] += range[0]; inExt[2*dimIndex+1] += range[1]; // now get the pointers for the extents inPtr = inData[0][0]->GetScalarPointerForExtent(inExt); outPtr = outData[0]->GetScalarPointerForExtent(outExt); // get the scalar type int outScalarType = outData[0]->GetScalarType(); int inScalarType = inData[0][0]->GetScalarType(); // and call the execute method if (outScalarType == inScalarType) { switch (inScalarType) { vtkTemplateAliasMacro( vtkImageSlabExecute(this, inData[0][0], static_cast(inPtr), outData[0], static_cast(outPtr), outExt, id)); default: vtkErrorMacro("Execute: Unknown ScalarType"); return; } } else if (outScalarType == VTK_FLOAT) { switch (inScalarType) { vtkTemplateAliasMacro( vtkImageSlabExecute( this, inData[0][0], static_cast(inPtr), outData[0], static_cast(outPtr), outExt, id)); default: vtkErrorMacro("Execute: Unknown ScalarType"); return; } } else if (outScalarType == VTK_DOUBLE) { switch (inScalarType) { vtkTemplateAliasMacro( vtkImageSlabExecute(this, inData[0][0], static_cast(inPtr), outData[0], static_cast(outPtr), outExt, id)); default: vtkErrorMacro("Execute: Unknown ScalarType"); return; } } else { vtkErrorMacro("Execute: Unknown ScalarType"); return; } } //---------------------------------------------------------------------------- void vtkImageSlab::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "Operation: " << this->GetOperationAsString() << "\n"; os << indent << "TrapezoidIntegration: " << (this->TrapezoidIntegration ? "On\n" : "Off\n"); os << indent << "Orientation: " << this->GetOrientation() << "\n"; os << indent << "SliceRange: " << this->GetSliceRange()[0] << " " << this->GetSliceRange()[1] << "\n"; os << indent << "OutputScalarType: " << this->OutputScalarType << "\n"; os << indent << "MultiSliceOutput: " << (this->MultiSliceOutput ? "On\n" : "Off\n"); } //---------------------------------------------------------------------------- const char *vtkImageSlab::GetOperationAsString() { switch (this->Operation) { case VTK_IMAGE_SLAB_MIN: return "Min"; case VTK_IMAGE_SLAB_MAX: return "Max"; case VTK_IMAGE_SLAB_MEAN: return "Mean"; case VTK_IMAGE_SLAB_SUM: return "Sum"; default: return ""; } }