Cloned library of VTK-5.0.0 with extra build files for internal package management.
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.
 
 
 
 
 
 

380 lines
11 KiB

/*=========================================================================
Program: Visualization Toolkit
Module: $RCSfile: vtkFiniteDifferenceGradientEstimator.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 "vtkFiniteDifferenceGradientEstimator.h"
#include "vtkCharArray.h"
#include "vtkDirectionEncoder.h"
#include "vtkDoubleArray.h"
#include "vtkFloatArray.h"
#include "vtkImageData.h"
#include "vtkIntArray.h"
#include "vtkLongArray.h"
#include "vtkMultiThreader.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkShortArray.h"
#include "vtkUnsignedCharArray.h"
#include "vtkUnsignedIntArray.h"
#include "vtkUnsignedLongArray.h"
#include "vtkUnsignedShortArray.h"
#include <math.h>
vtkCxxRevisionMacro(vtkFiniteDifferenceGradientEstimator, "$Revision: 1.2 $");
vtkStandardNewMacro(vtkFiniteDifferenceGradientEstimator);
// This is the templated function that actually computes the EncodedNormal
// and the GradientMagnitude
template <class T>
void vtkComputeGradients(
vtkFiniteDifferenceGradientEstimator *estimator, T *data_ptr,
int thread_id, int thread_count )
{
int xstep, ystep, zstep;
int x, y, z;
int offset;
int x_start, x_limit;
int y_start, y_limit;
int z_start, z_limit;
int useClip;
int *clip;
T *dptr;
unsigned char *gptr;
unsigned short *nptr;
float n[3], t;
float gvalue;
float zeroNormalThreshold;
int useBounds;
int bounds[6];
int size[3];
float aspect[3];
int xlow, xhigh;
float scale, bias;
int computeGradientMagnitudes;
vtkDirectionEncoder *direction_encoder;
int zeroPad;
estimator->GetInputSize( size );
estimator->GetInputAspect( aspect );
computeGradientMagnitudes = estimator->GetComputeGradientMagnitudes();
scale = estimator->GetGradientMagnitudeScale();
bias = estimator->GetGradientMagnitudeBias();
zeroPad = estimator->GetZeroPad();
// adjust the aspect
aspect[0] = aspect[0] * 2.0 * estimator->SampleSpacingInVoxels;
aspect[1] = aspect[1] * 2.0 * estimator->SampleSpacingInVoxels;
aspect[2] = aspect[2] * 2.0 * estimator->SampleSpacingInVoxels;
// Compute steps through the volume in x, y, and z
xstep = 1;
ystep = size[0];
zstep = size[0] * size[1];
// Multiply by the spacing used for normal estimation
xstep *= estimator->SampleSpacingInVoxels;
ystep *= estimator->SampleSpacingInVoxels;
zstep *= estimator->SampleSpacingInVoxels;
// Get the length at or below which normals are considered to
// be "zero"
zeroNormalThreshold = estimator->GetZeroNormalThreshold();
useBounds = estimator->GetBoundsClip();
// Compute an offset based on the thread_id. The volume will
// be broken into large slabs (thread_count slabs). For this thread
// we need to access the correct slab. Also compute the z plane that
// this slab starts on, and the z limit of this slab (one past the
// end of the slab)
if ( useBounds )
{
estimator->GetBounds( bounds );
x_start = bounds[0];
x_limit = bounds[1]+1;
y_start = bounds[2];
y_limit = bounds[3]+1;
z_start = (int)(( (float)thread_id / (float)thread_count ) *
(float)(bounds[5]-bounds[4]+1) ) + bounds[4];
z_limit = (int)(( (float)(thread_id + 1) / (float)thread_count ) *
(float)(bounds[5]-bounds[4]+1) ) + bounds[4];
}
else
{
x_start = 0;
x_limit = size[0];
y_start = 0;
y_limit = size[1];
z_start = (int)(( (float)thread_id / (float)thread_count ) *
size[2] );
z_limit = (int)(( (float)(thread_id + 1) / (float)thread_count ) *
size[2] );
}
// Do final error checking on limits - make sure they are all within bounds
// of the scalar input
x_start = (x_start<0)?(0):(x_start);
y_start = (y_start<0)?(0):(y_start);
z_start = (z_start<0)?(0):(z_start);
x_limit = (x_limit>size[0])?(size[0]):(x_limit);
y_limit = (y_limit>size[1])?(size[1]):(y_limit);
z_limit = (z_limit>size[2])?(size[2]):(z_limit);
direction_encoder = estimator->GetDirectionEncoder();
useClip = estimator->GetUseCylinderClip();
clip = estimator->GetCircleLimits();
// Loop through all the data and compute the encoded normal and
// gradient magnitude for each scalar location
for ( z = z_start; z < z_limit; z++ )
{
for ( y = y_start; y < y_limit; y++ )
{
if ( useClip )
{
xlow = ((clip[2*y])>x_start)?(clip[2*y]):(x_start);
xhigh = ((clip[2*y+1]+1)<x_limit)?(clip[2*y+1]+1):(x_limit);
}
else
{
xlow = x_start;
xhigh = x_limit;
}
offset = z * size[0] * size[1] + y * size[0] + xlow;
// Set some pointers
dptr = data_ptr + offset;
nptr = estimator->EncodedNormals + offset;
gptr = estimator->GradientMagnitudes + offset;
for ( x = xlow; x < xhigh; x++ )
{
// Use a central difference method if possible,
// otherwise use a forward or backward difference if
// we are on the edge
// Compute the X component
if ( x < estimator->SampleSpacingInVoxels )
{
if ( zeroPad )
{
n[0] = -((float)*(dptr+xstep));
}
else
{
n[0] = 2.0*((float)*(dptr) - (float)*(dptr+xstep));
}
}
else if ( x >= size[0] - estimator->SampleSpacingInVoxels )
{
if ( zeroPad )
{
n[0] = ((float)*(dptr-xstep));
}
else
{
n[0] = 2.0*((float)*(dptr-xstep) - (float)*(dptr));
}
}
else
{
n[0] = (float)*(dptr-xstep) - (float)*(dptr+xstep);
}
// Compute the Y component
if ( y < estimator->SampleSpacingInVoxels )
{
if ( zeroPad )
{
n[1] = -((float)*(dptr+ystep));
}
else
{
n[1] = 2.0*((float)*(dptr) - (float)*(dptr+ystep));
}
}
else if ( y >= size[1] - estimator->SampleSpacingInVoxels )
{
if ( zeroPad )
{
n[1] = ((float)*(dptr-ystep));
}
else
{
n[1] = 2.0*((float)*(dptr-ystep) - (float)*(dptr));
}
}
else
{
n[1] = (float)*(dptr-ystep) - (float)*(dptr+ystep);
}
// Compute the Z component
if ( z < estimator->SampleSpacingInVoxels )
{
if ( zeroPad )
{
n[2] = -((float)*(dptr+zstep));
}
else
{
n[2] = 2.0*((float)*(dptr) - (float)*(dptr+zstep));
}
}
else if ( z >= size[2] - estimator->SampleSpacingInVoxels )
{
if ( zeroPad )
{
n[2] = ((float)*(dptr-zstep));
}
else
{
n[2] = 2.0*((float)*(dptr-zstep) - (float)*(dptr));
}
}
else
{
n[2] = (float)*(dptr-zstep) - (float)*(dptr+zstep);
}
// Take care of the aspect ratio of the data
// Scaling in the vtkVolume is isotropic, so this is the
// only place we have to worry about non-isotropic scaling.
n[0] /= aspect[0];
n[1] /= aspect[1];
n[2] /= aspect[2];
// Compute the gradient magnitude
t = sqrt( (double)( n[0]*n[0] +
n[1]*n[1] +
n[2]*n[2] ) );
if ( computeGradientMagnitudes )
{
// Encode this into an 8 bit value
gvalue = (t + bias) * scale;
if ( gvalue < 0.0 )
{
*gptr = 0;
}
else if ( gvalue > 255.0 )
{
*gptr = 255;
}
else
{
*gptr = (unsigned char) gvalue;
}
gptr++;
}
// Normalize the gradient direction
if ( t > zeroNormalThreshold )
{
n[0] /= t;
n[1] /= t;
n[2] /= t;
}
else
{
n[0] = n[1] = n[2] = 0.0;
}
// Convert the gradient direction into an encoded index value
*nptr = direction_encoder->GetEncodedDirection( n );
nptr++;
dptr++;
}
}
}
}
// Construct a vtkFiniteDifferenceGradientEstimator
vtkFiniteDifferenceGradientEstimator::vtkFiniteDifferenceGradientEstimator()
{
this->SampleSpacingInVoxels = 1;
}
// Destruct a vtkFiniteDifferenceGradientEstimator - free up any memory used
vtkFiniteDifferenceGradientEstimator::~vtkFiniteDifferenceGradientEstimator()
{
}
static VTK_THREAD_RETURN_TYPE vtkSwitchOnDataType( void *arg )
{
vtkFiniteDifferenceGradientEstimator *estimator;
int thread_count;
int thread_id;
vtkDataArray *scalars;
thread_id = ((vtkMultiThreader::ThreadInfo *)(arg))->ThreadID;
thread_count = ((vtkMultiThreader::ThreadInfo *)(arg))->NumberOfThreads;
estimator = (vtkFiniteDifferenceGradientEstimator *)
(((vtkMultiThreader::ThreadInfo *)(arg))->UserData);
scalars = estimator->Input->GetPointData()->GetScalars();
if (scalars == NULL)
{
return VTK_THREAD_RETURN_VALUE;
}
// Find the data type of the Input and call the correct
// templated function to actually compute the normals and magnitudes
switch ( scalars->GetDataType() )
{
vtkTemplateMacro(
vtkComputeGradients(estimator,
static_cast<VTK_TT*>(scalars->GetVoidPointer(0)),
thread_id, thread_count)
);
default:
vtkGenericWarningMacro("unable to encode scalar type!");
}
return VTK_THREAD_RETURN_VALUE;
}
// This method is used to compute the encoded normal and the
// magnitude of the gradient for each voxel location in the
// Input.
void vtkFiniteDifferenceGradientEstimator::UpdateNormals( )
{
vtkDebugMacro( << "Updating Normals!" );
this->Threader->SetNumberOfThreads( this->NumberOfThreads );
this->Threader->SetSingleMethod( vtkSwitchOnDataType,
(vtkObject *)this );
this->Threader->SingleMethodExecute();
}
// Print the vtkFiniteDifferenceGradientEstimator
void vtkFiniteDifferenceGradientEstimator::PrintSelf(ostream& os,
vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
os << indent << "Sample spacing in voxels: " <<
this->SampleSpacingInVoxels << endl;
}