/*========================================================================= 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 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(scalarInputAspect[0]); this->InputAspect[1] = static_cast(scalarInputAspect[1]); this->InputAspect[2] = static_cast(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(endSeconds - startSeconds); this->LastUpdateTimeInCPUSeconds = static_cast(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(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(start); *(ptr++) = static_cast(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"); }