mirror of https://gitee.com/openkylin/vtk9.git
336 lines
10 KiB
C++
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");
|
|
}
|