You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
345 lines
12 KiB
345 lines
12 KiB
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: $RCSfile: vtkImageContinuousErode3D.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 "vtkImageContinuousErode3D.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(vtkImageContinuousErode3D, "$Revision: 1.34 $");
|
|
vtkStandardNewMacro(vtkImageContinuousErode3D);
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Construct an instance of vtkImageContinuousErode3D fitler.
|
|
// By default zero values are eroded.
|
|
vtkImageContinuousErode3D::vtkImageContinuousErode3D()
|
|
{
|
|
this->HandleBoundaries = 1;
|
|
this->KernelSize[0] = 1;
|
|
this->KernelSize[1] = 1;
|
|
this->KernelSize[2] = 1;
|
|
|
|
this->Ellipse = vtkImageEllipsoidSource::New();
|
|
// Setup the Ellipse to default size
|
|
this->SetKernelSize(1, 1, 1);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkImageContinuousErode3D::~vtkImageContinuousErode3D()
|
|
{
|
|
if (this->Ellipse)
|
|
{
|
|
this->Ellipse->Delete();
|
|
this->Ellipse = NULL;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkImageContinuousErode3D::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 eliptical foot print.
|
|
void vtkImageContinuousErode3D::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 <class T>
|
|
void vtkImageContinuousErode3DExecute(vtkImageContinuousErode3D *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 pixelMin;
|
|
unsigned long count = 0;
|
|
unsigned long target;
|
|
int *inExt = inData->GetExtent();
|
|
|
|
// Get information to march through data
|
|
inData->GetIncrements(inInc0, inInc1, inInc2);
|
|
inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_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
|
|
pixelMin = *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 < pixelMin)
|
|
{
|
|
pixelMin = *hoodPtr0;
|
|
}
|
|
}
|
|
}
|
|
|
|
hoodPtr0 += inInc0;
|
|
maskPtr0 += maskInc0;
|
|
}
|
|
hoodPtr1 += inInc1;
|
|
maskPtr1 += maskInc1;
|
|
}
|
|
hoodPtr2 += inInc2;
|
|
maskPtr2 += maskInc2;
|
|
}
|
|
*outPtr0 = pixelMin;
|
|
|
|
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 hanldes image boundaries, so the image does not shrink.
|
|
void vtkImageContinuousErode3D::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);
|
|
|
|
// The inPtr is reset anyway, so just get the id 0 pointer.
|
|
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(
|
|
vtkImageContinuousErode3DExecute(this, mask,
|
|
inData[0][0],
|
|
(VTK_TT *)(inPtr),
|
|
outData[0], outExt,
|
|
(VTK_TT *)(outPtr),id, inArray,
|
|
inInfo));
|
|
default:
|
|
vtkErrorMacro(<< "Execute: Unknown ScalarType");
|
|
return;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkImageContinuousErode3D::RequestData(vtkInformation *request,
|
|
vtkInformationVector **inputVector,
|
|
vtkInformationVector *outputVector)
|
|
{
|
|
this->Ellipse->GetOutput()->Update();
|
|
return this->Superclass::RequestData(request, inputVector, outputVector);
|
|
}
|
|
|