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.
455 lines
14 KiB
455 lines
14 KiB
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: $RCSfile: vtkImageToImageFilter.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 "vtkImageToImageFilter.h"
|
|
|
|
#include "vtkCellData.h"
|
|
#include "vtkDataArray.h"
|
|
#include "vtkImageData.h"
|
|
#include "vtkInformation.h"
|
|
#include "vtkMultiThreader.h"
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtkPointData.h"
|
|
|
|
vtkCxxRevisionMacro(vtkImageToImageFilter, "$Revision: 1.62 $");
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkImageToImageFilter::vtkImageToImageFilter()
|
|
{
|
|
this->NumberOfRequiredInputs = 1;
|
|
this->SetNumberOfInputPorts(1);
|
|
this->Bypass = 0;
|
|
this->Threader = vtkMultiThreader::New();
|
|
this->NumberOfThreads = this->Threader->GetNumberOfThreads();
|
|
this->InputScalarsSelection = NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkImageToImageFilter::~vtkImageToImageFilter()
|
|
{
|
|
this->Threader->Delete();
|
|
this->SetInputScalarsSelection(NULL);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkImageToImageFilter::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os,indent);
|
|
|
|
os << indent << "NumberOfThreads: " << this->NumberOfThreads << "\n";
|
|
|
|
if ( this->Bypass )
|
|
{
|
|
os << indent << "Bypass: On\n";
|
|
}
|
|
else
|
|
{
|
|
os << indent << "Bypass: Off\n";
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkImageToImageFilter::SetInput(vtkImageData *input)
|
|
{
|
|
this->vtkProcessObject::SetNthInput(0, input);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkImageData *vtkImageToImageFilter::GetInput()
|
|
{
|
|
if (this->NumberOfInputs < 1)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return (vtkImageData *)(this->Inputs[0]);
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// This method can be overriden in a subclass to compute the output
|
|
// Information: WholeExtent, Spacing, Origin, ScalarType and
|
|
// NumberOfScalarComponents.
|
|
void vtkImageToImageFilter::ExecuteInformation()
|
|
{
|
|
vtkImageData *input = this->GetInput();
|
|
vtkImageData *output = this->GetOutput();
|
|
|
|
// Make sure the Input has been set.
|
|
if ( input == NULL || output == NULL)
|
|
{
|
|
if (output)
|
|
{
|
|
// this means that input is NULL, but the output isn't
|
|
// in order to make this clear to filters down the line, we
|
|
// make sure outputData is completely empty
|
|
output->SetExtent(0, -1, 0, -1, 0, -1);
|
|
output->SetWholeExtent(0, -1, 0, -1, 0, -1);
|
|
output->SetUpdateExtent(0, -1, 0, -1, 0, -1);
|
|
output->AllocateScalars();
|
|
}
|
|
vtkErrorMacro(<< "ExecuteInformation: Input is not set.");
|
|
return;
|
|
}
|
|
|
|
// Start with some defaults.
|
|
output->CopyTypeSpecificInformation( input );
|
|
|
|
// take this opportunity to modify the defaults
|
|
this->ExecuteInformation(input, output);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkImageToImageFilter::ExecuteInformation(
|
|
vtkImageData *vtkNotUsed(inData), vtkImageData *vtkNotUsed(outData))
|
|
{
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// Call the alternate version of this method, and use the returned input
|
|
// update extent for all inputs
|
|
void vtkImageToImageFilter::ComputeInputUpdateExtents( vtkDataObject *output )
|
|
{
|
|
int outExt[6], inExt[6];
|
|
|
|
output->GetUpdateExtent( outExt );
|
|
|
|
if (this->NumberOfInputs)
|
|
{
|
|
this->ComputeInputUpdateExtent( inExt, outExt );
|
|
}
|
|
|
|
for (int idx = 0; idx < this->NumberOfInputs; ++idx)
|
|
{
|
|
if (this->Inputs[idx] != NULL)
|
|
{
|
|
if (this->Inputs[idx]->GetRequestExactExtent())
|
|
{
|
|
int *currentExt = this->Inputs[idx]->GetUpdateExtent();
|
|
for (int i = 0; i < 6; i += 2)
|
|
{
|
|
if (inExt[i] < currentExt[i] ||
|
|
inExt[i+1] > currentExt[i+1])
|
|
{
|
|
this->Inputs[idx]->SetUpdateExtent( inExt );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this->Inputs[idx]->SetUpdateExtent( inExt );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// By default, simply set the input update extent to match the given output
|
|
// extent
|
|
void vtkImageToImageFilter::ComputeInputUpdateExtent( int inExt[6],
|
|
int outExt[6] )
|
|
{
|
|
memcpy(inExt,outExt,sizeof(int)*6);
|
|
}
|
|
|
|
|
|
|
|
|
|
struct vtkImageThreadStruct
|
|
{
|
|
vtkImageToImageFilter *Filter;
|
|
vtkImageData *Input;
|
|
vtkImageData *Output;
|
|
};
|
|
|
|
|
|
|
|
// this mess is really a simple function. All it does is call
|
|
// the ThreadedExecute method after setting the correct
|
|
// extent for this thread. Its just a pain to calculate
|
|
// the correct extent.
|
|
VTK_THREAD_RETURN_TYPE vtkImageThreadedExecute( void *arg )
|
|
{
|
|
vtkImageThreadStruct *str;
|
|
int ext[6], splitExt[6], total;
|
|
int threadId, threadCount;
|
|
vtkImageData *output;
|
|
|
|
threadId = ((vtkMultiThreader::ThreadInfo *)(arg))->ThreadID;
|
|
threadCount = ((vtkMultiThreader::ThreadInfo *)(arg))->NumberOfThreads;
|
|
|
|
str = (vtkImageThreadStruct *)(((vtkMultiThreader::ThreadInfo *)(arg))->UserData);
|
|
output = str->Output;
|
|
output->GetUpdateExtent( ext );
|
|
|
|
// execute the actual method with appropriate extent
|
|
// first find out how many pieces extent can be split into.
|
|
total = str->Filter->SplitExtent(splitExt, ext, threadId, threadCount);
|
|
//total = 1;
|
|
|
|
if (threadId < total)
|
|
{
|
|
str->Filter->ThreadedExecute(str->Input, str->Output, splitExt, threadId);
|
|
}
|
|
// else
|
|
// {
|
|
// otherwise don't use this thread. Sometimes the threads dont
|
|
// break up very well and it is just as efficient to leave a
|
|
// few threads idle.
|
|
// }
|
|
|
|
return VTK_THREAD_RETURN_VALUE;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// For streaming and threads. Splits output update extent into num pieces.
|
|
// This method needs to be called num times. Results must not overlap for
|
|
// consistent starting extent. Subclass can override this method.
|
|
// This method returns the number of peices resulting from a successful split.
|
|
// This can be from 1 to "total".
|
|
// If 1 is returned, the extent cannot be split.
|
|
int vtkImageToImageFilter::SplitExtent(int splitExt[6], int startExt[6],
|
|
int num, int total)
|
|
{
|
|
int splitAxis;
|
|
int min, max;
|
|
|
|
vtkDebugMacro("SplitExtent: ( " << startExt[0] << ", " << startExt[1] << ", "
|
|
<< startExt[2] << ", " << startExt[3] << ", "
|
|
<< startExt[4] << ", " << startExt[5] << "), "
|
|
<< num << " of " << total);
|
|
|
|
// start with same extent
|
|
memcpy(splitExt, startExt, 6 * sizeof(int));
|
|
|
|
splitAxis = 2;
|
|
min = startExt[4];
|
|
max = startExt[5];
|
|
while (min == max)
|
|
{
|
|
--splitAxis;
|
|
if (splitAxis < 0)
|
|
{ // cannot split
|
|
vtkDebugMacro(" Cannot Split");
|
|
return 1;
|
|
}
|
|
min = startExt[splitAxis*2];
|
|
max = startExt[splitAxis*2+1];
|
|
}
|
|
|
|
// determine the actual number of pieces that will be generated
|
|
int range = max - min + 1;
|
|
int valuesPerThread = (int)ceil(range/(double)total);
|
|
int maxThreadIdUsed = (int)ceil(range/(double)valuesPerThread) - 1;
|
|
if (num < maxThreadIdUsed)
|
|
{
|
|
splitExt[splitAxis*2] = splitExt[splitAxis*2] + num*valuesPerThread;
|
|
splitExt[splitAxis*2+1] = splitExt[splitAxis*2] + valuesPerThread - 1;
|
|
}
|
|
if (num == maxThreadIdUsed)
|
|
{
|
|
splitExt[splitAxis*2] = splitExt[splitAxis*2] + num*valuesPerThread;
|
|
}
|
|
|
|
vtkDebugMacro(" Split Piece: ( " <<splitExt[0]<< ", " <<splitExt[1]<< ", "
|
|
<< splitExt[2] << ", " << splitExt[3] << ", "
|
|
<< splitExt[4] << ", " << splitExt[5] << ")");
|
|
|
|
return maxThreadIdUsed + 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkImageData *vtkImageToImageFilter::AllocateOutputData(vtkDataObject *out)
|
|
{
|
|
vtkImageData *output = vtkImageData::SafeDownCast(out);
|
|
vtkImageData *input = this->GetInput();
|
|
int inExt[6];
|
|
int outExt[6];
|
|
vtkDataArray *inArray;
|
|
vtkDataArray *outArray;
|
|
|
|
input->GetExtent(inExt);
|
|
output->SetExtent(output->GetUpdateExtent());
|
|
output->GetExtent(outExt);
|
|
|
|
// Do not copy the array we will be generating.
|
|
inArray = input->GetPointData()->GetScalars(this->InputScalarsSelection);
|
|
|
|
// Conditionally copy point and cell data.
|
|
// Only copy if corresponding indexes refer to identical points.
|
|
double *oIn = input->GetOrigin();
|
|
double *sIn = input->GetSpacing();
|
|
double *oOut = output->GetOrigin();
|
|
double *sOut = output->GetSpacing();
|
|
if (oIn[0] == oOut[0] && oIn[1] == oOut[1] && oIn[2] == oOut[2] &&
|
|
sIn[0] == sOut[0] && sIn[1] == sOut[1] && sIn[2] == sOut[2])
|
|
{
|
|
output->GetPointData()->CopyAllOn();
|
|
output->GetCellData()->CopyAllOn();
|
|
// Scalar copy flag trumps the array copy flag.
|
|
if (inArray == input->GetPointData()->GetScalars())
|
|
{
|
|
output->GetPointData()->CopyScalarsOff();
|
|
}
|
|
else
|
|
{
|
|
output->GetPointData()->CopyFieldOff(this->InputScalarsSelection);
|
|
}
|
|
|
|
// If the extents are the same, then pass the attribute data for efficiency.
|
|
if (inExt[0] == outExt[0] && inExt[1] == outExt[1] &&
|
|
inExt[2] == outExt[2] && inExt[3] == outExt[3] &&
|
|
inExt[4] == outExt[4] && inExt[5] == outExt[5])
|
|
{// Pass
|
|
output->GetPointData()->PassData(input->GetPointData());
|
|
output->GetCellData()->PassData(input->GetCellData());
|
|
}
|
|
else
|
|
{// Copy
|
|
// Since this can be expensive to copy all of these values,
|
|
// lets make sure there are arrays to copy (other than the scalars)
|
|
if (input->GetPointData()->GetNumberOfArrays() > 1)
|
|
{
|
|
// Copy the point data.
|
|
// CopyAllocate frees all arrays.
|
|
// Keep the old scalar array (not being copied).
|
|
// This is a hack, but avoids reallocation ...
|
|
vtkDataArray *tmp = NULL;
|
|
if ( ! output->GetPointData()->GetCopyScalars() )
|
|
{
|
|
tmp = output->GetPointData()->GetScalars();
|
|
}
|
|
output->GetPointData()->CopyAllocate(input->GetPointData(),
|
|
output->GetNumberOfPoints());
|
|
if (tmp)
|
|
{ // Restore the array.
|
|
output->GetPointData()->SetScalars(tmp);
|
|
}
|
|
// Now Copy The point data, but only if output is a subextent of the input.
|
|
if (outExt[0] >= inExt[0] && outExt[1] <= inExt[1] &&
|
|
outExt[2] >= inExt[2] && outExt[3] <= inExt[3] &&
|
|
outExt[4] >= inExt[4] && outExt[5] <= inExt[5])
|
|
{
|
|
output->GetPointData()->CopyStructuredData(input->GetPointData(),
|
|
inExt, outExt);
|
|
}
|
|
}
|
|
|
|
if (input->GetCellData()->GetNumberOfArrays() > 0)
|
|
{
|
|
output->GetCellData()->CopyAllocate(input->GetCellData(),
|
|
output->GetNumberOfCells());
|
|
// Cell extent is one less than point extent.
|
|
// Conditional to handle a colapsed axis (lower dimensional cells).
|
|
if (inExt[0] < inExt[1]) {--inExt[1];}
|
|
if (inExt[2] < inExt[3]) {--inExt[3];}
|
|
if (inExt[4] < inExt[5]) {--inExt[5];}
|
|
// Cell extent is one less than point extent.
|
|
if (outExt[0] < outExt[1]) {--outExt[1];}
|
|
if (outExt[2] < outExt[3]) {--outExt[3];}
|
|
if (outExt[4] < outExt[5]) {--outExt[5];}
|
|
// Now Copy The cell data, but only if output is a subextent of the input.
|
|
if (outExt[0] >= inExt[0] && outExt[1] <= inExt[1] &&
|
|
outExt[2] >= inExt[2] && outExt[3] <= inExt[3] &&
|
|
outExt[4] >= inExt[4] && outExt[5] <= inExt[5])
|
|
{
|
|
output->GetCellData()->CopyStructuredData(input->GetCellData(),
|
|
inExt, outExt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now create the scalars array that will hold the output data.
|
|
this->ExecuteInformation();
|
|
output->AllocateScalars();
|
|
outArray = output->GetPointData()->GetScalars();
|
|
if (inArray)
|
|
{
|
|
outArray->SetName(inArray->GetName());
|
|
}
|
|
return output;
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// This is the superclasses style of Execute method. Convert it into
|
|
// an imaging style Execute method.
|
|
void vtkImageToImageFilter::ExecuteData(vtkDataObject *out)
|
|
{
|
|
// Make sure the Input has been set.
|
|
if ( this->GetInput() == NULL )
|
|
{
|
|
vtkErrorMacro(<< "ExecuteData: Input is not set.");
|
|
return;
|
|
}
|
|
|
|
// Too many filters have floating point exceptions to execute
|
|
// with empty input/ no request.
|
|
if (this->UpdateExtentIsEmpty(out))
|
|
{
|
|
return;
|
|
}
|
|
|
|
vtkImageData *outData = this->AllocateOutputData(out);
|
|
int debug = this->Debug;
|
|
this->Debug = 0;
|
|
this->MultiThread(this->GetInput(),outData);
|
|
this->Debug = debug;
|
|
}
|
|
|
|
void vtkImageToImageFilter::MultiThread(vtkImageData *inData,
|
|
vtkImageData *outData)
|
|
{
|
|
vtkImageThreadStruct str;
|
|
|
|
str.Filter = this;
|
|
str.Input = inData;
|
|
str.Output = outData;
|
|
|
|
this->Threader->SetNumberOfThreads(this->NumberOfThreads);
|
|
|
|
// setup threading and the invoke threadedExecute
|
|
this->Threader->SetSingleMethod(vtkImageThreadedExecute, &str);
|
|
this->Threader->SingleMethodExecute();
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// The execute method created by the subclass.
|
|
void vtkImageToImageFilter::ThreadedExecute(vtkImageData *vtkNotUsed(inData),
|
|
vtkImageData *vtkNotUsed(outData),
|
|
int extent[6], int threadId)
|
|
{
|
|
extent = extent;
|
|
if (threadId == 0)
|
|
{
|
|
vtkErrorMacro("subclass should override ThreadedExecute!!!");
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkImageToImageFilter::FillInputPortInformation(int port,
|
|
vtkInformation* info)
|
|
{
|
|
if(!this->Superclass::FillInputPortInformation(port, info))
|
|
{
|
|
return 0;
|
|
}
|
|
info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageData");
|
|
return 1;
|
|
}
|
|
|