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.
480 lines
14 KiB
480 lines
14 KiB
2 years ago
|
/*=========================================================================
|
||
|
|
||
|
Program: Visualization Toolkit
|
||
|
Module: $RCSfile: vtkImageConvolve.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 "vtkImageConvolve.h"
|
||
|
#include "vtkImageData.h"
|
||
|
#include "vtkImageEllipsoidSource.h"
|
||
|
#include "vtkInformation.h"
|
||
|
#include "vtkInformationVector.h"
|
||
|
#include "vtkObjectFactory.h"
|
||
|
#include "vtkStreamingDemandDrivenPipeline.h"
|
||
|
|
||
|
vtkCxxRevisionMacro(vtkImageConvolve, "$Revision: 1.18 $");
|
||
|
vtkStandardNewMacro(vtkImageConvolve);
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Construct an instance of vtkImageConvolve fitler.
|
||
|
// By default zero values are eroded.
|
||
|
vtkImageConvolve::vtkImageConvolve()
|
||
|
{
|
||
|
int idx;
|
||
|
for (idx = 0; idx < 343; idx++)
|
||
|
{
|
||
|
this->Kernel[idx] = 0.0;
|
||
|
}
|
||
|
|
||
|
// Construct a primary id function kernel that does nothing at all
|
||
|
double kernel[9];
|
||
|
for (idx = 0; idx < 9; idx++)
|
||
|
{
|
||
|
kernel[idx] = 0.0;
|
||
|
}
|
||
|
kernel[4] = 1.0;
|
||
|
this->SetKernel3x3(kernel);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Destructor
|
||
|
vtkImageConvolve::~vtkImageConvolve()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
void vtkImageConvolve::PrintSelf(ostream& os, vtkIndent indent)
|
||
|
{
|
||
|
this->Superclass::PrintSelf(os, indent);
|
||
|
|
||
|
os << indent << "KernelSize: (" <<
|
||
|
this->KernelSize[0] << ", " <<
|
||
|
this->KernelSize[1] << ", " <<
|
||
|
this->KernelSize[2] << ")\n";
|
||
|
|
||
|
os << indent << "Kernel: (";
|
||
|
for (int k = 0; k < this->KernelSize[2]; k++)
|
||
|
{
|
||
|
for (int j = 0; j < this->KernelSize[1]; j++)
|
||
|
{
|
||
|
for (int i = 0; i < this->KernelSize[0]; i++)
|
||
|
{
|
||
|
os << this->Kernel[this->KernelSize[1]*this->KernelSize[0]*k +
|
||
|
this->KernelSize[0]*j +
|
||
|
i];
|
||
|
|
||
|
if (i != this->KernelSize[0] - 1)
|
||
|
{
|
||
|
os << ", ";
|
||
|
}
|
||
|
}
|
||
|
if (j != this->KernelSize[1] - 1 || k != this->KernelSize[2] - 1)
|
||
|
{
|
||
|
os << ",\n" << indent << " ";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
os << ")\n";
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Set a 3x3 kernel
|
||
|
void vtkImageConvolve::SetKernel3x3(const double kernel[9])
|
||
|
{
|
||
|
// Fill the kernel
|
||
|
this->SetKernel(kernel, 3, 3, 1);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Set a 5x5 kernel
|
||
|
void vtkImageConvolve::SetKernel5x5(const double kernel[25])
|
||
|
{
|
||
|
// Fill the kernel
|
||
|
this->SetKernel(kernel, 5, 5, 1);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Set a 7x7 kernel
|
||
|
void vtkImageConvolve::SetKernel7x7(double kernel[49])
|
||
|
{
|
||
|
// Fill the kernel
|
||
|
this->SetKernel(kernel, 7, 7, 1);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Set a 3x3x3 kernel
|
||
|
void vtkImageConvolve::SetKernel3x3x3(const double kernel[27])
|
||
|
{
|
||
|
// Fill the kernel
|
||
|
this->SetKernel(kernel, 3, 3, 3);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Set a 5x5x5 kernel
|
||
|
void vtkImageConvolve::SetKernel5x5x5(double kernel[125])
|
||
|
{
|
||
|
// Fill the kernel
|
||
|
this->SetKernel(kernel, 5, 5, 5);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Set a 7x7x7 kernel
|
||
|
void vtkImageConvolve::SetKernel7x7x7(double kernel[343])
|
||
|
{
|
||
|
// Fill the kernel
|
||
|
this->SetKernel(kernel, 7, 7, 7);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Set a kernel, this is an internal method
|
||
|
void vtkImageConvolve::SetKernel(const double* kernel,
|
||
|
int sizeX, int sizeY, int sizeZ)
|
||
|
{
|
||
|
int modified=0;
|
||
|
|
||
|
// Set the correct kernel size
|
||
|
this->KernelSize[0] = sizeX;
|
||
|
this->KernelSize[1] = sizeY;
|
||
|
this->KernelSize[2] = sizeZ;
|
||
|
|
||
|
int kernelLength = sizeX*sizeY*sizeZ;
|
||
|
|
||
|
for (int idx = 0; idx < kernelLength; idx++)
|
||
|
{
|
||
|
if ( this->Kernel[idx] != kernel[idx] )
|
||
|
{
|
||
|
modified = 1;
|
||
|
this->Kernel[idx] = kernel[idx];
|
||
|
}
|
||
|
}
|
||
|
if (modified)
|
||
|
{
|
||
|
this->Modified();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Get the 3x3 kernel
|
||
|
double* vtkImageConvolve::GetKernel3x3()
|
||
|
{
|
||
|
return this->GetKernel();
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Get the 5x5 kernel
|
||
|
double* vtkImageConvolve::GetKernel5x5()
|
||
|
{
|
||
|
return this->GetKernel();
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Get the 7x7 kernel
|
||
|
double* vtkImageConvolve::GetKernel7x7()
|
||
|
{
|
||
|
return this->GetKernel();
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Get the 3x3x3 kernel
|
||
|
double* vtkImageConvolve::GetKernel3x3x3()
|
||
|
{
|
||
|
return this->GetKernel();
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Get the 5x5x5 kernel
|
||
|
double* vtkImageConvolve::GetKernel5x5x5()
|
||
|
{
|
||
|
return this->GetKernel();
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Get the 7x7x7 kernel
|
||
|
double* vtkImageConvolve::GetKernel7x7x7()
|
||
|
{
|
||
|
return this->GetKernel();
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Get the kernel, this is an internal method
|
||
|
double* vtkImageConvolve::GetKernel()
|
||
|
{
|
||
|
return this->Kernel;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Get the kernel
|
||
|
void vtkImageConvolve::GetKernel3x3(double kernel[9])
|
||
|
{
|
||
|
this->GetKernel(kernel);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Get the kernel
|
||
|
void vtkImageConvolve::GetKernel5x5(double kernel[25])
|
||
|
{
|
||
|
this->GetKernel(kernel);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Get the kernel
|
||
|
void vtkImageConvolve::GetKernel7x7(double kernel[49])
|
||
|
{
|
||
|
this->GetKernel(kernel);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Get the kernel
|
||
|
void vtkImageConvolve::GetKernel3x3x3(double kernel[27])
|
||
|
{
|
||
|
this->GetKernel(kernel);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Get the kernel
|
||
|
void vtkImageConvolve::GetKernel5x5x5(double kernel[125])
|
||
|
{
|
||
|
this->GetKernel(kernel);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Get the kernel
|
||
|
void vtkImageConvolve::GetKernel7x7x7(double kernel[343])
|
||
|
{
|
||
|
this->GetKernel(kernel);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Get the kernel, this is an internal method
|
||
|
void vtkImageConvolve::GetKernel(double *kernel)
|
||
|
{
|
||
|
int kernelLength = this->KernelSize[0]*
|
||
|
this->KernelSize[1]*this->KernelSize[2];
|
||
|
|
||
|
for (int idx = 0; idx < kernelLength; idx++)
|
||
|
{
|
||
|
kernel[idx] = this->Kernel[idx];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// 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 vtkImageConvolveExecute(vtkImageConvolve *self,
|
||
|
vtkImageData *inData, T *inPtr,
|
||
|
vtkImageData *outData, T *outPtr,
|
||
|
int outExt[6], int id,
|
||
|
vtkInformation *inInfo)
|
||
|
{
|
||
|
int *kernelSize;
|
||
|
int kernelMiddle[3];
|
||
|
|
||
|
// 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 kernel, and compute the kernel result
|
||
|
int kernelIdx;
|
||
|
double sum;
|
||
|
|
||
|
// The extent of the whole input image
|
||
|
int inImageExt[6];
|
||
|
|
||
|
// to compute the range
|
||
|
unsigned long count = 0;
|
||
|
unsigned long target;
|
||
|
|
||
|
// Get information to march through data
|
||
|
inData->GetIncrements(inInc0, inInc1, inInc2);
|
||
|
inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), inImageExt);
|
||
|
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[0] = kernelSize[0] / 2;
|
||
|
kernelMiddle[1] = kernelSize[1] / 2;
|
||
|
kernelMiddle[2] = kernelSize[2] / 2;
|
||
|
|
||
|
hoodMin0 = -kernelMiddle[0];
|
||
|
hoodMin1 = -kernelMiddle[1];
|
||
|
hoodMin2 = -kernelMiddle[2];
|
||
|
|
||
|
hoodMax0 = hoodMin0 + kernelSize[0] - 1;
|
||
|
hoodMax1 = hoodMin1 + kernelSize[1] - 1;
|
||
|
hoodMax2 = hoodMin2 + kernelSize[2] - 1;
|
||
|
|
||
|
// Get the kernel, just use GetKernel7x7x7(kernel) if the kernel is smaller
|
||
|
// it still works :)
|
||
|
double kernel[343];
|
||
|
self->GetKernel7x7x7(kernel);
|
||
|
|
||
|
// in and out should be marching through corresponding pixels.
|
||
|
inPtr = (T *)(inData->GetScalarPointer(outMin0, outMin1, outMin2));
|
||
|
|
||
|
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;
|
||
|
outIdx1 <= outMax1 && !self->AbortExecute;
|
||
|
++outIdx1)
|
||
|
{
|
||
|
if (!id)
|
||
|
{
|
||
|
if (!(count%target))
|
||
|
{
|
||
|
self->UpdateProgress(count/(50.0*target));
|
||
|
}
|
||
|
count++;
|
||
|
}
|
||
|
|
||
|
outPtr0 = outPtr1;
|
||
|
inPtr0 = inPtr1;
|
||
|
|
||
|
for (outIdx0 = outMin0; outIdx0 <= outMax0; ++outIdx0)
|
||
|
{
|
||
|
// Inner loop where we compute the kernel
|
||
|
|
||
|
// Set the sum to zero
|
||
|
sum = 0;
|
||
|
|
||
|
// 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;
|
||
|
|
||
|
// Set the kernel index to the starting position
|
||
|
kernelIdx = 0;
|
||
|
|
||
|
for (hoodIdx2 = hoodMin2; hoodIdx2 <= hoodMax2; ++hoodIdx2)
|
||
|
{
|
||
|
hoodPtr1 = hoodPtr2;
|
||
|
|
||
|
for (hoodIdx1 = hoodMin1; hoodIdx1 <= hoodMax1; ++hoodIdx1)
|
||
|
{
|
||
|
hoodPtr0 = hoodPtr1;
|
||
|
|
||
|
for (hoodIdx0 = hoodMin0; hoodIdx0 <= hoodMax0; ++hoodIdx0)
|
||
|
{
|
||
|
// A quick but rather expensive way to handle boundaries
|
||
|
// This assumes the boundary values are zero
|
||
|
if (outIdx0 + hoodIdx0 >= inImageExt[0] &&
|
||
|
outIdx0 + hoodIdx0 <= inImageExt[1] &&
|
||
|
outIdx1 + hoodIdx1 >= inImageExt[2] &&
|
||
|
outIdx1 + hoodIdx1 <= inImageExt[3] &&
|
||
|
outIdx2 + hoodIdx2 >= inImageExt[4] &&
|
||
|
outIdx2 + hoodIdx2 <= inImageExt[5])
|
||
|
{
|
||
|
sum += *hoodPtr0 * kernel[kernelIdx];
|
||
|
|
||
|
// Take the next postion in the kernel
|
||
|
kernelIdx++;
|
||
|
}
|
||
|
|
||
|
hoodPtr0 += inInc0;
|
||
|
}
|
||
|
|
||
|
hoodPtr1 += inInc1;
|
||
|
}
|
||
|
|
||
|
hoodPtr2 += inInc2;
|
||
|
}
|
||
|
|
||
|
// Set the output pixel to the correct value
|
||
|
*outPtr0 = (T)sum;
|
||
|
|
||
|
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 vtkImageConvolve::ThreadedRequestData(
|
||
|
vtkInformation *vtkNotUsed(request),
|
||
|
vtkInformationVector **inputVector,
|
||
|
vtkInformationVector *vtkNotUsed(outputVector),
|
||
|
vtkImageData ***inData,
|
||
|
vtkImageData **outData,
|
||
|
int outExt[6], int id)
|
||
|
{
|
||
|
void *inPtr = inData[0][0]->GetScalarPointerForExtent(outExt);
|
||
|
void *outPtr = outData[0]->GetScalarPointerForExtent(outExt);
|
||
|
|
||
|
vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
|
||
|
|
||
|
// this filter expects the output type to be same as input
|
||
|
if (outData[0]->GetScalarType() != inData[0][0]->GetScalarType())
|
||
|
{
|
||
|
vtkErrorMacro(<< "Execute: output ScalarType, "
|
||
|
<< vtkImageScalarTypeNameMacro(outData[0]->GetScalarType())
|
||
|
<< " must match input scalar type");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
switch (inData[0][0]->GetScalarType())
|
||
|
{
|
||
|
vtkTemplateMacro(
|
||
|
vtkImageConvolveExecute(this, inData[0][0],
|
||
|
(VTK_TT *)(inPtr), outData[0],
|
||
|
(VTK_TT *)(outPtr),
|
||
|
outExt, id, inInfo));
|
||
|
|
||
|
default:
|
||
|
vtkErrorMacro(<< "Execute: Unknown ScalarType");
|
||
|
return;
|
||
|
}
|
||
|
}
|