vtk9/Rendering/Volume/vtkEncodedGradientEstimator...

336 lines
10 KiB
C++

/*=========================================================================
Program: Visualization Toolkit
Module: vtkEncodedGradientEstimator.cxx
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 "vtkEncodedGradientEstimator.h"
#include "vtkGarbageCollector.h"
#include "vtkImageData.h"
#include "vtkMultiThreader.h"
#include "vtkRecursiveSphereDirectionEncoder.h"
#include "vtkTimerLog.h"
#include <cmath>
vtkCxxSetObjectMacro(vtkEncodedGradientEstimator, InputData, vtkImageData);
// Construct a vtkEncodedGradientEstimator with initial values of nullptr for
// the Input, EncodedNormal, and GradientMagnitude. Also,
// indicate that the IndexTable has not yet been initialized. The
// GradientMagnitudeRange and the GradientMangitudeTable are
// initialized to default values - these will change in the future
// when magnitude of gradient opacities are included
vtkEncodedGradientEstimator::vtkEncodedGradientEstimator()
{
this->InputData = nullptr;
this->EncodedNormals = nullptr;
this->EncodedNormalsSize[0] = 0;
this->EncodedNormalsSize[1] = 0;
this->EncodedNormalsSize[2] = 0;
this->GradientMagnitudes = nullptr;
this->GradientMagnitudeScale = 1.0;
this->GradientMagnitudeBias = 0.0;
this->Threader = vtkMultiThreader::New();
this->NumberOfThreads = this->Threader->GetNumberOfThreads();
this->DirectionEncoder = vtkRecursiveSphereDirectionEncoder::New();
this->ComputeGradientMagnitudes = 1;
this->CylinderClip = 0;
this->CircleLimits = nullptr;
this->CircleLimitsSize = -1;
this->UseCylinderClip = 0;
this->LastUpdateTimeInSeconds = -1.0;
this->LastUpdateTimeInCPUSeconds = -1.0;
this->ZeroNormalThreshold = 0.0;
this->ZeroPad = 1;
this->BoundsClip = 0;
this->Bounds[0] = this->Bounds[1] = this->Bounds[2] = this->Bounds[3] = this->Bounds[4] =
this->Bounds[5] = 0;
}
// Destruct a vtkEncodedGradientEstimator - free up any memory used
vtkEncodedGradientEstimator::~vtkEncodedGradientEstimator()
{
this->SetInputData(nullptr);
this->Threader->Delete();
this->Threader = nullptr;
delete[] this->EncodedNormals;
delete[] this->GradientMagnitudes;
if (this->DirectionEncoder)
{
this->DirectionEncoder->UnRegister(this);
}
delete[] this->CircleLimits;
}
void vtkEncodedGradientEstimator::SetZeroNormalThreshold(float v)
{
if (this->ZeroNormalThreshold != v)
{
if (v < 0.0)
{
vtkErrorMacro(<< "The ZeroNormalThreshold must be a value >= 0.0");
return;
}
this->ZeroNormalThreshold = v;
this->Modified();
}
}
void vtkEncodedGradientEstimator::SetDirectionEncoder(vtkDirectionEncoder* direnc)
{
// If we are setting it to its current value, don't do anything
if (this->DirectionEncoder == direnc)
{
return;
}
// If we already have a direction encoder, unregister it.
if (this->DirectionEncoder)
{
this->DirectionEncoder->UnRegister(this);
this->DirectionEncoder = nullptr;
}
// If we are passing in a non-nullptr encoder, register it
if (direnc)
{
direnc->Register(this);
}
// Actually set the encoder, and consider the object Modified
this->DirectionEncoder = direnc;
this->Modified();
}
int vtkEncodedGradientEstimator::GetEncodedNormalIndex(vtkIdType xyzIndex)
{
this->Update();
return *(this->EncodedNormals + xyzIndex);
}
int vtkEncodedGradientEstimator::GetEncodedNormalIndex(int xIndex, int yIndex, int zIndex)
{
vtkIdType ystep, zstep;
this->Update();
// Compute steps through the volume in x, y, and z
ystep = this->InputSize[0];
zstep = ystep * this->InputSize[1];
return *(this->EncodedNormals + zIndex * zstep + yIndex * ystep + xIndex);
}
unsigned short* vtkEncodedGradientEstimator::GetEncodedNormals()
{
this->Update();
return this->EncodedNormals;
}
unsigned char* vtkEncodedGradientEstimator::GetGradientMagnitudes()
{
this->Update();
return this->GradientMagnitudes;
}
void vtkEncodedGradientEstimator::Update()
{
int scalarInputSize[3];
double scalarInputAspect[3];
double startSeconds, endSeconds;
double startCPUSeconds, endCPUSeconds;
if (!this->InputData)
{
vtkErrorMacro(<< "No input in gradient estimator.");
return;
}
if (this->GetMTime() > this->BuildTime || this->DirectionEncoder->GetMTime() > this->BuildTime ||
this->InputData->GetMTime() > this->BuildTime || !this->EncodedNormals)
{
startSeconds = vtkTimerLog::GetUniversalTime();
startCPUSeconds = vtkTimerLog::GetCPUTime();
// Get the dimensions of the data and its aspect ratio
this->InputData->GetDimensions(scalarInputSize);
this->InputData->GetSpacing(scalarInputAspect);
// If we previously have allocated space for the encoded normals,
// and this space is no longer the right size, delete it
if (this->EncodedNormalsSize[0] != scalarInputSize[0] ||
this->EncodedNormalsSize[1] != scalarInputSize[1] ||
this->EncodedNormalsSize[2] != scalarInputSize[2])
{
delete[] this->EncodedNormals;
this->EncodedNormals = nullptr;
delete[] this->GradientMagnitudes;
this->GradientMagnitudes = nullptr;
}
// Compute the number of encoded voxels
vtkIdType encodedSize = scalarInputSize[0];
encodedSize *= scalarInputSize[1];
encodedSize *= scalarInputSize[2];
// Allocate space for the encoded normals if necessary
if (!this->EncodedNormals)
{
this->EncodedNormals = new unsigned short[encodedSize];
this->EncodedNormalsSize[0] = scalarInputSize[0];
this->EncodedNormalsSize[1] = scalarInputSize[1];
this->EncodedNormalsSize[2] = scalarInputSize[2];
}
if (!this->GradientMagnitudes && this->ComputeGradientMagnitudes)
{
this->GradientMagnitudes = new unsigned char[encodedSize];
}
// Copy info that multi threaded function will need into temp variables
memcpy(this->InputSize, scalarInputSize, 3 * sizeof(int));
// TODO cleanup when double changes are further along
this->InputAspect[0] = static_cast<float>(scalarInputAspect[0]);
this->InputAspect[1] = static_cast<float>(scalarInputAspect[1]);
this->InputAspect[2] = static_cast<float>(scalarInputAspect[2]);
// memcpy( this->InputAspect, scalarInputAspect, 3 * sizeof(float) );
if (this->CylinderClip && (this->InputSize[0] == this->InputSize[1]))
{
this->UseCylinderClip = 1;
this->ComputeCircleLimits(this->InputSize[0]);
}
else
{
this->UseCylinderClip = 0;
}
this->UpdateNormals();
this->BuildTime.Modified();
endSeconds = vtkTimerLog::GetUniversalTime();
endCPUSeconds = vtkTimerLog::GetCPUTime();
this->LastUpdateTimeInSeconds = static_cast<float>(endSeconds - startSeconds);
this->LastUpdateTimeInCPUSeconds = static_cast<float>(endCPUSeconds - startCPUSeconds);
}
}
void vtkEncodedGradientEstimator::ComputeCircleLimits(int size)
{
int *ptr, y;
double w, halfsize, length, start, end;
if (this->CircleLimitsSize != size)
{
delete[] this->CircleLimits;
this->CircleLimits = new int[2 * size];
this->CircleLimitsSize = size;
}
ptr = this->CircleLimits;
halfsize = (size - 1) / 2.0;
for (y = 0; y < size; y++)
{
w = halfsize - y;
length = static_cast<int>(sqrt((halfsize * halfsize) - (w * w)) + 0.5);
start = halfsize - length - 1;
end = halfsize + length + 1;
start = (start < 0) ? (0) : (start);
end = (end > (size - 1)) ? (size - 1) : (end);
*(ptr++) = static_cast<int>(start);
*(ptr++) = static_cast<int>(end);
}
}
// Print the vtkEncodedGradientEstimator
void vtkEncodedGradientEstimator::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
if (this->InputData)
{
os << indent << "InputData: (" << this->InputData << ")\n";
}
else
{
os << indent << "Input: (none)\n";
}
if (this->DirectionEncoder)
{
os << indent << "DirectionEncoder: (" << this->DirectionEncoder << ")\n";
}
else
{
os << indent << "DirectionEncoder: (none)\n";
}
os << indent << "Build Time: " << this->BuildTime.GetMTime() << endl;
os << indent << "Gradient Magnitude Scale: " << this->GradientMagnitudeScale << endl;
os << indent << "Gradient Magnitude Bias: " << this->GradientMagnitudeBias << endl;
os << indent << "Zero Pad: " << ((this->ZeroPad) ? "On" : "Off") << endl;
os << indent << "Bounds Clip: " << ((this->BoundsClip) ? "On" : "Off") << endl;
os << indent << "Bounds: (" << this->Bounds[0] << ", " << this->Bounds[1] << ", "
<< this->Bounds[2] << ", " << this->Bounds[3] << ", " << this->Bounds[4] << ", "
<< this->Bounds[5] << ")\n";
os << indent << "Zero Normal Threshold: " << this->ZeroNormalThreshold << endl;
os << indent
<< "Compute Gradient Magnitudes: " << ((this->ComputeGradientMagnitudes) ? "On" : "Off")
<< endl;
os << indent << "Cylinder Clip: " << ((this->CylinderClip) ? "On" : "Off") << endl;
os << indent << "Number Of Threads: " << this->NumberOfThreads << endl;
os << indent << "Last Update Time In Seconds: " << this->LastUpdateTimeInSeconds << endl;
os << indent << "Last Update Time In CPU Seconds: " << this->LastUpdateTimeInCPUSeconds << endl;
// I don't want to print out these variables - they are
// internal and the get methods are included only for access
// within the threaded function
// os << indent << "Use Cylinder Clip: "
// << this->UseCylinderClip << endl;
// os << indent << " Input Size: "
// << this->InputSize << endl;
// os << indent << " Input Aspect Clip: "
// << this->InputAspect << endl;
}
//------------------------------------------------------------------------------
void vtkEncodedGradientEstimator::ReportReferences(vtkGarbageCollector* collector)
{
this->Superclass::ReportReferences(collector);
vtkGarbageCollectorReport(collector, this->InputData, "Input");
}