/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkImageContinuousDilate3D.cxx,v $ 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 "vtkImageContinuousDilate3D.h" #include "vtkDataArray.h" #include "vtkImageData.h" #include "vtkImageEllipsoidSource.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkObjectFactory.h" #include "vtkPointData.h" #include "vtkStreamingDemandDrivenPipeline.h" vtkCxxRevisionMacro(vtkImageContinuousDilate3D, "$Revision: 1.34 $"); vtkStandardNewMacro(vtkImageContinuousDilate3D); //---------------------------------------------------------------------------- // Construct an instance of vtkImageContinuousDilate3D fitler. // By default zero values are dilated. vtkImageContinuousDilate3D::vtkImageContinuousDilate3D() { this->HandleBoundaries = 1; this->KernelSize[0] = 0; this->KernelSize[1] = 0; this->KernelSize[2] = 0; this->Ellipse = vtkImageEllipsoidSource::New(); // Setup the Ellipse to default size this->SetKernelSize(1, 1, 1); } //---------------------------------------------------------------------------- vtkImageContinuousDilate3D::~vtkImageContinuousDilate3D() { if (this->Ellipse) { this->Ellipse->Delete(); this->Ellipse = NULL; } } //---------------------------------------------------------------------------- void vtkImageContinuousDilate3D::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); } //---------------------------------------------------------------------------- // This method sets the size of the neighborhood. It also sets the // default middle of the neighborhood and computes the elliptical foot print. void vtkImageContinuousDilate3D::SetKernelSize(int size0, int size1, int size2) { int modified = 0; if (this->KernelSize[0] != size0) { modified = 1; this->KernelSize[0] = size0; this->KernelMiddle[0] = size0 / 2; } if (this->KernelSize[1] != size1) { modified = 1; this->KernelSize[1] = size1; this->KernelMiddle[1] = size1 / 2; } if (this->KernelSize[2] != size2) { modified = 1; this->KernelSize[2] = size2; this->KernelMiddle[2] = size2 / 2; } if (modified) { this->Modified(); this->Ellipse->SetWholeExtent(0, this->KernelSize[0]-1, 0, this->KernelSize[1]-1, 0, this->KernelSize[2]-1); this->Ellipse->SetCenter((float)(this->KernelSize[0]-1)*0.5, (float)(this->KernelSize[1]-1)*0.5, (float)(this->KernelSize[2]-1)*0.5); this->Ellipse->SetRadius((float)(this->KernelSize[0])*0.5, (float)(this->KernelSize[1])*0.5, (float)(this->KernelSize[2])*0.5); // make sure scalars have been allocated (needed if multithreaded is used) vtkInformation *ellipseOutInfo = this->Ellipse->GetExecutive()->GetOutputInformation(0); ellipseOutInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), 0, this->KernelSize[0]-1, 0, this->KernelSize[1]-1, 0, this->KernelSize[2]-1); this->Ellipse->GetOutput()->Update(); } } //---------------------------------------------------------------------------- // This templated function executes the filter on any region, // whether it needs boundary checking or not. // If the filter needs to be faster, the function could be duplicated // for strictly center (no boundary ) processing. template void vtkImageContinuousDilate3DExecute(vtkImageContinuousDilate3D *self, vtkImageData *mask, vtkImageData *inData, T *inPtr, vtkImageData *outData, int *outExt, T *outPtr, int id, vtkDataArray *inArray, vtkInformation *inInfo) { int *kernelMiddle, *kernelSize; // For looping though output (and input) pixels. int outMin0, outMax0, outMin1, outMax1, outMin2, outMax2; int outIdx0, outIdx1, outIdx2; vtkIdType inInc0, inInc1, inInc2; vtkIdType outInc0, outInc1, outInc2; T *inPtr0, *inPtr1, *inPtr2; T *outPtr0, *outPtr1, *outPtr2; int numComps, outIdxC; // For looping through hood pixels int hoodMin0, hoodMax0, hoodMin1, hoodMax1, hoodMin2, hoodMax2; int hoodIdx0, hoodIdx1, hoodIdx2; T *hoodPtr0, *hoodPtr1, *hoodPtr2; // For looping through the mask. unsigned char *maskPtr, *maskPtr0, *maskPtr1, *maskPtr2; vtkIdType maskInc0, maskInc1, maskInc2; // The extent of the whole input image int inImageMin0, inImageMin1, inImageMin2; int inImageMax0, inImageMax1, inImageMax2; int inImageExt[6]; // to compute the range T pixelMax; unsigned long count = 0; unsigned long target; int *inExt; inExt = inData->GetExtent(); // Get information to march through data inData->GetIncrements(inInc0, inInc1, inInc2); inInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), inImageExt); inImageMin0 = inImageExt[0]; inImageMax0 = inImageExt[1]; inImageMin1 = inImageExt[2]; inImageMax1 = inImageExt[3]; inImageMin2 = inImageExt[4]; inImageMax2 = inImageExt[5]; outData->GetIncrements(outInc0, outInc1, outInc2); outMin0 = outExt[0]; outMax0 = outExt[1]; outMin1 = outExt[2]; outMax1 = outExt[3]; outMin2 = outExt[4]; outMax2 = outExt[5]; numComps = outData->GetNumberOfScalarComponents(); // Get ivars of this object (easier than making friends) kernelSize = self->GetKernelSize(); kernelMiddle = self->GetKernelMiddle(); hoodMin0 = - kernelMiddle[0]; hoodMin1 = - kernelMiddle[1]; hoodMin2 = - kernelMiddle[2]; hoodMax0 = hoodMin0 + kernelSize[0] - 1; hoodMax1 = hoodMin1 + kernelSize[1] - 1; hoodMax2 = hoodMin2 + kernelSize[2] - 1; // Setup mask info maskPtr = (unsigned char *)(mask->GetScalarPointer()); mask->GetIncrements(maskInc0, maskInc1, maskInc2); // in and out should be marching through corresponding pixels. inPtr = (T *)(inArray->GetVoidPointer((outMin0-inExt[0])*inInc0 + (outMin1-inExt[2])*inInc1 + (outMin2-inExt[4])*inInc2)); target = (unsigned long)(numComps*(outMax2-outMin2+1)* (outMax1-outMin1+1)/50.0); target++; // loop through components for (outIdxC = 0; outIdxC < numComps; ++outIdxC) { // loop through pixels of output outPtr2 = outPtr; inPtr2 = inPtr; for (outIdx2 = outMin2; outIdx2 <= outMax2; ++outIdx2) { outPtr1 = outPtr2; inPtr1 = inPtr2; for (outIdx1 = outMin1; !self->AbortExecute && outIdx1 <= outMax1; ++outIdx1) { if (!id) { if (!(count%target)) { self->UpdateProgress(count/(50.0*target)); } count++; } outPtr0 = outPtr1; inPtr0 = inPtr1; for (outIdx0 = outMin0; outIdx0 <= outMax0; ++outIdx0) { // Find min pixelMax = *inPtr0; // loop through neighborhood pixels // as sort of a hack to handle boundaries, // input pointer will be marching through data that does not exist. hoodPtr2 = inPtr0 - kernelMiddle[0] * inInc0 - kernelMiddle[1] * inInc1 - kernelMiddle[2] * inInc2; maskPtr2 = maskPtr; for (hoodIdx2 = hoodMin2; hoodIdx2 <= hoodMax2; ++hoodIdx2) { hoodPtr1 = hoodPtr2; maskPtr1 = maskPtr2; for (hoodIdx1 = hoodMin1; hoodIdx1 <= hoodMax1; ++hoodIdx1) { hoodPtr0 = hoodPtr1; maskPtr0 = maskPtr1; for (hoodIdx0 = hoodMin0; hoodIdx0 <= hoodMax0; ++hoodIdx0) { // A quick but rather expensive way to handle boundaries if ( outIdx0 + hoodIdx0 >= inImageMin0 && outIdx0 + hoodIdx0 <= inImageMax0 && outIdx1 + hoodIdx1 >= inImageMin1 && outIdx1 + hoodIdx1 <= inImageMax1 && outIdx2 + hoodIdx2 >= inImageMin2 && outIdx2 + hoodIdx2 <= inImageMax2) { if (*maskPtr0) { if (*hoodPtr0 > pixelMax) { pixelMax = *hoodPtr0; } } } hoodPtr0 += inInc0; maskPtr0 += maskInc0; } hoodPtr1 += inInc1; maskPtr1 += maskInc1; } hoodPtr2 += inInc2; maskPtr2 += maskInc2; } *outPtr0 = pixelMax; inPtr0 += inInc0; outPtr0 += outInc0; } inPtr1 += inInc1; outPtr1 += outInc1; } inPtr2 += inInc2; outPtr2 += outInc2; } ++inPtr; ++outPtr; } } //---------------------------------------------------------------------------- // This method contains the first switch statement that calls the correct // templated function for the input and output Data types. // It handles image boundaries, so the image does not shrink. void vtkImageContinuousDilate3D::ThreadedRequestData( vtkInformation *vtkNotUsed(request), vtkInformationVector **inputVector, vtkInformationVector *vtkNotUsed(outputVector), vtkImageData ***inData, vtkImageData **outData, int outExt[6], int id) { // return if nothing to do if (outExt[1] < outExt[0] || outExt[3] < outExt[2] || outExt[5] < outExt[4]) { return; } int inExt[6], wholeExt[6]; vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), wholeExt); this->InternalRequestUpdateExtent(inExt,outExt,wholeExt); void *inPtr; void *outPtr = outData[0]->GetScalarPointerForExtent(outExt); vtkImageData *mask; vtkDataArray *inArray = this->GetInputArrayToProcess(0,inputVector); // Reset later. inPtr = inArray->GetVoidPointer(0); // Error checking on mask mask = this->Ellipse->GetOutput(); if (mask->GetScalarType() != VTK_UNSIGNED_CHAR) { vtkErrorMacro(<< "Execute: mask has wrong scalar type"); return; } // this filter expects the output type to be same as input if (outData[0]->GetScalarType() != inArray->GetDataType()) { vtkErrorMacro(<< "Execute: output ScalarType, " << vtkImageScalarTypeNameMacro(outData[0]->GetScalarType()) << " must match input array data type"); return; } switch (inArray->GetDataType()) { vtkTemplateMacro( vtkImageContinuousDilate3DExecute(this, mask, inData[0][0], (VTK_TT *)(inPtr), outData[0], outExt, (VTK_TT *)(outPtr), id, inArray, inInfo) ); default: vtkErrorMacro(<< "Execute: Unknown ScalarType"); return; } } //---------------------------------------------------------------------------- int vtkImageContinuousDilate3D::RequestData( vtkInformation *request, vtkInformationVector **inputVector, vtkInformationVector *outputVector) { this->Ellipse->GetOutput()->Update(); return this->Superclass::RequestData(request, inputVector, outputVector); }